主要细看一下 register 这个函数,在上一章节的结尾头有\think\Loader::register();php
/** * 注册自动加载机制 * @access public * @param callable $autoload 自动加载处理方法 * @return void */
public static function register($autoload = null) {
// 注册系统自动加载
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
// Composer 自动加载支持
if (is_dir(VENDOR_PATH . 'composer')) {
if (PHP_VERSION_ID >= 50600 && is_file(VENDOR_PATH . 'composer' . DS . 'autoload_static.php')) {
require VENDOR_PATH . 'composer' . DS . 'autoload_static.php';
$declaredClass = get_declared_classes();
$composerClass = array_pop($declaredClass);
foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) {
if (property_exists($composerClass, $attr)) {
self::${$attr} = $composerClass::${$attr};
}
}
} else {
self::registerComposerLoader();
}
}
// 注册命名空间定义
self::addNamespace([
'think' => LIB_PATH . 'think' . DS,
'behavior' => LIB_PATH . 'behavior' . DS,
'traits' => LIB_PATH . 'traits' . DS,
]);
// 加载类库映射文件
if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {
self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));
}
self::loadComposerAutoloadFiles();
// 自动加载 extend 目录
self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);
}
复制代码
主要是用了函数 spl_autoload_register
函数里写到的是thinkphp
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
复制代码
说明 若是参数里传了要注册加载的函数(里面写了须要引入require的类),那么就直接调用,不然,就是将Loader
类里的 autoload
方法传进去,而后第二个参数为true
,意思是没法成功注册类的时候,抛出异常,第三个参数为 true
的意思是将其要注册的类排在队列之首。数组
下面看看autuload
方法。bash
/** * 自动加载 * @access public * @param string $class 类名 * @return bool */
public static function autoload($class) {
// 首先检测了是否存在命名空间别名数组是否有值,
if (!empty(self::$namespaceAlias)) { // 若是有
$namespace = dirname($class); // 获取该类的目录结构
if (isset(self::$namespaceAlias[$namespace])) { // 查看数组里是否有存
$original = self::$namespaceAlias[$namespace] . '\\' . basename($class); // 将类完整的命名空间拼接起来
if (class_exists($original)) { // 若是存在该类
return class_alias($original, $class, false); // 将其命类别名
}
}
}
if ($file = self::findFile($class)) { // 查找文件 (后面再详看)
// 非 Win 环境不严格区分大小写
if (!IS_WIN || pathinfo($file, PATHINFO_FILENAME) == pathinfo(realpath($file), PATHINFO_FILENAME)) {
__include_file($file); // 引入include该文件
return true;
}
}
return false;
}
复制代码
// 若是根目录vendor下面有composer目录
if (is_dir(VENDOR_PATH . 'composer')) {
// 若是php版本大于5.6, 而且composer目录下有 autoload_static.php 文件
if (PHP_VERSION_ID >= 50600 && is_file(VENDOR_PATH . 'composer' . DS . 'autoload_static.php')) {
// 将该php文件引入进来。
require VENDOR_PATH . 'composer' . DS . 'autoload_static.php';
// 获取引入过的全部的类名
$declaredClass = get_declared_classes();
// 弹出刚才引入的那个
$composerClass = array_pop($declaredClass);
// 循环这些字符串命名
foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) {
// 若是有该属性,就在loader类里也存该属性
if (property_exists($composerClass, $attr)) {
self::${$attr} = $composerClass::${$attr};
}
}
} else {
// 不然 注册 composer 自动加载
self::registerComposerLoader();
}
}
复制代码
这里TP里 是有 autoload_static.php
这个文件 ,因此走上面那个 if
里的程序。 继续往下走。composer
.
.
self::addNamespace([
'think' => LIB_PATH . 'think' . DS,
'behavior' => LIB_PATH . 'behavior' . DS,
'traits' => LIB_PATH . 'traits' . DS,
]);
.
.
复制代码
这里的关键就是Loader
类里的 addNamespace
函数了。函数
/** * 注册命名空间 * @access public * @param string|array $namespace 命名空间 * @param string $path 路径 * @return void */
public static function addNamespace($namespace, $path = '') {
// 若是是数组 刚才传的是一个数组。
if (is_array($namespace)) {
foreach ($namespace as $prefix => $paths) { // 进行遍历
self::addPsr4($prefix . '\\', rtrim($paths, DS), true);
}
} else {
self::addPsr4($namespace . '\\', rtrim($path, DS), true);
}
}
复制代码
拿遍历的第一次进行分析,看是怎样经过 addPsr4
方法添加。ui
/** * 添加 PSR-4 空间 * @access private * @param array|string $prefix 空间前缀 * @param string $paths 路径 * @param bool $prepend 预先设置的优先级更高 * @return void */
private static function addPsr4($prefix, $paths, $prepend = false) {
// $prefix: "think\"
// $paths: "C:\laragon\www\mini-project\thinkphp\library\think"
// $prepend: true
// 是否传了prefix
if (!$prefix) {
// Register directories for the root namespace.
// 没有就直接放到 $fallbackDirsPsr4 数组属性里
// $prepend 决定是否放在数组最前面。
self::$fallbackDirsPsr4 = $prepend ?
array_merge((array) $paths, self::$fallbackDirsPsr4) :
array_merge(self::$fallbackDirsPsr4, (array) $paths);
// 若是 $prefixDirsPsr4 数组里不存在 think\ 这个键名
} elseif (!isset(self::$prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix); // 计算键名的长度
if ('\\' !== $prefix[$length - 1]) { // 看键名末尾是否有 "\"
throw new \InvalidArgumentException(
"A non-empty PSR-4 prefix must end with a namespace separator."
);
}
// 最后按照 autoload_static.php 那种格式存起来。
self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
self::$prefixDirsPsr4[$prefix] = (array) $paths;
// 若是有$prefixDirsPsr4对应的键名
} else {
// 存起来
self::$prefixDirsPsr4[$prefix] = $prepend ?
// Prepend directories for an already registered namespace.
array_merge((array) $paths, self::$prefixDirsPsr4[$prefix]) :
// Append directories for an already registered namespace.
array_merge(self::$prefixDirsPsr4[$prefix], (array) $paths);
}
}
// 假设之前没有这个 `think/` 这个键名,最后$prefixDirsPsr4数组就应该是
/** $prefixLengthsPsr4 = [ ... 't' => [ ... 'think\\' => 6 ], ]; $prefixDirsPsr4 = [ ... 'think\\' => ['C:\laragon\www\mini-project\thinkphp\library\think'] ]; 对比composer目录下的 autoload_static.php文件类里的属性 **/
复制代码
这样就按psr4
的规则将这个类存到了数组 $prefixDirsPsr4
和数组 $prefixLengthsPsr4
等着注册就好了。spa
// 若是 存在文件 /runtime/classmap.php
if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {
// 就添加映射
self::addClassMap(_include_file(RUNTIME_PATH . 'classmap' . EXT));
}
复制代码
addClassMap方法code
/** * 注册 classmap * @access public * @param string|array $class 类名 * @param string $map 映射 * @return void */
public static function addClassMap($class, $map = '') {
// 通常映射文件里都是写成数组格式,能够参考composer里的文件。
if (is_array($class)) {
self::$classMap = array_merge(self::$classMap, $class);
} else {
self::$classMap[$class] = $map;
}
}
复制代码
通常状况下是没有的,接着看关键的自动加载composer的文件 file 类型队列
self::loadComposerAutoloadFiles();
复制代码
且细看 这个方法
public static function loadComposerAutoloadFiles() {
// 开始将静态变量 $files 里的文件进行遍历 require加载 了
foreach (self::$files as $fileIdentifier => $file) {
// 查看是否有全局变量 __composer_autoload_files 若是是空的 就进行记录。
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
// require进来
__require_file($file);
// 进行记录
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}
}
而在开始的时候就已经将 autoload_static.php 里的文件就加载到 files里了。
self::$files = [
'9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php',
'1cfd2761b63b0a29ed23657ea394cb2d' => __DIR__ . '/..' . '/topthink/think-captcha/src/helper.php',
'ddc3cd2a04224f9638c5d0de6a69c7e3' => __DIR__ . '/..' . '/topthink/think-migration/src/config.php',
'cc56288302d9df745d97c934d6a6e5f0' => __DIR__ . '/..' . '/topthink/think-queue/src/common.php',
];
这样对应着看就很清晰了,就把这4个文件引入了。
复制代码
在后面的章节笔记中,后面就一一看这四个文件。