目录

  • composer
    • autoload_classmap.php
    • autoload_files.php:助手文件
    • autoload_real.php:自动加载逻辑
    • autoload_static.php:
    • autoload_psr4.php:PSR4配置文件
    • autoload_namespace.php:PSR0配置文件
    • ClassLoader.php:类加载器实现一个PSR-0、PSR-4和classmap类加载器。
  • autoload.php:入口文件,加载 autoload_real.php 文件 调用加载器 getLoader()

生命周期

  • autoload.php:入口文件,加载 autoload_real.php 文件 调用加载器 getLoader()
  • autoload_real.php getLoader() 静态方法,注册loadClassLoader用来加载初始化ClassLoader类
  • 判断是否支持静态初始化(静态初始化只支持 PHP5.6 以上版本并且不支持 HHVM 虚拟机)
    这一过程主要将缓存的文件加载进程序,赋值给ClassLoader的属性
    • 支持静态初始化 加载autoload_static.php 文件
    • 不支持
      • 加载 autoload_namespaces.php
      • 加载 autoload_psr4.php’
      • 加载 autoload_classmap.php
  • 将 ClassLoader::loadClass注册为自动加载函数
  • 加载 autoload_files.php 助手文件

类的加载

类的加载调用ClassLoader::loadClass文件,顺序如下

  • 调用 findFile 查找具体文件

    • 在 classMap 数组中查找类 找到返回
    • 如果使用 composer —class-authorizative 或者 该类被标记为missing 返回false
    • 如果开启了 apcu扩展,在apcu扩展中查找
    • 在系统文件中查找(通过类名和后缀) ```php /**

      • 在文件系统中查找类
      • @param string $class 要查找的类
      • @param string $ext 后缀
      • @return bool|false|string / private function findFileWithExtension($class, $ext) { /*

        • PSR-4 lookup
        • 在符合 PSR-4 规范的集合中查找 */ // $class = App\Http\Controllers\Home\HomeController // $ext = .php // $logicalPathPsr4 = App/Http/Controllers/Home/HomeController.php $logicalPathPsr4 = strtr($class, ‘\‘, DIRECTORY_SEPARATOR) . $ext;

          $first = $class[0];// A // 查找前缀数组 // $first = A if (isset($this->prefixLengthsPsr4[$first])) { // $subPath = App\Http\Controllers\Home\HomeController $subPath = $class; // 循环逐级向上查找 // 第一次循环 $lastPos = 25 $subPath = App\Http\Controllers\Home $search = App\Http\Controllers\Home\ // 第二次循环 $lastPos = 20 $subPath = App\Http\Controllers $search = App\Http\Controllers\ // 第三次循环 $lastPos = 8 $subPath = App\Http $search = App\Http\ // 第四次循环 $lastPos = 3 $subPath = App $search = App\ // 查找到App\返回文件地址 $pathEnd = /Http/Controllers/Home/HomeController.php // $logicalPathPsr4 = App/Http/Controllers/Home/HomeController.php

          while (false !== $lastPos = strrpos($subPath, ‘\‘)) {

          1. $subPath = substr($subPath, 0, $lastPos);
          2. $search = $subPath . '\\';// Illuminate\Foundation\
          3. if (isset($this->prefixDirsPsr4[$search])) { // 在Psr-4 前缀目录 对应数组中查找
          4. $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
          5. // 在系统目录中查找文件,查找到返回
          6. foreach ($this->prefixDirsPsr4[$search] as $dir) {
          7. if (file_exists($file = $dir . $pathEnd)) {
          8. return $file;
          9. }
          10. }
          11. }

          } }

          // PSR-4 fallback dirs // 在fallbackDirsPsr4中查找文件 foreach ($this->fallbackDirsPsr4 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {

          1. return $file;

          } }

  1. /**
  2. * 在符合 PSR-0 规范的集合中查找
  3. * PSR-0 lookup
  4. *
  5. */
  6. // 如果包含命名空间
  7. if (false !== $pos = strrpos($class, '\\')) {
  8. // namespaced class name
  9. // 获取符合psr0规范的类名,包含命名空间
  10. // Dir1\Dir2\Dir3_ClassName1 ---> Dir1\Dir2/ClassName1
  11. $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
  12. . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
  13. } else {
  14. // PEAR-like class name
  15. // PEAR的风格类
  16. // Dir1_Dir2_ClassName1 --->Dir1/Dir2/ClassName1
  17. $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
  18. }
  19. if (isset($this->prefixesPsr0[$first])) {
  20. foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
  21. if (0 === strpos($class, $prefix)) {
  22. foreach ($dirs as $dir) {
  23. if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
  24. return $file;
  25. }
  26. }
  27. }
  28. }
  29. }
  30. // PSR-0 fallback dirs
  31. foreach ($this->fallbackDirsPsr0 as $dir) {
  32. if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
  33. return $file;
  34. }
  35. }
  36. // PSR-0 include paths.
  37. if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
  38. return $file;
  39. }
  40. return false;
  41. }

}

  1. - 如果开启了HHVM hack文件中搜索
  2. - 开启 apcu 扩展,缓存查找目标信息
  3. - 没有找到 标记missing
  4. - 调用外部函数 includeFile 加载找到的文件
  5. <a name="5b804b05"></a>
  6. ### 源码
  7. <a name="composer_real.php"></a>
  8. #### composer_real.php
  9. ```php
  10. <?php
  11. // autoload_real.php @generated by Composer
  12. class ComposerAutoloaderInite1402bdd050981d44622d2e1c72145b4
  13. {
  14. private static $loader;
  15. /**
  16. * 加载 ClassLoader.php
  17. * @param string $class 类名
  18. */
  19. public static function loadClassLoader($class)
  20. {
  21. if ('Composer\Autoload\ClassLoader' === $class) {
  22. // 如若是 Composer\Autoload\ClassLoader 类,包含当前目录下的ClassLoader
  23. require __DIR__ . '/ClassLoader.php';
  24. }
  25. }
  26. // 注册加载器
  27. public static function getLoader()
  28. {
  29. if (null !== self::$loader) {
  30. return self::$loader;
  31. }
  32. // 注册self::loadClassLoader为自动加载函数,用来加载 ClassLoader
  33. spl_autoload_register(array('ComposerAutoloaderInite1402bdd050981d44622d2e1c72145b4', 'loadClassLoader'), true, true);
  34. // 实例话ClassLoader
  35. self::$loader = $loader = new \Composer\Autoload\ClassLoader();
  36. // 注销 self::loadClassLoader 为自动加载函数
  37. spl_autoload_unregister(array('ComposerAutoloaderInite1402bdd050981d44622d2e1c72145b4', 'loadClassLoader'));
  38. /**
  39. * 初始化 自动加载核心对象
  40. * 声明静态加载器:静态初始化只支持 PHP5.6 以上版本并且不支持 HHVM 虚拟机(Facebook 的一个为php提高性能的一个开源项目)
  41. * PHP_VERSION_ID:70322
  42. * defined('HHVM_VERSION'):false
  43. * zend_loader_file_encoded:如果当前文件是Zend编码的文件,则返回true。
  44. */
  45. $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
  46. if ($useStaticLoader) {
  47. // 支持静态加载,加载 autoload_static 文件
  48. require_once __DIR__ . '/autoload_static.php';
  49. // 调用匿名函数 实现静态加载(实际对加载器$loader属性赋值)
  50. call_user_func(\Composer\Autoload\ComposerStaticInite1402bdd050981d44622d2e1c72145b4::getInitializer($loader));
  51. } else {
  52. // 不支持静态加载,加载 autoload_namespaces 文件,使用命名空间
  53. $map = require __DIR__ . '/autoload_namespaces.php';
  54. // 遍历文件 设置 prefixesPsr0
  55. foreach ($map as $namespace => $path) {
  56. $loader->set($namespace, $path);
  57. }
  58. // 加载 autoload_psr4.php
  59. $map = require __DIR__ . '/autoload_psr4.php';
  60. // 遍历文件 设置 prefixLengthsPsr4 prefixDirsPsr4
  61. foreach ($map as $namespace => $path) {
  62. $loader->setPsr4($namespace, $path);
  63. }
  64. // 加载 autoload_classmap.php
  65. $classMap = require __DIR__ . '/autoload_classmap.php';
  66. // 配置存在 设置 classMap
  67. if ($classMap) {
  68. $loader->addClassMap($classMap);
  69. }
  70. }
  71. $loader->register(true);
  72. if ($useStaticLoader) {
  73. $includeFiles = Composer\Autoload\ComposerStaticInite1402bdd050981d44622d2e1c72145b4::$files;
  74. } else {
  75. $includeFiles = require __DIR__ . '/autoload_files.php';
  76. }
  77. foreach ($includeFiles as $fileIdentifier => $file) {
  78. composerRequiree1402bdd050981d44622d2e1c72145b4($fileIdentifier, $file);
  79. }
  80. return $loader;
  81. }
  82. }
  83. function composerRequiree1402bdd050981d44622d2e1c72145b4($fileIdentifier, $file)
  84. {
  85. if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
  86. require $file;
  87. $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
  88. }
  89. }

ClassLoader.php

  1. <?php
  2. /*
  3. * This file is part of Composer.
  4. *
  5. * (c) Nils Adermann <naderman@naderman.de>
  6. * Jordi Boggiano <j.boggiano@seld.be>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Composer\Autoload;
  12. /**
  13. * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
  14. * 类加载器实现一个PSR-0、PSR-4和classmap类加载器。
  15. * $loader = new \Composer\Autoload\ClassLoader();
  16. *
  17. * // register classes with namespaces
  18. * // 通过命名空间注册类 使用add()方法
  19. * $loader->add('Symfony\Component', __DIR__.'/component');
  20. * $loader->add('Symfony', __DIR__.'/framework');
  21. *
  22. * // activate the autoloader
  23. * // 启动自动加载器 使用register()
  24. * $loader->register();
  25. *
  26. * // to enable searching the include path (eg. for PEAR packages)
  27. * // 启用对include路径的搜索(例如PEAR包)
  28. * $loader->setUseIncludePath(true);
  29. *
  30. * In this example,
  31. * if you try to use a class in the Symfony\Component namespace or one of its children
  32. * (Symfony\Component\Console for instance),
  33. * the autoloader will first look for the class under the component/ directory,
  34. * and it will then fallback to the framework/ directory
  35. * if not found before giving up.
  36. * 在本例中,如果您尝试使用Symfony\Component命名空间中的某个类或其子级之一
  37. * (例如Symfony\Component\Console),
  38. * 则autoloader将首先在Component/目录下查找该类,如果找不到该类,则会在放弃之前回退到framework/目录。
  39. *
  40. * This class is loosely based on the Symfony UniversalClassLoader.
  41. * 这个类松散地(不精确的)基于Symfony UniversalClassLoader。
  42. *
  43. * @author Fabien Potencier <fabien@symfony.com>
  44. * @author Jordi Boggiano <j.boggiano@seld.be>
  45. * @see http://www.php-fig.org/psr/psr-0/
  46. * @see http://www.php-fig.org/psr/psr-4/
  47. */
  48. class ClassLoader
  49. {
  50. // PSR-4
  51. /**
  52. * PSR-4 前缀-长度 数组
  53. * @var array
  54. * ['p'] => array(
  55. * ['phpDocumentor\Reflection\'] => 25
  56. * ),
  57. * ['X'] => array(
  58. * ['XdgBaseDir\'] => 11
  59. * ),
  60. */
  61. private $prefixLengthsPsr4 = array();// PSR-4 前缀-长度
  62. /**
  63. * PSR-4 前缀-目录
  64. * @var array
  65. * 'phpDocumentor\Reflection\'=>[
  66. * '/Users/sun/code/php/laravel/blog/vendor/composer/../phpdocumentor/reflection-common/src',
  67. * '/Users/sun/code/php/laravel/blog/vendor/composer/../phpdocumentor/reflection-docblock/src',
  68. * '/Users/sun/code/php/laravel/blog/vendor/composer/../phpdocumentor/type-resolver/src'
  69. * ]
  70. * 'XdgBaseDir\'=>[
  71. * '/Users/sun/code/php/laravel/blog/vendor/composer/../dnoegel/php-xdg-base-dir/src'
  72. * ]
  73. */
  74. private $prefixDirsPsr4 = array(); // PSR-4 前缀目录
  75. private $fallbackDirsPsr4 = array(); // PSR-4 回退目录
  76. // PSR-0
  77. /**
  78. * @var array
  79. * 'M'=>[
  80. * 'Mockery'=>'/Users/sun/code/php/laravel/blog/vendor/composer/../mockery/mockery/library'
  81. * ]
  82. */
  83. private $prefixesPsr0 = array();
  84. private $fallbackDirsPsr0 = array();
  85. private $useIncludePath = false; // 对include路径的搜索(例如PEAR包)
  86. /**
  87. * class 对应路径
  88. * @var array
  89. * 'phpDocumentor\Reflection\Utils'=>'/Users/sun/code/php/laravel/blog/vendor/composer/../phpdocumentor/reflection-docblock/src/Utils.php'
  90. */
  91. private $classMap = array();
  92. private $classMapAuthoritative = false;
  93. private $missingClasses = array();
  94. private $apcuPrefix; // apcu 缓存前缀 null
  95. /**
  96. * 获取prefixesPsr0数组
  97. * @return array|mixed
  98. */
  99. public function getPrefixes()
  100. {
  101. /**
  102. * 如果 PSR-0 数组不为空,合并 PSR-0 数组,并返回
  103. */
  104. if (!empty($this->prefixesPsr0)) {
  105. // 调用回调函数,并把一个数组参数作为回调函数的参数
  106. // 作用:数组降低维度,二维降到一维数组
  107. return call_user_func_array('array_merge', $this->prefixesPsr0);
  108. }
  109. return array();
  110. }
  111. /**
  112. * 返回 prefixDirsPsr4
  113. * @return array
  114. */
  115. public function getPrefixesPsr4()
  116. {
  117. return $this->prefixDirsPsr4;
  118. }
  119. /**
  120. * 获取 PSR-0 回退目录数组
  121. * @return array
  122. */
  123. public function getFallbackDirs()
  124. {
  125. return $this->fallbackDirsPsr0;
  126. }
  127. /**
  128. * 获取 PSR-4 回退目录数组
  129. * @return array
  130. */
  131. public function getFallbackDirsPsr4()
  132. {
  133. return $this->fallbackDirsPsr4;
  134. }
  135. /**
  136. * 获取 class Map
  137. * @return array
  138. */
  139. public function getClassMap()
  140. {
  141. return $this->classMap;
  142. }
  143. /**
  144. * 添加 class Map
  145. * 如果存在,追加
  146. * @param array $classMap Class to filename map
  147. */
  148. public function addClassMap(array $classMap)
  149. {
  150. if ($this->classMap) {
  151. $this->classMap = array_merge($this->classMap, $classMap);
  152. } else {
  153. $this->classMap = $classMap;
  154. }
  155. }
  156. /**
  157. * 为一个给定的前缀注册一组PSR-0目录,可以将其附加到先前为此前缀设置的目录的前面或后面。
  158. * Registers a set of PSR-0 directories for a given prefix, either
  159. * appending or prepending to the ones previously set for this prefix.
  160. * 如果 $prefix 存在,$path 添加到prefixesPsr0中,否则添加到fallbackDirsPsr0中
  161. *
  162. * @param string $prefix The prefix 前缀
  163. * @param array|string $paths The PSR-0 root directories PSR-0根目录
  164. * @param bool $prepend Whether to prepend the directories 是否在目录前
  165. */
  166. public function add($prefix, $paths, $prepend = false)
  167. {
  168. // 不存在前缀
  169. if (!$prefix) {
  170. // 是否添加到 Psr0回退目录 目录前
  171. if ($prepend) {
  172. // 添加到 Psr0回退目录 目录前
  173. $this->fallbackDirsPsr0 = array_merge(
  174. (array) $paths,
  175. $this->fallbackDirsPsr0
  176. );
  177. } else {
  178. // 添加到 Psr0回退目录 目录后
  179. $this->fallbackDirsPsr0 = array_merge(
  180. $this->fallbackDirsPsr0,
  181. (array) $paths
  182. );
  183. }
  184. return;
  185. }
  186. // 存在前缀
  187. $first = $prefix[0];
  188. // psr0 前缀数组中 不存在但前前缀
  189. if (!isset($this->prefixesPsr0[$first][$prefix])) {
  190. $this->prefixesPsr0[$first][$prefix] = (array) $paths;
  191. return;
  192. }
  193. // 按照 前置判断 合并前缀数组
  194. if ($prepend) {
  195. $this->prefixesPsr0[$first][$prefix] = array_merge(
  196. (array) $paths,
  197. $this->prefixesPsr0[$first][$prefix]
  198. );
  199. } else {
  200. $this->prefixesPsr0[$first][$prefix] = array_merge(
  201. $this->prefixesPsr0[$first][$prefix],
  202. (array) $paths
  203. );
  204. }
  205. }
  206. /**
  207. * 为给定的命名空间注册一组PSR-4目录,可以将其附加到先前为此命名空间设置的目录之前。
  208. * Registers a set of PSR-4 directories for a given namespace, either
  209. * appending or prepending to the ones previously set for this namespace.
  210. * 如果 $prefix 存在,
  211. * prefixDirsPsr4 存在 $path 更新到prefixesPsr4中,
  212. * prefixDirsPsr4 不存在 $path 添加到prefixesPsr4中,并设置prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
  213. * 如果 $prefix 不存在,添加到fallbackDirsPsr4中
  214. *
  215. * @param string $prefix The prefix/namespace, with trailing '\\' 前缀/命名空间,以\\结尾
  216. * @param array|string $paths The PSR-4 base directories
  217. * @param bool $prepend Whether to prepend the directories
  218. *
  219. * @throws \InvalidArgumentException
  220. */
  221. public function addPsr4($prefix, $paths, $prepend = false)
  222. {
  223. if (!$prefix) {
  224. // Register directories for the root namespace.
  225. if ($prepend) {
  226. $this->fallbackDirsPsr4 = array_merge(
  227. (array) $paths,
  228. $this->fallbackDirsPsr4
  229. );
  230. } else {
  231. $this->fallbackDirsPsr4 = array_merge(
  232. $this->fallbackDirsPsr4,
  233. (array) $paths
  234. );
  235. }
  236. } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
  237. // Register directories for a new namespace.
  238. $length = strlen($prefix);
  239. if ('\\' !== $prefix[$length - 1]) {
  240. throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
  241. }
  242. $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
  243. $this->prefixDirsPsr4[$prefix] = (array) $paths;
  244. } elseif ($prepend) {
  245. // Prepend directories for an already registered namespace.
  246. $this->prefixDirsPsr4[$prefix] = array_merge(
  247. (array) $paths,
  248. $this->prefixDirsPsr4[$prefix]
  249. );
  250. } else {
  251. // Append directories for an already registered namespace.
  252. $this->prefixDirsPsr4[$prefix] = array_merge(
  253. $this->prefixDirsPsr4[$prefix],
  254. (array) $paths
  255. );
  256. }
  257. }
  258. /**
  259. * 为给定前缀注册一组PSR-0目录,替换先前为此前缀设置的任何其他目录。
  260. * Registers a set of PSR-0 directories for a given prefix,
  261. * replacing any others previously set for this prefix.
  262. *
  263. * @param string $prefix The prefix
  264. * @param array|string $paths The PSR-0 base directories
  265. */
  266. public function set($prefix, $paths)
  267. {
  268. if (!$prefix) {
  269. $this->fallbackDirsPsr0 = (array) $paths;
  270. } else {
  271. $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
  272. }
  273. }
  274. /**
  275. * 为给定名称空间注册一组PSR-4目录,替换先前为此命名空间设置的任何其他项。
  276. * Registers a set of PSR-4 directories for a given namespace,
  277. * replacing any others previously set for this namespace.
  278. *
  279. * @param string $prefix The prefix/namespace, with trailing '\\'
  280. * @param array|string $paths The PSR-4 base directories
  281. *
  282. * @throws \InvalidArgumentException
  283. */
  284. public function setPsr4($prefix, $paths)
  285. {
  286. if (!$prefix) {
  287. $this->fallbackDirsPsr4 = (array) $paths;
  288. } else {
  289. $length = strlen($prefix);
  290. if ('\\' !== $prefix[$length - 1]) {
  291. // 非空的PSR-4前缀必须以命名空间分隔符结尾。
  292. throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
  293. }
  294. $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
  295. $this->prefixDirsPsr4[$prefix] = (array) $paths;
  296. }
  297. }
  298. /**
  299. * 启用对类文件的包含路径的搜索
  300. * Turns on searching the include path for class files.
  301. *
  302. * @param bool $useIncludePath
  303. */
  304. public function setUseIncludePath($useIncludePath)
  305. {
  306. $this->useIncludePath = $useIncludePath;
  307. }
  308. /**
  309. * 可用于检查autoloader是否使用include路径检查类
  310. * Can be used to check if the autoloader uses the include path to check for classes.
  311. *
  312. * @return bool
  313. */
  314. public function getUseIncludePath()
  315. {
  316. return $this->useIncludePath;
  317. }
  318. /**
  319. * 关闭在前缀和回退目录中搜索尚未在类映射中注册的类
  320. * Turns off searching the prefix and fallback directories for classes
  321. * that have not been registered with the class map.
  322. *
  323. * @param bool $classMapAuthoritative
  324. */
  325. public function setClassMapAuthoritative($classMapAuthoritative)
  326. {
  327. $this->classMapAuthoritative = $classMapAuthoritative;
  328. }
  329. /**
  330. * 如果在当前的类映射中找不到类,是否应该失败?
  331. * Should class lookup fail if not found in the current class map?
  332. *
  333. * @return bool
  334. */
  335. public function isClassMapAuthoritative()
  336. {
  337. return $this->classMapAuthoritative;
  338. }
  339. /**
  340. * APCu前缀,用于缓存已找到/未找到的类(如果已启用扩展)。
  341. * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
  342. *
  343. * @param string|null $apcuPrefix
  344. */
  345. public function setApcuPrefix($apcuPrefix)
  346. {
  347. /**
  348. * apcu_fetch:从缓存中提取变量
  349. * filter_var:函数通过指定的过滤器过滤一个变量。
  350. * ini_get():获取配置信息
  351. * FILTER_VAILIDATE:如果是 “1”, “true”, “on” 以及 “yes”,则返回 true,如果是 “0”, “false”, “off”, “no” 以及 “”,则返回 false。否则返回 NULL。
  352. *
  353. * 判断是否开启APC User Cache,没有开启设置null,开启设置传递的值
  354. */
  355. $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
  356. }
  357. /**
  358. * 获取正在使用的APCu前缀,如果未启用APCu缓存,则为null。
  359. * The APCu prefix in use, or null if APCu caching is not enabled.
  360. *
  361. * @return string|null
  362. */
  363. public function getApcuPrefix()
  364. {
  365. return $this->apcuPrefix;
  366. }
  367. /**
  368. * 将此实例注册为自动加载程序
  369. * Registers this instance as an autoloader.
  370. *
  371. * @param bool $prepend Whether to prepend the autoloader or not
  372. * 如果 $prepend 为true,则spl_autoload_register()将在自动加载堆栈上预加autoloader,而不是追加它。
  373. */
  374. public function register($prepend = false)
  375. {
  376. spl_autoload_register(array($this, 'loadClass'), true, $prepend);
  377. }
  378. /**
  379. * Unregisters this instance as an autoloader
  380. * 将此实例注销为自动加载程序
  381. */
  382. public function unregister()
  383. {
  384. spl_autoload_unregister(array($this, 'loadClass'));
  385. }
  386. /**
  387. * 加载给定的类或接口。(spl_autoload_register注册的方法)
  388. * Loads the given class or interface.
  389. * 不存在不处理
  390. * @param string $class The name of the class 类名
  391. * @return bool|null True if loaded, null otherwise
  392. */
  393. public function loadClass($class)
  394. {
  395. if ($file = $this->findFile($class)) {
  396. includeFile($file);
  397. return true;
  398. }
  399. }
  400. /**
  401. * 查找定义类的文件的路径
  402. * Finds the path to the file where the class is defined.
  403. *
  404. * @param string $class The name of the class
  405. *
  406. * @return string|false The path if found, false otherwise
  407. */
  408. public function findFile($class)
  409. {
  410. // class map lookup
  411. // 将 PSR-4/PSR-0 的规则转化为了 classmap 的规则,在classMap中查找类
  412. if (isset($this->classMap[$class])) {
  413. return $this->classMap[$class];
  414. }
  415. // composer 配置 --class-authorizative。在classMap查找不到的时候,认为为非法类,不在检索文件系统
  416. // 已经被记录过一次的不存在的类,返回false
  417. if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
  418. return false;
  419. }
  420. // 如果开启扩展 apcu ,在apcu中查找类
  421. // classmap 中找不到目标类时,将在文件系统中找到的结果存储到共享内存中, 当下次再查找时就可以从内存中直接返回,不用再去文件系统中再次查找。
  422. if (null !== $this->apcuPrefix) {
  423. $file = apcu_fetch($this->apcuPrefix.$class, $hit);
  424. if ($hit) {
  425. return $file;
  426. }
  427. }
  428. // 在文件系统中查找类
  429. $file = $this->findFileWithExtension($class, '.php');
  430. // Search for Hack files if we are running on HHVM
  431. // 如果我们在HHVM上运行,请搜索Hack文件
  432. if (false === $file && defined('HHVM_VERSION')) {
  433. $file = $this->findFileWithExtension($class, '.hh');
  434. }
  435. // classmap 中找不到目标类时,将在文件系统中找到的结果存储到共享内存中, 当下次再查找时就可以从内存中直接返回,不用再去文件系统中再次查找。
  436. if (null !== $this->apcuPrefix) {
  437. // 仅当变量尚未存储时,才在数据存储中缓存该变量
  438. apcu_add($this->apcuPrefix.$class, $file);
  439. }
  440. // 记住这个类不存在
  441. if (false === $file) {
  442. // Remember that this class does not exist.
  443. $this->missingClasses[$class] = true;
  444. }
  445. return $file;
  446. }
  447. /**
  448. * 在文件系统中查找类
  449. * @param string $class 要查找的类
  450. * @param string $ext 后缀
  451. * @return bool|false|string
  452. */
  453. private function findFileWithExtension($class, $ext)
  454. {
  455. /**
  456. * PSR-4 lookup
  457. * 在符合 PSR-4 规范的集合中查找
  458. */
  459. // $class = App\Http\Controllers\Home\HomeController
  460. // $ext = .php
  461. // $logicalPathPsr4 = App/Http/Controllers/Home/HomeController.php
  462. $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
  463. $first = $class[0];// A
  464. // 查找前缀数组
  465. // $first = A
  466. if (isset($this->prefixLengthsPsr4[$first])) {
  467. // $subPath = App\Http\Controllers\Home\HomeController
  468. $subPath = $class;
  469. // 循环逐级向上查找
  470. // 第一次循环 $lastPos = 25 $subPath = App\Http\Controllers\Home $search = App\Http\Controllers\Home\
  471. // 第二次循环 $lastPos = 20 $subPath = App\Http\Controllers $search = App\Http\Controllers\
  472. // 第三次循环 $lastPos = 8 $subPath = App\Http $search = App\Http\
  473. // 第四次循环 $lastPos = 3 $subPath = App $search = App\
  474. // 查找到App\返回文件地址 $pathEnd = /Http/Controllers/Home/HomeController.php
  475. // $logicalPathPsr4 = App/Http/Controllers/Home/HomeController.php
  476. while (false !== $lastPos = strrpos($subPath, '\\')) {
  477. $subPath = substr($subPath, 0, $lastPos);
  478. $search = $subPath . '\\';// Illuminate\Foundation\
  479. if (isset($this->prefixDirsPsr4[$search])) { // 在Psr-4 前缀目录 对应数组中查找
  480. $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
  481. // 在系统目录中查找文件,查找到返回
  482. foreach ($this->prefixDirsPsr4[$search] as $dir) {
  483. if (file_exists($file = $dir . $pathEnd)) {
  484. return $file;
  485. }
  486. }
  487. }
  488. }
  489. }
  490. // PSR-4 fallback dirs
  491. // 在fallbackDirsPsr4中查找文件
  492. foreach ($this->fallbackDirsPsr4 as $dir) {
  493. if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
  494. return $file;
  495. }
  496. }
  497. /**
  498. * 在符合 PSR-0 规范的集合中查找
  499. * PSR-0 lookup
  500. *
  501. */
  502. // 如果包含命名空间
  503. if (false !== $pos = strrpos($class, '\\')) {
  504. // namespaced class name
  505. // 获取符合psr0规范的类名,包含命名空间
  506. // Dir1\Dir2\Dir3_ClassName1 ---> Dir1\Dir2/ClassName1
  507. $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
  508. . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
  509. } else {
  510. // PEAR-like class name
  511. // PEAR的风格类
  512. // Dir1_Dir2_ClassName1 --->Dir1/Dir2/ClassName1
  513. $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
  514. }
  515. if (isset($this->prefixesPsr0[$first])) {
  516. foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
  517. if (0 === strpos($class, $prefix)) {
  518. foreach ($dirs as $dir) {
  519. if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
  520. return $file;
  521. }
  522. }
  523. }
  524. }
  525. }
  526. // PSR-0 fallback dirs
  527. foreach ($this->fallbackDirsPsr0 as $dir) {
  528. if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
  529. return $file;
  530. }
  531. }
  532. // PSR-0 include paths.
  533. if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
  534. return $file;
  535. }
  536. return false;
  537. }
  538. }
  539. /**
  540. * Scope isolated include.
  541. * 范围隔离包括(单独函数,非类方法)
  542. * Prevents access to $this/self from included files.
  543. * 阻止从包含的文件访问$this/self。
  544. */
  545. function includeFile($file)
  546. {
  547. include $file;
  548. }