PHP中的命名空间详细介绍 |
本文标签:PHP,命名空间 概述 PHP对于命名空间的支持,经历了一段艰难的旅程 。幸运的是,PHP从5.3开始引入了命名空间 。自从PHP引入了命名空间,PHP代码的适用结构也得到了大大的改善 。许多编程语言早就有了命名空间的概念,相对于其他语言来说,PHP对于命名空间的支持,稍微有点晚了 。不管如何,每一种新特性的引入都有其目的,和其他语言一样,PHP引入命名空间也主要是为了解决名字冲突的问题 。 命名空间(namespace)的概念 复制代码 代码如下: 当在字符串中使用命名空间名字的时候,一定不要忘了转义\ 可以将命名空间想象成一个抽屉,你可以在抽屉里放入铅笔、尺子、A4纸等,这些都是你自己的私有物品 。在你的抽屉下面是别人的抽屉,别人也可以在抽屉里放入相同的物品 。为了不拿错物品,你们决定在自己的抽屉上贴上标签,这样就可以清晰的看到某个物品是属于谁的了 。 之前,开发者必须在类、函数和常量中添加下划线,用来使自己的代码独立其他于代码库 。这相当于所有人都给自己的物品贴上标签之后,一起放入了一个更大的抽屉里 。尽管这也是一种组织代码的方式,但是这种方式是非常低效的 。 正在使用命名空间 有一点需要注意的是,我们正在间接的使用命名空间 。从PHP 5.3开始,所有在非用户定义的命名空间中的声明(类、函数、常量),都默认的属于全局命名空间 。 定义命名空间 命名空间的定义必须是PHP文件的第一条语句 。唯一允许在定义命名空间之前使用的语句是declare语句 。 复制代码 代码如下: namespace MyNamespace{ class Test{ } } 如果想定义一个属于全局空间的代码块,也是使用namespace关键字,但是后面不加命名空间的名字,如下: 复制代码 代码如下: namespace { class Test{ } } 我们甚至可以在一个文件中定义多个命名空间,如下: 复制代码 代码如下: <?php namespace MyNamespace { } namespace MySecondNamespace { } namespace { } 我们也可以将一个命名空间分散在不同的文件中,文件包含的处理程序会自动合并他们 。因此,限制大量的命名空间在同一个文件中定义是一个很好的编程实践,就像我们通常单独为每个类定义一个单独的文件一样 。 复制代码 代码如下: 有一点需要注意的是,包含命名空间代码块的{是可选的,可以用也可以不用 。事实上,只要我们坚持在一个文件中只定义一个命名空间,那么我们就可以完全省略{,这样也可以使我们的代码看起来更加简洁 。 子命名空间 命名空间可以遵循一个特定的层级,就像我们电脑文件系统中得目录一样 。子命名空间对于将一个项目结构化尤其特别有用 。例如,你的项目需要访问数据库,你可能会想将所有数据库相关的代码(如数据库异常处理等)放在同一个子目录下 。 为了保持灵活性,将子命名空间放在子目录中是非常明智的做法 。这会使你的代码结构更清晰,而且会使遵循PSR-0标准的autoloaders的使用变得更容易 。 PHP使用反斜线\作为命名空间的分隔符,有趣的是,PHP甚至考虑过使用笑脸:)作为命名空间的分隔符 。 子命名空间定义示例: 复制代码 代码如下: <?php namespace MyProject\Database class Connection { } 可以使用尽可能多的子命名空间: 复制代码 代码如下: <?php namespace MyProject\Blog\Auth\Handler\Social; class Twitter { } 有一点需要注意的是,PHP并不支持命名空间的嵌套定义,下面的代码会导致一个致命错误:Namespace declarations cannot be nested 。 复制代码 代码如下: <?php namespace MyProject { namespace Database { class Connection { } } } 从命名空间中调用代码 如果你想在不同的命名空间中实例化一个类、调用一个函数或者使用常量,需要使用反斜线\ 。他们可以从三个角度被解析: 1.未限定的名字 未限定的名字(Unqualified Name) 这是一个类的名称,函数或常量,但是不包括任何命名的引用 。如果命名空间对你来说还比较陌生,那么这就是你熟悉的角度 。 复制代码 代码如下: <?php namespace MyProject; class MyClass { static function static_method() { echo Hello, world!; } } // Unqualified name, resolves to the namespace you are currently in (MyProject\MyClass) MyClass:static_method(); 限定的名字(Qualified Name) 这是我们如何使用子命名空间的方式 。示例如下: 复制代码 代码如下: <?php namespace MyProject; require myproject/database/connection.php; // Qualified name, instantiating a class from a sub-namespace of MyProject $connection = new Database\Connection(); 完全限定的名字(Fully Qualified Name) 前面所说的使用限定的名字和未限定的名字,都是相对于当前所处的命名空间来说的 。以上两种方式仅可以被用来访问当前所处的命名空间和更深层次的子命名空间 。 如果想访问一个在比前命名空间更高的层级,那么就需要使用完全限定的名字—一个绝对路径而不是相对路径 。这可以归结为在命名空间的最前面加反斜杠\ 。使用完全限定的名字可以让PHP知道,这次调用是从全局空间开始的,而不是相对于当前所处的命名空间 。示例如下: 复制代码 代码如下: <?php namespace MyProject\Database; require myproject/fileaccess/input.php; // Trying to access the MyProject\FileAccess\Input class // This time it will work because we use the fully qualified name, note the leading backslash $input = new \MyProject\FileAccess\Input(); 对于PHP的内部函数来说,我们不必要使用完全限定的名字 。在当前所处的命名空间中,调用一个不存在的未限定的名字的类或函数,PHP会搜索全局空间 。 记住了这个规则,我们就可以像下面那样重写PHP的内部函数: 复制代码 代码如下: <?php 动态调用 复制代码 代码如下: <?php namespace OtherProject; $project_name = MyProject; $package_name = Database; $class_name = Connection; // Include a variable file require strtolower($project_name . /. $package_name . / . $class_name) . .php; // Name of a variable class in a variable namespace. Note how the backslash is escaped to use it properly $fully_qualified_name = $project_name . \\ . $package_name . \\ . $class_name; $connection = new $fully_qualified_name(); namespace关键字 复制代码 代码如下: <?php namespace MyProject; function run() { echo Running from a namespace!; } // Resolves to MyProject\run run(); // Explicitly resolves to MyProject\run namespace\run(); __NAMESPACE__常量 复制代码 代码如下: <?php namespace MyProject\Database; // MyProject\Database echo __NAMESPACE__; 这个关键字对于判断当前代码是否从命名空间开始时非常有用,而且也可以用来调试代码 。 导入或别名 复制代码 代码如下: use [name of class, interface or namespace] as [optional_custom_alias] 一个完全限定的名字可以用一个未限定的别名来代替,这样我们就不用在每次使用的时候都使用完全限定的名字,达到简化代码的目的 。导入应该在命名空间的最高层或者全局空间中使用,在函数作用域内使用导入功能是非法的语法 。 复制代码 代码如下: <?php namespace OtherProject; // This holds the MyProject\Database namespace with a Connection class in it require myproject/database/connection.php; // If we want to access the database connection of MyProject, we need to use its fully qualified name as were in a different name space $connection = new \MyProject\Database\Connection(); // Import the Connection class (it works exactly the same with interfaces) use MyProject\Database\Connection; // Now this works too! Before the Connection class was aliased PHP would not have found an OtherProject\Connection class $connection = new Connection(); // Import the MyProject\Database namespace use MyProject\Database; $connection = new Database\Connection() 我们可以通过使用别名来简化上面的代码: 复制代码 代码如下: <?php namespace OtherProject; require myproject/database/connection.php; use MyProject\Database\Connection as MyConnection; $connection = new MyConnection(); use MyProject\Database as MyDatabase; $connection = new MyDatabase\Connection(); 总结 命名空间是用来避免定义冲突,并且为代码引入了更加灵活和组织的方式 。有一点需要注意的时,我们并没有义务去使用命名空间,它是和面向对象结合使用的一种工作方式 。但是,如果使用了命名空间,我们的代码可能会达到一种新的层次,逼格也会显得更高吧 。 |