PHP中的命名空间

Note:
名为PHP或php的命名空间,以及以这些名字开头的命名空间(例如PHP\Classes)被保留用作语言内核使用,而不应该在用户空间的代码中使用

定义命名空间

定义: 通过namespace关键字声明

子名命名空间:与目录和文件的关系很像,PHP名命空间允许指定层次化的命名空间名称,这种使用分层次的方式定义的命名空间被称为子命名空间。

//定义命名空间
<?php
//declare(encoding='UTF-8');    //  namespace定义前面唯一合法语句,不是必须
namespace MyProject;
……
?>

//定义子命名空间
<?php
namespace MyProject\Sub\Level;
……
?>

说明:

  1. 任意合法PHP代码都可以包含在命名空间中,但只有以下类型的代码受命名空间的影响:类(包括抽象类和traits)接口函数常量
  2. 同一个命名空间可以定义在多个文件中,即允许将同一个命名空间的内容分割存放在不同的文件中。

注意事项: 如果一个文件中包含命名空间,它必须在所有代码之前声明命名空间,在声明命名空间之前唯一合法的代码是用于定义源文件编码方式的declare语句,另外,所有非PHP代码包括空白符都不能出现在命名空间的声明之前。

在同一个文件中定义多个名命空间(不提倡,这种方式主要用于将多个PHP脚本合并在同一个文件中 )

定义方式有两种:

  • 简单组合语法
<?php
namespace MyProject;
代码段……

namespace AnotherProject;
代码段……
?>
  • 大括号语法
<?php
namespace MyProject{
    代码段……
}

namespace AnotherProject{
    代码段……
}
?>

如果是将全局的非命名空间中的代码与名命空间中的代码组合在一起,则只能使用大括号形式的语法。全局代码必须用一个不带名称的namespace语句加上大括号括起来:

<?php
namespace MyProject{
    代码段……
}

namespace{      //全局空间
    代码段……
}
?>

注意: 除了开始的declare语句外,命名空间的括号外不得有任何PHP代码。

使用名命空间:基础

(PHP 5 >= 5.3.0, PHP 7)

在讨论如何使用命名空间之前,必须了解 PHP 是如何知道要使用哪一个命名空间中的元素的。可以将 PHP 命名空间与文件系统作一个简单的类比。

在文件系统中访问一个文件有三种方式:
  1. 相对文件名形式,如foo.txt。它会被解析为 currentdirectory/foo.txt,其中 currentdirectory 表示当前目录。因此如果当前目录是 /home/foo,则该文件名被解析为/home/foo/foo.txt。
  2. 相对路径名形式如subdirectory/foo.txt。它会被解析为 currentdirectory/subdirectory/foo.txt。
  3. 绝对路径名形式如/main/foo.txt。它会被解析为/main/foo.txt。
PHP 命名空间中的元素使用同样的原理。例如,类名可以通过三种方式引用:
  1. 非限定名称,或不包含前缀的类名称,例如 $a=new foo(); 或 foo::staticmethod();。如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为foo。

备注: 如果命名空间中的函数或常量未定义,则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称。即:如果在当前命名空间中没有找到函数或常量,则接着会尝试去全局空间中找。如果仍未找到,则此时会报错。,如果类没有在当前命名空间中定义,则不会去全局空间找。详情参见 使用命名空间:后备全局函数名称/常量名称一节。

  1. 限定名称,或包含前缀的名称,例如 $a = new subnamespace\foo(); 或 subnamespace\foo::staticmethod();。如果当前的命名空间是 currentnamespace,则 foo 会被解析为 currentnamespace\subnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,foo 会被解析为subnamespace\foo。
  1. 完全限定名称,或包含了全局前缀操作符的名称,例如, $a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();。在这种情况下,foo 总是被解析为代码中的文字名(literal name)currentnamespace\foo。

示例:

file1.php

<?php
namespace Foo\Bar\subnamespace;
const FOO=1;
function foo(){}
class foo{
    static function staticmethod(){}
}
?>

file2.php

<?php
namespace Foo\Bar;
include 'file1.php';

const FOO = 2;
function foo() {}
class foo
{
    static function staticmethod() {}
}

/* 非限定名称 */
foo();                  // 解析为 Foo\Bar\foo 
foo::staticmethod();    // 解析为类 Foo\Bar\foo的静态方法staticmethod。
echo FOO;               // 解析为 Foo\Bar\FOO

/* 限定名称 */
subnamespace\foo();                 // 解析为函数 Foo\Bar\subnamespace\foo
subnamespace\foo::staticmethod();   // 解析为类 Foo\Bar\subnamespace\foo,以及类的方法 staticmethod
echo subnamespace\FOO;              // 解析为常量 Foo\Bar\subnamespace\FOO
                                  
/* 完全限定名称 */
\Foo\Bar\foo();                      // 解析为函数 Foo\Bar\foo
\Foo\Bar\foo::staticmethod();       // 解析为类 Foo\Bar\foo, 以及类的方法 staticmethod
echo \Foo\Bar\FOO;                  // 解析为常量 Foo\Bar\FOO
?>

注意: 访问任意全局类、函数或常量,都可以使用完全限定名称,例如:\strlen()\Exception\INI_ALL

命名空间和动态语言特征

说明: 名命空间的实现会受到其语言自身的动态特征的影响。在使用动态的类名称、函数名称、常量名称时,需要使用【完全】限定名称。

注意: 因为在动态的类名称、函数名称、常量名称中,限定名称和完全限定名称没有区别,因此,其前导的反斜杠是不必要的。

<?php

namespace{
    class classname{
        function __construct(){
            echo __METHOD__,"<br>";
        }
    }

    function funcname(){
        echo __FUNCTION__,"<br>";
    }

    const constname = "global";

    $a = 'classname';
    $obj = new $a;                          //打印 classname::__construct
    $b = 'funcname';
    $b(); // prints funcname
    echo constant('constname'), "<br>";     //打印 prints global
    }


namespace namespacename{
    class classname{
        function __construct(){
            echo __METHOD__,"<br>";
        }
    }

    function funcname(){
        echo __FUNCTION__,"<br>";
    }

    const constname = "namespaced";

    $a = 'classname';
    $obj = new $a;                          //打印 classname::__construct
    $b = 'funcname';
    $b(); // prints funcname
    echo constant('constname'), "<br>";     //打印 global
    
    echo '<hr>';
    
    $a = '\namespacename\classname';
    $obj = new $a;      //打印 namespacename\classname::__construct
    $a = 'namespacename\classname';
    $obj = new $a;      //打印 namespacename\classname::__construct
    $b = 'namespacename\funcname';
    $b();               //打印 namespacename\funcname
    $b = '\namespacename\funcname';
    $b();               //打印 namespacename\funcname
    echo constant('\namespacename\constname'), "<br>";  // 打印 namespaced
    echo constant('namespacename\constname'), "<br>";   // 打印 namespaced
}
?>

namespace关键字和__NAMESPACE__常量

PHP支持两种抽象的访问当前命名空间内部的方法:

1. __NAMESPACE__魔术常量

返回值: 包含当前名命空间名称的字符串。在全局的,不包括在任何命名空间中的代码,它返回一个空字符串

该常量在动态创建类时很有用:

    <?php
    namespace MyProject;
    function get($classname){
        $a=__NAMESPACE__.'\\'.$classname;
        return new $a;
    }
    ?>

2. namespace关键字

作用: 用来显式访问当前名命空间或子名命空间中的元素。它等价于类中的self操作符

名命空间中的代码

    <?php
    namespace MyProject;
    use blah\blah as mine;
    blah\mine();    //Myproject\blah\mine()
    namespace\blah\mine();  //MyProject\blah\mine
    
    namespace\func();       //MyProject\func()
    namespace\sub\func();   //MyProject\sub\func()
    namaspace\cname::method();  MyProject\cname::method()
    $a=new namespace\sub\cname();   //MyProject\sub\cname
    $b=namespace\CONSTANT;  //MyProject\CONSTANT
    ?>

全局代码

<?php
namespace\func();               //func()
namespace\sub\func();           //sub\func()
namespace\cname::method();      //cname::method()
$a=new namespace\sub\cname();   //sub\cname
$b=namespace\CONSTANT;          //CONSTANT

使用命名空间:别名/导入

PHP允许通过别名应用或导入外部的完全限定名称。

所有支持名命空间的PHP版本都支持三种别名或导入方式:

  • 为类名称使用别名
  • 为接口使用别名
  • 为名命空间使用别名

PHP5.6开始允许导入函数常量或者为它们设置别名

导入关键字:use

  • 为了简化操作,PHP支持在一行中使用多个use语句,中间用‘,’隔开。

特别说明: 对命名空间中的名称(包含命名空间分隔符的完全限定名称)来说,前导的反斜杠是不必要的也是不推荐的,因为导入的名称必须是完全限定的,不会根据当前的名命空间作相对解析。

以下说的意思算是对上面内容的解释吧:

  1. 导入操作是在编译执行的,但动态的类名称、函数名称或常量名称则不是。(大概是说,导入是在编译期执行,而实际代码中的动态名称是在执行时确定)

  2. 导入操作只影响非限定名称和限定名称。完全限定名称由于是确定的,故不受导入的影响。(大概是说,use 语句引用的时候都是完全限定名称,而实际代码中使用过的时候要注意非限定名称、限定名称和完全限定名称的使用)

两个示例:

导入和动态名称:

<?php
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // 实例化一个 My\Full\Classname 对象
$a = 'Another';
$obj = new $a;      // 实际化一个 Another 对象
?>

导入和完全限定名称

<?php
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // instantiates object of class My\Full\Classname
$obj = new \Another; // instantiates object of class Another
$obj = new Another\thing; // instantiates object of class My\Full\Classname\thing
$obj = new \Another\thing; // instantiates object of class Another\thing
?>

使用命名空间:后备全局函数/常量

重要: 在一个命名空间中,当 PHP 遇到一个非限定的类、函数或常量名称时,它使用不同的优先策略来解析该名称。类名称总是解析到当前命名空间中的名称。因此在访问系统内部或不包含在命名空间中的类名称时,必须使用完全限定名称。
对于函数和常量来说,如果当前命名空间中不存在该函数或常量,PHP 会退而使用全局空间中的函数或常量

posted @ 2019-05-24 14:33  Black_Kitty  阅读(1246)  评论(0编辑  收藏  举报