ThinkPHP 5.1 没有直接用 composer 自带的自动加载,但是他直接借用了composer 里面的一个文件autoload_static.php,还用了部分 composer 里面 ClassLoader.php的源码。
入口文件 public/index.php<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st
// +----------------------------------------------------------------------
// [ 应用入口文件 ]
namespace think;
// 加载基础文件
require __DIR__ . '/../thinkphp/base.php';
// 支持事先使用静态方法设置Request对象和Config对象
// 执行应用并响应
Container::get('app')->run()->send();
base.php<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st
// +----------------------------------------------------------------------
namespace think;
// 载入Loader类
require __DIR__ . '/library/think/Loader.php';
// 注册自动加载
Loader::register();
// 注册错误和异常处理机制
Error::register();
// 实现日志接口
if (interface_exists('Psr\Log\LoggerInterface')) {
interface LoggerInterface extends \Psr\Log\LoggerInterface
{}
} else {
interface LoggerInterface
{}
}
// 注册类库别名
Loader::addClassAlias([
'App' => facade\App::class,
'Build' => facade\Build::class,
'Cache' => facade\Cache::class,
'Config' => facade\Config::class,
'Cookie' => facade\Cookie::class,
'Db' => Db::class,
'Debug' => facade\Debug::class,
'Env' => facade\Env::class,
'Facade' => Facade::class,
'Hook' => facade\Hook::class,
'Lang' => facade\Lang::class,
'Log' => facade\Log::class,
'Request' => facade\Request::class,
'Response' => facade\Response::class,
'Route' => facade\Route::class,
'Session' => facade\Session::class,
'Url' => facade\Url::class,
'Validate' => facade\Validate::class,
'View' => facade\View::class,
]);
Loader::register()// 注册自动加载机制
public static function register($autoload = '')
{
// 注册系统自动加载
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
$rootPath = self::getRootPath();
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();
$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',
]);
// 加载类库映射文件
if (is_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php')) {
self::addClassMap(__include_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php'));
}
// 自动加载extend目录
self::addAutoLoadDir($rootPath . 'extend');
}
可以看出来后期自动加载要靠 Loader类中的autoload函数来实现。
register 具体做了什么// 注册系统自动加载
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
base.php 中调用的时候没有传参数,所以自动加载注册的是autoload函数。$rootPath = self::getRootPath(); // C:\code\tp5.1\
// 获取应用根目录
public static function getRootPath()
{
if ('cli' == PHP_SAPI) {
$scriptName = realpath($_SERVER['argv'][0]);
} else {
$scriptName = $_SERVER['SCRIPT_FILENAME'];
//'C:\code\tp5.1\public\index.php'
}
$path = realpath(dirname($scriptName));
if (!is_file($path . DIRECTORY_SEPARATOR . 'think')) {
$path = dirname($path);
}
return $path . DIRECTORY_SEPARATOR;
}
通过判断是否存在think文件,来确定根目录。本机是:C:\code\tp5.1\self::$composerPath = $rootPath . 'vendor' . DIRECTORY_SEPARATOR . 'composer' . DIRECTORY_SEPARATOR;
这个是定位composer 目录,即:C:\code\tp5.1\vendor\composer\
找到composer目录后,就把composer下面的autoload_static.php 文件包含进来。
这个文件里面有几个变量,记录了类和具体路径的映射信息。$declaredClass = get_declared_classes();
$composerClass = array_pop($declaredClass);
这两行代码是用来找autoload_static.php 里面的类foreach ([
'prefixLengthsPsr4',
'prefixDirsPsr4',
'fallbackDirsPsr4',
'prefixesPsr0',
'fallbackDirsPsr0',
'classMap',
'files'] as $attr) {
if (property_exists($composerClass, $attr)) {
self::${$attr} = $composerClass::${$attr};
}
}
如果有这几个属性,把这几个属性挂到Loader类里面。
如果找不到autoload_static.php文件,就注册一个composer的自动加载// 注册composer自动加载
public static function registerComposerLoader($composerPath)
{
if (is_file($composerPath . 'autoload_namespaces.php')) {
$map = require $composerPath . 'autoload_namespaces.php';
foreach ($map as $namespace => $path) {
self::addPsr0($namespace, $path);
}
}
if (is_file($composerPath . 'autoload_psr4.php')) {
$map = require $composerPath . 'autoload_psr4.php';
foreach ($map as $namespace => $path) {
self::addPsr4($namespace, $path);
}
}
if (is_file($composerPath . 'autoload_classmap.php')) {
$classMap = require $composerPath . 'autoload_classmap.php';
if ($classMap) {
self::addClassMap($classMap);
}
}
if (is_file($composerPath . 'autoload_files.php')) {
self::$files = require $composerPath . 'autoload_files.php';
}
}
这个流程和原始的composer基本一样,原始的composer在非5.6以上的版本,或者在HHVM环境中也是直接加载的这四个文件,autoload_static.php 相当于是这四个文件的合并。// 注册命名空间定义
self::addNamespace([
'think' => __DIR__,
'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits',
]);public static function addNamespace($namespace, $path = '')
{
if (is_array($namespace)) {
foreach ($namespace as $prefix => $paths) {
self::addPsr4($prefix . '\\', rtrim($paths, DIRECTORY_SEPARATOR), true);
}
} else {
self::addPsr4($namespace . '\\', rtrim($path, DIRECTORY_SEPARATOR), true);
}
}
autoload_static.php 里面只有app 这个命名空间,指向application 目录,think 和 traits 这两个在这里用psr4的方式注册一下。// 加载类库映射文件
if (is_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php')) {
self::addClassMap(__include_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php'));
}
正式项目中可以通过php think optimize:autoload 生成类库的映射文件。// 自动加载extend目录
self::addAutoLoadDir($rootPath . 'extend');// 注册自动加载类库目录
public static function addAutoLoadDir($path)
{
self::$fallbackDirsPsr4[] = $path;
}
extend 目录下的文件自动加载。没有设置命名空间的类就会进入到fallback里面。
总结一下,register的作用就是注册自动加载类,挂载类和文件的映射关系。
Loader::autolodif (isset(self::$classAlias[$class])) {
return class_alias(self::$classAlias[$class], $class);
}
有别名的,返回它的原始名称,然后再去加载,加载不了的,会到这里重新以原始的名称加载一下。
加载的原理还是找文件,然后包含文件。
Loader::findFile
这是个私有方法,只在本类使用。
查找文件的方法用的和原始的composer是一个道理,感觉就是照搬的原始composer的代码。
查找顺序:
$classMap
PSR-4
PSR4 fallback
PSR-0
PSR-0 fallback貌似少一个files的方式if ($file = self::findFile($class)) {
// Win环境严格区分大小写
if (strpos(PHP_OS, 'WIN') !== false && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) {
return false;
}
__include_file($file);
return true;
}function __include_file($file)
{
return include $file;
}
找到具体的文件,就include一下。