composer的出現真是讓人們眼前一亮,web開發今後變成了一件很『好玩』的事情,開發一個CMS就像在搭積木,從packagist中取出『積木』搭建在本身的代碼中,一點一點搭建出一個屬於本身的王國。
今後之後我基本就拋棄了require和include函數,一個項目中,這兩個函數只可能出現一次,那就是require '../vendor/autoload.php'
。
那麼,既然拋棄了傳統的文件包含方法,咱們使用全部類庫都將用namespace和composer自帶的autoload。但是,咱們本身編寫的函數庫與類庫,怎麼用composer的方法來自動加載呢?php
這就要從composer.json提及,咱們須要經過修改這個文件來達到目的。
composer.json至關因而composer的配置文件,這個配置文件中有一個autoload段,好比個人一個項目:web
其中又包含主要的兩個選項: files 和 psr-4。
files就是須要composer自動幫咱們加載的函數庫(不含類),只要在後面的數組中將函數庫的文件路徑寫入便可。
psr-4顧名思義,是一個基於psr-4(http://www.php-fig.org/psr/psr-4/)規則的類庫自動加載對應關係,只要在其後的對象中,以 "命名空間": "路徑"
的方式寫入本身的類庫信息便可。
修改完成後,只要執行一下composer update
,便可完成對應工做。json
以後,咱們在項目中,用以下方式便可加載自定義類庫:數組
\Mrgoon\AliSms\AliSms();new
composer的autoload將會自動包含」./mrgoon/aliyun-sms/src/.AliSms.php」,並找到其中的Mrgoon\AliSms命名空間下的AliSms類。composer
咱們來深挖一下,探索一下autoload的原理。
在咱們修改完composer.json並執行update後,將會修改./vender/composer/autoload_psr4.php,好比個人某個項目,其中增長了這樣一個對應關係:函數
這其實就是我剛剛在.json中添加的對應關係,他等於將.josn的配置文件,換成了php的形式。
那麼我看到vendor/autoload.php:ui
<?php // autoload.php @generated by Composer require_once __DIR__ . '/composer' . '/autoload_real.php'; return ComposerAutoloaderInitff1d77c91141523097b07ee2acc23326::getLoader();
其執行了一個自動生成的類ComposerAutoloaderInitff1d77c91141523097b07ee2acc23326
中的getLoader方法。
跟進:this
public static function getLoader() { if (null !== self::$loader) { return self::$loader; } spl_autoload_register(array('ComposerAutoloaderInitff1d77c91141523097b07ee2acc23326', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitff1d77c91141523097b07ee2acc23326', 'loadClassLoader')); $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } $map = require __DIR__ . '/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } $loader->register(true); $includeFiles = require __DIR__ . '/autoload_files.php'; foreach ($includeFiles as $file) { composerRequireff1d77c91141523097b07ee2acc23326($file); } return $loader; }
能夠明顯看到,他將autoload_namespaces.php、autoload_psr4.php、autoload_classmap.php、autoload_files.php等幾個配置文件包含了進來,並進行了相關處理(setPsr4
),最後註冊(register
)。
那麼咱們跟進register
方法:spa
public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); }
這函數就一行,但簡單明瞭,直接調用php自帶的spl_autoload_register
函數,註冊處理__autoload
的方法,也就是loadClass
方法。再跟進loadClass
方法:.net
public function loadClass($class) { if ($file = $this->findFile($class)) { includeFile($file); return true; } }
從函數名字就能夠大概知道流程:若是存在$class對應的這個$file,則include進來。
那麼進findFile
方法裏看看吧:
public function findFile($class) { // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 if ('\\' == $class[0]) { $class = substr($class, 1); } // class map lookup if (isset($this->classMap[$class])) { return $this->classMap[$class]; } if ($this->classMapAuthoritative) { return false; } $file = $this->findFileWithExtension($class, '.php'); // Search for Hack files if we are running on HHVM if ($file === null && defined('HHVM_VERSION')) { $file = $this->findFileWithExtension($class, '.hh'); } if ($file === null) { // Remember that this class does not exist. return $this->classMap[$class] = false; } return $file; }
經過類名找文件,最終鎖定在findFileWithExtension
方法中。
不過發現了一個小寶藏:在php5.3.0~5.3.2版本下,類名的第一個字符是\的小bug,也許之後挖漏洞會用上。
仍是跟進findFileWithExtension
方法:
private function findFileWithExtension($class, $ext) { // PSR-4 lookup $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; $first = $class[0]; if (isset($this->prefixLengthsPsr4[$first])) { foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { if (0 === strpos($class, $prefix)) { foreach ($this->prefixDirsPsr4[$prefix] as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { return $file; } } } } } // PSR-4 fallback dirs foreach ($this->fallbackDirsPsr4 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { return $file; } } // PSR-0 lookup if (false !== $pos = strrpos($class, '\\')) { // namespaced class name $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); } else { // PEAR-like class name $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; }