PHP-FIG,它的網站是:www.php-fig.org。就是這個聯盟組織發明和創造了PSR規範,其中自動加載涉及其中兩個規範,一個是PSR0,一個是PSR4, PSR0規範已通過時了,官方有提示,如今主要是用PSR4規範定義自動加載標準。php
這個 PSR 描述的是經過文件路徑自動載入類的指南;它做爲對 PSR-0 的補充;根據這個 指導如何規範存放文件來自動載入;
術語「類」是一個泛稱;它包含類,接口,traits 以及其餘相似的結構;web
徹底限定類名應該相似以下範例:數組
( app)*
徹底限定類名必須有一個頂級命名空間(Vendor Name);
徹底限定類名能夠有多個子命名空間;
徹底限定類名應該有一個終止類名;
下劃線在徹底限定類名中是沒有特殊含義的;
字母在徹底限定類名中能夠是任何大小寫的組合;
全部類名必須以大小寫敏感的方式引用;
當從徹底限定類名載入文件時:函數
在徹底限定類名中,連續的一個或幾個子命名空間構成的命名空間前綴(不包括頂級命名空間的分隔符),至少對應着至少一個基礎目錄。
在「命名空間前綴」後的連續子命名空間名稱對應一個「基礎目錄」下的子目錄,其中的命名 空間分隔符表示目錄分隔符。子目錄名稱必須和子命名空間名大小寫匹配;
終止類名對應一個以 .php 結尾的文件。文件名必須和終止類名大小寫匹配;
自動載入器的實現不可拋出任何異常,不可引起任何等級的錯誤;也不該返回值;優化
徹底限定類名 | 命名空間前綴 | 基礎路徑 | 徹底路徑 |
---|---|---|---|
\Acme\Log\Writer\File_Writer | Acme\Log\Write | ./acme-log-writer/lib/ | ./acme-log-writer/lib/File_Writer.php |
\Aura\Web\Response\Status | Aura\Web | /path/to/aura-web/src/ | /path/to/aura-web/src/Response/Status.php |
\Symfony\Core\Request | Symfony\Core | ./vendor/Symfony/Core/ | ./vendor/Symfony/Core/Request.php |
\Zend\Acl | Zend | /usr/includes/Zend/ | /usr/includes/Zend/Acl.php |
你們注意看第二列和第四列,命名空間前綴對應基礎路徑,命名空間前綴以後的子命名空間必須對應代碼目錄(類名必須是PHP文件)網站
上一節中封裝自動加載的方法比較簡單,沒法自動加載帶命名空間的類ui
spl_autoload_register(function ($class) { // 命名空間前綴 $prefix = 'Foo\\Bar\\'; // 命名空間前綴對應的基礎目錄 $base_dir = __DIR__ . '/src/'; // 檢查new的類是否有命名空間前綴 $len = strlen($prefix); if (strncmp($prefix, $class, $len) !== 0) { return; } // 獲取去掉命名空間前綴後的類名 $relative_class = substr($class, $len); // 將命名空間的中的分隔符替換爲目錄分隔符,再加上基礎目錄和.php後綴,最終拼接成 // 文件路徑 $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php'; // 若是文件存在則require if (file_exists($file)) { require $file; } });
可是上面的方法只能適用固定的命名空間前綴,不能通用。this
<?php namespace Example; /** * 下面這個例子實現了一個命名空間前綴對應多個基礎目錄 * * 如今咱們的目錄結構是下面這樣: * * /demo/autoload/ * controller/ * DemoController.php # Foo\Bar\DemoController * Admin/ * AdminController.php # Foo\Bar\Admin\AdminController * model/ * DemoModel.php # Foo\Bar\DemoModel * Admin/ * AdminModel.php # Foo\Bar\Admin\AdminModel * * Foo\Bar分別對應基礎路徑 /demo/autoload/controller 和 /demo/autoload/model */ class Psr4AutoloaderClass { /** * 一個數組,key爲命名空間前綴,值爲基礎路徑 * * @var array */ protected $prefixes = array(); /** * 封裝自動加載函數 * * @return void */ public function register() { spl_autoload_register(array($this, 'loadClass')); } /** * * 添加一個基礎路徑對應一個命名空間前綴 * * @param string $prefix 命名空間前綴. * @param string $base_dir 命名空間類文件的基礎路徑 * @param bool true爲往數組頭部添加元素,false爲往數組尾部添加元素 * @return void */ public function addNamespace($prefix, $base_dir, $prepend = false) { // 去掉左邊的\ $prefix = trim($prefix, '\\') . '\\'; // 規範基礎路徑 $base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/'; // 初始化數組 if (isset($this->prefixes[$prefix]) === false) { $this->prefixes[$prefix] = array(); } // 將命名空間前綴和基礎路徑存入數組 if ($prepend) { array_unshift($this->prefixes[$prefix], $base_dir); } else { array_push($this->prefixes[$prefix], $base_dir); } } /** * 真正包含文件方法,將給到類名文件包含進來 * * @param string $class 全限定類名(包含命名空間). * @return 成功將返回文件路徑,失敗則返回false */ public function loadClass($class) { $prefix = $class; //查找$prefix最後一個\的位置,看看最後一個\以前的字符串是否在$this->prefixes中 //若是不存在則繼續查詢上一個\的位置,獲取上一個\以前的字符串是否在$this->prefixes中 //若是循環結束仍是沒有找到則返回false while (false !== $pos = strrpos($prefix, '\\')) { $prefix = substr($class, 0, $pos + 1); $relative_class = substr($class, $pos + 1); $mapped_file = $this->loadMappedFile($prefix, $relative_class); if ($mapped_file) { return $mapped_file; } //去掉右邊的\ $prefix = rtrim($prefix, '\\'); } return false; } /** * 若是參數中的$prefix在$this->prefixes中存在,那麼將循環$this->prefixes[$prefix]裏的value(基礎路徑) * 以後拼接文件路徑,若是文件存在將文件包含進來 * * @param string $prefix 命名空間前綴. * @param string $relative_class 真正的類名(不包含命名空間路徑的類名). * @return mixed 包含成功返回文件路徑,不然返回false */ protected function loadMappedFile($prefix, $relative_class) { // 檢查數組中是否有$prefix這個key if (isset($this->prefixes[$prefix]) === false) { return false; } // 將數組中全部的基礎路徑中的文件包含進來 foreach ($this->prefixes[$prefix] as $base_dir) { // 拼接文件絕對路徑 $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php'; // 若是文件存在則包含進來 if ($this->requireFile($file)) { // 返回文件路徑 return $file; } } // 沒有找到文件 return false; } /** *若是文件存在則包含進來. * * @param string $file 文件路徑. * @return bool */ protected function requireFile($file) { if (file_exists($file)) { require $file; return true; } return false; } }