通過在composer.json檔案中配置需要載入的類、命名空間,通過執行composer install 命令自動產生類名和對應的類檔案的映射,而後通過註冊loadClass方法,實現對composer管理的諸多類的自動載入;
如何在composer.json檔案中配置類和命名空間 ?
共有四種方式:
PSR-0(網上查到的例子和PSR-4沒有看出太大區別,且已不推薦使用);
PSR-4;
Class-map;
Files;
在composer.json檔案中添加以下代碼塊:
"autoload": { "psr-4": { "src\\darren\\": "src/", "project\\darren\\": "project" }, "files": ["common/Darren.php", "common/Since.php"], "classmamp": [lib]}
測試代碼目錄結構如下:
common Darren.php Since.phplib Darren.php Since.phpproject Darren.phpsrc Darren.phpvendorcomposer.jsonindex.php
代碼中的命名空間習慣為:目錄名/Darren
當我們配置好composer.json檔案,並執行compoer install命令後,在vendor/composer目錄下會自動產生一些php檔案,這些檔案實際上記錄了類、命名空間和對應的類檔案的映射,下面一一舉例說明;
PSR-4
如上所述,通過psr-4方式配置了兩個命名空間的自動載入,分別是src\daren和projecr\darren;vendor/composer目錄下自動產生了autoload_psr4.php檔案,具體代碼如下所示:
<?php// autoload_psr4.php @generated by Composer$vendorDir = dirname(dirname(__FILE__));$baseDir = dirname($vendorDir);return array('src\\darren\\' => array($baseDir . '/src'),'project\\darren\\' => array($baseDir . '/project'),);
Classmap方式
classmap方式只需要我們配置需要自動載入的目錄,compoer會自動掃描目錄下的的.php檔案或.inc檔案中的class,並自動產生這些類和其對應的類檔案的映射關係,儲存在vendor/composer目錄下的autoload_classmap.php檔案中,具體代碼如下:
<?php// autoload_classmap.php @generated by Composer$vendorDir = dirname(dirname(__FILE__));$baseDir = dirname($vendorDir);return array('lib\\darren\\Darren' => $baseDir . '/lib/Darren.php','lib\\since\\Since' => $baseDir . '/lib/Since.php',);
其中lib\darren為命名空間,Darren為類名;
Files
files方式其實就是手動指定要載入的檔案,這通常適用於一些全域的functions,可以將這些functions統一放在一個檔案裡,然後直接進行載入;
上述的設定檔通過files方式載入了兩個檔案common/Darren.php和common/Since.php,vendor/composer目錄下自動產生了autoload_files.php檔案,具體代碼如下所示:
<?php// autoload_files.php @generated by Composer$vendorDir = dirname(dirname(__FILE__));$baseDir = dirname($vendorDir);return array('b704865b506bf33e8097e6f62604fc7f' => $baseDir . '/common/Darren.php','603921ee67f9053beb44a88f05b115d2' => $baseDir . '/common/Since.php',);
composer是如何?自動載入的 ?
配置完compoer.json檔案,跑完了composer install命令,在檔案的開始引用vendor/autolaod.php即可實作類別的自動載入,那麼composer是如何?自動載入的呢?
這裡先插敘一點php的特性:當調用不存在的類時,系統會自動調用__autoload( )方法來載入相應的類;
舉例子說明如下:
我們在index.php檔案中調用Darren類中的testAutoload( )方法【Darren類與index.php檔案在同級目錄】,這裡我們沒有在index.php檔案中引入Darren類,那麼肯定是會報錯的;但是我們可以重寫__autoload( )方法實現Darren類的載入,具體代碼如下:
//當調用不存在的類時,系統自動調用__autolaod()尋找function __autolaod($class){ $file = $class . '.php'; if (is_file($file)) { require_once($file); }}Darren::testAutoload();
我們也可以通過spl_autoload_register( )方法來註冊一個其它方法來替代__autoload( );
舉例如下:
function loader($class){ $file = $class . '.php'; if (is_file($file)) { require_once($file); }}spl_autoload_register('loader');Darren::testAutoload();
這樣,loader方法就取代了__autoload;
接下來我們繼續研究composer背後的載入機制,autoload.php中引入了autoload_real.php然後調用了getLoader( )方法,getLoader( )方法具體代碼如下:
public static function getLoader(){ if (null !== self::$loader) { return self::$loader; } spl_autoload_register(array('ComposerAutoloaderInit0a8197b9e4da93df051721eff8ed7b28', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInit0a8197b9e4da93df051721eff8ed7b28', 'loadClassLoader')); $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } //整理通過psr-4方式配置的類 $map = require __DIR__ . '/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } //整理通過classmap方式配置的類 $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } //註冊實現自動載入的方法 $loader->register(true); //直接引入通過files配置的類 $includeFiles = require __DIR__ . '/autoload_files.php'; foreach ($includeFiles as $fileIdentifier => $file) { composerRequire0a8197b9e4da93df051721eff8ed7b28($fileIdentifier, $file); } return $loader;}
該方法先是建立了一個ClassLoader類的對象,然後載入composer自動產生的那些記錄類、命名空間與檔案對應關係的檔案,調用ClassLoader中的方法對這些檔案整理,並將映射關係通過數組儲存,數組的鍵為類名或者命名空間加類名,數組的值為類對應的類檔案地址;這裡值得注意的一點是,我們通過files配置的那些需要自動載入的類,是直接將類檔案引入進來,並不是在調用時才去載入,代碼如下:
function composerRequire0a8197b9e4da93df051721eff8ed7b28($fileIdentifier, $file){ if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { require $file;//直接引入類檔案 $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; }}
ClassLoader類中值得注意的方法:
通過register方法註冊loadClass方法,取代__autoload( )方法,實作類別的載入
public function register($prepend = false){ spl_autoload_register(array($this, 'loadClass'), true, $prepend);}
loadClass方法尋找類對應的檔案,並引入:
public function loadClass($class){ if ($file = $this->findFile($class)) { includeFile($file); return true; }}
具體如何尋找數組從而得到該類對應的類檔案,可以通過xdebug跟一遍代碼,並不難理解;