PHP 命名空间把类、函数和常量放在一个前缀之下,这样它们的名字就不会和别人的冲突。在文件的第一条语句写 namespace App\Models; 来声明它,其他文件可以用 \App\Models\User 引用这个类,或者用 use App\Models\User; 导入后直接写 User。
在命名空间内部,User 指的是 App\Models\User。User::class 以字符串形式给出完整名称,而不会创建任何东西,这在记录日志和编写自动加载器时很方便。
一个文件中的两个命名空间
实际项目中每个文件放一个命名空间。不过为了做一个独立的演示,如果每个命名空间都用花括号包起来,一个文件也可以包含多个命名空间。不属于任何命名空间的代码放在 namespace { } 中:
两个都叫 Order 的类可以并存,因为它们的完整名称不同。安装第三方库之后,命名空间解决的正是这个问题:你的 Order、支付库的 Order 和 PDF 库的 Order 不会再冲突。
用 use 导入,用 as 起别名
到处都写 \Shop\Order 太啰嗦了。在文件(或命名空间块)顶部用 use 导入一个名称,就可以使用简短的形式。当两个导入的短名称相同时,可以用 as 给其中一个换个名字:
use 只是为文件的其余部分创建一个短名称。它不会加载文件,也不会运行代码,无论这个类是否已经存在,效果都一样。也可以分组导入:use Shop\{Order, Invoice};。
use function 和 use const
命名空间中的函数和常量用 use function 和 use const 导入:
全局类需要加反斜杠
在命名空间内部,不带限定的类名会相对于该命名空间解析,所以在 namespace App; 中写 new DateTime() 会去找 App\DateTime。函数和常量则不同:如果 App\strlen 不存在,PHP 会回退到全局的 strlen。类永远不会回退。
catch (\Error $e) 需要反斜杠也是同样的原因:没有它,PHP 会去找 App\Error,永远匹配不上真正的错误,脚本会因为未捕获的 Error 而停止。修复方法有两种:像上面那样在开头加反斜杠,或者在文件顶部写 use DateTime;。\Exception、\Throwable 以及其他所有内置类也是如此;关于如何捕获它们,请参阅 try catch。
自动加载与 PSR-4
在项目中,每个类都放在自己的文件里,文件路径与命名空间相对应。PSR-4 就是这样的约定:命名空间前缀对应一个基础文件夹,名称的其余部分就成为路径。然后,用 spl_autoload_register() 注册的自动加载器会在类第一次被使用时加载它的文件。这段代码会把两个类文件写入磁盘,并自动加载它们:
每个类文件只在第一次需要这个类时加载一次。在实际项目中,你不需要自己编写这个函数:Composer 会根据 composer.json 中的几行配置生成它:
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
运行 composer dump-autoload 之后,只要在入口脚本开头写一行 require __DIR__ . '/vendor/autoload.php';,就会按需加载每个 App\... 类。
会导致错误的命名空间规则
namespace必须是文件中的第一条语句。只有declare(strict_types=1);和注释可以写在它前面。use导入只对单个文件有效。在index.php中导入一个类,并不能让被引入的文件也使用这个短名称;每个文件都需要自己的use语句。use应该写在文件或命名空间块的顶层。在类的内部,use的含义完全不同:它用来引入一个 trait。- 字符串永远不会被解析:
$class = 'User'; new $class();会去找全局的User。请保存完整名称,最好使用User::class。
最后一条值得快速验证一下:
常见问题
PHP 中的命名空间是什么?
一个把相关的类、接口、函数和常量归在一起的前缀,就像存放名字的文件夹。在文件顶部写 namespace App\Models;,其中的 User 类就位于 App\Models\User,因此不会和其他库的 User 冲突。
PHP 中 use 有什么作用?
在文件顶部写 use App\Models\User;,就能在这个文件中用 User 代替完整名称 \App\Models\User。它不会加载或引入任何东西;加载类文件是自动加载器的工作。use ... as Alias; 可以选用另一个短名称。
为什么在命名空间里会出现 Class not found?
在 namespace App; 中,不带前缀的类名会相对于当前命名空间解析,所以 new DateTime() 会去找 App\DateTime。改写成 new \DateTime(),或者在顶部加上 use DateTime;。函数和常量会自动回退到全局命名空间,类则不会。
PHP 中类名前面的反斜杠是什么意思?
开头的反斜杠让名称成为从全局命名空间开始的完全限定名称:无论代码位于哪个命名空间,\Exception 始终是 PHP 内置的 Exception。没有它时,名称会根据当前命名空间和 use 导入来解析。
命名空间和文件夹、PSR-4 有什么关系?
PHP 本身并不把命名空间和文件夹绑定在一起。PSR-4 是建立这种对应关系的约定:像 App\ 这样的前缀对应像 src/ 这样的目录,所以 App\Models\User 位于 src/Models/User.php。Composer 的自动加载器遵循这一约定,所以两者通常是一致的。