Composer 好像这样引入可以提升性能,大家觉得如何来讨论哈?

2018-08-27 14:44:29 +08:00
 doyouhaobaby

现在 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'),
);

我觉得可以。

3711 次点击
所在节点    PHP
7 条回复
jswh
2018-08-27 15:32:33 +08:00
opcache 了解一下。除非要大规模扫文件,否则不用太纠结 autoload 的性能。当然,如果你觉得这样代码符合你的审美就是另说。
jfcherng
2018-08-27 16:01:12 +08:00
PHP 的 autoload 並不載入用不到的類。只有類被使用並且尚未被定義時,才會喚起 autoload 機制。
doyouhaobaby
2018-08-27 16:08:25 +08:00
@jswh opcache 知道,未开启 opcache 的情况下,每每做基本的优化减少磁盘 IO 看到这里消耗过多的性能。心痛。
doyouhaobaby
2018-08-27 16:12:39 +08:00
@jfcherng 我这是在 composer 前注册自己的自定义类加载机制提升匹配效率,小几率失败然后注册 composer 的类加载机制
jswh
2018-08-27 16:20:01 +08:00
@doyouhaobaby opcache 开了以后,之前载入过的文件,只要文件没有变动,就不用重新 load.
lincanbin
2018-08-27 20:00:02 +08:00
你代码都写了怎么不做个性能测试呢……
说不定你会发现性能下降了。
doyouhaobaby
2018-08-27 22:21:45 +08:00
@lincanbin 你可以试试,不开启 opcache 有 30-40 ms 提升,在我的 macbook pro 上。
https://github.com/hunzhiwange/queryphp/blob/master/www/index.php

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/483646

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX