29 05 2020

入口文件 public/index.php 

<?php
// [ 应用入口文件 ]
namespace think;

// 加载基础文件
require __DIR__ . '/../thinkphp/base.php';

......


基础文件 thinkphp/base.php 

<?php
namespace think;

// 载入Loader类
require __DIR__ . '/library/think/Loader.php';

// 注册自动加载
Loader::register();

......


自动加载类 thinkphp/library/think/Loader.php

......

// 注册自动加载机制
public static function register($autoload = '')
{
    /*
     spl_autoload_register函数是实现自动加载未定义类功能的的重要方法,当new一个未事先引入的不存在的类时,自动调用该方法
     
     PHP碰到没有定义的类就执行think\\Loader::autoload()

     spl_autoload_register有三个参数

     autoload_function
     欲注册的自动装载函数。如果没有提供任何参数,则自动注册 autoload 的默认实现函数spl_autoload()。

     throw
     此参数设置了 autoload_function 无法成功注册时, spl_autoload_register()是否抛出异常。

     prepend
     如果是 true,spl_autoload_register() 会添加函数到队列之首,而不是队列尾部。
    */
    // 注册系统自动加载
    spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);

    // 获取根目录路径
    $rootPath = self::getRootPath();
    
    // 获取composer目录路径
    self::$composerPath = $rootPath . 'vendor' . DIRECTORY_SEPARATOR . 'composer' . DIRECTORY_SEPARATOR;

    // Composer自动加载支持
    if (is_dir(self::$composerPath)) {
        if (is_file(self::$composerPath . 'autoload_static.php')) {
            require self::$composerPath . 'autoload_static.php';

            // 返回当前脚本中已定义的类名组成的数组(自定义类、系统内置类)
            $declaredClass = get_declared_classes();
            
            // 取出上面数组中最后一个类,即上面引入的 autoload_static.php 中的类
            $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::$composerPath);
        }
    }

    // 注册命名空间定义
    self::addNamespace([
        'think'  => __DIR__,
        'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits',
    ]);

    // 类的映射键值对,需手动生成(php think optimize:autoload)才有这个文件
    // 可提升性能
    // 加载类库映射文件
    if (is_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php')) {
        self::addClassMap(__include_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php'));
    }

    // 自动加载extend目录, 添加类自动加载的目录
    self::addAutoLoadDir($rootPath . 'extend');
    // 如果需要添加多个自动加载目录,可以多次调用此方法
    // self::addAutoLoadDir($rootPath . 'other');
}

// 自动加载
public static function autoload($class)
{

    if (isset(self::$classAlias[$class])) {
        // 设置类的别名  如 /Users/wenbinzhao/Documents/dnmp/www/tp5/thinkphp/library/think/Env.php  设为  think\Env
        // 使用的类的时候就可以直接使用别名 think\Env
        return class_alias(self::$classAlias[$class], $class);
    }

    // 通过命名空间查找自动加载类的文件路径
    if ($file = self::findFile($class)) {

        // Win环境严格区分大小写
        if (strpos(PHP_OS, 'WIN') !== false && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) {
            return false;
        }
        
        // 通过 include 引入文件
        __include_file($file);
        return true;
    }
}

......


总结

类的自动加载,其实就是先绑定了自动加载函数,然后在类的属性上面定义了一系列的映射关系,然后在自动加载函数中通过命名空间/类名参数查询映射关系,将映射的目录include引入的操作