thinkphp 框架自动加载原理_ThinkPHP 5.1 自动加载原理

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一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值