7.4. Готовые подклассы

7.4.1. Введение

Zend Framework предоставляет некоторые альтернативы принятым по умолчанию классам, в которые входят объекты запросов, маршрутизаторы и объекты ответов.

7.4.2. Zend_Controller_Request_Http

7.4.2.1. Введение

Zend_Controller_Request_Http предоставляет объект запроса для использования в среде HTTP. Zend_Controller_Request_Http является классом запроса, используемым Zend_Controller_Dispatcher по умолчанию.

7.4.2.2. Доступ к данным запроса

Zend_Controller_Request_Http инкапсулирует доступ к релевантным значениям, таким, как ключевое имя и значение для переменных маршрутизатора и все дополнительные параметры, полученные из URI. Посредством передачи к Zend_Controller_Request_Http он также предоставяет доступ к значениям, содержащимся в суперглобальных переменных как к открытым членам и управляет текущим базовым URL и URI запроса. Суперглобальные значения не могут быть установлены в объекте запроса, за исключением методов setParam/getParam для установки или извлечения пользовательских переменных.

[Замечание] Суперглобальные данные

Когда обращаетесь к суперглобальным переменным через Zend_Controller_Request_Http как к открытым свойствам объекта, необходимо помнить, что имя свойства (ключ суперглобального массива) проверяется на наличие в суперглобальных массивах в определенном порядке: 1. GET, 2. POST, 3. COOKIE, 4. SERVER, 5. ENV.

К определенным суперглобальным массивам можно также обращаться через открытые методы. Например, можно получить необработанное значение $_POST['user'] посредством вызова метода getPost('user') в объекте запроса.

7.4.2.3. Базовый URL и поддиректории

Zend_Controller_Request_Http позволяет использовать Zend_Controller_Router_Rewrite в поддиректориях. Zend_Controller_Request_Http будет пытаться автоматически определить ваш базовый URL и установить его.

Например, если вы храните файл загрузки index.php в поддиректории веб-сервера, которая называется /projects/myapp/index.php, то базовый URL (базовый адрес перезаписи) должен быть установлен в /projects/myapp. Эта стока будет исключаться из начала пути перед любыми вычислениями соответствий маршрутов. Это освобождает от необходимости ее указывания в начале каждого маршрута. Маршрут 'user/:username' будет соответствовать URI вида http://localhost/projects/myapp/user/martel и http://example.com/user/martel.

[Замечание] Определение URL чувствительно к регистру

Автоматическое определение базового URL является чувствительно к регистру, поэтому убедитесь, что ваш URL соответствует имени поддиректории в файловой системе (даже на платформе Windows). Если не соответствует, то будет вызываться действие noRoute.

Если базовый URL определяется некорректно, вы можете заменить его своим базовым путем с помощью метода setBaseUrl(), который есть в классах Zend_Http_Request, Zend_Controller_Request_Http и Zend_Controller_Front. Легче всего установить его через Zend_Controller_Front, который передаст его объекту запроса. Пример установки своего базового URL:

$router     = new Zend_Controller_Router_Rewrite();
$controller = Zend_Controller_Front::getInstance();
$controller->setControllerDirectory('./application/controllers')
           ->setRouter($router)
           ->setBaseUrl('/projects/myapp'); // set the base url!
$response   = $controller->dispatch();

7.4.3. Zend_Controller_Router_Rewrite

7.4.3.1. Введение

Zend_Controller_Router_Rewrite является стандартным маршрутизатором фреймворка. Маршрутизация является процессом принятия конечной точки URI (той части URI, которая идет после базового URL) и ее разложения на параметры для определения того, какой контроллер и какое действие этого контроллера должны получить запрос. Значения контроллера, действия и необязательных параметров упаковывается в объект Zend_Controller_Request_Http, который затем обрабатывается Zend_Controller_Dispatcher_Standard. Маршрутизация производится только один раз – когда вначале получен запрос и до того, как первый контроллер будет запущен.

Zend_Controller_Router_Rewrite предназначен для того, чтобы обеспечить функциональность, подобную mod_rewrite, с применением чистого php. Он отчасти основан на маршрутизации в Ruby on Rails и не требует каких-либо предварительных знаний о перезаписи URL веб-сервером. Он спроектирован для работы с единственным правилом mod_rewrite, пример которого приведен ниже:

RewriteEngine on
RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php

или:

RewriteEngine on
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 

Rewrite Router может также использоваться с веб-сервером IIS, если Isapi_Rewrite был установлен как расширение Isapi со следующими правилами перезаписи:

RewriteRule ^[\w/\%]*(?:\.(?!(?:js|ico|gif|jpg|png|css)$)[\w\%]*$)? /index.php [I]
[Замечание] IIS Isapi_Rewrite

Если используется IIS, то $_SERVER['REQUEST_URI'] не будет существовать, либо будет установлен как пустая строка. В этом случае Zend_Controller_Request_Http попытается использовать $_SERVER['HTTP_X_REWRITE_URL'], значение которого устанавливается расширением Isapi_Rewrite.

Если используется Lighttpd, то валидным будет следующее правило перезаписи:

url.rewrite-once = ( ".*\.(js|ico|gif|jpg|png|css)$" => "$0", "" => "/index.php")

7.4.3.2. Использование маршрутизатора

Чтобы использовать Rewrite Router, нужно инстанцировать его, добавить маршруты и установить его в контроллере. Следующий код демонстрирует эту процедуру:

/* Создание маршрутизатора */

// возвращает используемый по умолчанию маршрутизатор
$router = $ctrl->getRouter();

$router->addRoute(
    'user',
    new Zend_Controller_Router_Route('user/:username', array('controller' => 'user', 'action' => 'info'))
);

7.4.3.3. Основные маршруты

Сущностью RewriteRouter является определение пользовательских маршрутов. Маршруты создаются вызовом метода addRoute() и передачей ему нового экземпляра Zend_Controller_Router_Route:

$router->addRoute('user', new Zend_Controller_Router_Route('user/:username'));

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

Первым параметром для конструктора Zend_Controller_Router_Route является маршрут, который будет сопоставляться с URL – например, маршрут в примере выше будет соответствовать http://example.com/user/martel. Двоеточие в маршруте обозначает переменную URL. После успешной маршрутизации значения всех определенных переменных будут добавлены в Zend_Controller_Request. К ним можно будет получить доступ через методы Zend_Controller_Request::getParam или Zend_Controller_Action::_getParam. В нашем примере параметру под именем username будет присвоено значение 'martel'.

[Замечание] Порядок сопоставления

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

[Замечание] Использование символов

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

Есть две специальные переменные, которые можно использовать в маршрутах — ':controller' и ':action'. Эти специальные переменные могут использоваться для получения контроллера и/или действия, выбранных в URL. Переменная ':action' всегда должна быть определена в маршруте или как параметр по умолчанию. Переменная 'controller' по умолчанию будет IndexController, если не была определена.

[Замечание] Специальные переменные

Имена специальных переменных могут отличаться, если вы измените значения по умолчанию в Zend_Controller_Request_Http через методы setControllerKey и setActionKey.

$router->addRoute(
    'user', new Zend_Controller_Router_Route(':controller/:action')
);

Если вы наберете в вашем броузере адрес 'http://example.com/news/latest', то при сопоставлении с таким маршрутом Zend_Controller_Dispatcher вызовет метод latestAction в вашем классе контроллера NewsController.

7.4.3.4. Значения по умолчанию

Любая переменная в маршруте может иметь значение по умолчанию. Для его определения вы должны добавить второй параметр для конструктора Zend_Controller_Router_Route. Этот параметр является массивом с ключами, соответствующими именам переменных, и значениями, соответствующими желаемым значениям этих переменных по умолчанию.

$router->addRoute(
    'archive', new Zend_Controller_Router_Route('archive/:year', array('year' => 2006))
);

Этот маршрут будет соответствовать таким URL, как 'http://example.com/archive/2005' и 'http://example.com/archive'. В последнем случае переменная ':year' будет иметь значение 2006.

В примере выше результатом будет только добавление переменной ':year' в запрос. Маршрутизация не будет выполняться корректно до тех пор, пока не будут установлены параметры контроллера и действия. Для того, чтобы сделать этот код более полезным, вы должны указать конкретные контроллер и действие как значения по умолчанию.

$router->addRoute(
    'archive',
    new Zend_Controller_Router_Route('archive/:year', array('year' => 2006, 'controller' => 'archive', 'action' => 'show')
);

Результатом будет вызов действия showAction в классе контроллера ArchiveController.

7.4.3.5. Требования к переменным

Можно добавить третий параметр в вызове конструктора Zend_Controller_Router_Route, в котором устанавливаются требования к переменным. Они указываются в виде регулярных выражений.

$router->addRoute(
    'archive',
    new Zend_Controller_Router_Route('archive/:year', array('year' => 2006), array('year' => '\d+'))
);
[Замечание] Особенности маршрутизации

В отличие от Ruby on Rails, ZF RewriteRouter будет использовать значение по умолчанию, если нет соответствия требованиям, определенным в третьем параметре. Таким образом, в случае URL http://example.com/archive/test год будет равен 2006. Это поведение может быть изменено в будущем, так как все еще было предметом обсуждения во время написания этой документации.

7.4.3.6. Базовый URL и поддиректории

Rewrite Router может использоваться в поддиректориях, базовый URL автоматически определяется в Zend_Controller_Request_Http.

Если базовый URL определяется некорректно, то вы можете переопределить его через метод setBaseUrl() объекта Zend_Controller_Request_Http (см. Раздел 7.4.2.3, «Базовый URL и поддиректории»).

7.4.3.7. Маршруты по умолчанию

Zend_Controller_Router_Rewrite изначально сконфигурирован с одним маршрутом по умолчанию. Он будет соответствовать URI вида 'controller/action'. Кроме этого, имя модуля может быть определено как первый элемент пути, что позволяет использовать URI вида 'module/controller/action. Он будет также соответствовать любым дополнительным параметрам, по умолчанию добавляемым в конец URI.

Некоторые примеры того, как сопоставляются маршруты:

// Допустим следующее:
// $ctrl->setControllerDirectory(array(
//     'default' => '/path/to/default/controllers',
//     'news'    => '/path/to/blog/controllers',
//     'blog'    => '/path/to/blog/controllers'
// ));

Только модуль:
http://example/news
    module == news

Если модуль неверный, то считается, что это имя контроллера:
http://example/foo
    controller == foo

Модуль + контроллер:
http://example/blog/archive
    module     == blog
    controller == archive

Модуль + контроллер + действие:
http://example/blog/archive/list
    module     == blog
    controller == archive
    action     == list

Модуль + контроллер + действие + параметры:
http://example/blog/archive/list/sort/alpha/date/desc
    module     == blog
    controller == archive
    action     == list
    sort       == alpha
    date       == desc

Маршрутом, используемым по умолчанию, является объект Zend_Controller_Router_Route_Module, инстанцированный без значений по умолчанию.

// Маршрут для совместимости с первой версией маршрутизатора
$compat = new Zend_Controller_Router_Route_Module();
$this->addRoute('default', $compat);
[Замечание] Сопоставление URI

Zend_Controller_Router_Rewrite сконфигурирован c учетом обратной совместимости. Он будет автоматически использовать маршрут controller/action с дополнительными параметрами. Дополнительные параметры не требуют добавления новых маршрутов, если они не должны иметь значений по умолчанию или требований к переменным. Эти дополнительные параметры будут доступны через метод Zend_Controller_Action::_getParam.

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

// Удаление маршрута по умолчанию
$router->removeDefaultRoutes();

7.4.3.8. Статические маршруты

В примерах выше все импользуемые маршруты были динамическими – маршруты, которые содержат паттерны для сопоставления с URI. Тем не менее, определенные маршруты могут быть неизменными, и применение к ним методов регулярных выражений будет совершенно излишним. Решением данной ситуации будет использование статических маршрутов:

$loginRoute = new Zend_Controller_Router_Route_Static('login', array('controller' => 'login', 'action' => 'form'));
$router->addRoute('login', $loginRoute);

7.4.3.9. Regex Routes

In addition to the default and static route types, a Regular Expression route type is available. This route offers more power and flexibility than the others, but at a slight cost of performance, due to its use of the PCRE engine.

Zend_Controller_Router_Route_Regex provides a regex-based route type for use with the rewrite router. Some usage rules to consider:

  • It uses the # character for a delimiter. This means that you will not need to escape forward slashes ('/'), but will need to escape hash characters ('#').

  • Line start and line end anchors ('^' and '$', respectively) are automatically pre- and appended to all expressions. Thus, you should not use these in your regular expressions. Also, it means that you should write your expressions to match the entire URL. As an example, given the URL 'http://example.com/foo/bar/baz/bat', if you simply wish to match 'foo/bar', you should write your expression as 'foo/bar(/.*)?'.

  • The leading and trailing slash are trimmed prior to a match. As a result, matching the URL "http://example.com/foo/bar/" would involve a regex of "foo/bar".

One strength of using regular expressions is the ability to capture matching segments for use later. Zend_Controller_Router_Regex_Route makes use of this capability in two ways, either:

  • Setting parameters in the request object as match index (integer) => match. As an example:

    <?php
    // Anything after /foo/bar in the path would be retrieved via the key '1' in the
    // request object:
    $route = new Zend_Controller_Router_Route_Regex(
        'foo/bar(/.*)?', 
        array('controller' => 'foo', 'action' => 'bar')
    );
    
    // After routing:
    $fooPath = $request->getParam('1');
    
  • Using a map list from the third parameter passed to the route constructor to map indices to parameter keys. As an example:

    <?php
    // Place anything after /foo/bar in the path into the 'fooPath' request key:
    $route = new Zend_Controller_Router_Route_Regex(
        'foo/bar(/.*)?', 
        array('controller' => 'foo', 'action' => 'bar'),
        array(1 => 'fooPath')
    );
    

As some examples of creating regex routes to use with your router:

// View archived blogs with urls like:
// http://example.com/blog/archive/01-Using_the_Regex_Router.html
// placing '01' as the 'id' key, and 'Using_the_Regex_Router' as the 'title' key
$blogArchive = new Zend_Controller_Router_Route_Regex(
    'blog/archive/(\d+)-(.*)\.html',
    array('controller' => 'blog', 'action' => 'view'), 
    array(1 => 'id', 2 => 'title')
);
$router->addRoute('blogArchive', $blogArchive);

// View news items with urls like:
// http://example.com/news/1193328
// placing '1193328' as the 'id' key
$newsItem = new Zend_Controlelr_Router_Route_Regex(
    'news/(\d+)',
    array('controller' => 'news', 'action' => 'view'),
    array(1 => 'id')
);
$router->addRoute('newsItem', $newsItem);

7.4.3.10. Использование Zend_Config вместе с RewriteRouter

Иногда было бы более удобным обновлять конфигурационные данные для новых маршрутов, чем изменять код. Это возможно через метод addConfig(). По существу, вы создаете конфигурацию, совместимую с Zend_Config, считываете ее в своем коде и передаете RewriteRouter.

В качестве примера рассмотрим следующий INI-файл:

[production]
routes.archive.route = "archive/:year/*"
routes.archive.defaults.controller = archive
routes.archive.defaults.action = show
routes.archive.defaults.year = 2000
routes.archive.reqs.year = "\d+"

routes.news.type = "Zend_Controller_Router_Route_Static"
routes.news.route = "news"
routes.news.defaults.controller = "news"
routes.news.defaults.action = "list"

Этот INI-файл может быть затем прочитан в объект Zend_Config как показано ниже:

$config = new Zend_Config_Ini('/path/to/config.ini', 'production');
$router = new Zend_Controller_Router_Rewrite();
$router->addConfig($config, 'routes');

В примере выше мы говорим маршрутизатору, чтобы он использовал раздел 'routes' в файле INI для определения своих маршрутов. Ключ первого уровня в этом разделе используется для имени маршрута, в примере выше определяются маршруты 'archive' и 'news'. Каждый маршрут требует, как минимум, запись 'route', одну или более записей 'defaults', необязательно может быть одна или более записей 'reqs' (сокращение от 'required'). Все это соответствет трем аргументам, предоставляемым объекту Zend_Controller_Router_Route_Interface. Опция 'type' может использоваться для определения класса, используемого для данного маршрута, по умолчанию используется класс Zend_Controller_Router_Route. В примере выше для маршрута 'news' должен использоваться класс Zend_Controller_Router_Route_Static.

7.4.4. Zend_Controller_Response_Http

Zend_Controller_Response_Http является объектом ответа, пригодным к использованию в среде HTTP. Он содержит методы для установки, получения и удаления заголовков, а также метод __toString(), который отправляет все заголовки одновременно до того, как будет возвращено содержимое ответа.

setHeader() принимает два аргумента – тип заголовка и значение заголовка. Если передан третий необязательный параметр и он имеет значение true, то производится принудительная замена значения заголовка с тем же типом, если он уже был зарегистрирован.

7.4.5. Zend_Controller_Response_Cli

Zend_Controller_Response_Cli является объектом ответа, подходящим для использования в среде CLI. Он не имеет методов для управления заголовками и просто возвращает все содержимое ответа при вызове метода __toString().