<sect1 id="zend.controller.front">
    <title>Фронт-контроллер</title>

    <sect2 id="zend.controller.front.overview">
        <title>Обзор</title>

        <para>
            <code>Zend_Controller_Front</code> реализует
            <ulink url="http://www.martinfowler.com/eaaCatalog/frontController.html">паттерн
            Front Controller</ulink>, используемый в приложениях MVC. Его
            назначение состоит в инициализации окружения запроса, проложении
            маршрута приходящего запроса и последующем запуске выявленных
            действий. Он агрегирует все ответы и возвращает их по завершении
            процесса.
        </para>

        <para>
            <code>Zend_Controller_Front</code> также реализует
            <ulink url="http://en.wikipedia.org/wiki/Singleton_pattern">паттерн
            Singleton</ulink>, это означает, что в любое время может быть
            доступен только один экземпляр этого класса. Это позволяет ему также
            выступать в качестве реестра, в котором хранятся другие объекты,
            участвующие в процессе диспетчеризации.
        </para>

        <para>
            <code>Zend_Controller_Front</code> регистрирует в себе <link
            linkend="zend.controller.plugins">брокер плагинов</link> (plugin
            broker), что позволяет с помощью плагинов отслеживать события,
            инициируемые фронт-контроллером.
            В большинстве случаев это дает возможность подгонять
            процесс диспетчеризации под конкретный сайт без расширения
            фронт-контроллера для добавления функциональности.
        </para>

        <para>
            Фронт-контроллеру для его работы необходим, как минимум,
            один или более путей к директориям, содержащим
            <link linkend="zend.controller.action">контроллеры действий</link>.
            Можно также использовать различные методы для дальнейшей
            настройки конфигураций фронт-контроллера и его помощников.
        </para>

        <note>
            <title>Поведение по умолчанию</title>
            <para>
                По умолчанию фронт-контроллер загружает плагин <link
                linkend="zend.controller.plugins.standard.errorhandler">ErrorHandler</link>
                и помощник действий <link
                linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer</link>.
                Это сделано для упрощения обработки ошибок и рендеринга видов в
                контроллерах, соответственно.
            </para>

            <para>
                Для того, чтобы отключить <code>ErrorHandler</code>, произведите
                следующее до вызова метода <code>dispatch()</code>:
            </para>

            <programlisting role="php"><![CDATA[
// Отключение плагина ErrorHandler:
$front->setParam('noErrorHandler', true);
]]>
            </programlisting>

            <para>
                Для того, чтобы отключить <code>ViewRenderer</code>, сделайте
                следующее до вызова метода <code>dispatch()</code>:
            </para>

            <programlisting role="php"><![CDATA[
// Отключение помощника ViewRenderer:
$front->setParam('noViewRenderer', true);
]]>
            </programlisting>
        </note>

    </sect2>

    <sect2 id="zend.controller.front.methods.primary">
        <title>Ключевые методы</title>

        <para>
            Фронт-контроллер имеет несколько аксессоров для настройки его
            конфигурации. Тем не менее, он имеет несколько ключевых в
            его функционале методов.
        </para>

        <sect3 id="zend.controller.front.methods.primary.getinstance">
            <title>getInstance()</title>

            <para>
                <code>getInstance()</code> используется для получения экземпляра
                фронт-контроллера. Поскольку фронт-контроллер реализует паттерн
                Singleton, то это также единственно доступный способ
                инстанцирования объекта фронт-контроллера.
            </para>

            <programlisting role="php"><![CDATA[
$front = Zend_Controller_Front::getInstance();
]]>
            </programlisting>
        </sect3>

        <sect3 id="zend.controller.front.methods.primary.setcontrollerdirectory">
            <title>setControllerDirectory() и addControllerDirectory()</title>

            <para>
                <code>setControllerDirectory()</code> используется для того,
                чтобы указать <link
                linkend="zend.controller.dispatcher">диспетчеру</link>, где
                искать файлы классов <link
                linkend="zend.controller.action">контроллеров действий</link>.
                Он принимает один путь или ассоциативный массив пар
                модуль => путь в качестве параметра.
            </para>

            <para>
                Примеры:
            </para>

            <programlisting role="php"><![CDATA[
// Установка директории контроллеров, используемой по умолчанию:
$front->setControllerDirectory('../application/controllers');

// Одновременная установка нескольких директорий модулей:
$front->setControllerDirectory(array(
    'default' => '../application/controllers',
    'blog'    => '../modules/blog/controllers',
    'news'    => '../modules/news/controllers',
));

// Добавление директории модуля 'foo':
$front->addControllerDirectory('../modules/foo/controllers', 'foo');
]]>
            </programlisting>

            <note>
                <para>
                    Если вы используете <code>addControllerDirectory()</code>
                    без имени модуля, то он установит директорию для модуля
                    <code>default</code>. Если директория уже существует, то она
                    будет перезаписана.
                </para>
            </note>

            <para>
                Можно получить текущие установки для директорий
                контроллеров, используя метод
                <code>getControllerDirectory()</code>. Он вернет массив пар
                модуль => директория.
            </para>
        </sect3>

        <sect3 id="zend.controller.front.methods.primary.addmoduledirectory">
            <title>addModuleDirectory() и getModuleDirectory()</title>

            <para>
                Одной из возможностей фронт-контроллера является то, что вы
                можете <link
                    linkend="zend.controller.modular">определять модульную
                структуру директорий</link> для создания отдельных компонент,
                которые называются "модулями".
            </para>

            <para>
                Каждый модуль должен находиться в собственной директории и
                отражать структуру директорий используемого по умолчанию
                модуля - т.е., он должен содержать, как минимум, поддиректорию
                "controllers"; с ней, как правило, присутствует поддиректория
                "views" и другие поддиректории приложения.
            </para>

            <para>
                Метод <code>addModuleDirectory()</code> позволяет передавать имя
                директории, содержащей один или более директорий модулей.
                Он их сканирует и добавляет в качестве директорий контроллеров
                во фронт-контроллер.
            </para>

            <para>
                Если вы хотите получить путь к определенному модулю или к
                текущему модулю, то можете использовать метод
                <code>getModuleDirectory()</code>, при передаче имени модуля он
                возвращает путь к нему, иначе возвращается путь к текущему
                модулю.
            </para>
        </sect3>

        <sect3 id="zend.controller.front.methods.primary.dispatch">
            <title>dispatch()</title>

            <para>
                <code>dispatch(Zend_Controller_Request_Abstract $request = null,
                    Zend_Controller_Response_Abstract $response = null)</code>
                    является "рабочей лошадкой" фронт-контроллера. Он может
                    опционально принимать
                    <link linkend="zend.controller.request">объект
                    запроса</link> и/или
                    <link linkend="zend.controller.response">объект
                    ответа</link>, что дает разработчикам возможность передавать
                    свои объекты.
            </para>

            <para>
                Если методу <code>dispatch()</code> не были переданы объект
                запроса или ответа, то он будет проверять, были ли ранее
                зарегистрированы объекты, и использовать их, либо инстанцировать
                версии по умолчанию (в обоих случаях по умолчанию будут
                использоваться разновидность HTTP).
            </para>

            <para>
                Аналогичным образом <code>dispatch()</code> производит проверку
                на уже зарегистрированные объекты
                <link linkend="zend.controller.router">маршрутизатора</link> и
                <link linkend="zend.controller.dispatcher">диспетчера</link>, и
                если они не найдены, то инстанцирует версии по умолчанию.
            </para>

            <para>
                Процесс диспетчеризации имеет три отдельных события:
            </para>

            <itemizedlist>
                <listitem><para>
                    Маршрутизация
                </para></listitem>
                <listitem><para>
                    Диспетчеризация
                </para></listitem>
                <listitem><para>
                    Ответ
                </para></listitem>
            </itemizedlist>

            <para>
                Маршрутизация производится только один раз, когда вызывается
                <code>dispatch()</code>, при этом используются значения в
                объекте запроса. Диспетчеризация производится циклически. Запрос
                может означать вызов нескольких действий, контроллер или плагин
                могут сбрасывать объект запроса для дополнительного вызова
                других действий. Когда все действия выполнены, фронт-контроллер
                возвращает ответ.
            </para>
        </sect3>

        <sect3 id="zend.controller.front.methods.primary.run">
            <title>run()</title>

            <para>
                <code>Zend_Controller_Front::run($path)</code> - статический
                метод, принимающий только путь к директории контроллеров. Он
                извлекает экземпляр фронт-контроллера через
                <link linkend="zend.controller.front.methods.primary.getinstance">getInstance()</link>),
                регистрирует этот путь через
                <link linkend="zend.controller.front.methods.primary.setcontrollerdirectory">setControllerDirectory()</link>, и в конце вызывает метод <link linkend="zend.controller.front.methods.primary.dispatch">dispatch()</link>.
            </para>

            <para>
                В сущности, <code>run()</code> представляет собой удобный
                метод, который можно использовать при разработке сайта, не
                требующего специальной настройки фронт-контроллера.
            </para>

            <programlisting role="php"><![CDATA[
// Инстанцирование фронт-контроллера, установка директории контроллера и
// выполнение диспетчеризации в одном вызове:
Zend_Controller_Front::run('../application/controllers');
]]>
            </programlisting>
        </sect3>
    </sect2>

    <sect2 id="zend.controller.front.methods.environment">
        <title>Методы-аксессоры для конфигурирования</title>

        <para>
            Кроме методов, перечисленных выше, есть методы-аксессоры, которые
            можно использовать для управления конфигурацией фронт-контроллера -
            и одновременно конфигурацией классов, которым фронт-контроллер
            делегирует выполнение.
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <code>resetInstance()</code> может использоваться для сброса
                    всех текущих настроек. Он в основном предназначен для
                    тестирования, но может также использоваться в тех случаях,
                    когда нужно связать между собой несколько
                    фронт-контроллеров.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>(set|get)DefaultControllerName()</code> позволяют
                    установить другое имя используемого
                    по умолчанию контроллера (иначе используется 'index') и
                    получить текущее значение. Они служат посредниками к
                    <link linkend="zend.controller.dispatcher">диспетчеру</link>.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>(set|get)DefaultAction()</code> позволяют
                    установить другое имя используемого по умолчанию действия
                    (иначе используется 'index') и получить текущее
                    значение. Они служат посредниками к
                    <link linkend="zend.controller.dispatcher">диспетчеру</link>.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>(set|get)Request()</code> позволяют установить
                    класс <link linkend="zend.controller.request">запроса</link>
                    или его объект для использования в процессе диспетчеризации
                    и получить текущий объект. При установке объекта
                    запроса вы можете передать имя класса запроса, в этом случае
                    метод загрузит файл класса и инстанцирует его.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>(set|get)Router()</code> позволяют установить
                    класс
                    <link linkend="zend.controller.router">маршрутизатора</link>
                    или его объект, используемые в течение процесса
                    диспетчеризации, и получить текущий объект. Когда
                    устанавливается объект маршрутизатора, вы можете передать
                    имя класса маршрутизатора, в этом случае метод загрузит файл
                    класса и инстанцирует его.
                </para>

                <para>
                    При извлечении объекта маршрутизатора сначала производится
                    проверка, представлен ли во фронт-контроллере такой объект и
                    в случае его отсутствия инстанцирует используемый по
                    умолчанию маршрутизатор (Rewrite Router).
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>(set|get)BaseUrl()</code> дают возможность установить
                    <link linkend="zend.controller.request.http.baseurl">базовый
                    URL</link>, который удаляется из начала при
                    маршрутизации запросов, и получить его текущее значение.
                    Это значение передается объекту запроса непосредственно до
                    маршрутизации.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>(set|get)Dispatcher()</code> дают возможность
                    установки класса
                    <link linkend="zend.controller.dispatcher">диспетчера</link>
                    или его объекта для использования в течение диспетчеризации,
                    и получения текущего объекта. При установке объекта
                    диспетчера можно передать имя класса диспетчера, в этом
                    случае метод загрузит файл класса и инстанцирует его.
                </para>

                <para>
                    При извлечении объекта диспетчера, метод сначала производит
                    проверку того, представлен ли во фронт-контроллере такой
                    объект и в случае его отсутствия инстанцирует диспетчер,
                    используемый по умолчанию.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>(set|get)Response()</code> дают возможность установить
                    класс <link linkend="zend.controller.response">ответа</link>
                    или его объект для использования в процессе диспетчеризации,
                    и извлечь текущий объект. При установке объекта
                    ответа можно передать имя класса ответа, в этом случае метод
                    загрузит файл класса и инстанцирует его.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>registerPlugin(Zend_Controller_Plugin_Abstract $plugin, $stackIndex = null)</code>
                    позволяет регистрировать <link
                        linkend="zend.controller.plugins">объекты плагинов</link>.
                    Путем установки опционального параметра
                    <code>$stackIndex</code>, вы можете
                    контролировать порядок, в котором выполняются плагины.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>unregisterPlugin($plugin)</code> позволяет отменять
                    регистрацию <link
                        linkend="zend.controller.plugins">объектов плагинов</link>.
                    <code>$plugin</code> может быть как объектом плагина, так и строкой, обозначающей класс плагина, регистрацию которого надо отменить.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>throwExceptions($flag)</code> используется для
                    включения/отключения возможности генерации исключений в
                    течение процесса диспетчеризации. По умолчанию исключения
                    отлавливаются и размещаются в
                    <link linkend="zend.controller.response">объекте
                    ответа</link>; включение <code>throwExceptions()</code>
                    переопределит это поведение
                </para>

                <para>
                    Более полробную информацию читайте в <xref linkend="zend.controller.exceptions"/>.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>returnResponse($flag)</code> используется для того,
                    чтобы указать фронт-контроллеру - возвращать ли ответ
                    из <code>dispatch()</code> (<code>true</code>), либо ответ
                    должен быть отправлен автоматически (<code>false</code>). По
                    умолчанию ответ отправляется автоматически
                    (через вызов
                    <code>Zend_Controller_Response_Abstract::sendResponse()</code>);
                    включение <code>returnResponse()</code> переопределит это
                    поведение.
                </para>

                <para>
                    Ситуации, в которых может потребоваться возврат ответа,
                    включают в себя проверку на предмет исключений до отправки
                    ответа клиенту, необходимость журналирования различных
                    аспектов ответа (таких, как заголовки) и т.д.
                </para>
            </listitem>
        </itemizedlist>
    </sect2>

    <sect2 id="zend.controller.front.methods.params">
        <title>Параметры фронт-контроллера</title>

        <para>
            Во введении мы указали на то, что фронт-контроллер также выступает
            как реестр для различных компонент контроллера. Это реализуется
            через группу методов для параметров. Эти методы позволяют
            регистрировать произвольные данные - объекты и переменные - во
            фронт-контроллере, чтобы их можно было извлечь в любой точке цепочки
            диспетчеризации. Эти значения передаются маршрутизатору, диспетчеру,
            и контроллерам действий. Эти методы включают в себя:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <code>setParam($name, $value)</code> дает возможность
                    установить единственный параметр с именем <code>$name</code>
                    и значением <code>$value</code>.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>setParams(array $params)</code> позволяет
                    установить несколько параметров одновременно с помощью
                    ассоциативного массива.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>getParam($name)</code> дает возможность извлечь один
                    параметр за один раз, используя его имя <code>$name</code> в
                    качестве идентификатора.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>getParams()</code> позволяет извлечь все
                    параметры за один раз.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>clearParams()</code> позволяет удалить один параметр
                    (путем передачи строкового идентификатора), нескольких
                    параметров (путем передачи массива строковых
                    идентификаторов) или весь стек параметров (без передачи
                    чего-либо).
                </para>
            </listitem>
        </itemizedlist>

        <para>
            Есть несколько предопределенных параметров специально для
            использования в цепочке диспетчеризации:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <code>useDefaultControllerAlways</code> используется для
                    указания
                    <link linkend="zend.controller.dispatcher">диспетчеру</link>,
                    что следует использовать контроллер по умолчанию в модуле
                    по умолчанию для любых запросов, диспетчеризация которых
                    невозможна (т.е. модуль, контроллер и/или действие не
                    существуют). По умолчанию он отключен.
                </para>

                <para>
                    См. <xref linkend="zend.controller.exceptions.internal"/> за
                    более подробной информацией об использовании этого параметра
                    настройки.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>disableOutputBuffering</code> используется для
                    указания
                    <link linkend="zend.controller.dispatcher">диспетчеру</link>,
                    что не следует использовать буферизацию вывода для сбора
                    данных на вывод, генерируемых контроллерами действий. По
                    умолчанию диспетчер собирает весь вывод и присоединяет его к
                    телу содержимого в объекте ответа.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>noViewRenderer</code> используется для отключения
                    помощника <link
                        linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer</link>.
                    Установите этот параметр в true для его отключения.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>noErrorHandler</code> используется для отключения
                    <link
                        linkend="zend.controller.plugins.standard.errorhandler">плагина
                        Error Handler</link>
                    Установите этот параметр в true для его отключения.
                </para>
            </listitem>
        </itemizedlist>
    </sect2>

    <sect2 id="zend.controller.front.subclassing">
        <title>Создание подклассов фронт-контроллера</title>

        <para>
            При создании подкласса фронт-контроллера, необходимо, как
            минимум, переопределить метод <code>getInstance()</code>.
        </para>

        <programlisting role="php"><![CDATA[
class My_Controller_Front extends Zend_Controller_Front
{
    public static function getInstance()
    {
        if (null === self::$_instance) {
            self::$_instance = new self();
        }

        return self::$_instance;
    }
}
]]>
        </programlisting>

        <para>
            Такое переопределение метода <code>getInstance()</code> гарантирует
            то, что последующий вызов
            <code>Zend_Controller_Front::getInstance()</code> вернет экземпляр
            созданного вами подкласса вместо экземпляра
            <code>Zend_Controller_Front</code> - это особенно полезно для
            некоторых альтернативных маршрутизаторов и помощников видов.
        </para>

        <para>
            Как правило, нет необходимости создавать подклассы
            фронт-контроллера, если только не нужно добавить новый
            функционал (например, автозагрузку плагинов или способ
            определения путей к помощникам действий). Целями, для достижения
            которых может потребоваться изменение поведения фронт-контроллера,
            могут быть изменение принципа хранения директорий
            контроллеров, установка других используемых по умолчанию
            маршрутизатора и диспетчера и т.д.
        </para>
    </sect2>
</sect1>
<!--
vim:se ts=4 sw=4 et:
-->