Fecshop 2016-08-03 09:18:59 8066次浏览 2条评论 2 0 0

昨天,我通过日志打印,发现我的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()

最终找到了原因,步骤如下:

对于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的方式查看。

  • 评论于 2016-08-15 10:07 举报

    不能自动判断重复的配置啊

    1 条回复
    评论于 2016-08-15 11:15 回复

    对于有key的数组值,是可以重复覆盖的,
    譬如:['key' =>$val],

    但是对于没有key的数组,是没有的,譬如:['xxx','yyyy']

    觉得很赞
  • 评论于 2016-12-22 09:16 举报

    最后,推荐一下我的Fecshop ,开源商城,github地址:https://github.com/fancyecommerce/yii2_fecshop

    演示地址:http://fecshop.appfront.fancyecommerce.com/

    截止到2016-11-12号,产品,分类,首页,评论,用户中心,搜索,多语言,多货币 等功能已经做完,除了购物车和支付部分,其他的基本都已经完成,关注fecshop的 在等2-3个月,也就是明年2,3月份,版本已经就可以出来,2017年4,5月份在把手机web 做一下,预计到明年5月份,后台,pc前台,手机web前台 ,命令控制台 这几个入口 基本可以完善,多谢大家关注和你们的Star,谢谢,我会坚持把他写好。

    作者QQ:2358269014

您需要登录后才可以评论。登录 | 立即注册