Composer自動載入機制原始碼剖析
阿新 • • 發佈:2019-01-02
1、autoload.php
要使用Composer的自動載入,首先需要引入該檔案
<?php
// autoload.php @generated by Composer
// 引入autoload_real.php
require_once __DIR__ . '/composer' . '/autoload_real.php';
// 下面一大長串是在我們安裝composer時由Composer自動生成的,我們需要關注的是它呼叫的autoload_real.php裡的靜態方法 getLoader()
return ComposerAutoloaderInit85b4dbf6b714d62ec745fcf3dd1a5041::getLoader();
2、autoload_real.php
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit85b4dbf6b714d62ec745fcf3dd1a5041
{
private static $loader;
// 在例項化ClassLoader時呼叫該函式
public static function loadClassLoader($class)
{
// 該函式在getLoader方法裡被註冊為__autoload的實現,在例項化類時,如果類不存在,會自動呼叫該方法
// 如果例項化的函式是Composer\Autoload\ClassLoader 則引入該類
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
// 註冊 loadClassLoader函式作為 __autoload 的實現 spl_autoload_register(array('ComposerAutoloaderInit85b4dbf6b714d62ec745fcf3dd1a5041', 'loadClassLoader'), true, true);
// 例項化該方法時會自動呼叫上述方法註冊的函式,
// loadClassLoader函式裡引入 ClassLoader類
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit85b4dbf6b714d62ec745fcf3dd1a5041', 'loadClassLoader'));
// PSR-0 的規則
$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 = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
// 註冊給定的函式作為 __autoload 的實現,具體參考下文
// Class-Map部分
$loader->register(true);
// Files方式 直接載入需要訪問的檔案
$includeFiles = require __DIR__ . '/autoload_files.php';
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire85b4dbf6b714d62ec745fcf3dd1a5041($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire85b4dbf6b714d62ec745fcf3dd1a5041($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
// 載入需要呼叫的檔案
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}
3、ClassLoader.php (部分核心程式碼)
① PSR-0 規則
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
// $prefix、$paths分別相當於autoload_namespaces.php裡返回陣列的key和value值
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
// $prefix[0]取得是首個字元
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
② PSR-4規則
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
// PSR-4 名稱空間以 \ 結尾,如果不是,則不符合規則
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
③ Class-map方式
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{ // $prepend為true時,spl_autoload_register() 會新增函式到佇列之首,而不是佇列尾部。 (具體請參考php文件)
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
什麼時候才會去呼叫上述註冊的loadClass函式呢?舉例來說
<?php
require './vendor/autoload.php';
// 當我們例項化TestController的時候,就會自動呼叫spl_autoload_register註冊的函式loadClass,在loadClass中又會去呼叫findFile方法去查詢類檔案所在的位置,然後require引入
$test = new TestController;
echo $test->show();
至此Composer自動載入流程已講述完畢。具體的程式碼還需各位自己去研究。不得不驚歎,有了Composer真是太方便了!