PHP中的命名空间详细介绍


概述

PHP对于命名空间的支持,经历了一段艰难的旅程。幸运的是,PHP从5.3开始引入了命名空间。自从PHP引入了命名空间,PHP代码的适用结构也得到了大大的改善。许多编程语言早就有了命名空间的概念,相对于其他语言来说,PHP对于命名空间的支持,稍微有点晚了。不管如何,每一种新特性的引入都有其目的,和其他语言一样,PHP引入命名空间也主要是为了解决名字冲突的问题。

命名空间(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.未限定的名字
2.限定的名字
3.完全限定的名字

未限定的名字(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
namespace MyProject;
 
var_dump($query); // Overloaded
\var_dump($query); // Internal
 
// We want to access the global Exception class
// The following will not work because there's no class called Exception in the MyProject\Database namespace and unqualified class names do not have a fallback to global space
// throw new Exception('Query failed!');
 
// Instead, we use a single backslash to indicate we want to resolve from global space
throw new \Exception('ailed!');
 
function var_dump() {
    echo 'Overloaded global var_dump()!<br />';
}

动态调用
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关键字
关键字namespace不仅仅可以用来定义一个命名空间,它也可以用来显示的表示当前命名空间,它此时的作用相当于类中的self关键字。

复制代码 代码如下:

<?php
namespace MyProject;
 
function run()
{
    echo 'Running from a namespace!';
}
 
// Resolves to MyProject\run
run();
// Explicitly resolves to MyProject\run
namespace\run();

__NAMESPACE__常量
就像self关键字不能表示当前类的名字一样,namespace关键字也不能用来表示当前命名空间的名字。__NAMESPACE__关键字就是用来解决这个问题的。

复制代码 代码如下:

<?php
namespace MyProject\Database;
 
// 'MyProject\Database'
echo __NAMESPACE__;

这个关键字对于判断当前代码是否从命名空间开始时非常有用,而且也可以用来调试代码。

导入或别名
PHP中得命名空间也支持导入,导入也被成为别名。只有类、接口和命名空间可以被导入(别名)。导入是命名空间中一个非常有用和基础的功能。它使我们可以使用外部的代码包,而不用担心名字的冲突。使用use关键字可以实现导入功能。也可以使用as关键字,在导入的时候指定一个别名。

复制代码 代码如下:

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 we're 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();

总结

命名空间是用来避免定义冲突,并且为代码引入了更加灵活和组织的方式。有一点需要注意的时,我们并没有义务去使用命名空间,它是和面向对象结合使用的一种工作方式。但是,如果使用了命名空间,我们的代码可能会达到一种新的层次,逼格也会显得更高吧。


« 
» 

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3