🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] * * * * * ## 1 自动加载器源代码(thinkphp/library/think/Loader.php的加载器部分,其余的实例化部分分析见[另:自动加载器实例化](http://www.kancloud.cn/zmwtp/tp5/120008)) ~~~ protected static $map = []; protected static $load = []; protected static $namespace = []; private static $prefixLengthsPsr4 = []; private static $prefixDirsPsr4 = []; private static $prefixesPsr0 = []; ~~~ ~~~ public static function autoload($class) { if (isset(self::$map[$class])) { if (is_file(self::$map[$class])) { APP_DEBUG && self::$load[] = self::$map[$class]; include self::$map[$class]; } } elseif ($file = self::findFileInComposer($class)) { APP_DEBUG && self::$load[] = $file; include $file; } else { if (!strpos($class, '\\')) { return; } list($name, $class) = explode('\\', $class, 2); if (isset(self::$namespace[$name])) { $path = self::$namespace[$name]; } elseif (is_dir(EXTEND_PATH . $name)) { $path = EXTEND_PATH . $name . DS; } else { return; } $filename = $path . str_replace('\\', DS, $class) . EXT; if (is_file($filename)) { if (APP_DEBUG && IS_WIN && false === strpos(realpath($filename), $class . EXT)) { return; } APP_DEBUG && self::$load[] = $filename; include $filename; } else { Log::record('autoloader error : ' . $filename, 'notice'); } } } ~~~ ~~~ public static function addMap($class, $map = '') { if (is_array($class)) { self::$map = array_merge(self::$map, $class); } else { self::$map[$class] = $map; } } ~~~ ~~~ public static function addNamespace($namespace, $path = '') { if (is_array($namespace)) { self::$namespace = array_merge(self::$namespace, $namespace); } else { self::$namespace[$namespace] = $path; } } ~~~ ~~~ public static function register($autoload = '') { spl_autoload_register($autoload ? $autoload : 'think\\Loader::autoload'); self::registerComposerLoader(); } private static function registerComposerLoader() { if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) { $map = require VENDOR_PATH . 'composer/autoload_namespaces.php'; foreach ($map as $namespace => $path) { self::$prefixesPsr0[$namespace[0]][$namespace] = (array) $path; } } if (is_file(VENDOR_PATH . 'composer/autoload_psr4.php')) { $map = require VENDOR_PATH . 'composer/autoload_psr4.php'; foreach ($map as $namespace => $path) { $length = strlen($namespace); if ('\\' !== $namespace[$length - 1]) { throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } self::$prefixLengthsPsr4[$namespace[0]][$namespace] = $length; self::$prefixDirsPsr4[$namespace] = (array) $path; } } if (is_file(VENDOR_PATH . 'composer/autoload_classmap.php')) { $classMap = require VENDOR_PATH . 'composer/autoload_classmap.php'; if ($classMap) { self::addMap($classMap); } } if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) { $includeFiles = require VENDOR_PATH . 'composer/autoload_files.php'; foreach ($includeFiles as $fileIdentifier => $file) { self::composerRequire($fileIdentifier, $file); } } } ~~~ ~~~ private static function composerRequire($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { require $file; $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; } } ~~~ ~~~ private static function findFileInComposer($class, $ext = '.php') { $logicalPathPsr4 = strtr($class, '\\', DS) . $ext; $first = $class[0]; if (isset(self::$prefixLengthsPsr4[$first])) { foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) { if (0 === strpos($class, $prefix)) { foreach (self::$prefixDirsPsr4[$prefix] as $dir) { if (file_exists($file = $dir . DS . substr($logicalPathPsr4, $length))) { return $file; } } } } } if (false !== $pos = strrpos($class, '\\')) { $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) . strtr(substr($logicalPathPsr4, $pos + 1), '_', DS); } else { $logicalPathPsr0 = strtr($class, '_', DS) . $ext; } if (isset(self::$prefixesPsr0[$first])) { foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) { if (0 === strpos($class, $prefix)) { foreach ($dirs as $dir) { if (file_exists($file = $dir . DS . $logicalPathPsr0)) { return $file; } } } } } return self::$map[$class] = false; } ~~~ ~~~ public static function import($class, $baseUrl = '', $ext = EXT) { static $_file = []; $class = str_replace(['.', '#'], [DS, '.'], $class); if (isset($_file[$class . $baseUrl])) { return true; } else { $_file[$class . $baseUrl] = true; } if (empty($baseUrl)) { list($name, $class) = explode(DS, $class, 2); if (isset(self::$namespace[$name])) { $baseUrl = self::$namespace[$name]; } elseif ('@' == $name || MODULE_NAME == $name) { $baseUrl = MODULE_PATH; } elseif (in_array($name, ['traits', 'think', 'behavior'])) { $baseUrl = LIB_PATH; } elseif (APP_NAMESPACE == $name) { $baseUrl = APP_PATH; } elseif (is_dir(EXTEND_PATH . $name)) { $baseUrl = EXTEND_PATH; } else { $baseUrl = APP_PATH . $name . DS; } } elseif (substr($baseUrl, -1) != DS) { $baseUrl .= DS; } $filename = $baseUrl . $class . $ext; if (is_file($filename)) { if (APP_DEBUG && IS_WIN && false === strpos(realpath($filename), $class . $ext)) { return false; } include $filename; return true; } return false; } ~~~ ## 2 分析 Loader.php是框架的类的自动加载实现文件。可以使用autoload()自动挡加载类,import()手动加载指定目录的类。 `protected static $map = [];` 系统的类名映射缓存数组,在下面的addMap()使用 ` protected static $load = [];` 系统的加载列表缓存数组,在下面的autoload()使用 `protected static $namespace = [];` 系统的命名空间缓存数组,在下面的addNamespace()使用 ~~~ private static $prefixLengthsPsr4 = []; private static $prefixDirsPsr4 = []; ~~~ 系统的PSR-4缓存数组,在下面的registerComposerLoader()使用 `private static $prefixesPsr0 = [];` 系统的PSR-0缓存数组,在下面的registerComposerLoader()使用。 上面四个静态变量用作加载器的缓存数组,保证加载效率。 * * * * * 1 autoload($class) 根据类名自动加载。 `public static function autoload($class){}` 使用类库映射加载对应的类。 ~~~ if (isset(self::$map[$class])) { if (is_file(self::$map[$class])) { APP_DEBUG && self::$load[] = self::$map[$class]; include self::$map[$class]; } } ~~~ 使用composer加载类 ~~~ elseif ($file = self::findFileInComposer($class)) { APP_DEBUG && self::$load[] = $file; include $file; } ~~~ 根据命名空间自动加载类 ~~~ else { if (!strpos($class, '\\')) { return; } list($name, $class) = explode('\\', $class, 2); if (isset(self::$namespace[$name])) { $path = self::$namespace[$name]; } elseif (is_dir(EXTEND_PATH . $name)) { $path = EXTEND_PATH . $name . DS; } else { return; } $filename = $path . str_replace('\\', DS, $class) . EXT; if (is_file($filename)) { if (APP_DEBUG && IS_WIN && false === strpos(realpath($filename), $class . EXT)) { return; } APP_DEBUG && self::$load[] = $filename; include $filename; } else { Log::record('autoloader error : ' . $filename, 'notice'); } } ~~~ * * * * * 2 addMap($class,$map='') 注册类名映射 `public static function addMap($class, $map = ''){}` 数组合并到$map,字符串关联到$class. ~~~ if (is_array($class)) { self::$map = array_merge(self::$map, $class); } else { self::$map[$class] = $map; } ~~~ * * * * * 3 addNamespace($namespace,$path) 注册命名空间和路径的关联 `public static function addNamespace($namespace, $path = ''){}` 数组合并到$namespace,字符串关联到path. ~~~ if (is_array($namespace)) { self::$namespace = array_merge(self::$namespace, $namespace); } else { self::$namespace[$namespace] = $path; } ~~~ * * * * * 4 register($autoload='') 注册自动加载机制 `public static function register($autoload = ''){}` think\Loader::autoload()系统自动加载 self::registerComposerLoader() composer自动加载 ~~~ spl_autoload_register($autoload ? $autoload : 'think\\Loader::autoload'); self::registerComposerLoader(); ~~~ 5 composer自动加载的注册 `private static function registerComposerLoader(){}` 关于composer自动加载机制见 基础原理的php的composer自动加载 * * * * * 6 composer自动加载的两个私有方法 ~~~ private static function composerRequire($fileIdentifier, $file){} private static function findFileInComposer($class, $ext = '.php'){} ~~~ * * * * * 7 import($class,$baseUrl='',$ext=EXT) 手动加载$baseUrl下的$class命名的$ext后缀文件 `public static function import($class, $baseUrl = '', $ext = EXT){}` 首先分析$baseUrl, 然后查找$baserUrl.$class.$ext 文件名,然后加载对应文件 ## 3 总结 Loader类是系统的系统的自动加载类。 五个静态变量用来缓存自动加载的结果。 addMap() addNamespace() 用来添加$map,$namespace的关联信息。 register() 注册自动加载机制 autoload() 自动加载类 import()手动加载类