<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<sect1 id="zend.application.theory-of-operation">
    <title>Теоретические основы работы с Zend_Application</title>

    <para>
        Получение сконфигурированного <acronym>MVC</acronym>-приложения,
        готового к обработке запроса,
        требует наличия дополнительного кода, объем которого зависит от
        используемого функционала:
        установка соединения с базой данных, конфигурирование видов и
        их помощников, конфигурирование макетов (layouts), регистрация плагинов,
        регистрация помощников действий и так далее.
    </para>

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

    <para>
        <classname>Zend_Application</classname> облегчает управление начальной
        загрузкой и способствует повторному использованию путем инкапсуляции
        загрузки в соответствии с парадигмой <acronym>ООП</acronym>.
    </para>

    <para>
        <classname>Zend_Application</classname> состоит из трех областей:
    </para>

    <itemizedlist>
        <listitem>
            <para>
                <classname>Zend_Application</classname>: загружает окружение
                <acronym>PHP</acronym>, включая include_paths и автозагрузку
                (autoloading), инстанцирует запрошенный загрузочный класс.
            </para>
        </listitem>
        <listitem>
            <para>
                <classname>Zend_Application_Bootstrap</classname>: предоставляет
                интерфейсы для загрузочных классов.
                <classname>Zend_Application_Bootstrap_Bootstrap</classname>
                предоставляет общий функционал, удовлетворяющий большинство
                нужд по начальной загрузке, включающие в себя алгоритмы проверки
                зависимостей и возможность загрузки ресурсов по требованию.
            </para>
        </listitem>
        <listitem>
            <para>
                <classname>Zend_Application_Resource</classname> предоставляет
                интерфейс для стандартных ресурсов загрузки,
                которые могут быть загружены по требованию через экземпляр
                загрузочного класса, и несколько реализаций ресурсов,
                используемых по умолчанию.
            </para>
        </listitem>
    </itemizedlist>

    <para>
        Разработчики могут создавать загрузочный класс для приложения,
        расширяя <classname>Zend_Application_Bootstrap_Bootstrap</classname>
        или, как минимум, реализуя интерфейс
        <classname>Zend_Application_Bootstrap_Bootstrapper</classname>.
        Входная точка (например, <filename>public/index.php</filename>)
        будет загружать <classname>Zend_Application</classname> и
        инстанцировать его путем передачи:
    </para>

    <itemizedlist>
        <listitem>
            <para>
                Текущего окружения
            </para>
        </listitem>
        <listitem>
            <para>
                Опций для загрузки
            </para>
        </listitem>
    </itemizedlist>

    <para>
        Опции загрузки включают в себя путь к файлу, содержащему в себе
        загрузочный класс и, опционально:
    </para>

    <itemizedlist>
        <listitem>
            <para>
                Любые дополнительные пути для добавления в include_path
            </para>
        </listitem>
        <listitem>
            <para>
                Любые дополнительные пространства имен автозагрузки,
                которые требуется зарегистрировать
            </para>
        </listitem>
        <listitem>
            <para>
                Любые установки <filename>php.ini</filename> для инициализации
            </para>
        </listitem>
        <listitem>
            <para>
                Имя класса загрузки (если используется имя, отличное от
                "Bootstrap")
            </para>
        </listitem>
        <listitem>
            <para>
                Пары префикс-путь для ресурсов
            </para>
        </listitem>
        <listitem>
            <para>
                Любые ресурсы для использования (указываются через имя класса
                или их короткое имя)
            </para>
        </listitem>
        <listitem>
            <para>
                Дополнительный путь к загружаемому конфигурационному файлу
            </para>
        </listitem>
        <listitem>
            <para>
                Дополнительные опции конфигурации
            </para>
        </listitem>
    </itemizedlist>

    <para>
        Опции могут быть массивом, объектом <classname>Zend_Config</classname>
        или путью к конфигурационному файлу.
    </para>

    <sect2 id="zend.application.theory-of-operation.bootstrap">
        <title>Начальная загрузка</title>

        <para>
            Второй областью отвественности компоненты
            <classname>Zend_Application</classname> является
            выполнение загрузки приложения. Загрузочные классы
            должны как минимум реализовывать интерфейс
            <classname>Zend_Application_Bootstrap_Bootstrapper</classname>,
            который определяет следующий <acronym>API</acronym>:
        </para>

        <programlisting language="php"><![CDATA[
interface Zend_Application_Bootstrap_Bootstrapper
{
    public function __construct($application);
    public function setOptions(array $options);
    public function getApplication();
    public function getEnvironment();
    public function getClassResources();
    public function getClassResourceNames();
    public function bootstrap($resource = null);
    public function run();
}
]]></programlisting>

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

        <para>
            Вы можете сами реализовывать этот интерфейс, расширять
            <classname>Zend_Application_Bootstrap_BootstrapAbstract</classname>
            или использовать
            <classname>Zend_Application_Bootstrap_Bootstrap</classname>.
        </para>

        <para>
            Кроме этого функционала есть и другие требующие внимания области,
            с которыми вы должны ознакомиться.
        </para>

        <sect3 id="zend.application.theory-of-operation.bootstrap.resource-methods">
            <title>Методы ресурсов</title>

            <para>
                <classname>Zend_Application_Bootstrap_BootstrapAbstract</classname>
                предоставляет простое соглашение для определения
                методов ресурсов. Любой защищенный метод с именем,
                начинающимся с <emphasis>_init</emphasis>, будет считаться
                методом ресурса.
            </para>

            <para>
                Для того, чтобы запустить один метод ресурса, вызывайте
                метод <methodname>bootstrap()</methodname> с именем ресурса в
                качестве аргумента. Именем ресурса будет имя метода без префикса
                <emphasis>_init</emphasis>.
            </para>

            <para>
                Для того, чтобы запустить несколько методов ресурсов,
                передавайте массив имен. А для того, чтобы запустить
                все методы ресурсов, вызывайте метод без аргументов.
            </para>

            <para>
                Возьмем следующий загрузочный класс:
            </para>

            <programlisting language="php"><![CDATA[
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    protected function _initFoo()
    {
        // ...
    }

    protected function _initBar()
    {
        // ...
    }

    protected function _initBaz()
    {
        // ...
    }
}
]]></programlisting>

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

            <programlisting language="php"><![CDATA[
$bootstrap->bootstrap('foo');
]]></programlisting>

            <para>
                Для того, чтобы запустить методы
                <methodname>_initFoo()</methodname> и
                <methodname>_initBar()</methodname>, сделайте следующее:
            </para>

            <programlisting language="php"><![CDATA[
$bootstrap->bootstrap(array('foo', 'bar'));
]]></programlisting>

            <para>
                Для того, чтобы запустить все методы ресурсов,
                используйте <methodname>bootstrap()</methodname> без аргументов:
            </para>

            <programlisting language="php"><![CDATA[
$bootstrap->bootstrap();
]]></programlisting>
        </sect3>

        <sect3 id="zend.application.theory-of-operation.bootstrap.resource-plugins">
            <title>Загрузки, использующие плагины ресурсов</title>

            <para>
                Для того, чтобы вы могли сделать свои загрузки более пригодными
                для повторного использования, мы реализовали возможность
                помещать свои ресурсы в классы-плагины ресурсов.
                Это позволит вам легко комбинировать ресурсы, используя
                конфигурацию.
                Ниже будет описание того, <link
                    linkend="zend.application.theory-of-operation.resources">как
                    создавать ресурсы</link>, в данном разделе мы только
                покажем, как использовать их.
            </para>

            <para>
                Если ваша загрузка должна поддерживать
                плагины ресурсов, то вам нужно реализовать дополнительный
                интерфейс
                <classname>Zend_Application_Bootstrap_ResourceBootstrapper</classname>.
                Этот интерфейс определяет <acronym>API</acronym> для
                определения местонахождения, регистрации и загрузки плагинов
                ресурсов:
            </para>

        <programlisting language="php"><![CDATA[
interface Zend_Application_Bootstrap_ResourceBootstrapper
{
    public function registerPluginResource($resource, $options = null);
    public function unregisterPluginResource($resource);
    public function hasPluginResource($resource);
    public function getPluginResource($resource);
    public function getPluginResources();
    public function getPluginResourceNames();
    public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader);
    public function getPluginLoader();
}
]]></programlisting>

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

            <para>
                <classname>Zend_Application_Bootstrap_BootstrapAbstract</classname>
                (и, следовательно, наследующий от него класс
                <classname>Zend_Application_Bootstrap_Bootstrap</classname>)
                реализуют этот интерфейс, позволяя вам использовать
                плагины ресурсов.
            </para>

            <para>
                Для того, чтобы использовать плагины ресурсов, вы должны
                указывать их в опциях, передаваемых объектам приложения и/или
                загрузки. Эти опции могут указываться через конфигурационный
                файл или передаваться вручную.
                Опции будут массивом пар ключ/опции, где ключом является
                имя ресурса. Именем ресурса будет часть строки, следующая
                после префикса класса. Например,
                ресурсы, поставляемые c Zend Framework'ом, имеют префикс класса
                "<classname>Zend_Application_Resource_</classname>", все, что
                следует за ним, будет именем ресурса. Например:
            </para>

            <programlisting language="php"><![CDATA[
$application = new Zend_Application(APPLICATION_ENV, array(
    'resources' => array(
        'FrontController' => array(
            'controllerDirectory' => APPLICATION_PATH . '/controllers',
        ),
    ),
));
]]></programlisting>

            <para>
                Это означает, что должен использоваться ресурс
                "FrontController" с указанными опциями.
            </para>

            <para>
                Если вы планируете писать собственные плагины ресурсов
                либо добавить сторонние, то нужно будет указать вашей
                загрузке, где их искать.
                Внутри себя загрузка использует
                <classname>Zend_Loader_PluginLoader</classname>, поэтому
                достаточно указать префикс класса и путь к директории с
                плагинами ресурсов.
            </para>

            <para>
                Для примера предположим, что вы имеете свои плагины ресурсов
                в директории <filename>APPLICATION_PATH/resources/</filename>,
                и они используют общий префикс
                <classname>My_Resource</classname>.
                Вы можете передать эту информацию объекту приложения так,
                как показано ниже:
            </para>

            <programlisting language="php"><![CDATA[
$application = new Zend_Application(APPLICATION_ENV, array(
    'pluginPaths' => array(
        'My_Resource' => APPLICATION_PATH . '/resources/',
    ),
    'resources' => array(
        'FrontController' => array(
            'controllerDirectory' => APPLICATION_PATH . '/controllers',
        ),
    ),
));
]]></programlisting>

            <para>
                После этого вы можете использовать ресурсы из этой директории.
            </para>

            <para>
                Так же, как и в случае с методами ресурсов, вы используете
                метод <methodname>bootstrap()</methodname> для выполнения
                плагинов ресурсов.
                И точно так же вы можете указывать один плагин ресурса,
                несколько плагинов ресурсов (через массив), либо запускать все плагины
                сразу. Кроме того, вы можете комбинировать их с методами
                ресурсов.
            </para>

            <programlisting language="php"><![CDATA[
// Выполнить один:
$bootstrap->bootstrap('FrontController');

// Выполнить несколько:
$bootstrap->bootstrap(array('FrontController', 'Foo'));

// Выполнить все ресурсы и плагины:
$bootstrap->bootstrap();
]]></programlisting>
        </sect3>

        <sect3 id="zend.application.theory-of-operation.bootstrap.registry">
            <title>Реестр ресурсов</title>

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

            <para>
                <classname>Zend_Application_Bootstrap_BootstrapAbstract</classname>
                предоставляет локальный реестр для этих объектов.
                Для того, чтобы сохранять свои объекты в нем, просто возвращайте
                их из своего ресурса.
            </para>

            <para>
                Для большей гибкости этот реестр внутри себя ссылается на
                "контейнеры";
                единственное требование состоит в том, чтобы это был объект.
                Ресурсы регистрируются как свойства, имена которых совпадают
                с именами ресурсов.
                По умолчанию используется экземпляр
                <classname>Zend_Registry</classname>, но вы можете при
                желании указывать любой другой объект.
                Для работы с контейнерами могут использоваться методы
                <methodname>setContainer()</methodname> и
                <methodname>getContainer()</methodname>.
                Метод <methodname>getResource($resource)</methodname>
                может использоваться для извлечения ресурса из контейнера,
                а <methodname>hasResource($resource)</methodname> - для
                проверки того, был ли зарегистрирован данный ресурс.
            </para>

            <para>
                Для примера рассмотрим базовый ресурс вида:
            </para>

            <programlisting language="php"><![CDATA[
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    protected function _initView()
    {
        $view = new Zend_View();
        // дальнейшая инициализация...

        return $view;
    }
}
]]></programlisting>

            <para>
                Вы можете затем проверять его наличие и/или извлекать его
                как показано ниже:
            </para>

            <programlisting language="php"><![CDATA[
// Использование пары has/getResource()
if ($bootstrap->hasResource('view')) {
    $view = $bootstrap->getResource('view');
}

// Через контейнер:
$container = $bootstrap->getContainer();
if (isset($container->view)) {
    $view = $container->view;
}
]]></programlisting>

            <para>
                Следует заметить, что реестр и контейнер не являются
                глобальными. Это означает, что вам нужно иметь доступ к объекту
                загрузки с тем, чтобы можно было извлекать ресурсы.
                <classname>Zend_Application_Bootstrap_Bootstrap</classname>
                предоставляет некоторые удобства для этого:
                во время выполнения <methodname>run()</methodname> он
                регистрирует себя в качестве параметра "bootstrap"
                во фронт-контроллере, это позволяет извлекать его внутри
                маршрутизатора, диспетчера, плагинов и контроллеров действий.
            </para>

            <para>
                Например, если вы хотите внутри своего контроллера действий
                получить доступ к ресурсу вида из примеров выше, то
                можете сделать следующее:
            </para>

            <programlisting language="php"><![CDATA[
class FooController extends Zend_Controller_Action
{
    public function init()
    {
        $bootstrap = $this->getInvokeArg('bootstrap');
        $view = $bootstrap->getResource('view');
        // ...
    }
}
]]></programlisting>
        </sect3>

        <sect3 id="zend.application.theory-of-operation.bootstrap.dependency-tracking">
            <title>Отслеживание зависимостей</title>

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

            <para>
                В то же время некоторые ресурсы могут зависеть от других и
                требовать их предварительной загрузки до начала своего
                выполнения.
                Для решения этих двух проблем
                <classname>Zend_Application_Bootstrap_BootstrapAbstract</classname>
                предоставляет простой и эффективный механизм для отслеживания
                зависимостей.
            </para>

            <para>
                Как было сказано ранее, все ресурсы - как методы, так и
                плагины, - загружаются путем вызова
                <methodname>bootstrap($resource)</methodname>, где
                <varname>$resource</varname> является именем ресурса или
                массивом ресурсов. Если параметр <varname>$resource</varname>
                опущен, то это означает, что все ресурсы должны быть запущены.
            </para>

            <para>
                Если ресурс зависит от других ресурсов, то он должен вызывать
                метод <methodname>bootstrap()</methodname> в своем коде
                для обеспечения выполнения этих ресурсов.
                Последующие вызовы для этих ресурсов будут проигнорированы.
            </para>

            <para>
                В методе ресурса такой вызов будет выглядеть следующим образом:
            </para>

            <programlisting language="php"><![CDATA[
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    protected function _initRequest()
    {
        // Обеспечение инициализации контроллера
        $this->bootstrap('FrontController');

        // Извлечение фронт-контроллера из реестра загрузки
        $front = $this->getResource('FrontController');

        $request = new Zend_Controller_Request_Http();
        $request->setBaseUrl('/foo');
        $front->setRequest($request);

        // Обеспечение сохранения запроса в реестре загрузки
        return $request;
    }
}
]]></programlisting>
        </sect3>
    </sect2>

    <sect2 id="zend.application.theory-of-operation.resources">
        <title>Плагины ресурсов</title>

        <para>
            <link linkend="zend.application.theory-of-operation.bootstrap.resource-plugins">Как
                было сказано ранее</link>,
            хорошим способом создания повторно используемых ресурсов загрузки
            и выделения кода в отдельные классы является
            использование плагинов ресурсов.
            Хотя Zend Framework поставляется с набором стандартных плагинов
            ресурсов, замысел состоит в том, что разработчики должны
            писать собственные плагины с целью инкапсуляции собственного
            кода, предназначенного для инициализации.
        </para>

        <para>
            Ресурсы должны только реализовывать интерфейс
            <classname>Zend_Application_Resource_Resource</classname> или, что
            является более простым вариантом, расширять абстрактный класс
            <classname>Zend_Application_Resource_ResourceAbstract</classname>.
            Базовый интерфейс довольно прост:
        </para>

        <programlisting language="php"><![CDATA[
interface Zend_Application_Resource_Resource
{
    public function __construct($options = null);
    public function setBootstrap(
        Zend_Application_Bootstrap_Bootstrapper $bootstrap
    );
    public function getBootstrap();
    public function setOptions(array $options);
    public function getOptions();
    public function init();
}
]]></programlisting>

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

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

        <programlisting language="php"><![CDATA[
class My_Resource_View extends Zend_Application_Resource_ResourceAbstract
{
    protected $_view;

    public function init()
    {
        // Возвращает вид, таким образом, он будет сохранен в реестре
        return $this->getView();
    }

    public function getView()
    {
        if (null === $this->_view) {
            $options = $this->getOptions();
            $title   = '';
            if (array_key_exists('title', $options)) {
                $title = $options['title'];
                unset($options['title']);
            }

            $view = new Zend_View($options);
            $view->doctype('XHTML1_STRICT');
            $view->headTitle($title);
            $view->headLink()->appendStylesheet('/css/site.css');
            $view->headScript()->appendfile('/js/analytics.js');

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

            $this->_view = $view;
        }
        return $this->_view;
    }
}
]]></programlisting>

        <para>
            Зарегистрировав путь к этому плагину ресурса, вы можете
            использовать его в своем приложении. Сверх того, благодаря
            использованию загрузчика плагинов вы эффективно
            переопределите идущий в поставке плагин ресурса "View", тем самым
            обеспечивая использование своего плагина вместо него.
        </para>
    </sect2>
</sect1>
