Yii2 一个隐藏的小坑,致使我的组件的bootstrap方法执行了多次

技巧库 · fecommerce · 于 7年前 发布 · 7282 次阅读

我通过日志打印,发现我的store 组件的bootstrap在初始化的时候被莫名的执行了两次,日志如下:

原文链接:FancyEcommerce - Yii2 一个隐藏的小坑,致使我的组件的bootstrap方法执行了多次

 store
    (),fecshop\services\Store::actionBootstrap()
    /www/web/develop/fecshop/vendor/fancyecommerce/fecshop/services/Service.php(58),::call_user_func_array()
    /www/web/develop/fecshop/vendor/fancyecommerce/fecshop/components/Store.php(22),fecshop\services\Service::__call()
    /www/web/develop/fecshop/vendor/fancyecommerce/fecshop/components/Store.php(22),fecshop\services\Store::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Application.php(316),fecshop\components\Store::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/web/Application.php(66),yii\base\Application::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Application.php(267),yii\web\Application::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Object.php(107),yii\base\Application::init()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Application.php(206),yii\base\Object::__construct()
    /www/web/develop/fecshop/appfront/web/index.php(44),yii\base\Application::__construct()
    store
    (),fecshop\services\Store::actionBootstrap()
    /www/web/develop/fecshop/vendor/fancyecommerce/fecshop/services/Service.php(58),::call_user_func_array()
    /www/web/develop/fecshop/vendor/fancyecommerce/fecshop/components/Store.php(22),fecshop\services\Service::__call()
    /www/web/develop/fecshop/vendor/fancyecommerce/fecshop/components/Store.php(22),fecshop\services\Store::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Application.php(316),fecshop\components\Store::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/web/Application.php(66),yii\base\Application::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Application.php(267),yii\web\Application::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Object.php(107),yii\base\Application::init()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Application.php(206),yii\base\Object::__construct()
    /www/web/develop/fecshop/appfront/web/index.php(44),yii\base\Application::__construct()

最终找到了原因,因为:bootstrap' => ['store'] 我在配置文件中配置了两次造成的。

查找步骤如下:

对于Yii2的方法:yii\helpers\ArrayHelper::merge();我们知道,对于数组中key为数字的部分,譬如:

yii\helpers\ArrayHelper::merge(['store','view'],['log','store']);

合并后的结果为['store','log','view','store'] ,而不是['store','log','view'],因此就要出问题了,store会被执行2次

对于yii2的bootstrap,我写了一个store组件,然后,没有注意到,在两个地方加入了这个配置:

'bootstrap' => ['store'],

bootstrap的执行代码在:

yii\base\Application的bootstrap()方法中,大约298行出的代码:

   foreach ($this->bootstrap as $class) {
                $component = null;
                if (is_string($class)) {
                    if ($this->has($class)) {
                        $component = $this->get($class);
                    } elseif ($this->hasModule($class)) {
                        $component = $this->getModule($class);
                    } elseif (strpos($class, '\\') === false) {
                        throw new InvalidConfigException("Unknown bootstrapping component ID: $class");
                    }
                }
                if (!isset($component)) {
                    $component = Yii::createObject($class);
                }
                if ($component instanceof BootstrapInterface) {
                    Yii::trace('Bootstrap with ' . get_class($component) . '::bootstrap()', __METHOD__);
                    $component->bootstrap($this);
                } else {
                    Yii::trace('Bootstrap with ' . get_class($component), __METHOD__);
                }
            }

我加了下打印,

 foreach ($this->bootstrap as $class) {
         echo $class.'<br/>';
               $component = null;
               if (is_string($class)) {
                   if ($this->has($class)) {
                       $component = $this->get($class);
                   } elseif ($this->hasModule($class)) {
                       $component = $this->getModule($class);
                   } elseif (strpos($class, '\\') === false) {
                       throw new InvalidConfigException("Unknown bootstrapping component ID: $class");
                   }
               }
               if (!isset($component)) {
                   $component = Yii::createObject($class);
               }
               if ($component instanceof BootstrapInterface) {
                   Yii::trace('Bootstrap with ' . get_class($component) . '::bootstrap()', __METHOD__);
                   $component->bootstrap($this);
               } else {
                   Yii::trace('Bootstrap with ' . get_class($component), __METHOD__);
               }
           }

然后在调用组件的地方:

然后在Store组件的bootstrap方法中加入

  public function bootstrap($app){
        $d = debug_backtrace();
        foreach($d as $e){
          $function = $e['function'];
          $class = $e['class'];
          $file = $e['file'];
          $line = $e['line'];
          echo $file.'('.$line.'),'.
          $class.'::'.$function.'()<br/>';
        }
        echo '<br/><br/>';

通过debug_backtrace(),进行打印输出:

结果如下:

  debug
    gii
    store
    (),fecshop\services\Store::actionBootstrap()
    /www/web/develop/fecshop/vendor/fancyecommerce/fecshop/services/Service.php(58),::call_user_func_array()
    /www/web/develop/fecshop/vendor/fancyecommerce/fecshop/components/Store.php(22),fecshop\services\Service::__call()
    /www/web/develop/fecshop/vendor/fancyecommerce/fecshop/components/Store.php(22),fecshop\services\Store::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Application.php(316),fecshop\components\Store::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/web/Application.php(66),yii\base\Application::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Application.php(267),yii\web\Application::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Object.php(107),yii\base\Application::init()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Application.php(206),yii\base\Object::__construct()
    /www/web/develop/fecshop/appfront/web/index.php(44),yii\base\Application::__construct()
    store
    (),fecshop\services\Store::actionBootstrap()
    /www/web/develop/fecshop/vendor/fancyecommerce/fecshop/services/Service.php(58),::call_user_func_array()
    /www/web/develop/fecshop/vendor/fancyecommerce/fecshop/components/Store.php(22),fecshop\services\Service::__call()
    /www/web/develop/fecshop/vendor/fancyecommerce/fecshop/components/Store.php(22),fecshop\services\Store::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Application.php(316),fecshop\components\Store::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/web/Application.php(66),yii\base\Application::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Application.php(267),yii\web\Application::bootstrap()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Object.php(107),yii\base\Application::init()
    /www/web/develop/fecshop/vendor/yiisoft/yii2/base/Application.php(206),yii\base\Object::__construct()
    /www/web/develop/fecshop/appfront/web/index.php(44),yii\base\Application::__construct()

发现我的store 组件确实被执行了2次,

原因就是,\appfront\config\fecshop_local.php加入了配置:对store组件的配置

  return [
      'modules'=>$modules,
      'bootstrap' => ['store'],
        'services' => $services,
    ];
    

在另外一个地方,我也加入了配置:

  'modules'=>$modules,
      /* only config in front web */
      'bootstrap' => ['store'],
      'params'  => [
        /* appfront base theme dir   */
        'appfrontBaseTheme'   => '@fecshop/app/appfront/theme/base/front',
        'appfrontBaseLayoutName'=> 'main.php',
      ],

造成store组件的bootstrap被执行了两次,

不知道为什么yii2,不在这里执行一次数组的array_unique方法,

不然配置乱了,在很多地方配置了bootstrap方法,但是又没有注意到,尤其是bootstrap()方法在每次初始化的时候都要执行,造成额外开销,这个小坑,还是得通过打印$config的方式查看。

本文由 fecommerce 创作,采用 知识共享署名 3.0 中国大陆许可协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。


微信

共收到 7 条回复 Yii2 坑
whatcq#11年前 0 个赞

这确实是小坑, 框架加了也许更完善(但是现在兼容东西多了越来越臃肿了)

-- 感谢分享。

添加回复 (需要登录)
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册