composer的源码分析
composer的源码分析
在上一篇文章《如何用composer来构建自己的MVC项目》中,记录了如何使用composer来构建项目。那么Compoer是如何实现自动加载功能的?下面我们通过源码来进行分析。
一、目录结构
当前的目录结构如下:
composer.json内容如下:
1 { 2 "name": "hxq/composer-analysis", 3 "authors": [ 4 { 5 "name": "xxx", 6 "email": "10033333@qq.com" 7 } 8 ], 9 "require": {}, 10 "autoload": { 11 "classmap": [ 12 "app/Library/Crypt", 13 "app/Library/Phonecrypt", 14 "app/Library/Highlight" 15 ], 16 "psr-4": { 17 "App\\": "app/" 18 }, 19 "files": [ 20 "app/Helpers/functions.php" 21 ] 22 } 23 }
下面就是整个分析的过程啦!
二、启动
入口文件 index.php
1 <?php 2 define('APP_DEBUG', true); //开启调试模式 3 date_default_timezone_set("Asia/Shanghai"); 4 // 入口文件中利用composer来实现自动加载功能 5 require __DIR__ . '/vendor/autoload.php';//自动加载
三、autoload.php
具体的分析在代码注释里面:
1 <?php 2 3 // autoload.php @generated by Composer 4 // autoload.php不负责具体功能逻辑,只做两件事:自动加载类的初始化和注册 5 // composer真正开始的地方 6 7 require_once __DIR__ . '/composer/autoload_real.php'; 8 9 return ComposerAutoloaderInit03d4bf55ac53dfdc34a9b856a0bd37a2::getLoader();
四、autoload_real.php
1. 类结构
自动加载功能的引导类,这个类的结构如下图:
程序主要调用了这个类的getLoader方法,因此这个方法是我们分析的重点。
自动加载引导类 autoload_real.php ,具体的分析在代码注释里面:
1 <?php 2 3 // autoload_real.php @generated by Composer 4 // 自动加载功能的引导类:composer加载类的初始化(顶级命名空间与文件路径映射初始化)和注册 5 6 class ComposerAutoloaderInit03d4bf55ac53dfdc34a9b856a0bd37a2 7 { 8 private static $loader; 9 10 public static function loadClassLoader($class) 11 { 12 if ('Composer\Autoload\ClassLoader' === $class) { 13 require __DIR__ . '/ClassLoader.php'; 14 } 15 } 16 17 /** 18 * @return \Composer\Autoload\ClassLoader 19 */ 20 public static function getLoader() 21 { 22 // 1.单例模式:自动加载类只能有一个 23 if (null !== self::$loader) { 24 return self::$loader; 25 } 26 27 // 2.实例化一个自动加载的核心类对象 28 spl_autoload_register(array('ComposerAutoloaderInit03d4bf55ac53dfdc34a9b856a0bd37a2', 'loadClassLoader'), true, true); 29 self::$loader = $loader = new \Composer\Autoload\ClassLoader(); 30 spl_autoload_unregister(array('ComposerAutoloaderInit03d4bf55ac53dfdc34a9b856a0bd37a2', 'loadClassLoader')); 31 32 // 3.自动加载类的初始化 33 // 静态初始化只支持 PHP5.6 以上版本并且不支持 HHVM 虚拟机 34 $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); 35 if ($useStaticLoader) { 36 // 方式一:静态初始化 37 require_once __DIR__ . '/autoload_static.php'; 38 39 call_user_func(\Composer\Autoload\ComposerStaticInit03d4bf55ac53dfdc34a9b856a0bd37a2::getInitializer($loader)); 40 } else { 41 42 // 方式二:调用核心类接口初始化 43 44 // PSR0标准 45 $map = require __DIR__ . '/autoload_namespaces.php'; 46 foreach ($map as $namespace => $path) { 47 $loader->set($namespace, $path); 48 } 49 50 // PSR4标准 51 $map = require __DIR__ . '/autoload_psr4.php'; 52 foreach ($map as $namespace => $path) { 53 $loader->setPsr4($namespace, $path); 54 } 55 56 $classMap = require __DIR__ . '/autoload_classmap.php'; 57 if ($classMap) { 58 $loader->addClassMap($classMap); 59 } 60 } 61 62 // 4.注册(负责顶层以下的命名空间的映射规则) 63 $loader->register(true); 64 65 // 5.自动加载全局函数:分为两种方式 66 if ($useStaticLoader) { 67 // 方式一:静态初始化 68 $includeFiles = Composer\Autoload\ComposerStaticInit03d4bf55ac53dfdc34a9b856a0bd37a2::$files; 69 } else { 70 // 方式二:普通初始化 71 $includeFiles = require __DIR__ . '/autoload_files.php'; 72 } 73 foreach ($includeFiles as $fileIdentifier => $file) { 74 composerRequire03d4bf55ac53dfdc34a9b856a0bd37a2($fileIdentifier, $file); 75 } 76 77 return $loader; 78 } 79 } 80 81 function composerRequire03d4bf55ac53dfdc34a9b856a0bd37a2($fileIdentifier, $file) 82 { 83 if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { 84 require $file; 85 86 $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; 87 } 88 }
由上面的代码注释分析,我们可以看到,自动加载引导类分成5个部分
- 单例模式
- 实例化一个自动加载的核心类ClassLoader对象
- 自动加载核心类的初始化
- 注册(负责顶层以下的命名空间的映射规则)
- 自动加载全局函数
其实这里面最重要的就是自动加载核心类对象的初始化以及注册。
2. 关注点
除了以上之外,这里还有几个需要关注的地方
1)关注点1: autoload_real.php 中的类名为 ComposerAutoloaderInit... 此处为了防止用户自定义类名跟这个类重复冲突,为什么采用的是加上哈希值而不是定义命名空间呢?
原因是:之所以这里没有定义命名空间,是因为命名空间一般都是为了复用,而这个类只需要运行一次即可,以后也不会用得到,用哈希值更加合适。
2)关注点2:代码28-30行中,实例化自动加载核心类对象时,为什么不直接require核心类ClassLoader?
原因是:防止用户也定义了 \Composer\Autoload\ClassLoader 命名空间,导致自动加载错误文件。
这个地方利用spl_autoload_register,最后一个参数是true,作用是将自定义的loadClassLoader()添加到__autoload函数堆栈的队列之首。确保能够正确地加载到ClassLoader类。
3)关注点3:为什么ClassLoader类是用命名空间来定义的,而不是像autoload_real或者autoload_static类一样加个哈希值呢?
原因是:使用命名空间一般都是为了复用,ClassLoader这个类是可以复用的,框架允许用户使用这个类。
4)关注点4:73-75行,为什么不直接 require $includeFiles 里面的每个文件名,而要用类外面的函数 composerRequire... ?
原因是:
- 是为了避免和用户定义函数冲突
- 防止用户在全局函数所在的文件写 $this 或者 self。
假如 $includeFiles 有个 app/helper.php 文件,这个 helper.php 文件的函数外有一行代码: $this->foo()。如果引导类在 getLoader() 函数直接 require($file),那么引导类就会运行这句代码,调用自己的 foo() 函数,这显然是错的。
5)关注点5:81-88行,自动加载全局函数的实现中,为什么要用 hash 作为 $fileIdentifier?
原因是:这个变量是用来控制全局函数只被 require 一次的,那为什么不用 require_once 呢?事实上 require_once 比 require 效率低很多(具体原因参考文章《再一次, 不要使用(include/require)_once》),使用全局变量 $GLOBALS 这样控制加载会更快。
6)关注点6:为什么要使用spl_autoload_unregister 卸载之前注册的自动加载函数?
原因是:在创建 ClassLoader
对象后,loadClassLoader
方法不再需要继续处理类的加载,这样可以避免潜在的冲突或重复调用。
五、autoload_static.php 用于静态初始化的类
注意:这里的类名与autload_real类相似,也是用ComposerStaticInit加上哈希值组成的。不同的地方是autoload_static还使用了命名空间的定义。
使用静态初始化是有条件的:只支持 PHP 5.6 以上版本、不支持 HHVM 虚拟机、不存在 Zend-encoded file。
具体的分析在代码注释里面:
1 <?php 2 3 // autoload_static.php @generated by Composer 4 // 用于静态初始化的类 5 6 namespace Composer\Autoload; 7 8 class ComposerStaticInit03d4bf55ac53dfdc34a9b856a0bd37a2 9 { 10 public static $files = array ( 11 'bbaaff3a6e68bfb5889450e10efe9617' => __DIR__ . '/../..' . '/app/Helpers/functions.php', 12 ); 13 14 // PSR4标准顶级命名空间映射数组:分为$prefixLengthsPsr4和$prefixDirsPsr4 15 // 用命名空间第一个字母作为前缀索引,对应的是一维数组,key为顶级命名空间,value为顶级命名空间的长度 16 // PSR4 标准是用顶级命名空间目录替换顶级命名空间,所以获得顶级命名空间的长度很重要 17 public static $prefixLengthsPsr4 = array ( 18 'A' => 19 array ( 20 'App\\' => 4, 21 ), 22 ); 23 24 // 顶级命名空间的映射目录数组,注意:映射目录可能不止一条 25 public static $prefixDirsPsr4 = array ( 26 'App\\' => 27 array ( 28 0 => __DIR__ . '/../..' . '/app', 29 ), 30 ); 31 32 // 命名空间映射:直接命名空间全名与目录的映射 33 public static $classMap = array ( 34 'ErrorCode' => __DIR__ . '/../..' . '/app/Library/Crypt/errorCode.php', 35 'ErrorCode1' => __DIR__ . '/../..' . '/app/Library/Phonecrypt/errorCode1.php', 36 'Highlight\\Autoloader' => __DIR__ . '/../..' . '/app/Library/Highlight/Autoloader.php', 37 'Highlight\\Highlighter' => __DIR__ . '/../..' . '/app/Library/Highlight/Highlighter.php', 38 'Highlight\\JsonRef' => __DIR__ . '/../..' . '/app/Library/Highlight/JsonRef.php', 39 'Highlight\\Language' => __DIR__ . '/../..' . '/app/Library/Highlight/Language.php', 40 'PKCS7Encoder' => __DIR__ . '/../..' . '/app/Library/Crypt/pkcs7Encoder.php', 41 'Prpcrypt' => __DIR__ . '/../..' . '/app/Library/Crypt/pkcs7Encoder.php', 42 'SHA1' => __DIR__ . '/../..' . '/app/Library/Crypt/sha1.php', 43 'WXBizDataCrypt1' => __DIR__ . '/../..' . '/app/Library/Phonecrypt/wxBizDataCrypt1.php', 44 'WXBizMsgCrypt' => __DIR__ . '/../..' . '/app/Library/Crypt/wxBizMsgCrypt.php', 45 'XMLParse' => __DIR__ . '/../..' . '/app/Library/Crypt/xmlparse.php', 46 ); 47 48 /** 49 * 这个方法是静态初始化类的核心 50 * 将自己类中的顶级命名空间映射给了ClassLoader类 51 * @param ClassLoader $loader 52 * @return mixed 53 * 返回一个已经绑定$this对象和类作用域的闭包(说明:类作用域可以访问私有属性) 54 */ 55 public static function getInitializer(ClassLoader $loader) 56 { 57 // 匿名函数的绑定功能,返回的是一个匿名函数(闭包) 58 return \Closure::bind(function () use ($loader) { 59 $loader->prefixLengthsPsr4 = ComposerStaticInit03d4bf55ac53dfdc34a9b856a0bd37a2::$prefixLengthsPsr4; 60 $loader->prefixDirsPsr4 = ComposerStaticInit03d4bf55ac53dfdc34a9b856a0bd37a2::$prefixDirsPsr4; 61 $loader->classMap = ComposerStaticInit03d4bf55ac53dfdc34a9b856a0bd37a2::$classMap; 62 63 }, null, ClassLoader::class); // 这里相当于给ClassLoader类添加了一个静态成员方法 64 } 65 }
这个类里面重点要关注getInitializer(),这个方法返回的是一个已经绑定$this对象和类作用域的闭包,利用匿名函数的绑定功能,将autoload_static类中变量的值赋给ClassLoader类中相应的私有成员变量。从而实现将自己类中的顶级命名空间映射给了 ClassLoader 类。
六、 ClassLoader.php 自动加载核心类
自动加载核心类对象的注册,调用核心类接口初始化顶级命名空间是在ClassLoader类里面实现的。
由于ClassLoader代码比较多,这里只摘取相关功能实现所涉及到的部分代码来说明。具体的分析在代码注释里面:
1 <?php 2 namespace Composer\Autoload; 3 class ClassLoader 4 { 5 // PSR-4 6 private $prefixLengthsPsr4 = array(); 7 private $prefixDirsPsr4 = array(); 8 private $fallbackDirsPsr4 = array(); 9 10 // PSR-0 11 private $prefixesPsr0 = array(); 12 private $fallbackDirsPsr0 = array(); 13 14 private $useIncludePath = false; 15 private $classMap = array(); 16 private $classMapAuthoritative = false; 17 private $missingClasses = array(); 18 private $apcuPrefix; 19 /** 20 * 注册自动加载核心类对象 21 * Registers this instance as an autoloader. 22 * 23 * @param bool $prepend Whether to prepend the autoloader or not 24 */ 25 public function register($prepend = false) 26 { 27 spl_autoload_register(array($this, 'loadClass'), true, $prepend); 28 } 29 30 /** 31 * 自动加载的关键 32 * Loads the given class or interface. 33 * 34 * @param string $class The name of the class 35 * @return bool|null True if loaded, null otherwise 36 */ 37 public function loadClass($class) 38 { 39 if ($file = $this->findFile($class)) { 40 // 注意:这个用于查找路径成功后,用于加载文件的includeFile()仍然是类外面的函数 41 includeFile($file); 42 43 return true; 44 } 45 } 46 47 /** 48 * 在解析命名空间的时候主要分为两部分:classMap和findFileWithExtension函数 49 * Finds the path to the file where the class is defined. 50 * 51 * @param string $class The name of the class 52 * 53 * @return string|false The path if found, false otherwise 54 */ 55 public function findFile($class) 56 { 57 // class map lookup 58 // 第一部分:直接看命名空间是否在映射数组中 59 if (isset($this->classMap[$class])) { 60 return $this->classMap[$class]; 61 } 62 if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 63 return false; 64 } 65 if (null !== $this->apcuPrefix) { 66 $file = apcu_fetch($this->apcuPrefix.$class, $hit); 67 if ($hit) { 68 return $file; 69 } 70 } 71 72 // 第二部分:这个地方要麻烦一些,包含了PSRO和PSR4的实现 73 // 首先默认用 .php 后缀名调用 findFileWithExtension 函数里,利用PSR4标准尝试解析目录文件,如果文件不存在则继续用PSR0标准解析 74 $file = $this->findFileWithExtension($class, '.php'); 75 76 // 如果解析出来的目录文件仍然不存在,但是环境是 HHVM 虚拟机,继续用后缀名 .hh 再次调用 findFileWithExtension 函数 77 // Search for Hack files if we are running on HHVM 78 if (false === $file && defined('HHVM_VERSION')) { 79 $file = $this->findFileWithExtension($class, '.hh'); 80 } 81 82 if (null !== $this->apcuPrefix) { 83 apcu_add($this->apcuPrefix.$class, $file); 84 } 85 86 87 // 如果不存在,说明此命名空间无法加载,放到missingClasses中设为true 88 if (false === $file) { 89 // Remember that this class does not exist. 90 $this->missingClasses[$class] = true; 91 } 92 93 return $file; 94 } 95 96 /** 97 * 这个方法是重点:包含了PSR0和PSR4标准的实现 98 * @param $class 99 * @param $ext 100 * @return false|string 101 */ 102 private function findFileWithExtension($class, $ext) 103 { 104 // PSR-4 lookup 105 // 尝试利用 PSR4 标准映射目录:如果命名空间是以\开头的,要去掉\然后再匹配 106 $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 107 108 $first = $class[0]; 109 if (isset($this->prefixLengthsPsr4[$first])) { 110 $subPath = $class; 111 while (false !== $lastPos = strrpos($subPath, '\\')) { 112 $subPath = substr($subPath, 0, $lastPos); 113 $search = $subPath . '\\'; 114 if (isset($this->prefixDirsPsr4[$search])) { 115 $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); 116 foreach ($this->prefixDirsPsr4[$search] as $dir) { 117 if (file_exists($file = $dir . $pathEnd)) { 118 return $file; 119 } 120 } 121 } 122 } 123 } 124 125 // PSR-4 fallback dirs 126 // 如果失败,则利用 fallbackDirsPsr4 数组里面的目录继续判断是否存在文件 127 foreach ($this->fallbackDirsPsr4 as $dir) { 128 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 129 return $file; 130 } 131 } 132 133 // PSR-0 lookup 134 // 如果PSR4标准加载失败,则要进行PSR0标准加载 135 if (false !== $pos = strrpos($class, '\\')) { 136 // namespaced class name 137 $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 138 . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 139 } else { 140 // PEAR-like class name 141 $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 142 } 143 144 if (isset($this->prefixesPsr0[$first])) { 145 foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 146 if (0 === strpos($class, $prefix)) { 147 foreach ($dirs as $dir) { 148 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 149 return $file; 150 } 151 } 152 } 153 } 154 } 155 156 // PSR-0 fallback dirs 157 // 如果失败,则利用 fallbackDirsPsr0 数组里面的目录继续判断是否存在文件 158 foreach ($this->fallbackDirsPsr0 as $dir) { 159 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 160 return $file; 161 } 162 } 163 164 // PSR-0 include paths. 165 if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 166 return $file; 167 } 168 169 return false; 170 } 171 } 172 173 /** 174 * Scope isolated include. 175 * 176 * Prevents access to $this/self from included files. 177 */ 178 function includeFile($file) 179 { 180 include $file; 181 }
上面代码仔细阅读几遍后,发现其实并不难。主要抓住PSR4 标准和PSR0标准各自是如何映射目录的,PSR4和PSR0的区别。
为了加深理解,这里把autoload_static类的成员属性prefixLengthsPsr4、prefixDirsPsr4、prefixesPsr0单独拿出来说明。
注意:prefixLengthsPsr4和prefixesPsr0,都是以字母作为索引,这些字母是区分大小写的。
1 <?php 2 namespace Composer\Autoload; 3 4 class ComposerStaticInit03d4bf55ac53dfdc34a9b856a0bd37a2 5 { 6 public static $prefixLengthsPsr4 = array ( 7 'Q' =>array ( 8 'Qcloud\\Sms\\' => 11, 9 ), 10 'P' => 11 array ( 12 'Psy\\' => 4, 13 'Psr\\SimpleCache\\' => 16, 14 'Psr\\Log\\' => 8, 15 'Psr\\Http\\Message\\' => 17, 16 'Psr\\Http\\Client\\' => 16, 17 'Psr\\Container\\' => 14, 18 'Prophecy\\' => 9, 19 'Predis\\' => 7, 20 'PhpParser\\' => 10, 21 'PhpOffice\\PhpSpreadsheet\\' => 25, 22 ) 23 ); 24 25 public static $prefixDirsPsr4 = array ( 26 'phpDocumentor\\Reflection\\' => 27 array ( 28 0 => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src', 29 1 => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src', 30 2 => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src', 31 ), 32 'ZipStream\\' => 33 array ( 34 0 => __DIR__ . '/..' . '/maennchen/zipstream-php/src', 35 ) 36 ); 37 38 public static $prefixesPsr0 = array ( 39 'U' => 40 array ( 41 'UpdateHelper\\' => 42 array ( 43 0 => __DIR__ . '/..' . '/kylekatarnls/update-helper/src', 44 ), 45 ), 46 'G' => 47 array ( 48 'Guzzle\\Tests' => 49 array ( 50 0 => __DIR__ . '/..' . '/guzzle/guzzle/tests', 51 ), 52 'Guzzle' => 53 array ( 54 0 => __DIR__ . '/..' . '/guzzle/guzzle/src', 55 ), 56 ) 57 ); 58 }
由于整体描述起来比较复杂,建议通过仔细阅读源码,以及用debug多调试几次,来熟悉这个具体实现过程。
这里强调其中的几个重点:
1)PSR4标准顶级命名空间映射数组:分为$prefixLengthsPsr4和$prefixDirsPsr4,PSR4 标准是用顶级命名空间目录替换顶级命名空间。
2)利用PSR4标准映射目录的流程:比如我们要找$class为phpDocumentor\Reflection\Element()的命名空间,首先就会取第一个字母p,到prefixLengthsPsr4中查找是否有记录,如果有,
再到$prefixDirsPsr4中去查找phpDocumentor\Reflection\\对应的索引数组,如果有,就将这个数组遍历,然后以这个值作为目录$dir,Element.php作为$pathEnd,将$dir与$pathEnd拼接起来作为一个文件,然后去查找这个文件是否存在,如果存在就返回该文件。这里只是描述了一个大体流程,具体细节比如某些地方针对'\'的处理还要仔细阅读源码。
3)利用PSR0标准映射目录的流程:还是以上面的例子,要找phpDocumentor\Reflection\Element(),首先就会取第一个字母p,到prefixesPsr0中查找是否有记录,如果有,就将对应的数组遍历,这个$k比如为phpDocumentor\\Reflection,如果$class中存在这个$k,那么就继续遍历对应的索引数组$v,如果根据$dir . DIRECTORY_SEPARATOR . $logicalPathPsr0拼装的文件存在,则返回这个文件。具体细节比如某些地方针对'_'的处理还要仔细阅读源码。
七、总结
整体目录如下图:
composer中各个文件(总共8个文件)的作用,根据作用依次顺序为:
1. autoload.php 不做具体逻辑,引入自动加载引导类autoload_real.php,调用引导类中getLoader方法
2. autoload_real.php 自动加载核心类对象的初始化和注册
3. autoload_static.php 自动加载核心类对象的顶级命名空间静态初始化(通过匿名绑定函数实现)
如下图:
4. ClassLoader.php 自动加载核心类,其实所有功能的实现都是围绕着这个类来展开。
5. autoload_files.php 用于加载全局函数的文件,返回值为数组,key为哈希值,value为文件路径普通初始化需要用到的文件
如下图:
6. autoload_namespaces.php :返回一个数组,存放着顶级命名空间与文件目录的映射(符合PSR0 标准)
如下图:
7. autoload_psr4.php:返回一个数组,存放着顶级命名空间与文件目录的映射(符合PSR4 标准)
如下图:
8. autoload_classmap.php: 返回一个数组,存放着完整命名空间与文件路径的直接映射(自动加载的最简单形式)
如下图:
一些值得特别关注的地方
到这里,composer的源码分析就基本结束了,还有几个值得关注的地方:
1. 根据apcu前缀来查找文件,这个作用是什么?
ClassLoader类中:
1 class ClassLoader 2 { 3 // APCu 缓存的命名空间 4 private $apcuPrefix; 5 6 if (null !== $this->apcuPrefix) { 7 $file = apcu_fetch($this->apcuPrefix.$class, $hit); 8 if ($hit) { 9 return $file; 10 } 11 } 12 ... 13 14 if (null !== $this->apcuPrefix) { 15 apcu_add($this->apcuPrefix.$class, $file); 16 } 17 18 }
APCu 前缀用于优化自动加载过程,提升性能和管理缓存。
2. 为什么对于加载类和全局函数都要根据 $useStaticLoader 判断使用何种方式初始化?
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
通过判断 $useStaticLoader ,可以决定使用哪种方式进行初始化,以优化性能和适应不同的应用需求。
1) 静态初始化: 性能较好,适合简单项目,加载后不可更改。
2) 普通初始化: 灵活性强,适合复杂项目,可以动态管理类和函数的加载。
3. 使用静态初始化的条件为什么跟PHP版本小于5.6、不支持HHVM虚拟机环境、存在zend_loader_file_encoded有关系?
1) PHP_VERSION_ID >= 50600
这个条件检查当前 PHP 版本是否大于或等于 5.6。较新版本的 PHP 提供了更好的自动加载机制和性能优化,因此要求较高的版本才能使用静态加载器。
2) !defined('HHVM_VERSION')
HHVM 是一种替代 PHP 的虚拟机,它的自动加载机制与 PHP 的实现可能有所不同,因此在 HHVM 中不使用静态加载器。
3) (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded())
这个条件首先检查 zend_loader_file_encoded 函数是否存在。如果存在,则进一步判断它的返回值。
zend_loader_file_encoded 通常用于判断当前脚本是否经过 Zend 编码(如 Zend Guard),在这种情况下,静态加载器可能无法正常工作。
参考链接:
https://segmentfault.com/a/1190000014948542
https://zyf.im/2019/04/28/composer-autoload-in-laravel/
https://segmentfault.com/q/1010000007149666
https://www.laruence.com/2012/09/12/2765.html