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