PSR4是一种自动加载规范,老版本是PSR0,尽管thinkPHP支持PSR4和PSR0的自动加载方式,但是默认也是优先进行PSR4加载,如果失败,再进行PSR0的加载。本篇文章只会讨论PSR4的加载。
代码示例:
spl_autoload_register(function ($class) { // 指定项目的命名空间前缀 $prefix = 'Foo\\Bar\\'; // 指定项目的根目录 $base_dir = __DIR__ . '/src/'; // 判断类的前缀是不是上面的命名空间前缀 $len = strlen($prefix); if (strncmp($prefix, $class, $len) !== 0) { // 不是,则交给其他类加载器去加载,不使用PSR4 return; } // 获取相对的命名空间 $relative_class = substr($class, $len); // replace the namespace prefix with the base directory, replace namespace // separators with directory separators in the relative class name, append // with .php // 使用根目录替换命名空间前缀,使用目录分隔符替换相对类名中的命名空间分隔符,然后再加上.php $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php'; // 如果该类存在,则require它 if (file_exists($file)) { require $file; } });
从上面的代码中,我们就可以知道PSR4到底是干什么的。
这个规范主要就是将类转换成物理路径。
- 命名空间前缀使用根目录替换
- 去掉命名空间前缀剩下的部分,叫做它相对类名
- 使用目录分隔符替换掉相对类名中的命名空间分隔符(也就是\变成/)
- 加上.php
比如thinkphp典型的一个控制器:Index.php
<?php
namespace app\index\controller;
class Index{ }
那么在加载的时候,传给这个PSR4的时候,$class=app\index\controller\Index;
假设当前的根目录为:E:\app
当前的命名空间前缀为:app
根据前面的转换步骤,这个类的路径应该是在:E:/app/index/controller/Index.php
路径的斜杆(/)跟反斜杆(\)。在windows中通常是使用反斜杆作为路径分隔符的,而在linux中,通过是使用斜杆作为路径分隔符的。不过windows现在的API都支持使用斜杆作为路径分隔符了,而如果使用反斜杆的话,那么在单引号中,还需要写成\才能当成\。而linux中是使用/,因此我们的路径表示便是使用斜杆了。
有同学跟我反映说,php单引号不是不会被转义吗?这个确实是,不过对于要输出单引号,因此在单引号字符串中,单引号是需要使用反斜杆转义才能输出的,而反斜杆本身也是需要使用反斜杆来转义,对于其他的转义,就真的不会了。
thinkphp5的类加载
类库映射实际上就是保存一个键值对数组,键值为类名,值为物理路径,这样当类加载的时候,直接查找该数组就行。
而对于PSR4,我们根据上面知道,需要注册根目录跟命名空间前缀。
在thinkphp中注册了五个命名空间前缀:
self::addNamespace([ 'think' => LIB_PATH . 'think' . DS, 'behavior' => LIB_PATH . 'behavior' . DS, 'traits' => LIB_PATH . 'traits' . DS, ]);
还有两个是可以配置的:
self::$namespace = $config['app_namespace']; Loader::addNamespace($config['app_namespace'], APP_PATH); if (!empty($config['root_namespace'])) { Loader::addNamespace($config['root_namespace']); }
因此在配置文件中的app_namespace就是指明应用的命名空间前缀。比如默认为app对应着跟目录为application。
PSR4的备选目录
当使用PSR4方式读取失败的话,那么还有备选的目录可以用来读取,thinkphp便是使用这种方式作为扩展目录的读取:
// 查找 PSR-4 fallback dirs
foreach (self::$fallbackDirsPsr4 as $dir) { if (is_file($file = $dir . DS . $logicalPathPsr4)) { return $file; } }
$logicalPathPsr4
就是这事将命名空间分隔符转换为目录分隔符再加上.php
而已。
不过,通常来说,开源的扩展应该使用composer开发。而只有私人的扩展,一般才放在extend目录下的。
为什么TP不完全使用composer的加载器呢?
一是完全基于composer的话,那么当composer更新的时候,框架也需要相对应地调整,因此不能够完全地专注于框架的开发。二如果完全抛弃composer而专注于框架,又像是捡了芝麻丢了西瓜。因此将composer作为框架的延伸扩展,无疑是一种比较好的折中方案。
原文链接:https://www.kancloud.cn/code7/tp5/343057