Lazy load modules bootstrap

    Многие как и я используют Modules ресурс, который позволяет использовать отдельные Bootstrap файлы для каждого модуля. Но этого ресурса есть один недостаток — он всегда загружает все Bootstrap файлы в независимости от того какое модули мы используем в конкретном запросе. Я решил предоставить решение данной проблемы

    Для начала слегка изменим сам ресурс
    <?php
    class System_Application_Resource_Modules extends Zend_Application_Resource_ResourceAbstract
    {
      public function init()
      {
        $modulePlugin = new System_Controller_Plugin_ModuleBootstrap();
        $modulePlugin->setBootstrap($this->getBootstrap());
        
        $this->getBootstrap()
          ->bootstrap('FrontController')
          ->getResource('FrontController')
          ->registerPlugin($modulePlugin);
          
        return $modulePlugin;
      }
    }


    Код прост до безобразия. Создаем новый Controller Plugin и регистрируем его в фронт контроллер FrontController
    Как Вы уже догадались вся «магия» происходит в System_Controller_Plugin_ModuleBootstrap
    <?php
    class System_Controller_Plugin_ModuleBootstrap extends Zend_Controller_Plugin_Abstract
    {
      /**
       * var Zend_Application_Bootstrap_BootstrapAbstract
       */
      protected $_bootstrap = null;
      
      /**
       * var array
       */
      protected $_bootstrapedModules = array();
      
      /**
       * Constructor
       * param array $options
       **/
      public function __construct($options = null) {
        if ($options !== null) {
          $this->setOptions($options);
        }
      }
      
      /**
       * Implement configurable object pattern
       * param array $options
       */
      public function setOptions($options)
      {
        foreach ((array)$options as $name => $value) {
          $setter = 'set'.ucfirst($name);
          if (is_callable(array($this, $setter))) {
            $this->$setter($value);
          }
        }  
      }
      
      /**
       * Bootstrap setter
       */
      public function setBootstrap(Zend_Application_Bootstrap_BootstrapAbstract $bootstrap)
      {
        $this->_bootstrap = $bootstrap;
      }
      
      /**
       * get Front Controller
       * return Zend_Controller_Front
       */
      protected function _getFront()
      {
        return Zend_Controller_Front::getInstance();
      }
      /**
       * Return is module run
       * param string $module
       * return bool
       */  
      public function isBootsraped($module)
      {
        return isset($this->_bootstrapedModules[$module]);
      }
      
      
      /**
       * Get bootstraps that have been run
       *
       * return Array
       */
      public function getExecutedBootstraps()
      {
        return $this->_bootstrapedModules;
      }
      
      /**
       * Format a module name to the module class prefix
       *
       * param string $name
       * return string
       */
      protected function _formatModuleName($name)
      {
        $name = strtolower($name);
        $name = str_replace(array('-', '.'), ' ', $name);
        $name = ucwords($name);
        $name = str_replace(' ', '', $name);
        return $name;
      }
      
      /**
       * preDispatch hook
       */
      public function preDispatch(Zend_Controller_Request_Abstract $request)
      {
        $module = $request->getModuleName();
        if (empty($module)) {
          $module = $this->_getFront()->getDefaultModule();
        }
        
        if (! $this->isBootsraped($module)) {
          $moduleDirectory = $this->_getFront()->getControllerDirectory($module);
          $bootstrapClass = $this->_formatModuleName($module). '_Bootstrap';
          
          if (!class_exists($bootstrapClass, false)) {
            $bootstrapPath = dirname($moduleDirectory). '/Bootstrap.php';
            if (file_exists($bootstrapPath)) {
              $eMsgTpl = 'Bootstrap file found for module "%s" but bootstrap class "%s" not found';
              include_once $bootstrapPath;
              if (!class_exists($bootstrapClass, false)) {
                throw new Zend_Application_Resource_Exception(sprintf(
                  $eMsgTpl, $module, $bootstrapClass
                ));
              }
            } else {
              return;
            }
          }

          $moduleBootstrap = new $bootstrapClass($this->_bootstrap);
          $moduleBootstrap->bootstrap();
          $this->_bootstrapedModules[$module] = $moduleBootstrap;
        }
        
      }
    }


    В плагине обрабатываем событие preDispatch, в котором, загружаем и запускаем Bootstrap для текущего модуля, если он не был загружен до этого.

    Для ипользования нашего класса вместо стандартного просто добавим в конфиг путь к нашим классам
    pluginPaths.System_Application_Resource = «System/Application/Resource»



    Приятного использования

    Комментарии 12

      0
      Интересно, bootstap`ами для модулей я не пользовался, но буду знать.

      А ведь в FrontController`е есть еще одна не оптимальная операция связанная с модулями — это то, что при добавлении директории с модулями идет ее обход с целью поиска контроллеров, что также сильно влияет на производительность. Я решал это с помощью переопределения FrontController`а cо вставкой кеширования.

      Я с этим столкнулся из-за большого числа модулей.
      Вы с этим не сталкивались?
        0
        итерация по одной директории модулей с целью поиска папок — это тяжелая операция ??
          0
          В моем случае, большим числом модулей и контроллеров, да. Порядка 200-400 мсек. А по вашему нет?!

          По-моему там даже рефлексия или проверка интерфейса производится.
          0
          Нет пока не сталкивался.
          Но в ZF точно найдется что по оптимизировать :)
          +1
          Очепятки:
          — регестрируем
          — плогине обрабатуем
          — небил
          — путо
          — класа
          — класам

          Это только что мне бросается в глаза. Может Word еще чего найдет
            0
            У вас неточность в терминологии.
            Zend_Application_Resource_Modules — это не «ресурс», а «плагин бутстрапа ресурса» (или проще «плагин ресурса») — 4.3.2. Resource Plugins (ZF manual).

            По делу:
            — Я бы не стал перекрывать именно ресурс modules. Почему бы не назвать его прямо LazyLoadModules?
            — В этом ресурсе я бы использовал не Bootstrap.php, а LazyLoadBootstrap.php?

            Тогда функционал стандартного плагина останется доступен, и не будет зависеть от lazyload механизма
              0
              Хотелось бы узнать прирост в производительности от применения данного подхода.
              Меня особенно интересуют затраты времени на автозагрузку.
                0
                Здесь ответ стандартен, как и при любой другой оптимизации, все очень сильно зависит от ваших приложений, конфигураций, подхода к атозагрузке
                  0
                  Да, согласен. Но хотел бы узнать конкретно о Вашем случае.
                0
                Но этого ресурса есть один недостаток — он всегда загружает все Bootstrap файлы в независимости от того какое модули мы используем в конкретном запросе.

                Я не считаю это недостатком. Напротив, в этом есть определенный смысл, ведь Bootstrap модуля регистрирует в автозагрузчике свои ресурсы (например модели, помощники видов и пр.). Если мы не загрузим все эти ресурсы при старте, то не сможем получить доступ к ресурсам одного модуля из другого.

                Например: в модуле System у нас определена модель User. А в модуле Blog нам нужно ее использовать. Если мы не загрузим все модули сразу, то нет возможности получить модель «на лету». Придутся к месту танцы с бубном, вроде написания брокеров моделей, сервисов, инклудивших нужные модели самостоятельно и прочее и прочее. Не очень красиво.
                  0
                  Вот уж точно — как же автозагрузка!
                    0
                    я, например, использую Module_Bootstrap для инициализации acl, навигации, роутов специфичных для модулей, чтобы каждый модуль был законченным и относительно независимым.

                    Module_LazyLoad_Bootstrap действительно был бы хорошим дополнением к обычному Bootstrap, но никак не смог бы заместить его. У них просто различные функции.

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                Самое читаемое