新手必了解的Composer 實現(xiàn)自動加載原理

下面由composer教程欄目給大家介紹新手必了解的Composer 實現(xiàn)自動加載原理,希望對需要的朋友有所幫助!

新手必了解的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 (&#39;ComposerAutoloadClassLoader&#39; === $class) {             require __DIR__ . &#39;/ClassLoader.php&#39;;         }     }      public static function getLoader()     {         if (null !== self::$loader) {             return self::$loader;         }         spl_autoload_register(array(&#39;ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273&#39;, &#39;loadClassLoader&#39;), true, true);         self::$loader = $loader = new ComposerAutoloadClassLoader();         spl_autoload_unregister(array(&#39;ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273&#39;, &#39;loadClassLoader&#39;));          $useStaticLoader = PHP_VERSION_ID >=?50600?&amp;&amp;?!defined('HHVM_VERSION')?&amp;&amp;?(!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?=&gt;?$path)?{ ????????????????$loader-&gt;set($namespace,?$path); ????????????}  ????????????$map?=?require?__DIR__?.?'/autoload_psr4.php'; ????????????foreach?($map?as?$namespace?=&gt;?$path)?{ ????????????????$loader-&gt;setPsr4($namespace,?$path); ????????????}  ????????????$classMap?=?require?__DIR__?.?'/autoload_classmap.php'; ????????????if?($classMap)?{ ????????????????$loader-&gt;addClassMap($classMap); ????????????} ????????}  ????????$loader-&gt;register(true);  ????????if?($useStaticLoader)?{ ????????????$includeFiles?=?ComposerAutoloadComposerStaticInit1215780529014c2b50a6fca7ce889273::$files; ????????}?else?{ ????????????$includeFiles?=?require?__DIR__?.?'/autoload_files.php'; ????????} ????????foreach?($includeFiles?as?$fileIdentifier?=&gt;?$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-&gt;classMap[$class]))?{ ????????????????return?$this-&gt;classMap[$class]; ????????????} ????????????if?($this-&gt;classMapAuthoritative?||?isset($this-&gt;missingClasses[$class]))?{ ????????????????return?false; ????????????} ????????????if?(null?!==?$this-&gt;apcuPrefix)?{ ????????????????$file?=?apcu_fetch($this-&gt;apcuPrefix.$class,?$hit); ????????????????if?($hit)?{ ????????????????????return?$file; ????????????????} ????????????}  ????????????$file?=?$this-&gt;findFileWithExtension($class,?'.php');  ????????????//?Search?for?Hack?files?if?we?are?running?on?HHVM ????????????if?(false?===?$file?&amp;&amp;?defined('HHVM_VERSION'))?{ ????????????????$file?=?$this-&gt;findFileWithExtension($class,?'.hh'); ????????????}  ????????????if?(null?!==?$this-&gt;apcuPrefix)?{ ????????????????apcu_add($this-&gt;apcuPrefix.$class,?$file); ????????????}  ????????????if?(false?===?$file)?{ ????????????????//?Remember?that?this?class?does?not?exist. ????????????????$this-&gt;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-&gt;prefixLengthsPsr4[$first]))?{ ????????????????$subPath?=?$class; ????????????????while?(false?!==?$lastPos?=?strrpos($subPath,?''))?{ ????????????????????$subPath?=?substr($subPath,?0,?$lastPos); ????????????????????$search?=?$subPath.''; ????????????????????if?(isset($this-&gt;prefixDirsPsr4[$search]))?{ ????????????????????????$pathEnd?=?DIRECTORY_SEPARATOR?.?substr($logicalPathPsr4,?$lastPos?+?1); ????????????????????????foreach?($this-&gt;prefixDirsPsr4[$search]?as?$dir)?{ ????????????????????????????if?(file_exists($file?=?$dir?.?$pathEnd))?{ ????????????????????????????????return?$file; ????????????????????????????} ????????????????????????} ????????????????????} ????????????????} ????????????}  ????????????//?PSR-4?fallback?dirs ????????????foreach?($this-&gt;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-&gt;prefixesPsr0[$first]))?{ ????????????????foreach?($this-&gt;prefixesPsr0[$first]?as?$prefix?=&gt;?$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-&gt;fallbackDirsPsr0?as?$dir)?{ ????????????????if?(file_exists($file?=?$dir?.?DIRECTORY_SEPARATOR?.?$logicalPathPsr0))?{ ????????????????????return?$file; ????????????????} ????????????}  ????????????//?PSR-0?include?paths. ????????????if?($this-&gt;useIncludePath?&amp;&amp;?$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?)

? 版權聲明
THE END
喜歡就支持一下吧
點贊8 分享