当前位置:首页 > CMS教程 > 其它CMS > 列表

分析Composer实现自动加载原理

发布:smiling 来源: PHP粉丝网  添加日期:2022-05-02 10:40:58 浏览: 评论:0 

一般在框架中都会用到composer工具, 用它来管理依赖。其中 composer有类的自动加载机制,可以加载composer下载的库中的所有的类文件。那么composer的自动加载机制是怎么实现的呢?

1. 入口文件

(/public/index.php)中引入了 autoload.php

require __DIR__.'/../vendor/autoload.php';<br data-filtered="filtered">

2.  autoload.php

  1. require_once __DIR__ . '/composer/autoload_real.php'; 
  2.  
  3. return ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273::getLoader();<br data-filtered="filtered"> 

3. autoload_real.php

  1. <?php 
  2. // autoload_real.php @generated by Composer 
  3. class ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273{ 
  4.     private static $loader; 
  5.     public static function loadClassLoader($class){ 
  6.         if ('Composer\Autoload\ClassLoader' === $class) { 
  7.             require __DIR__ . '/ClassLoader.php'; 
  8.         } 
  9.     } 
  10.     public static function getLoader(){ 
  11.         if (null !== self::$loader) { 
  12.             return self::$loader; 
  13.         } 
  14.         spl_autoload_register(array('ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273', 'loadClassLoader'), true, true); 
  15.         self::$loader = $loader = new \Composer\Autoload\ClassLoader(); 
  16.         spl_autoload_unregister(array('ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273', 'loadClassLoader')); 
  17.         $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); 
  18.         if ($useStaticLoader) { 
  19.             require_once __DIR__ . '/autoload_static.php'; 
  20.    
  21.             call_user_func(\Composer\Autoload\ComposerStaticInit1215780529014c2b50a6fca7ce889273::getInitializer($loader)); 
  22.         } else { 
  23.             $map = require __DIR__ . '/autoload_namespaces.php'; 
  24.             foreach ($map as $namespace => $path) { 
  25.                 $loader->set($namespace, $path); 
  26.             } 
  27.             $map = require __DIR__ . '/autoload_psr4.php'; 
  28.             foreach ($map as $namespace => $path) { 
  29.                 $loader->setPsr4($namespace, $path); 
  30.             } 
  31.             $classMap = require __DIR__ . '/autoload_classmap.php'; 
  32.             if ($classMap) { 
  33.                 $loader->addClassMap($classMap); 
  34.             } 
  35.         } 
  36.         $loader->register(true); 
  37.         if ($useStaticLoader) { 
  38.             $includeFiles = Composer\Autoload\ComposerStaticInit1215780529014c2b50a6fca7ce889273::$files; 
  39.         } else { 
  40.             $includeFiles = require __DIR__ . '/autoload_files.php'; 
  41.         } 
  42.         foreach ($includeFiles as $fileIdentifier => $file) { 
  43.             composerRequire1215780529014c2b50a6fca7ce889273($fileIdentifier, $file); 
  44.         } 
  45.    
  46.         return $loader; 
  47.     } 
  48. } 
  49. function composerRequire1215780529014c2b50a6fca7ce889273($fileIdentifier, $file){ 
  50.     if (emptyempty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { 
  51.         require $file; 
  52.    
  53.         $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; 
  54.     } 
  55. } 

可以看出这一段是 composer 自动加载的重点,首先在 autoload.php 中调用

ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273::getLoader () 方法,getLoader () 首先判断当前loader是不是null,如果不为null就直接返回,否则就初始化一个ClassLoader类给赋值给 loader 是不是 null,如果不为 null 就直接返回,否则就初始化一个 ClassLoader 类给赋值给 loader,接着将 autoload_namespaces.php、autoload_psr4.php、autoload_classmap.php 文件中的内容加入到 $loader 中对应的数组中,然后给注册 loadClass 函数,将 autoload_files.php 中的所有路径所示的文件都包含进来,当在 new 一个类的时候如果没有找到相关的类就会触发这个 loadClass 函数,在 loadClass () 又调用了 findFile () 去查找相应的文件,找到相应文件后就会返回该文件,然后 loadClass 调用 includeFile () 方法将该文件 include 进去,否则 findFile 返回 false,这样就完成了自动加载

4.  findFile ()

  1. public function findFile($class){ 
  2.     // class map lookup 
  3.     if (isset($this->classMap[$class])) { 
  4.         return $this->classMap[$class]; 
  5.     } 
  6.     if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 
  7.         return false; 
  8.     } 
  9.     if (null !== $this->apcuPrefix) { 
  10.         $file = apcu_fetch($this->apcuPrefix.$class, $hit); 
  11.         if ($hit) { 
  12.             return $file; 
  13.         } 
  14.     } 
  15.     $file = $this->findFileWithExtension($class, '.php'); 
  16.     // Search for Hack files if we are running on HHVM 
  17.     if (false === $file && defined('HHVM_VERSION')) { 
  18.         $file = $this->findFileWithExtension($class, '.hh'); 
  19.     } 
  20.     if (null !== $this->apcuPrefix) { 
  21.         apcu_add($this->apcuPrefix.$class, $file); 
  22.     } 
  23.     if (false === $file) { 
  24.         // Remember that this class does not exist. 
  25.         $this->missingClasses[$class] = true; 
  26.     } 
  27.     return $file; 
  28. } 
  29.  
  30. private function findFileWithExtension($class, $ext){ 
  31.     // PSR-4 lookup 
  32.     $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 
  33.  
  34.     $first = $class[0]; 
  35.     if (isset($this->prefixLengthsPsr4[$first])) { 
  36.         $subPath = $class; 
  37.         while (false !== $lastPos = strrpos($subPath, '\\')) { 
  38.             $subPath = substr($subPath, 0, $lastPos); 
  39.             $search = $subPath.'\\'; 
  40.             if (isset($this->prefixDirsPsr4[$search])) { 
  41.                 $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); 
  42.                 foreach ($this->prefixDirsPsr4[$search] as $dir) { 
  43.                     if (file_exists($file = $dir . $pathEnd)) { 
  44.                         return $file; 
  45.                     } 
  46.                 } 
  47.             } 
  48.         } 
  49.     } 
  50.     // PSR-4 fallback dirs 
  51.     foreach ($this->fallbackDirsPsr4 as $dir) { 
  52.         if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 
  53.             return $file; 
  54.         } 
  55.     } 
  56.  
  57.     // PSR-0 lookup 
  58.     if (false !== $pos = strrpos($class, '\\')) { 
  59.         // namespaced class name 
  60.         $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 
  61.         . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 
  62.     } else { 
  63.         // PEAR-like class name 
  64.         $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 
  65.     } 
  66.  
  67.     if (isset($this->prefixesPsr0[$first])) { 
  68.         foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 
  69.             if (0 === strpos($class, $prefix)) { 
  70.                 foreach ($dirs as $dir) { 
  71.                     if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 
  72.                         return $file; 
  73.                     } 
  74.                 } 
  75.             } 
  76.         } 
  77.     } 
  78.  
  79.     // PSR-0 fallback dirs 
  80.     foreach ($this->fallbackDirsPsr0 as $dir) { 
  81.         if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 
  82.             return $file; 
  83.         } 
  84.     } 
  85.     // PSR-0 include paths. 
  86.     if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 
  87.         return $file; 
  88.     } 
  89.     return false; 
  90. } 

findFile () 函数先在 classMap 中查找,如果找不到的话就会尝试在 apcu 缓存中查找,如果还是找不到的话就会调用 findFileWithExtension () 函数查找,如果找到了就会将该文件加到apcu缓存,如果找不到的话就会在 missingClasses 数组中设一个标记表示识这个类找不到findFileWithExtension()方法根据之前通过loader->set(loader−>set(namespace, path)和path)和loader->setPsr4(namespace,namespace,path)方法设置的信息找出类文件的路径信息

5. spl_autoload_register和spl_autoload_unregister函数

1. spl_autoload_register 函数

1.spl_autoload_register — 注册给定的函数作为 __autoload 的实现,

bool spl_autoload_register([callable autoloadfunction[,boolautoloadfunction[,boolthrow = true [, bool $prepend = false ]]])

2.prepend

如果是 true,spl_autoload_register () 会添加函数到队列之首,而不是队列尾部。

3.如果在你的程序中已经实现了 autoload () 函数,它必须显式注册到 autoload () 队列中。因为 spl_autoload_register () 函数会将 Zend Engine 中的__autoload () 函数取代为 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 函数

spl_autoload_unregister — 注销已注册的 autoload () 函数,如果该函数队列处于激活状态,并且在给定函数注销后该队列变为空,则该函数队列将会变为无效。如果该函数注销后使得自动装载函数队列无效,即使存在有 autoload 函数它也不会自动激活。

bool spl_autoload_unregister ( mixed $autoload_function )

Tags: Composer

分享到: