现在 PHP 开发基本上都用上 composer,大部分我们都只是下面一句话引入 composer
require_once __DIR__.'/vendor/autoload.php';
这里面其实有很多东西,很多组件的助手会人手一个助手函数,每次请求会载入很多用不到的助手函数和一些类映射,例如 laravel 框架。
[0] => /data/codes/blog/public/index.php
[1] => /data/codes/blog/vendor/autoload.php
[2] => /data/codes/blog/vendor/composer/autoload_real.php
[3] => /data/codes/blog/vendor/composer/ClassLoader.php
[4] => /data/codes/blog/vendor/composer/autoload_static.php
[5] => /data/codes/blog/vendor/symfony/polyfill-mbstring/bootstrap.php
[6] => /data/codes/blog/vendor/symfony/polyfill-php72/bootstrap.php
[7] => /data/codes/blog/vendor/symfony/polyfill-php72/Php72.php
[8] => /data/codes/blog/vendor/symfony/var-dumper/Resources/functions/dump.php
[9] => /data/codes/blog/vendor/swiftmailer/swiftmailer/lib/swift_required.php
[10] => /data/codes/blog/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php
[11] => /data/codes/blog/vendor/paragonie/random_compat/lib/random.php
[12] => /data/codes/blog/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php
[13] => /data/codes/blog/vendor/laravel/framework/src/Illuminate/Support/helpers.php
[14] => /data/codes/blog/vendor/myclabs/deep-copy/src/DeepCopy/deep_copy.php
[15] => /data/codes/blog/vendor/psy/psysh/src/Psy/functions.php
Tp5,yii2,phalcon 好像可以有自己的去实现类加载器,我想为啥子我要载入多么多东西,所以我改进了一个 composer 的载入
/**
* ---------------------------------------------------------------
* Composer
* ---------------------------------------------------------------.
*
* 用于管理 PHP 依赖包
* 优化 composer 性能,优先载入 composer 注册的 Psr4
* 如果 Psr4 不存在,才会去初始化 composer 加载,使得大部分请求不走 composer 的自动载入
* 如果你使用的包是比较新的,基本都遵循 Psr4 规则,这个时候会提升一部分性能
* 对于助手函数需要自己引入
*/
$psr4s = include_once __DIR__.'/../vendor/composer/autoload_psr4.php';
require_once __DIR__.'/../vendor/hunzhiwange/framework/src/Queryyetsimple/Bootstrap/function.php';
spl_autoload_register(function ($className) use ($psr4s) {
static $loadedComposer;
$name = explode('\\', $className);
$topLevel = '';
for($i = 0; $i <= 2; $i++) {
$topLevel .= $name[$i].'\\';
if (isset($psr4s[$topLevel])) {
foreach ($psr4s[$topLevel] as $dir) {
$file = $dir.'/'.str_replace('\\', '/', substr($className, strlen($topLevel))).'.php';
if (is_file($file)) {
return require_once $file;
}
}
//return;
}
}
if (null === $loadedComposer) {
$composer = require_once __DIR__.'/../vendor/autoload.php';
$composer->loadClass($className);
$loadedComposer = true;
}
});
这使得每次载入
/data/codes/queryphp/www/index.php
/data/codes/queryphp/vendor/composer/autoload_psr4.php
/data/codes/queryphp/vendor/hunzhiwange/framework/src/Queryyetsimple/Bootstrap/function.php
/data/codes/queryphp/vendor/hunzhiwange/framework/src/Queryyetsimple/Bootstrap/Project.php
/data/codes/queryphp/vendor/hunzhiwange/framework/src/Queryyetsimple/Di/Container.php
/data/codes/queryphp/vendor/hunzhiwange/framework/src/Queryyetsimple/Di/IContainer.php
/data/codes/queryphp/vendor/hunzhiwange/framework/src/Queryyetsimple/Kernel/IProject.php
只载入 composer/autoload_psr4.php ,如果遇到不是 psr4 这种类才去载入完整的 composer,助手函数需要自己引入,需要的地方手动载入,比如 swagger 那个做当。
/**
* 生成 swagger.
*
* @return \Swagger\Annotations\Swagger
*/
protected function makeSwagger()
{
require_once \Leevel::path().'/vendor/zircote/swagger-php/src/functions.php';
return \Swagger\scan($this->swaggerScan);
}
这样子可以两全其美,实现代价很小,干净。
基本上 3 层 psr4 基本可以完整覆盖所有组件,下面是 lavavel prs4 注册的。只有 Symfony 的组件有 3 层目录。
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'phpDocumentor\\Reflection\\' => array($vendorDir . '/phpdocumentor/reflection-common/src', $vendorDir . '/phpdocumentor/reflection-docblock/src', $vendorDir . '/phpdocumentor/type-resolver/src'),
'XdgBaseDir\\' => array($vendorDir . '/dnoegel/php-xdg-base-dir/src'),
'Whoops\\' => array($vendorDir . '/filp/whoops/src/Whoops'),
'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'),
'TijsVerkoyen\\CssToInlineStyles\\' => array($vendorDir . '/tijsverkoyen/css-to-inline-styles/src'),
'Tests\\' => array($baseDir . '/tests'),
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'),
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'),
'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'),
'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'),
'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'),
'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'),
'Psy\\' => array($vendorDir . '/psy/psysh/src/Psy'),
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
'NunoMaduro\\Collision\\' => array($vendorDir . '/nunomaduro/collision/src'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
'Laravel\\Tinker\\' => array($vendorDir . '/laravel/tinker/src'),
'Illuminate\\' => array($vendorDir . '/laravel/framework/src/Illuminate'),
'Fideloper\\Proxy\\' => array($vendorDir . '/fideloper/proxy/src'),
'Faker\\' => array($vendorDir . '/fzaninotto/faker/src/Faker'),
'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/EmailValidator'),
'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'),
'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'),
'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Common/Inflector'),
'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
'Cron\\' => array($vendorDir . '/dragonmantank/cron-expression/src/Cron'),
'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
'App\\' => array($baseDir . '/app'),
);
我觉得可以。
/**
* ---------------------------------------------------------------
* Composer
* ---------------------------------------------------------------.
*
* 用于管理 PHP 依赖包
* 优化 composer 性能,提炼 composer 中的 autoload_static 中的我们关注的 psr4 命名空间映射
* 我们 classmap 需要通过 `php leevel autoload` 生成,包含命令 `composer dump-autoload -o`
* 对于助手函数需要自己引入
*/
https://github.com/hunzhiwange/queryphp/blob/master/composer.json
获取 autoload.psr-4
"autoload": {
"psr-4": {
"App\\" : "application/app",
"Admin\\" : "application/admin",
"Common\\" : "common"
}
}
和 extra.leevel-console.autoload.namespace
"leevel-console" : {
"autoload": {
"namespaces": [
"Leevel",
"Dotenv",
"Carbon",
"Monolog",
"Whoops",
"Swagger"
]
}
}
获取我们系统常用的 psr4 命名空间。
php leevel autoload (包含composer dumo-autoload -o)
字符限制,见文件 https://github.com/hunzhiwange/queryphp/blob/master/www/index.php
完结。
1
jswh 2018-08-27 15:32:33 +08:00
opcache 了解一下。除非要大规模扫文件,否则不用太纠结 autoload 的性能。当然,如果你觉得这样代码符合你的审美就是另说。
|
2
jfcherng 2018-08-27 16:01:12 +08:00
PHP 的 autoload 並不載入用不到的類。只有類被使用並且尚未被定義時,才會喚起 autoload 機制。
|
3
doyouhaobaby OP @jswh opcache 知道,未开启 opcache 的情况下,每每做基本的优化减少磁盘 IO 看到这里消耗过多的性能。心痛。
|
4
doyouhaobaby OP @jfcherng 我这是在 composer 前注册自己的自定义类加载机制提升匹配效率,小几率失败然后注册 composer 的类加载机制
|
5
jswh 2018-08-27 16:20:01 +08:00
@doyouhaobaby opcache 开了以后,之前载入过的文件,只要文件没有变动,就不用重新 load.
|
6
lincanbin 2018-08-27 20:00:02 +08:00
你代码都写了怎么不做个性能测试呢……
说不定你会发现性能下降了。 |
7
doyouhaobaby OP @lincanbin 你可以试试,不开启 opcache 有 30-40 ms 提升,在我的 macbook pro 上。
https://github.com/hunzhiwange/queryphp/blob/master/www/index.php |