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

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

## Переход с 1.7.x на 1.8.0 и более поздние версии

### Изменения в стандартном маршруте

Поскольку в новый стандартный маршрут были добавлены переводимые сегменты, символ `@` стал специальным символом в начале сегмента маршрута. Для того, чтобы можно было использовать его в статических сегментах, вы должны экранировать его путем добавления второго символа `@`. То же самое правило теперь применяется и к символу `:`.

## Переход с 1.6.x на 1.7.0 и более поздние версии

### Изменения в интерфейсе диспетчера

Пользователи обратили наше внимание на тот факт, что `Zend_Controller_Action_Helper_ViewRenderer` использует не присутствующий в интерфейсе метод из абстрактного класса диспетчера. Мы добавили этот метод в интерфейс диспетчера для того, чтобы гарантировать работу созданного вами диспетчера с классами, входящими в поставку Zend Framework:

- `formatModuleName()`: должен принимать необработанное имя контроллера (в том виде, в котором оно хранится в объекте запроса) и преобразовывать его в имя класса контроллера, наследующего от `Zend_Controller_Action`.

## Переход с 1.5.x на 1.6.0 и более поздние версии

### Изменения в интерфейсе диспетчера

Пользователи обратили наше внимание на тот факт, что `Zend_Controller_Front` и `Zend_Controller_Router_Route_Module` используют методы диспетчера, которые не определены в его интерфейсе. Мы добавили следующие три метода в его интерфейс для того, чтобы гарантировать работу созданного вами диспетчера с классами, входящими в поставку Zend Framework:

- `getDefaultModule()`: должен возвращать имя используемого по умолчанию модуля.

- `getDefaultControllerName()`: должен возвращать имя используемого по умолчанию контроллера.

- `getDefaultAction()`: должен возвращать имя используемого по умолчанию действия.

## Переход с 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`; вы можете изменить правила инфлектора так, чтобы он более не разделял слова в имени действия знаком тире:

      $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':

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

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

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

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

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

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

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

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

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

    // $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`. Это легко можно сделать в любой момент времени.

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

      // Предполагается, что $view уже определен
      $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view);
      Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);

- В любой точке процесса загрузки (bootstrap process):

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

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

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

## Переход с 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()`.

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

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

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

    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 на месте соответствующих аргументов.

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

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

    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()` теперь принимает и возвращает объект запроса вместо метки `Zend_Controller_Dispatcher_Token`.

- `Zend_Controller_Router_Interface::route()` теперь принимает и возвращает объект ответа вместо метки `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();

        // Извлечение параметров запроса
        // Оно не изменилось; _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`. Все методы теперь принимают и возвращают объект ответа вместо метки диспетчеризации.