7.4. Dostępne podklasy

7.4.1. Wprowadzenie

Zend Framework zapewnia kilka alternatyw dla domyślnych klas, włączając w to alternatywne obiekty żądania, routery oraz obiekty odpowiedzi.

7.4.2. Zend_Controller_Request_Http

7.4.2.1. Wprowadzenie

Zend_Controller_Request_Http zapewnia obiekt żądania do użycia w środowisku HTTP. Klasa Zend_Controller_Request_Http jest domyślną klasą żądania używaną przez Zend_Controller_Dispatcher.

7.4.2.2. Dostęp do danych żądania

Zend_Controller_Request_Http obudowuje dostęp do odpowiednich wartości takich jak nazwa klucza i wartość dla zmiennych kontrolera i akcji routera, oraz do dodatkowych parametrów pobranych z adresu URI. Rozszerzając Zend_Controller_Request_Http dodatkowo pozwala na uzyskanie dostępu do wartości zawartych w superglobalnych tablicach jako do publicznych właściwości obiektu i zarządza obecnym bazowym adresem URL oraz adresem URL żądania. Superglobalne wartości nie mogą być ustawione w obiekcie żądania, zamiast tego użyj metod setParam/getParam aby ustawić lub odebrać parametry użytkownika.

[Notatka] Dane superglobalne

Kiedy uzyskujemy dostęp do danych superglobalnych za pomocą klasy Zend_Controller_Request_Http jak do jej publicznych właściwości, ważne jest aby pamiętać, że nazwa właściwości (klucz tablicy superglobalnej) jest dopasowana do superglobalnych w określonej kolejności: 1. GET, 2. POST, 3. COOKIE, 4. SERVER, 5. ENV.

Konkretne zmienne superglobalne mogą być alternatywnie dostępne za pomocą publicznej metody. Na przykład, wartość $_POST['user'] może być dostępna przez wywołanie metody getPost('user') na obiekcie żądania.

7.4.2.3. Bazowy Url oraz podkatalogi

Klasa Zend_Controller_Request_Http pozwala na użycie klasy Zend_Controller_Router_Rewrite w podkatalogach. Zend_Controller_Request_Http spróbuje automatycznie wykryć twój bazowy adres URL i ustawi go odpowiednio.

Na przykład jeśli twój plik index.php jest w podkatalogu nazwanym /projects/myapp/index.php, bazowy URL (bazowy adres przepisania) powinien być ustawiony na /projects/myapp. Ten łańcuch znaków zostanie obcięty z początu ścieżki zanim będą dopasowane jakiekolwiek trasy. To zwalnia z konieczności dołączania tego adresu do każdej z tras. Trasa 'user/:username' dopasuje adresy URI takie jak http://localhost/projects/myapp/user/martel oraz http://example.com/user/martel.

[Notatka] Detekcja URL jest wrażliwa na małe i duże litery

Automatyczna detekcja adresów URL jest wrażliwa na małe i duże litery, więc upewnij się, że adres URL zostanie dobrze dopasowany do nazwy podkatalogu w systemie plików (nawet w systemie Windows). Jeśli nie zostanie, zostanie wywołana akcja noRoute.

Jeśli bazowy adres URL jest wykrywany nieprawidłowo, możesz go nadpisać w obiekcie Zend_Http_Request wywołując metodę setBaseUrl() lub tą samą metodę klasy Zend_Controller_Request_Http lub klasy Zend_Controller_Front. Najłatwiejszy sposób to ustawienie tego w klasie Zend_Controller_Front, która przekaże to do obiektu żądania. Przykładowe użycie ustawiania własnego bazowego adresu URL:

/** 
 * Uruchom żądanie z własnym bazowym URL za pomocą Zend_Controller_Front.
 */
$router     = new Zend_Controller_Router_Rewrite();
$controller = Zend_Controller_Front::getInstance();
$controller->setControllerDirectory('./application/controllers')
           ->setRouter($router)
           ->setBaseUrl('/projects/myapp'); // ustaw bazowy URL!
$response   = $controller->dispatch();

7.4.3. Zend_Controller_Router_Rewrite

7.4.3.1. Wprowadzenie

Zend_Controller_Router_Rewrite jest standardowym routerem we frameworku. Routing jest procesem pobrania adresu URI i rozłożenia go w celu ustalenia jaki kontroler i jaka akcja powinny otrzymać żądanie. Ta informacja o kontrolerze, akcji i opcjonalnych parametrach jest pakowana do obiektu Zend_Controller_Request_Http, który jest potem przetwarzany przez Zend_Controller_Dispatcher_Standard. Routing ma miejsce tylko raz: wtedy gdy żądanie jest po raz pierwszy otrzymane, przed wywołaniem pierwszego kontrolera.

Zend_Controller_Router_Rewrite jest zaprojektowany w celu uzyskania w czystym PHP takiej funkcjonalności jak w mod_rewrite. Jest to luźno wzorowane na routingu Ruby on Rails i nie wymaga żadnej wiedzy o przepisywaniu adresów przez serwer www. Jest to zaprojektowane w taki sposób, aby działało po dodaniu tylko jednej reguły mod_rewrite (jednej z poniższych):

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

lub:

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

Rewrite router może być także użyty z serwerem IIS, jeśli moduł Isapi_Rewrite jest zainstalowany jako rozszerzenie Isapi, z taką regułą przepisywania:

RewriteRule ^[\w/\%]*(?:\.(?!(?:js|ico|gif|jpg|png|css)$)[\w\%]*$)? /index.php [I]
[Notatka] IIS Isapi_Rewrite

Jeśli używasz serwera IIS, to wartość $_SERVER['REQUEST_URI'] nie istnieje lub jest pustym łańcuchem znaków. W takim wypadku, Zend_Controller_Request_Http spróbuje użyć wartości $_SERVER['HTTP_X_REWRITE_URL'] ustawionej przez rozszerzenie Isapi_Rewrite.

Jeśli używasz serwera Lighttpd, poniższa reguła jest prawidłowa:

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

7.4.3.2. Użycie routera

Aby prawidłowo użyć rewrite routera musisz utworzyć jego instancję, zdefiniować jakieś trasy i przekazać ten obiekt do kontrolera. Poniższy kod pokazuje tą procedurę:

/* Utwórz router */

$router = $ctrl->getRouter(); // domyślnie zwraca rewrite router
$router->addRoute(
	'user',
	new Zend_Controller_Router_Route('user/:username', array('controller' => 'user', 'action' => 'info'))
);

7.4.3.3. Podstawowe trasy

Sercem RewriteRoutera jest definicja tras określonych przez użytkownika. Trasy są tworzone przez wywołanie metody addRoute obiektu RewriteRouter i przekazanie do niej nowej instancji obiektu Zend_Controller_Router_Route:

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

Pierwszy parametr jest nazwą trasy. Obecnie nie jest konieczne jego definiowanie, jednak będzie on używany w przyszłości w klasie pomocniczej widoku ułatwiającej łatwe generowanie adresów URL. Jeśli chcesz użyć wcześniej skonfigurowanej trasy, możesz ją odebrać za pomocą metody getRoute RewriteRoutera. Drugi parametr jest instancją Zend_Controller_Router_Route.

Pierwszy parametr konstruktora obiektu Zend_Controller_Router_Route jest trasą, która ma być dopasowana do adresu URL - na przykład powyższa trasa zostanie dopasowana do adresu http://example.com/user/martel. Dwukropek w trasie oznacza zmienną adresu URL. Po udanym dopasowaniu trasy, wartości wszystkich zdefiniowanych zmiennych zostaną przekazane do obiektu Zend_Controller_Request. Po tym będą one dostępne za pomocą metod Zend_Controller_Request::getParam oraz Zend_Controller_Action::_getParam. W naszym przykładzie parametr nazwany username będzie miał ustawioną wartość 'martel'.

[Notatka] Odwrotne dopasowywanie

Trasy są dopasowywane w odwrotnej kolejności więc musisz pamiętać żeby podstawowe trasy były zdefiniowane na początku.

[Notatka] Użycie znaków

Teraz obecna implementacja pozwala na użycie w nazwie zmiennej dowolnych znaków z wyjątkiem ukośnika (/), ale jest mocno zalecane używanie jedynie znaków, które są bezproblemowo obsługiwane ptzrz PHP. W przyszłości implementacja prawdopodobnie zostanie zmodyfikowana co mogłoby wprowadzić do twojego kodu błędy.

Są dwie specjalne zmienne które nie mogą być użyte w twoich trasach - ':controller' oraz ':action'. Te specjalne zmienne będą użyte aby znaleść kotroler oraz akcję w danym adresie URL. Zmienna ':action' zawsze musi być zdefiniowana w trasie lub jako domyślny parametr. Zmienna ':controller' będzie domyślnie ustawiona na IndexController jeśli nie będzie zdefiniowana.

[Notatka] Specjalne zmienne

Nazwy tych specjalnych zmiennych mogą być inne, jeśli zdecydujesz zmienić je w obiekcie Zend_Controller_Request_Http za pomocą metod setControllerKey oraz setActionKey.

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

Jeśli skierujesz przeglądarkę na adres 'http://example.com/news/latest' ze zdefiniowaną powyższą trasą Zend_Controller_Dispatcher odwoła się do akcji latestAction z kontrolera NewsController.

7.4.3.4. Domyślne wartości zmiennych

Każda zmienna w trasie może mieć wartość domyślną. Aby to zrobić, musisz przekazać drugi parametr do konstruktora Zend_Controller_Router_Route. Ten parametr jest tablicą z nazwami zmiennych jako kluczami i z wartościami, które mają być uznane za domyślne.

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

Może nie jest wyraźnie widoczne to, że powyższa trasa dopasuje adresy URL takie jak 'http://example.com/archive/2005' oraz 'http://example.com/archive'. Ostatecznie zmienna year i tak będzie miała wartość 2006.

Powyższy przykład spowoduje jedynie przekazanie zmiennej oznaczającej rok do żądania. Nie będzie miał miejsca routing, ponieważ parametry oznaczające kontroler i akcję nie są ustawione. Aby było to bardziej użyteczne, musisz zapewnić prawidłową nazwę kontrolera i akcji jako domyślne wartości.

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

Ta trasa spowoduje uruchomienie akcji showAction kontrolera ArchiveController.

7.4.3.5. Wymagania zmiennych

Można dodać trzeci parametr do konstruktora Zend_Controller_Router_Route w którym będą ustawione wymagania zmiennych. Są one zdefiniowane jako wyrażenia regularne:

$router->addRoute(
    'archive',
    new Zend_Controller_Router_Route('archive/:year', array('year' => 2006), array('year' => '\d+'))
);
[Notatka] Zachowanie routera

W przeciwieństwie do Ruby on Rails, RewriteRouter z ZF dopasuje trasę i użyje domyślnej gdy trzeci parametr zawierający wymagania zmiennych nie zostanie znaleziony. Więc adres URL 'http://example.com/archive/test' zostanie dopasowany do powyższej trasy, a rok zostanie ustawiony na 2006. Ta funkcjonalność może w przyszłości się zmienić, ponieważ w momencie pisania tej dokumentacji ta kwestia jest jeszcze uzgadniana.

7.4.3.6. Bazowy adres URL i podkatalogi

Rewrite router może być użyty w podkatalogach, a bazowy adres URL zostanie automatycznie wykryty przez obiekt Zend_Controller_Request_Http.

Jeśli bazowy adres URL jest wykrywany nieprawidłowo, możesz go nadpisać w obiekcie Zend_Controller_Request_Http wywołując metodę setBaseUrl() (zobacz Sekcja 7.4.2.3, „Bazowy Url oraz podkatalogi”).

7.4.3.7. Domyślne trasy

Zend_Controller_Router_Rewrite jest skonfigurowany z jedną domyślną trasą. Dopasuje on adresy w postaci 'controller/action' . Dodatkowo, nazwa modułu może być określoa jako pierwszy element ścieżki, pozwalając na użycie adresów w postaci 'module/controller/action. Ostatecznie domyślnie dopasuje dodatkowe parametry dołączone do adresu URI.

Kilka przykładów doopasowania tras:

// Zakładając poniższe:
// $ctrl->setControllerDirectory(array(
//     'default' => '/path/to/default/controllers',
//     'news'    => '/path/to/blog/controllers',
//     'blog'    => '/path/to/blog/controllers'
// ));

Tylko moduł:
http://example/news
    module == news

Nieprawidłowy moduł mapuje do nazwy kontrolera:
http://example/foo
    controller == foo

Moduł oraz kontroler:
http://example/blog/archive
    module     == blog
    controller == archive

Moduł, kontroler oraz akcja:
http://example/blog/archive/list
    module     == blog
    controller == archive
    action     == list

Moduł, kontroler, akcja oraz parametry:
http://example/blog/archive/list/sort/alpha/date/desc
    module     == blog
    controller == archive
    action     == list
    sort       == alpha
    date       == desc

Domyślną trasą jest obiekt Zend_Controller_Router_Route_Module utworzony bez żadnych domyślnych parametrów:

// Trasa kompatybilna z pierwszą wersja routera
$compat = new Zend_Controller_Router_Route_Module();
$this->addRoute('default', $compat);
[Notatka] Dopasowanie adresów URI

Zend_Controller_Router_Rewrite jest skonfigurowany z kompatybilnością wsteczną. Automatycznie dopasuje kontroler i akcję z adresu wraz z dodatkowymi parametrami. Dodatkowe parametry nie wymagają dodawania nowych tras, o ile nie chcemy zdefiniować dla nich domyślnych wartości lub wymagań. Te dodatkowe parametry będą dostępne za pomocą metody Zend_Controller_Action::_getParam().

Jeśli nie potrzebujesz domyślnych tras w swoim schemacie routingu, możesz je usunąć używając metody removeDefaultRoutes():

// Usuwa domyślną trasę
$router->removeDefaultRoutes();

7.4.3.8. Trasy statyczne

Wszystkie powyższe przykłady używają dynamicznych tras -- tras, które są dopasowywane do szablonów. Czasem jakaś trasa jest niezmienna, a ciągłe sprawdzanie wyrażenia regularnego może być zabójcze dla serwera. Rozwiązaniem takiej sytuacji jest użycie statycznych tras:

$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. Użycie Zend_Config z RewriteRouterem

Czasem wygodniej jest uaktualnić plik konfiguracyjny z nowymi trasami niż zmieniać kod. Jest to możliwe za pomocą metody addConfig(). Zasadniczo tworzysz konfigurację kompatybilną z Zend_Config, a w kodzie odczytujesz ją i przekazujesz ją do RewriteRoutera.

Przeanalizuj poniższy przykładowy plik 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"

Powyższy plik INI może być odczytany przez obiekt Zend_Config w taki sposób:

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

W powyższym przykładzie, nakazujemy routerowi użyć sekcji 'routes' pliku INI aby użyć tras zdefiniowanych w tym pliku. Każdy element pierwszego poziomu w tej sekcji będzie użyty do zdefiiniowania nazwy trasy; powyższy przekład definiuje trasy 'archive' oraz 'news'. Wymagane jest aby każda trasa miała określony przynajmniej parametr 'route' i jeden lub więcej parametrów 'defaults'; opcjonalnie mogą być zdeiniowane parametry 'reqs' (skrót 'required'). Wszystkie te parametry odpowiadają trzem argumentom przekazywanym do obiektu Zend_Controller_Router_Route_Interface Klucz opcji 'type' może być użyty aby określić typ klasy, która ma być użyta dla danej trasy; domyślnie używana jest klasa Zend_Controller_Router_Route. W powyższym przykładzie, trasa 'news' jest zdefiniowana aby używała Zend_Controller_Router_Route_Static.

7.4.4. Zend_Controller_Response_Http

Zend_Controller_Response_Http jest obiektem odpowiedzi odpowiednim do użycia w środowisku HTTP. Zawiera metody do ustawiania, odbierania i czyszczenia nagłówków, a metoda __toString() wysyła wszystkie nagłówki na raz przed wysłaniem zawartości odpowiedzi.

Metoda setHeader() przyjmuje dwa argumenty, typ nagłówka oraz wartość nagłówka. Trzeci opcjonalny parametr, jeśli jest przekazany i ma wartość true, spowoduje, że nowy nagłówek zastąpi inne zarejestrowane nagłówki o tym typie.

7.4.5. Zend_Controller_Response_Cli

Zend_Controller_Response_Cli jest obiektem odpowiedzi odpowiednim do użycia w środowisku CLI. Nie ma on metod do obsługi nagłówków i w prosty sposób zwraca całą zawartość gdy wywoływana jest metoda __toString().