下面由composer教程欄目給大家介紹新手必了解的Composer 實現(xiàn)自動加載原理,希望對需要的朋友有所幫助!
簡介
一般在框架中都會用到 composer 工具 , 用它來管理依賴。其中 composer 有類的自動加載機制,可以加載 composer 下載的庫中的所有的類文件。那么 composer 的自動加載機制是怎么實現(xiàn)的呢?
composer 自動加載原理
以在 Laravel 框架中為例:
1.首先在入口文件(/public/index.php)中引入了 autoload.php
require?__DIR__.'/../vendor/autoload.php';
2. ?我們看看 autoload.php 的內(nèi)容
require_once?__DIR__?.?'/composer/autoload_real.php'; return?ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273::getLoader();
3. ?我們再看看 autoload_real.php 的內(nèi)容
<?php // autoload_real.php @generated by Composer class ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273 { private static $loader; public static function loadClassLoader($class) { if ('ComposerAutoloadClassLoader' === $class) { require __DIR__ . '/ClassLoader.php'; } } public static function getLoader() { if (null !== self::$loader) { return self::$loader; } spl_autoload_register(array('ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273', 'loadClassLoader'), true, true); self::$loader = $loader = new ComposerAutoloadClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >=?50600?&&?!defined('HHVM_VERSION')?&&?(!function_exists('zend_loader_file_encoded')?||?!zend_loader_file_encoded()); ????????if?($useStaticLoader)?{ ????????????require_once?__DIR__?.?'/autoload_static.php'; ????????????call_user_func(ComposerAutoloadComposerStaticInit1215780529014c2b50a6fca7ce889273::getInitializer($loader)); ????????}?else?{ ????????????$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); ????????if?($useStaticLoader)?{ ????????????$includeFiles?=?ComposerAutoloadComposerStaticInit1215780529014c2b50a6fca7ce889273::$files; ????????}?else?{ ????????????$includeFiles?=?require?__DIR__?.?'/autoload_files.php'; ????????} ????????foreach?($includeFiles?as?$fileIdentifier?=>?$file)?{ ????????????composerRequire1215780529014c2b50a6fca7ce889273($fileIdentifier,?$file); ????????} ????????return?$loader; ????} } function?composerRequire1215780529014c2b50a6fca7ce889273($fileIdentifier,?$file) { ????if?(empty($GLOBALS['__composer_autoload_files'][$fileIdentifier]))?{ ????????require?$file; ????????$GLOBALS['__composer_autoload_files'][$fileIdentifier]?=?true; ????} }
可以看出這一段是 composer 自動加載的重點,首先在 autoload.php 中調(diào)用
ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273::getLoader () 方法,getLoader () 首先判斷當前l(fā)oader是不是null,如果不為null就直接返回,否則就初始化一個calssloader類給賦值給 loader 是不是 null,如果不為 null 就直接返回,否則就初始化一個 ClassLoader 類給賦值給 loader,接著將 autoload_namespaces.php、autoload_psr4.php、autoload_classmap.php 文件中的內(nèi)容加入到 $loader 中對應的數(shù)組中,然后給注冊 loadClass 函數(shù),將 autoload_files.php 中的所有路徑所示的文件都包含進來,當在 new 一個類的時候如果沒有找到相關的類就會觸發(fā)這個 loadClass 函數(shù),在 loadClass () 又調(diào)用了 findFile () 去查找相應的文件,找到相應文件后就會返回該文件,然后 loadClass 調(diào)用 includeFile () 方法將該文件 include 進去,否則 findFile 返回 false,這樣就完成了自動加載
4. ?下面來看一下 findFile ()
public?function?findFile($class) ??{ ????????????//?class?map?lookup ????????????if?(isset($this->classMap[$class]))?{ ????????????????return?$this->classMap[$class]; ????????????} ????????????if?($this->classMapAuthoritative?||?isset($this->missingClasses[$class]))?{ ????????????????return?false; ????????????} ????????????if?(null?!==?$this->apcuPrefix)?{ ????????????????$file?=?apcu_fetch($this->apcuPrefix.$class,?$hit); ????????????????if?($hit)?{ ????????????????????return?$file; ????????????????} ????????????} ????????????$file?=?$this->findFileWithExtension($class,?'.php'); ????????????//?Search?for?Hack?files?if?we?are?running?on?HHVM ????????????if?(false?===?$file?&&?defined('HHVM_VERSION'))?{ ????????????????$file?=?$this->findFileWithExtension($class,?'.hh'); ????????????} ????????????if?(null?!==?$this->apcuPrefix)?{ ????????????????apcu_add($this->apcuPrefix.$class,?$file); ????????????} ????????????if?(false?===?$file)?{ ????????????????//?Remember?that?this?class?does?not?exist. ????????????????$this->missingClasses[$class]?=?true; ????????????} ????????????return?$file; ????????} ?????????private?function?findFileWithExtension($class,?$ext) ????????{ ????????????//?PSR-4?lookup ????????????$logicalPathPsr4?=?strtr($class,?'',?DIRECTORY_SEPARATOR)?.?$ext; ????????????$first?=?$class[0]; ????????????if?(isset($this->prefixLengthsPsr4[$first]))?{ ????????????????$subPath?=?$class; ????????????????while?(false?!==?$lastPos?=?strrpos($subPath,?''))?{ ????????????????????$subPath?=?substr($subPath,?0,?$lastPos); ????????????????????$search?=?$subPath.''; ????????????????????if?(isset($this->prefixDirsPsr4[$search]))?{ ????????????????????????$pathEnd?=?DIRECTORY_SEPARATOR?.?substr($logicalPathPsr4,?$lastPos?+?1); ????????????????????????foreach?($this->prefixDirsPsr4[$search]?as?$dir)?{ ????????????????????????????if?(file_exists($file?=?$dir?.?$pathEnd))?{ ????????????????????????????????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; ????????????} ????????????if?(isset($this->prefixesPsr0[$first]))?{ ????????????????foreach?($this->prefixesPsr0[$first]?as?$prefix?=>?$dirs)?{ ????????????????????if?(0?===?strpos($class,?$prefix))?{ ????????????????????????foreach?($dirs?as?$dir)?{ ????????????????????????????if?(file_exists($file?=?$dir?.?DIRECTORY_SEPARATOR?.?$logicalPathPsr0))?{ ????????????????????????????????return?$file; ????????????????????????????} ????????????????????????} ????????????????????} ????????????????} ????????????} ????????????//?PSR-0?fallback?dirs ????????????foreach?($this->fallbackDirsPsr0?as?$dir)?{ ????????????????if?(file_exists($file?=?$dir?.?DIRECTORY_SEPARATOR?.?$logicalPathPsr0))?{ ????????????????????return?$file; ????????????????} ????????????} ????????????//?PSR-0?include?paths. ????????????if?($this->useIncludePath?&&?$file?=?stream_resolve_include_path($logicalPathPsr0))?{ ????????????????return?$file; ????????????} ????????????return?false; ????????}
4. findFile () 函數(shù)先在 classMap 中查找,如果找不到的話就會嘗試在 apcu 緩存中查找,如果還是找不到的話就會調(diào)用 findFileWithExtension () 函數(shù)查找,如果找到了就會將該文件加到 apcu 緩存,如果找不到的話就會在 missingClasses 數(shù)組中設一個標記表示識這個類找不到
findFileWithExtension()方法根據(jù)之前通過 ? ? ??> ? (loader?>set(namespace, ? ??)和path)和loader->setPsr4( ? ? ? ? ,namespace,path)方法設置的信息找出類文件的路徑信息
5. ?在上面有的地方用到了 spl_autoload_register 和 spl_autoload_unregister 函數(shù)
1. ?spl_autoload_register 函數(shù)
1.spl_autoload_register — 注冊給定的函數(shù)作為 __autoload 的實現(xiàn),
bool?spl_autoload_register?([?callable?????????????????[,????autoloadfunction[,boolthrow?=?true?[,?bool?$prepend?=?false?]]]?)
2.prepend
如果是 true,spl_autoload_register () 會添加函數(shù)到隊列之首,而不是隊列尾部。
3.如果在你的程序中已經(jīng)實現(xiàn)了 autoload () 函數(shù),它必須顯式注冊到 autoload () 隊列中。因為 spl_autoload_register () 函數(shù)會將 Zend Engine 中的__autoload () 函數(shù)取代為 spl_autoload () 或 spl_autoload_call ()
例:
function?__autoload($name)?{?require??'class/'.$name.'.php';?echo??'1';?}?function?autoload_test($name)?{?echo??'2';?}?spl_autoload_register('autoload_test');?spl_autoload_register('__autoload');?$ca=new?Ca();
2. spl_autoload_unregister 函數(shù)
spl_autoload_unregister — 注銷已注冊的 autoload () 函數(shù),如果該函數(shù)隊列處于激活狀態(tài),并且在給定函數(shù)注銷后該隊列變?yōu)榭眨瑒t該函數(shù)隊列將會變?yōu)闊o效。如果該函數(shù)注銷后使得自動裝載函數(shù)隊列無效,即使存在有 autoload 函數(shù)它也不會自動激活。
bool?spl_autoload_unregister?(?mixed?$autoload_function?)