7.13. Переход с предыдущих версий

API компонент системы MVC претерпевал изменения со временем. Если вы начали использование Zend Framework с его ранних версий, то следуйте приведенным ниже рекомендациям по переносу вашего кода на новую архитектуру.

7.13.1. Переход с 1.0.x на 1.5.0 и более поздние версии

Хотя основной набор функциональных возможностей остался тем же, и все документированные возможности не претерпели изменений, есть одна недокументированная "возможность", которая была изменена.

При написании URL-ов, документированным способом написания имен действий в формате camelCase является использование разделителей слов. По умолчанию это '.' или '-', но они могут быть заменены на другие символы путем настройки диспетчера. Диспетчер внутри себя приводит имена действий к нижнему регистру и использует эти разделители слов для "пересборки" имен действий с использованием формата camelCase. Но из-за того, что функции PHP не чувствительны к регистру, вы могли по-прежнему писать URL-ы в формате camelCasе, и результатом был запуск тех же методов действий. Например, 'camel-cased' должен был преобразовываться диспетчером в 'camelCasedAction', а 'camelCased' - в 'camelcasedAction', но из-за нечувствительности PHP к регистру имен функций в обоих случаях будет произведен вызов одного и того же метода.

Это вызывало проблемы с ViewRenderer при определении имени скрипта вида. Документированный способ состоит в том, что все разделители слов преобразуются в тире, и слова приводятся к нижнему регистру. Это создает семантическую связь между действиями и скриптами видов, а нормализация гарантирует, что скрипты могут быть найдены. Тем не менее, если вызывается действие с именем 'camelCased' и благополучно обработано, то разделитель слов более не присутствует в имени, и ViewRenderer пытается вызвать другой скрипт вида - 'camelcased.phtml' вместо 'camel-cased.phtml'.

Некоторые разработчики полагались на эту незапланированную "возможность". Тем не менее, некоторые изменения в дереве 1.5.0, привели к тому, что ViewRenderer более не ищет такие пути; семантическая связь теперь усилена. Главное, диспетчер теперь чувствителен к регистру в именах действий. Это значит, что ссылка на действие через URL с использованием формата camelCase не будет приводить к вызову того же метода, что и с использованием разделителей слов (т.е. 'camel-casing').

Если получилось, что вы используете эту "возможность", то в имеете несколько вариантов решения:

  • Наилучший вариант: переименуйте ваши скрипты вида. Плюсы: будущая совместимость. Минусы: если вы имеете много скриптов вида, которые полагаются на старое, незапланированное поведение, то вам придется сделать много переименований.

  • Второй лучший вариант: ViewRenderer теперь делегирует определение скриптов вида инфлектору Zend_Filter_Inflector; вы можете изменить правила инфлектора так, чтобы он более не разделял слова в имени действия знаком тире:

    <?php
    $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
    $inflector = $viewRenderer->getInflector();
    $inflector->setFilterRule(':action', array(
        new Zend_Filter_PregReplace(
            '#[^a-z0-9' . preg_quote(DIRECTORY_SEPARATOR, '#') . ']+#i',
            ''
        ),
        'StringToLower'
    ));
    ?>

    Приведенный выше код изменит инфлектор таким образом, чтобы он более не разделял слова в имени действия знаком тире; вы можете также убрать фильтр 'StringToLower', если хотите, чтобы реальные имена скриптов вида тоже были в формате camelCase.

    Если переименование скриптов вида слишком утомительно или требует много времени, то этот вариант будет наилучшим решением на тот период, пока вы не найдете время на переименование.

  • Менее желательное решение: Вы можете заставить диспетчер принимать имена действий в формате camelCase, установив новый флаг фронт-контроллера 'useCaseSensitiveActions':

    <?php
    $front->setParam('useCaseSensitiveActions', true);
    ?>

    Это позволит вам использовать camelCase в URL-ах и они будут приводить к запуску тех действий, что и при использовании разделителей слов. Тем не менее, это будет означать, что исходная проблема может повлечь за собой другие; возможно, вам потребуется также использовать описанный выше второй вариант, чтобы все работало наверняка.

    Также заметьте, что использование этого флага приведет к появлению предупреждения (notice) о том, что его использование не рекомендуется.

7.13.2. Переход с 0.9.3 на 1.0.0RC1 и более поздние версии

Основные изменения, появившиеся в 1.0.0RC1 - это добавление включенного по умолчанию плагина ErrorHandler и помощника действий ViewRenderer. Пожалуйста, прочитайте внимательно документацию к ним, чтобы понять, как они работают, и как они могут повлиять на работу ваших приложений.

Плагин ErrorHandler производит в методе postDispatch() проверку на предмет исключений и переход (forwarding) к определенному контроллеру-обработчику исключений. Вы можете отключить его путем установки параметра noErrorHandler во фронт-контроллере:

<?php
$front->setParam('noErrorHandler', true);
        

Помощник действий ViewRenderer автоматизирует добавление вида в контроллеры действий и производит авторендеринг скрипта вида, выбранного по текущему действию. Первая проблема, с которой вы можете встретиться - у вас есть действия, которые не производят рендеринг скриптов вида и не производят переход или перенаправление, поскольку ViewRenderer будет пытаться запустить скрипт вида, выбранного по имени действия.

Есть несколько стратегий, используя которые, вы можете обновить свой код. В краткострочной перспективе решением может быть глобальное отключение ViewRenderer во фронт-контроллере до начала процесса диспетчеризации:

<?php
// $front является экземпляром Zend_Controller_Front
$front->setParam('noViewRenderer', true);
        

Но в долгосрочной перспективе это не лучшее решение, поскольку оно означает, что вам в будущем придется писать больше кода.

Когда вы будете готовы начать использование функционала ViewRenderer, то необходимо проверить некоторые места в коде контроллеров. Первое, просмотрите методы действий (методы, заканчивающиеся на 'Action') и определите, что делает каждый метод. Если не происходит ничего из следующего, то нужно произвести изменения:

  • Вызов $this->render()

  • Вызов $this->_forward()

  • Вызов $this->_redirect()

  • Вызов помощника действий Redirector

Наиболее легким способом будет отключение авторендеринга в данном методе:

$this->_helper->viewRenderer->setNoRender();
        

Если вы обнаружили, что ни один из методов действий не производит рендеринг, переход или перенаправление, то, скорее всего, нужно поместить эту строку в методы preDispatch() или init():

public function preDispatch()
{
    // отключение авторендеринга скриптов вида
    $this->_helper->viewRenderer->setNoRender()
    // ... еще код ..
}
        

Если вы вызываете render() и используете определенную соглашением модульную структуру директорий, то нужно изменить свой код так, чтобы использовался авторендеринг:

  • Если производится рендеринг нескольких скриптов вида в одном действии, то не нужно ничего изменять.

  • Если производится простой вызов метода render() без аргументов, то можете удалить соответствующие строки.

  • Если вызывается render() с аргументами и не производится впоследствии каких-либо действий или рендеринга нескольких скриптов вида, то можно заменить эти вызовы на чтение $this->_helper->viewRenderer().

На тот случай, если вы не используете определенную соглашением модульную структуру директорий, есть набор методов для установки базового пути к видам и спецификаций пути ко скрипту, поэтому вы сможете и в этом случае использовать ViewRenderer. Информацию об этих методах вы найдете в документации по ViewRenderer

Если вы используете объект вида из реестра, создали свой объект вида, либо используете другие реализации встроенного, то может потребоваться добавить этот объект в ViewRenderer. Это легко можно сделать в любой момент времени.

  • До начала диспетчеризации экземпляра фронт-контроллера:

    <?php
    // Предполагается, что $view уже определен
    $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view);
    Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
                    
  • В любой точке процесса загрузки (bootstrap process):

    <?php
    $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
    $viewRenderer->setView($view);
                    

Есть много способов модификации ViewRenderer, включая установку других скриптов вида для рендеринга, определение замещений для всех замещаемых элементов пути ко скрипту вида (включая суффикс), выбор именованного сегмента ответа в качестве используемого и др. Если вы не используете определенную соглашением модульную структуру директорий, то можете ассоциировать с ViewRenderer другие спецификации путей.

Рекомендуется адаптировать свой код для использования ErrorHandler и ViewRenderer, так как сейчас это лежащий в основе фреймворка функционал.

7.13.3. Переход с 0.9.2 на 0.9.3 и более поздние версии

В версии 0.9.3 были добавлены помощники действий (action helpers). Это изменение включает в себя удаление перечисленных ниже методов из-за того, что сейчас они инкапсулированы в помощнике перенаправлений (redirector action helper):

  • setRedirectCode(); используйте Zend_Controller_Action_Helper_Redirector::setCode().

  • setRedirectPrependBase(); используйте Zend_Controller_Action_Helper_Redirector::setPrependBase().

  • setRedirectExit(); используйте Zend_Controller_Action_Helper_Redirector::setExit().

Более подробную информацию о том, как извлекать и работать с объектами действий, читайте в документации по помощникам действий. Об установке опций перенаправления и альтернативных методах перенаправления читайте в документации по помощнику перенаправлений.

7.13.4. Переход с 0.6.0 на 0.8.0 и более поздние версии

Базовое использование компонент MVC не изменилось:

require_once 'Zend/Controller/Front.php';
Zend_Controller_Front::run('/path/to/controllers');
        

Тем не менее, структура директорий была подвергнута пересмотру, некоторые компоненты были удалены, другие добавлены или переименованы. Изменения включают в себя следующее:

  • Zend_Controller_Router удален в пользу использования Rewrite Router.

  • Zend_Controller_RewriteRouter переименован в Zend_Controller_Router_Rewrite, теперь это стандартный маршрутизатор, поставляемый с фреймворком. Zend_Controller_Front будет использовать его по умолчанию, если не был установлен другой маршрутизатор.

  • Добавлен новый класс маршрута для использования с Rewrite Router - Zend_Controller_Router_Route_Module. Он включает в себя маршрут по умолчанию, используемый MVC, и поддерживает модули контроллеров.

  • Zend_Controller_Router_StaticRoute переименован в Zend_Controller_Router_Route_Static.

  • Zend_Controller_Dispatcher переименован в Zend_Controller_Dispatcher_Standard.

  • Аргументы метода Zend_Controller_Action::_forward() изменились. Его сигнатура теперь:

    final protected function _forward($action, $controller = null, $module = null, array $params = null);
                    

    $action - обязательный аргумент. Если не был определен контроллер, то предполагается, что вызывается действие в текущем контроллере. $module всегда игнорируется, если не определен контроллер. Все переданные в аргументе $params параметры будут добавлены в объект запроса. Если вы не запрашиваете контроллер или модуль, но нужно передать параметры, то просто укажите null на месте соответствующих аргументов.

7.13.5. Переход с 0.2.0 и более ранних версий на 0.6.0

Базовое использование компонент системы MVC не изменилось, следующий код будет корректно выполняться и в версии 0.6.0:

require_once 'Zend/Controller/Front.php';
Zend_Controller_Front::run('/path/to/controllers');
        
/* -- создание маршрутизатора -- */
$router = new Zend_Controller_RewriteRouter();
$router->addRoute('user', 'user/:username', array('controller' => 'user',
'action' => 'info'));

/* -- установка его во фронт-контроллере -- */
$ctrl = Zend_Controller_Front::getInstance();
$ctrl->setRouter($router);

/* -- установка директории контроллеров и запуск диспетчеризации -- */
$ctrl->setControllerDirectory('/path/to/controllers');
$ctrl->dispatch();
        

Рекомендуется использовать объект ответа для сбора содержимого и заголовков. Это дает большую гибкость при переключении между разными форматами вывода (например, JSON или XML вместо XHTML) в приложениях. По умолчанию dispatch() будет возвращать ответ, отправляя заголовки и выводя весь контент. Можно также сделать так, чтобы фронт-контроллер возвращал ответ, используя метод returnResponse(), и затем выводить ответ так, как нужно вам. Будущая версия фронт-контроллер может принуждать к использованию объекта ответа посредством буферизации вывода.

Также добавлено много новых функциональных возможностей, расширяющих существующий API, они описаны в документации.

Основные изменения, о которых следует знать, касаются расширения существующих компонент. Наиболее важные из них следующие:

  • Zend_Controller_Front::dispatch() по умолчанию отлавливает все исключения в объекте ответа и не отображает их для предотвращения раскрытия данных о системе. Вы можете переопределить это поведение несколькими способами:

    • Установка throwExceptions() во фронт-контроллере:

      $front->throwExceptions(true);
                              
    • Установка renderExceptions() в объекте ответа:

      $response->renderExceptions(true);
      $front->setResponse($response);
      $front->dispatch();
      
      // или:
      $front->returnResponse(true);
      $response = $front->dispatch();
      $response->renderExceptions(true);
      echo $response;
                              
  • Zend_Controller_Dispatcher_Interface::dispatch() теперь принимает и возвращает объект запроса Раздел 7.4, «Объект запроса» вместо метки Zend_Controller_Dispatcher_Token.

  • Zend_Controller_Router_Interface::route() теперь принимает и возвращает объект ответа Раздел 7.4, «Объект запроса» вместо метки Zend_Controller_Dispatcher_Token

  • Изменения Zend_Controller_Action включают в себя следующие:

    • Его конструктор теперь включает в себя три аргумента: Zend_Controller_Request_Abstract $request, Zend_Controller_Response_Abstract $response и array $params (необязательный). Zend_Controller_Action::__construct() использует их для установки запроса, ответа и свойств объекта (аргументов вызова); переопределяя конструктор, вам следует реализовать те же операции. Но лучше использовать метод init() для выполнения любого конфигурирования экземпляра класса, так как этот метод вызывается в конце конструктора.

    • Метод run() теперь не определен с ключевым словом final, но он также и не используется во фронт-контроллере; единственным его назначением является использование класса как контроллера страниц. Теперь он принимает два необязательных аргумента – Zend_Controller_Request_Abstract $request и Zend_Controller_Response_Abstract $response.

    • indexAction() уже не обязателен для определения, но все же рекомендуется определять его в качестве действия по умолчанию. Это позволяет использовать RewriteRouter и контроллеры действий для указания других используемых по умолчанию методов действий.

    • __call() должен переопределяться для автоматической обработки вызовов действий, не определенных в классе контроллера.

    • Метод _redirect() теперь принимает второй необязательный аргумент, HTTP-код, который должен возвращаться при перенаправлении, и третий необязательный аргумент, $prependBase, который указывает, что базовый URL, зарегистрированный в объекте запроса, должен предшествовать URL, переданному в первом аргументе.

    • Свойство _action больще не устанавливается. Это свойство было объектом класса Zend_Controller_Dispatcher_Token, которй больше не существует в текущем воплощении. Единственным назначением метки (token) было предоставление информации о запрошенных контроллере, действии и параметрах URL. Эта информация теперь доступна в объекте запроса, и доступ к ней можно получить следующим образом:

      // Извлечение имени запрошенного контроллера
      // Ранее доступ был через $this->_action->getControllerName().
      // Пример ниже использует getRequest(), хотя вы можете обращаться напрямую
      // ко свойству $_request; рекомендуется использовать getRequest(), поскольку
      // родительский класс может переопределить доступ к объекту запроса.
      $controller = $this->getRequest()->getControllerName();
      
      // Извлечение имени запрошенного действия
      // Ранее доступ был через $this->_action->getActionName().
      $action = $this->getRequest()->getActionName();
      
      // Retrieve the request parameters
      // This hasn't changed; the _getParams() and _getParam() methods simply proxy to
      // the request object now.
      
      // Извлечение параметров запроса
      // Оно не изменилось; _getParams() и _getParam() просто вызывают аналогичные
      // методы объекта запроса
      $params = $this->_getParams();
      
      // запрашивается параметр 'foo', если параметр не найден,
      // то используется значение по умолчанию 'default'
      $foo = $this->_getParam('foo', 'default');
                              
    • Удален метод noRouteAction(). Подходящим способом обработки несуществующих методов действий будет перенаправление к действию по умолчанию через __call():

      public function __call($method, $args)
      {
          // Если запрошен несуществующий метод действия, то вызывается метод
          // действия по умолчанию:
          if ('Action' == substr($method, -6)) {
              return $this->defaultAction();
          }
      
          throw new Zend_Controller_Exception('Invalid method called');
      }
                              
  • Удален метод Zend_Controller_RewriteRouter::setRewriteBase(). Вместо него используйте Zend_Controller_Front::setBaseUrl() (или Zend_Controller_Request_Http::setBaseUrl(), если используется класс запроса).

  • Zend_Controller_Plugin_Interface был заменен на Zend_Controller_Plugin_Abstract. Все методы теперь принимают и возвращают объект ответа Раздел 7.4, «Объект запроса» вместо метки диспетчеризации.

    Поддержать сайт на родительском проекте КГБ