V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
doyouhaobaby
V2EX  ›  PHP

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

  •  
  •   doyouhaobaby · 2018-08-27 14:44:29 +08:00 · 3706 次点击
    这是一个创建于 2307 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    我觉得可以。

    第 1 条附言  ·  2018-09-02 09:57:55 +08:00

    重新优化

    /**
     * ---------------------------------------------------------------
     * Composer
     * ---------------------------------------------------------------.
     *
     * 用于管理 PHP 依赖包
     * 优化 composer 性能,提炼 composer 中的 autoload_static 中的我们关注的 psr4 命名空间映射
     * 我们 classmap 需要通过 `php leevel autoload` 生成,包含命令 `composer dump-autoload -o`
     * 对于助手函数需要自己引入
     */
    

    读取 composer.json 配置

    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 命名空间。

    生成 mapclass 和 psr4

    https://github.com/hunzhiwange/framework/blob/master/src/Queryyetsimple/Bootstrap/Console/Autoload.php

    php leevel autoload (包含composer dumo-autoload -o)
    

    首页注册我们的

    字符限制,见文件 https://github.com/hunzhiwange/queryphp/blob/master/www/index.php

    完结。

    7 条回复    2018-08-27 22:21:45 +08:00
    jswh
        1
    jswh  
       2018-08-27 15:32:33 +08:00
    opcache 了解一下。除非要大规模扫文件,否则不用太纠结 autoload 的性能。当然,如果你觉得这样代码符合你的审美就是另说。
    jfcherng
        2
    jfcherng  
       2018-08-27 16:01:12 +08:00
    PHP 的 autoload 並不載入用不到的類。只有類被使用並且尚未被定義時,才會喚起 autoload 機制。
    doyouhaobaby
        3
    doyouhaobaby  
    OP
       2018-08-27 16:08:25 +08:00
    @jswh opcache 知道,未开启 opcache 的情况下,每每做基本的优化减少磁盘 IO 看到这里消耗过多的性能。心痛。
    doyouhaobaby
        4
    doyouhaobaby  
    OP
       2018-08-27 16:12:39 +08:00
    @jfcherng 我这是在 composer 前注册自己的自定义类加载机制提升匹配效率,小几率失败然后注册 composer 的类加载机制
    jswh
        5
    jswh  
       2018-08-27 16:20:01 +08:00
    @doyouhaobaby opcache 开了以后,之前载入过的文件,只要文件没有变动,就不用重新 load.
    lincanbin
        6
    lincanbin  
       2018-08-27 20:00:02 +08:00
    你代码都写了怎么不做个性能测试呢……
    说不定你会发现性能下降了。
    doyouhaobaby
        7
    doyouhaobaby  
    OP
       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
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1095 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 19:01 · PVG 03:01 · LAX 11:01 · JFK 14:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.