如题,今天感觉好久没有更新博客了。最近迷上了物联网开发。一直在研究stm32、51这些东西。想起来前几天群里面有人问到tp扩展包原理。其实这个前几年也就研究过。网上搜了搜发现相关文章也很少(也有可能是我搜索姿势不对)今天就来写一篇thinkphp composer包加载原理
composer update
或者 composer require
操作时。则会执行service:discover
这个命令。把当前所有已经加载的库信息都进行一次匹配。如果匹配到了think关键字的services属性。则把服务类输出成配置文件到vendor/services.php
文件中vendor/services.php
中的service服务类到当前应用内进行初始化在第三步进入到Http->runWithRequest
这个方法中后。可以看到又调用了initialize
方法
追进这个方法可以看到
追进initialize
方法看实现
/** * 初始化应用 * @access public * @return $this */ public function initialize() { // 设置当前初始化状态 $this->initialized = true; // 设置应用开始时间 $this->beginTime = microtime(true); // 获取到php的内存 $this->beginMem = memory_get_usage(); // 加载环境变量 例如当前应用目录下的 .env文件 $this->loadEnv($this->envName); // 设置配置文件后缀 $this->configExt = $this->env->get('config_ext', '.php'); // 调试模式设置 $this->debugModeInit(); // 加载全局初始化文件 $this->load(); // 加载应用默认语言包 $this->loadLangPack(); // 监听AppInit $this->event->trigger(AppInit::class); // 设置php默认时区 date_default_timezone_set($this->config->get('app.default_timezone', 'Asia/Shanghai')); // 初始化当前系统配置的默认服务 foreach ($this->initializers as $initializer) { // 调用make函数生成对象。并且执行对象中的init方法 $this->make($initializer)->init($this); } return $this; }
重点是初始化当前系统配置的默认服务
这个$this->make($initializer)->init($this)
函数,看看initializers
属性
/** * 应用初始化器 * @var array */ protected $initializers = [ Error::class, RegisterService::class, BootService::class, ];
追到这里就是关键了。上面把这里面的类进行初始化。并且执行类中的init方法。直接看RegisterService::class
类的init
方法
public function init(App $app) { // 获取当前项目根目录。拼接上 vendor/services.php $file = $app->getRootPath() . 'vendor/services.php'; $services = $this->services; if (is_file($file)) { $services = array_merge($services, include $file); } // 初始化services foreach ($services as $service) { if (class_exists($service)) { $app->register($service); } } }
读到这里的可以看看自己项目vendor目录下是不是有一个services.php
,接下来讲一讲composer.json
这个文件
在tp框架中的composer.json
有这样一个配置
这里这个概念我直接让chatgpt来解读。解读内容如下
接下来直接看service:discover
这个命令。追到vendor\topthink\framework\src\think\console\command\ServiceDiscover.php
文件
public function execute(Input $input, Output $output) { // 获取到当前项目根目录下的 vendor/composer/installed.json 文件 if (is_file($path = $this->app->getRootPath() . 'vendor/composer/installed.json')) { // json解析 $packages = json_decode(@file_get_contents($path), true); // Compatibility with Composer 2.0 if (isset($packages['packages'])) { $packages = $packages['packages']; } $services = []; foreach ($packages as $package) { // 判断当前包是否在extra字段里面声明了think关键字中的services属性。如果声明了就把services给装载到services变量内 if (!empty($package['extra']['think']['services'])) { $services = array_merge($services, (array) $package['extra']['think']['services']); } } $header = '// This file is automatically generated at:' . date('Y-m-d H:i:s') . PHP_EOL . 'declare (strict_types = 1);' . PHP_EOL; // 用var_export函数把services变量打印成可读性代码。并且写入到根目录vendor目录下的services $content = '<?php ' . PHP_EOL . $header . "return " . var_export($services, true) . ';'; file_put_contents($this->app->getRootPath() . 'vendor/services.php', $content); $output->writeln('<info>Succeed!</info>'); }
一直到这就算结束了
如果觉得这篇文章对你有帮助。不妨点个赞留个关注再走