Table des matières
Jusque récemment, créer
et gérer le bootstrapping d'une application basée sur Zend Framework était
une tâche à la charge du développeur et de son imagination. Cela
encourageait l'utilisation de plusieurs pratiques distinctes, allant de
l'utilisation de scripts procéduraux monolithiques à la mise en place de
structures complexes, en passant par l'écriture de classes, sans compter
que cela rendait difficile la conception d'un outil en ligne de commande.
Avec l'introduction de Zend_Application
, la mise en
place du boostrap a maintenant une base commune, utilisable pour toute
application par n'importe quel développeur, ce qui est un pas en avant
vers la standardisation de ce besoin commun à toutes les applications. Si
seulement ils avaient rendu cela évident en l'appelant
Zend_Bootstrap
...
Zend_Application
a été conçue de manière à être modulaire, personnalisable, et configurable
avec un minimum d'efforts. Pour que sachions un peu de quoi il retourne,
puisque nous utiliserons (beaucoup)
Zend_Application
au cours de ce livre, nous allons
revoir notre exemple Hello World précédent, et ré-architecturer la classe
ZFExt_Bootstrap
. Une chose que je ne changerai pas
est l'emplacement de notre bootstrap, qui restera
/library/ZFExt/Bootstrap.php
. Il n'y a pas de raison
particulière pour laquelle nous devrions le conserver ici, si ce n'est que
l'espace de noms ZFExt formera la base de notre bibliothèque générique
d'applications, où nous conserverons toutes les classes spécifiques au
Zend Framework, pour éventuellement pouvoir les réutiliser à l'avenir dans
d'autres applications.
Les modifications que nous devons apporter à notre classe de bootstrap commencent par quelque chose de simple :
class ZFExt_Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
}
Nous aurons besoin
d'effectuer quelques modifications pour en arriver à ce que nous faisions
avec la classe ZFExt_Bootstrap
de départ, mais si
vous n'aviez aucune modification à apporter, votre création d'une classe
de boostrap pourrait s'arrêter là. Sans que nous n'ayons à ajouter la
moindre portion de code supplémentaire,
Zend_Application
peut initialiser tout ce qui est
nécessaire, en utilisant les valeurs utiles par défaut de chaque
composant.
Ici, nous étendons
Zend_Application_Bootstrap_Bootstrap
, qui hérite
elle-même de
Zend_Application_Bootstrap_BootstrapAbstract
, et
fourni tout ce qui est nécessaire pour un bootstrap typique, comme le
chargement de Ressources, le dispatching via le Front Controller, et
quelques vérifications pour être sûr que nous avons précisé un mode
d'opération (production ou développement, par exemple).
Comme précédemment,
notre fichier index.php
dans le répertoire
/public
est l'endroit où nous commençons par inclure
et exécuter le bootstrapping. Nous devons ajouter un brin de complexité
ici. Cela signifie notamment définir quelques constantes pour qu'elles
contiennent des chemins de l'application, le chargement d'une
configuration pour diriger le bootstrapping de notre environnement, et
enfin, lancer le boostrapping lui-même.
<?php
if (!defined('APPLICATION_PATH')) {
define('APPLICATION_PATH',
realpath(dirname(__FILE__) . '/../application'));
}
if (!defined('APPLICATION_ROOT')) {
define('APPLICATION_ROOT', realpath(dirname(__FILE__) . '/..'));
}
if (!defined('APPLICATION_ENV')) {
if (getenv('APPLICATION_ENV')) {
$env = getenv('APPLICATION_ENV');
} else {
$env = 'production';
}
define('APPLICATION_ENV', $env);
}
set_include_path(
APPLICATION_ROOT . '/library' . PATH_SEPARATOR
. APPLICATION_ROOT . '/vendor' . PATH_SEPARATOR
. get_include_path()
);
require_once 'Zend/Application.php';
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_ROOT . '/config/application.ini'
);
$application->bootstrap()->run();
Ici, nous voyons que
index.php
est toujours l'emplacement pour la
configuration de points spécifiques à l'environnement. C'est dans
index.php
que nous créons un certain de nombre de
constantes, que nous configurons notre include_path, and et que nous
lançons le processus de bootstrapping. En interne,
Zend_Application
charge notre classe
ZFExt_Bootstrap
, que nous configurerons via un
fichier de configuration d'application, placé dans
/config/application.ini
pour qu'il soit pris en
compte. Même si notre classe est vide, elle étend toujours une classe
abstraite contenant, elle, toutes les méthodes nécessaires.
Un point à noter est que
ces constantes doivent être utilisées judicieusement. Les constantes sont
des valeurs globales accessibles depuis n'importe quel point de
l'application, et comme toute autre variable globale, leur utilisation
doit être minimisée et effectuée avec soin, parce que la tentation de les
utiliser partout est toujours présente. D'autres applications pourraient
bien ne même pas créer ces constantes, donc tout code qui en dépend peut
ne pas être réutilisable. Une bien meilleure solution, en dehors de
index.php
, est de continuer à utiliser
dirname()
et realpath()
et/ou de
passer ces variables via un objet Registre, ou comme paramètres du Front
Controller, accessibles depuis les actions de vos Contrôleurs.
En m'écartant quelque
peu du Guide de Référence, je maintiens le fichier
index.php
en me conformant aux normes de codage de
Zend Framework. Il est préférable de ne pas utiliser de raccourcis ou
d'écritures abbrégées, puisqu'ils ne sont pas vraiment faciles à
lire.
Vous noterez que notre
fichier index.php
fait maintenant référence, par
défaut, à une configuration de "production" en l'absence d'une variable
d'environnement assigée à la constante APPLICATION_ENV
. De
toute évidence, nous ne voulons pas développer en utilisant une
configuration de production, sauf si nous aimons débugger une application
qui, mystérieusement, n'affiche pas ses erreurs, mais nous pouvons
résoudre ce problème en ajoutant la définition de la variable
d'environnement correspondante à notre fichier
.htaccess
:
SetEnv APPLICATION_ENV development RewriteEngine On RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^.*$ - [NC,L] RewriteRule ^.*$ /index.php [NC,L]
Ceci fonctionne pour Apache. Pour d'autres serveurs web, il vous faudra consulter leur documentation.
Un des principaux
avantages de l'utilisation de Zend_Application
est
que, une fois que vous avez supprimé le besoin de code personnalisé, vous
pouvez concentrer vos efforts sur la mise en place des valeurs par défaut
des composants, et gérer tout le reste via de la configuration. Comme le
contenu de notre index.php
semblait le suggérer plus
haut, nous devrions ajouter un fichier de configuration dans
/config/application.ini
, qui contiendra :
[production] phpSettings.display_startup_errors = 0 phpSettings.display_errors = 0 bootstrap.path = APPLICATION_ROOT "/library/ZFExt/Bootstrap.php" bootstrap.class = "ZFExt_Bootstrap" resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers" [staging : production] [testing : production] phpSettings.display_startup_errors = 1 phpSettings.display_errors = 1 resources.frontController.throwExceptions = 1 [development : production] phpSettings.display_startup_errors = 1 phpSettings.display_errors = 1 resources.frontController.throwExceptions = 1
Ici, le fichier de
configuration est découpé en quatre modes distincts : production, staging,
testing, et development. Tous ces modes héritent toutes les valeurs mises
en place pour la production, mais peuvent définir des exceptions. Par
exemple, la configuration de production désactive display_errors, alors
que les modes de développement et de test assurent que cette directive
sera activée. Vous pouvez faire confiance à
Zend_Application
, qui chargera le mode approprié en
se basant sur la valeur que lui avons indiquée via la constante
APPLICATION_ENV
, que nous avons définie dans
index.php
.
Les valeurs d'INI
utilisées peuvent paraître un peu étranges, puisqu'elles montrent des
constantes PHP suivies d'un espace, puis d'un chemin relatif entre
guillemets doubles à partir du chemin absolu défini par ces constantes.
Zend_Application
se chargera en interne de la
concaténation de ces deux parties ; mais notez que vous pouvez configurer
des chemins de cette manière pour le composant. En fait, vous pouvez aussi
définir des listes à partir de fichiers de configuration .ini, comme nous
le verrons dans de futurs chapîtres.
Zend_Application
partage toute la configuration en quelques catégories, identifiables au
sein du fichier de configuration par leurs préfixes. "phpSettings" fait
référence à tout ce que vous pourriez définir en utilisant
ini_set()
en PHP. "includePaths" contient tous les
chemins que vous pourriez vouloir ajouter à l'include_path de PHP (en
dehors de ceux définis dans index.php
). Par exemple,
je pourrais utiliser :
includePaths.foolib = APPLICATION_ROOT "/foolib/lib"
A la place, je
configures plus directement l'include_path depuis
index.php
pour les répertoires standard
/library
et /vendor
, mais vous
pourriez en avoir d'autres qui auraient aussi besoin d'être ajoutés.
"bootstrap" indique à Zend_Application
où notre
classe de Bootstrap se trouve, et quel nom de classe est utilisé
(rappelez-vous que nous avons toujours besoin de notre classe
ZFExt_Bootstrap
pour modifier la façon dont le
bootsrapping configure certains composants).
Et enfin, nous avons
"resource", qui fait référence à des Ressources de
Zend_Application
.
Pour faire simple, une
Ressource Zend_Application
est une classe que
connait Zend_Application
, qui pourra la configurer
durant le boostrapping pour utilisation ultérieure. Au-dessus, nous avons
défini des options pour un FrontController (les noms d'options ont leur
première lettre en minuscule) pour configurer l'instance de
Zend_Controller_Front
que
Zend_Application
utilisera lorsque notre
application sera en train de fonctionner. Pour l'instant, nous indiquons
seulement à Zend_Controller_Front
où est-ce que
notre répertoire par défaut, contenant les classes de Contrôleurs, se
trouve, et qu'il doit lever des exceptions pour les modes de développement
et de test. Si la convention de configuration vous semble confuse,
"controllerDirectory" correspond à
Zend_Controller_Front::setControllerDirectory()
,
tout comme si nous utilisions une liste de valeurs de configuration, en
utilisant la même convention pour faire correspondre des clefs de tableaux
à méthodes setter auxquelles chaque clef correspond.
Dans notre classe
ZFExt_Bootstrap
de départ, j'avais fait remarquer
que certains composants sont configurés par défaut d'une manière qui n'est
pas forcément adaptée à nos besoins. Par exemple,
Zend_View
utilise l'encodage ISO-8859-1 par défaut,
ce qui est correct, pour peu que vous évitiez tout caractère accentué ou
multi-octets. Vous pouvez configurer l'instance par défaut de
Zend_View
comme nous l'avions fait au départ, à peu
de choses près de la même manière, maintenant que nous utilisons
Zend_Application
:
<?php
class ZFExt_Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initView()
{
$view = new Zend_View();
$view->setEncoding('UTF-8');
$view->doctype('XHTML1_STRICT');
$view->headMeta()->appendHttpEquiv(
'Content-Type', 'text/html;charset=utf-8'
);
$viewRenderer =
Zend_Controller_Action_HelperBroker::getStaticHelper(
'ViewRenderer'
);
$viewRenderer->setView($view);
return $view;
}
}
Nous voyons ici que
notre classe de Bootstrap retouchée peut configurer une instance de
Zend_View
en appelant
_initView()
, qui est quelque chose à quoi nous
faisons référence sous le terme de Méthode de Ressource, puisqu'elle
configure ou modifie une Ressource pour que nous puissions l'utiliser.
Ici, nous configurons une nouvelle instance de Zend_View
pour
qu'elle soit utilisée par l'aide d'action ViewRenderer (nous ferons
connaissance des aides d'action plus tard), où elle agira en tant
qu'instance de Zend_View
par défaut pour le
rendering des templates de l'application. Toutes les Méthodes de
Ressources qui doivent être exécutées doivent respecter le format
"_initResourceName()
" s'il s'agit de méthodes
protégées. Vous pouvez effectuer la même chose avec des méthodes publiques
en les définissant sous la forme
"bootstrapResourceName()
". Le choix d'utiliser
des méthodes publiques ou protégées vous revient.
Au sein de ces Méthodes
de Ressources, nous pouvons en faire un peu plus, en utilisant le système
de Ressources de Zend_Application
. Pour faire
simple, Zend_Application
définit un certain nombre
de classes appelées Plugins de Ressources, qu'il utilise pour initialiser
les Ressources, comme alternative aux plus simples Méthodes de
Ressources.
Donc, qu'est-ce qu'une
Ressource après tout cela ? Le terme de Ressource peut porter à confusion,
du fait qu'il ne raconte pas toute l'histoire à lui tout seul. La
meilleure façon d'y penser est en termes de configuration d'objets
uniques. Notre application n'a généralement besoin que d'une seule
instance de chaque objet, au cours de son bootstrapping. Elle a besoin
d'une instance de Zend_View
, d'une instance de
Zend_Controller_Front
, d'une instance d'un Router,
etc. Donc, une Resource est unique : une seule de chaque sorte. L'ensemble
d'actions requises pour créer, configurer, et enregistrer ces ressources
uniques avec Zend_Application
(et la classe de bootstrap)
vient sous deux formes alternatives distinctes : les Méthodes de
Ressources, et les Plugins de Ressources. Ces deux solutions suivent une
convention qui dit que la Ressource à laquelle elles s'appliquent est
utilisée dans le nom de la méthode pour une Méthode de Ressource, et dans
le nom de la classe pour un Plugin de Ressource. Donc,
_initView()
crée une Ressource nommée View. Mais, attendez :
il y a aussi un Plugin de Ressource
(Zend_Application_Resource_View
) qui peut aussi
créer une Ressource nommée View. Pouvons-nous avoir deux Ressources View ?
La réponse est non : nous ne pouvons en avoir qu'une seule. En définissant
une Méthode de Ressource, nous écrasons tout Plugin de Ressource
applicable à la même Ressource.
Un Plugin de Ressource
est une classe qui étend
Zend_Application_Resource_ResourceAbstract
(ou
Zend_Application_Resource_Resource
) qui s'occupe de
l'initialisation, de la configuration, et du passage de ces classes en
tant qu'objets au Front Controller. Une Méthode de Ressource est comme un
Plugin de Ressource, mais elle est définie dans le bootstrap, plutôt que
d'être placée dans sa propre classe distincte. Pour créer de nouveaux
objets à utiliser pendant le boostrapping, vous pouvez donc ajouter une
classe personnalisée de Plugin de Ressource, ou une Méthode de Ressource
comme alternative plus courte.
Bien entendu,
Zend_Application
va automatiquement gérer, sans
interférence, quelques classes standard dont votre application pourrait
avoir besoin. Les Plugins de Ressources fournis avec
Zend_Application
comprennent :
Zend_Application_Resource_Db |
Zend_Application_Resource_FrontController |
Zend_Application_Resource_Router |
Zend_Application_Resource_Modules |
Zend_Application_Resource_Navigation |
Zend_Application_Resource_Session |
Zend_Application_Resource_View |
Zend_Application_Resource_Layout |
Zend_Application_Resource_Locale |
Zend_Application_Resource_Translate |
Dans notre méthode
_initView()
, nous avons créé un objet
Zend_View
de remplacement ou de substitution, parce
que Zend_Application_Resource_View
va juste créer
une instance par défaut avec l'encodage de caractères ISO-8859-1 et
quelques autres options par défaut. En retournant la nouvelle instance de
Zend_View
depuis
_initView()
,
Zend_Application
va prendre en compte le
remplacement, et ne tentera pas d'écraser nos modifications en lançant
Zend_Application_Resource_View
pour obtenir une
instance par défaut de Zend_View
, comportant les
problèmes que nous venons de corriger.
Lorsque nous créons une
nouvelle Ressource pour en remplacer une autre, nous n'avons pas à
récupérer celle initialement mise en place dans
Zend_Application
par le Plugin de Ressource par
défaut, à moins que cela ne soit nécessaire. Ajoutons maintenant une autre
méthode de ressource, _initFrontController()
, qui
modifie seulement quelques options d'une ressource existante, telle que
mise en place par
Zend_Application_Resource_Frontcontroller
.
<?php
class ZFExt_Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initView()
{
$view = new Zend_View();
$view->setEncoding('UTF-8');
$view->doctype('XHTML1_STRICT');
$view->headMeta()->appendHttpEquiv(
'Content-Type', 'text/html;charset=utf-8'
);
$viewRenderer =
Zend_Controller_Action_HelperBroker::getStaticHelper(
'ViewRenderer'
);
$viewRenderer->setView($view);
return $view;
}
protected function _initFrontController()
{
$this->bootstrap('FrontController');
$front = $this->getResource('FrontController');
$response = new Zend_Controller_Response_Http;
$response->setHeader('Content-Type',
'text/html; charset=UTF-8', true);
$front->setResponse($response);
}
}
Dans notre classe de
bootstrap de départ, nous voulions nous assurer que, par défaut, toutes
les réponses utiliseraient une en-tête Content-Type valant "text/html;
charset=UTF-8". Ici, nous faisons la même chose en utilisant la méthode
getResource()
pour obtenir une instance de
Zend_Controller_Front
créée et configurée par
Zend_Application_Resource_Frontcontroller
et
uniquement faire en sorte qu'elle utilise notre propre objet de Réponse
plutôt que d'en créer un par défaut lorsque nécessaire. Avant de récupérer
la Ressource FrontController, c'est-à-dire un objet de type
Zend_Controller_Front
, nous devons tout d'abord
nous assurer que Zend_Application
fasse tout ce qui
est nécessaire pour la créer.
Ceci est fait en passant
le nom de la Ressource (c'est-à-dire, le terme final dans le nom du Plugin
de Ressource correspondant) à la méthode
bootstrap()
. Cela force
Zend_Application
à bootstrapper uniquement cette
Ressource. Cette méthode bootstrap()
est
relativement souple, et accepte une liste de Noms de Ressources si vous
avez besoin que plusieurs d'entre elles soient disponibles immédiatement,
sans attendre que l'exécution globale du bootstrap n'ait été
effectuée.
Vous pouvez aller plus loin en remplaçant simplement les Plugins de Ressources par défaut. Je ne traiterai pas ce point ici, puisque nous verrons comment ajouter des Plugins de Ressources personnalisés pour d'autres objets utilisés par notre application plus loin dans ce livre.
Maintenant, comparons la
classe ZFExt_Bootstrap
de départ avec sa version
revue. Moins de code, gérée par configuration, facile à utiliser, et
extensible via des Plugins de Ressources. La nouvelle version est de toute
évidence une amélioration. Bien entendu, comprendre comment cela
fonctionne est encore une autre marche sur la route qui vous ménera au
statut de Guru Zend Framework, mais c'est un coût que l'on retrouve
toujours lorsque l'on utilise plus de classes abstraites, qui n'en sont
pas pour autant moins extensibles et réutilisables que leurs alternatives
monolithiques.
Si vous essayez
d'accéder à http://helloworld.tld
une nouvelle fois, vous
remarquerez quelque chose de bizarre. Une exception quelque peu anonyme
est maintenant levée, indiquant "Circular resource dependency detected".
Cela arrive parce que nous avons défini une Méthode de Ressource
_initFrontController()
, qui entre en conflit avec
Zend_Application_Resource_Frontcontroller
lorsque
nous appelons bootstrap('FrontController')
. En pratique, nous
violons la règle qui définit qu'une Ressource doit être unique : lorsque
nous essayons d'utiliser la Méthode de Ressource et forçons l'utilisation
du Plugin de Ressource (qui est lancée par bootstrap()
),
Zend_Application
interprête cela comme une
tentative de créer deux objets similaires, alors que nous n'avons besoin
que d'un. Jusqu'à ce que nous ayons l'habitude de penser au travail
effectué par le bootstrap comme des Ressources uniques, c'est une erreur
qui reviendra souvent.
Ce point est uniquement
pour mettre en évidence l'importance des Méthodes Ressources par rapport
aux Plugins de Ressources (ainsi que pour expliquer ce qui cause ce petit
problème un peu obscur lorsque l'on commence à travailler avec
Zend_Application
). Vous pouvez utiliser l'une ou
l'autre des deux solutions, mais jamais les deux avec le même Nom de
Ressource en même temps. Plus haut, notre méthode
_initView()
fonctionnait parce que nous
n'utilisons aucune référence à
Zend_Application_Resource_View
: notre Méthode de
Ressource la remplace. Si nous avions essayé de modifier l'objet généré
par Zend_Application_Resource_View
en utilisant les
méthodes bootstrap()
et
getResource()
pour générer et obtenir l'objet,
nous aurions eu à renommer la Méthode de Ressource, pour mettre en
évidence le fait que nous étions en train de construire par-dessus la
Ressource initialisée par le Plugin, en en créant une version modifiée (en
pratique, une Ressource différente, même si la différence n'est qu'un
point de configuration).
Ceci illustre un point
important : même si Zend_Application
parle de
Ressources, vous pouvez avoir un objet unique représenté par deux ou plus
Ressources nommées. Cela semble un peu idiot, puisque, finalement, seul
cet objet est utilisé, indépendamment du nombre de Noms de Ressources qui
y font référence. Cela dit, c'est uniquement un artifice : pour pouvoir
modifier des objets après leur mise en place initiale par les Plugins ou
Méthodes (non recommandé : l'ordre des méthodes est un peu traitre), nous
devons utiliser un Nom différent de Ressource. Il n'est pas nécessaire que
vous compreniez pourquoi ; c'est juste comme ça que ça fonctionne.
Dans
_initFrontController()
, nous avons fait l'erreur
de croire que nous pouvions modifier un Front Controller existant en le
chargeant depuis
Zend_Application_Resource_FrontController
, sans
réaliser que nous effectuions ceci dans une Méthode de Ressource en
utilisant le même nom de Ressource. A partir de l'instant où une unité de
Ressource essaye d'utiliser une autre unité de Ressource nommée à
l'identique, la confusion est de la partie, puisque nous ne pouvons pas
avoir deux objets qui représentent la même Ressource ; donc,
Zend_Application
lève une Exception en se plaignant
à propos du noeud de dépendances que nous essayons de créer. C'est un
filet de sécurité délibérément mis en place.
Pour résoudre ce conflit, nous devrions renommer toute Méthode de Ressource non destinée à remplacer un Plugin de Ressource, en utilisant des noms un peu plus uniques. Dans ce cas, j'ai décidé de préfixer le Nom de Ressource existant "FrontController" par "Modified", de manière à indiquer que notre Méthode de Ressource n'écrase pas un Plugin de Ressource, ce qui signifie que nous pouvons utiliser le résultat de ce Plugin de Ressource quand il est lancé plutôt que d'effectuer notre propre travail séparé de création. Nous ne retournons pas non plus l'objet, puisque le Plugin de Ressource l'enregistrera pour utilisation de son côté.
<?php
class ZFExt_Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initView()
{
$view = new Zend_View();
$view->setEncoding('UTF-8');
$view->doctype('XHTML1_STRICT');
$view->headMeta()->appendHttpEquiv(
'Content-Type', 'text/html;charset=utf-8'
);
$viewRenderer =
Zend_Controller_Action_HelperBroker::getStaticHelper(
'ViewRenderer'
);
$viewRenderer->setView($view);
return $view;
}
protected function _initModifiedFrontController()
{
$this->bootstrap('FrontController');
$front = $this->getResource('FrontController');
$response = new Zend_Controller_Response_Http;
$response->setHeader('Content-Type',
'text/html; charset=UTF-8', true);
$front->setResponse($response);
}
}
Maintenant, nous pouvons
essayer http://helloworld.tld
une fois de
plus, sans rencontrer erreur ni exception.
Dans le fichier de
configuration de notre application, application.ini
,
nous avons noté que nous pouvions définir la configuration des Ressources
en utilisant le préfixe "resources". Ces entrées de configuration sont
automatiquement utilisées par les Plugins de Ressources de
Zend_Application
pour passer les options aux objets
qu'ils créent. Donc, pour notre Ressource FrontController, nous pouvons
ajouter des entrées de ce type :
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
Ici, le nom de la
Ressource est camel-cased, avec la première lettre en minuscule ;
autrement dit, FrontController devient frontController. Ce n'est pas
quelque chose dont nous avons besoin de nous soucier immédiatement, mais
nous pouvons nettoyer un petit peu notre Méthode de Ressource remplaçant
Zend_View
en faisant en sorte que toutes les
options de configuration ajoutées à application.ini
soient passées à la nouvelle instance. Même si nous n'y gagnons rien
d'autre, cela nous permet de définir l'encodage de caractères dans le
fichier de configuration.
<?php
class ZFExt_Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
public static function autoload($class) {
include str_replace('_', '/', $class) . '.php';
return $class;
}
protected function _initView()
{
$options = $this->getOptions();
if (isset($options['resources']['view'])) {
$view = new Zend_View($options['resources']['view']);
} else {
$view = new Zend_View;
}
if (isset($options['resources']['view']['doctype'])) {
$view->doctype($options['resources']['view']['doctype']);
}
if (isset($options['resources']['view']['contentType'])) {
$view->headMeta()->appendHttpEquiv('Content-Type',
$options['resources']['view']['contentType']);
}
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(
'ViewRenderer'
);
$viewRenderer->setView($view);
return $view;
}
protected function _initModifiedFrontController()
{
$options = $this->getOptions();
if (!isset($options['resources']['modifiedFrontController']['contentType'])) {
return;
}
$this->bootstrap('FrontController');
$front = $this->getResource('FrontController');
$response = new Zend_Controller_Response_Http;
$response->setHeader('Content-Type',
$options['resources']['modifiedFrontController']['contentType'], true);
$front->setResponse($response);
}
}
Maintenant, nous pouvons
placer la configuration de l'encodage de caractères, ou toute autre option
de configuration personnalisée dont nous aurions besoin, dans le fichier
de configuration application.ini
.
[production] phpSettings.display_startup_errors = 0 phpSettings.display_errors = 0 bootstrap.path = APPLICATION_ROOT "/library/ZFExt/Bootstrap.php" bootstrap.class = "ZFExt_Bootstrap" resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers" resources.view.encoding = "UTF-8" resources.view.doctype = "XHTML1_STRICT" resources.view.contentType = "text/html;charset=utf-8" resources.modifiedFrontController.contentType = "text/html;charset=utf-8" [staging : production] [testing : production] phpSettings.display_startup_errors = 1 phpSettings.display_errors = 1 resources.frontController.throwExceptions = 1 [development : production] phpSettings.display_startup_errors = 1 phpSettings.display_errors = 1 resources.frontController.throwExceptions = 1
Dans notre classe de
bootstrap précédente, nous avions créé une méthode d'autoload
personnalisée, qui n'effectuait aucune vérification sur les fichiers, et
essayait juste d'exécuter un include()
en supposant
que toutes les classes correspondaient à leurs fichiers en utilisant la
convention PEAR. Ce choix avait été effectué pour éviter de faire appel à
la méthode Zend_Loader::loadClass()
de
l'autoloader plus typique utilisé dans les applications Zend Framework,
qui fait tout un tas de vérifications d'erreurs, généralement inutiles.
Zend_Application
est aussi conçu de manière à
implémenter un mécanisme d'autoload en utilisant un autre nouveau
composant, Zend_Loader_Autoloader
. Malheureusement,
Zend_Loader_Autoloader
(qui ajoute des
fonctionnalités nettement améliorées d'auto-chargement de classes) utilise
la même méthode Zend_Loader::loadClass()
, que
nous souhaitions éviter.
Nous pouvons toujours
implémenter notre méthode plus légère, mais plus depuis la classe de
bootstrap. A la place, il nous faut réinitialiser la méthode
d'auto-chargement par défaut de
Zend_Loader_Autoloader
avant que
Zend_Application
ne soit initialisée, et cela ne
peut être fait que depuis index.php
.
<?php
if (!defined('APPLICATION_PATH')) {
define('APPLICATION_PATH',
realpath(dirname(__FILE__) . '/../application'));
}
if (!defined('APPLICATION_ROOT')) {
define('APPLICATION_ROOT', realpath(dirname(__FILE__) . '/..'));
}
if (!defined('APPLICATION_ENV')) {
if (getenv('APPLICATION_ENV')) {
$env = getenv('APPLICATION_ENV');
} else {
$env = 'production';
}
define('APPLICATION_ENV', $env);
}
set_include_path(
APPLICATION_ROOT . '/library' . PATH_SEPARATOR
. APPLICATION_ROOT . '/vendor' . PATH_SEPARATOR
. get_include_path()
);
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->setDefaultAutoloader(create_function('$class',
"include str_replace('_', '/', \$class) . '.php';"
));
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_ROOT . '/config/application.ini'
);
$application->bootstrap()->run();
Puisque nous ne pouvons
même pas faire référence au bootstrap avant que
Zend_Application
n'ait été initialisée, nous
pouvons plutôt passer une fonction anonyme, créée à la volée, à
setDefaultAutoloader()
.
En PHP, où les bibliothèques respectent la convention PEAR, tous les noms de classes comportent un préfixe commun, auquel nous faisons référence sous le terme de "namespace" (ce qui n'est pas un espace de nom réel de la manière dont PHP 5.3 les défini). Notre autoloader est configuré de manière à reconnaitre dans les noms de classes les espaces de noms incluant "Zend_" et "ZendX_". Pour pouvoir charger d'autres classes namespacées, nous devons en premier lieu indiquer à l'autoloader qu'elles existent. Cela dit, nous pouvons commencer par nous demander pourquoi est-ce que cela est nécessaire.
Le problème avec la méthode d'auto-chargement de départ était simple : elle sait charger à peu près n'importe quoi. Au premier coup d'oeil, ça a l'air fantastique, c'est simple, facile à comprendre, et ne nécessite aucune configuration. Donc, où est le problème ? Le problème avec l'idée de charger n'importe quoi est qu'elle n'offre aucun contrôle. Parfois, vous voudriez vous assurer que seules certaines bibliothèques soient chargées. En plus de cela, la restriction mise en place permet aussi des recherches plus rapides.
La nouvelle fonctionnalité d'espaces de noms n'est pas parfaite,
mais c'est principalement dû au fait que certaines bibliothèques n'ont pas
un préfixe de plus haut niveau. Par exemple, les composants PEAR ne
commencent pas par PEAR_
, ce qui signifie que vous pouvez
avoir HTTP_Client
, Crypt_RSA
, et ainsi de suite,
qui ne partagent pas un préfixe commun. Un problème similaire existe avec
n'importe quelle classe "racine" ; par exemple; HTMLPurifier utilise le
préfixe d'espace de nom HTMLPurifer_
, sauf pour une classe
racine dans HTMLPurifier.php
. Dans ces circonstances,
vous pourrez avoir besoin de revenir au comportement de départ, où les
espaces de noms ne sont pas suivis ni restreint, en utilisant :
Zend_Loader_Autoloader::getInstance()->setFallbackAutoloader(true);
En d'autres circonstances, une bibliothèque peut utiliser son propre
autoloader, que vous pouvez enregistrer en tant qu'alternative à celui
implémenté par défaut par
Zend_Loader_Autoloader
.
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->pushAutoloader('HTMLPurifier_Bootstrap', 'autoload');
Notez que cela, puisque HTMLPurifier n'utilise pas d'espace de nom, ajoute un nouvel autoloader global, un peu comme l'option de fallback fait fonctionner l'autoloader par défaut à peu de chose près sans aucune restriction. Vous pouvez passer un autoloader restreint à un espace de noms de manière similaire, pour qu'il ne soit appelé que lorsqu'une classe avec un espace de nom spécifique est détectée.
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->pushAutoloader('XLib_Autoloader', 'autoload', 'XLib_');
Cela dit, assumons pour l'instant que cette solution par défaut et
les autres autoloaders ne soient pas nécessaire. Le contre-coup du nouvel
autoloader est qu'il nous faut enregistrer tous les espaces de noms que
nous avons l'intention d'utiliser. Bien que nous puissions effectuer ceci
sous forme de code dans index.php
, il est plus simple
d'en garder une trace dans notre fichier
application.ini
, ce qui rend plus facile l'ajout
d'espaces de noms lorsqu'ils sont nécessaires. Voici un exemple où nous
avons l'intention d'utiliser une bibliothèque (en fait, la bibliothèque de
notre future application) dont toutes les classes sont préfixées par
"ZFExt_". Comme vous pouvez probablement le voir, vous pouvez continuer à
ajouter des espaces de noms de cette manière, puisque les crochets
[]
indiquent qu'ils seront ajoutés à une liste au moment où
le fichier .ini sera analysé par Zend_Config
(qui
gère toutes les analyses et chargements de fichiers de configuration au
sein du Framework) :
[production] phpSettings.display_startup_errors = 0 phpSettings.display_errors = 0 bootstrap.path = APPLICATION_ROOT "/library/ZFExt/Bootstrap.php" bootstrap.class = "ZFExt_Bootstrap" resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers" resources.view.encoding = "UTF-8" resources.view.doctype = "XHTML1_STRICT" resources.view.contentType = "text/html;charset=utf-8" resources.modifiedFrontController.contentType = "text/html;charset=utf-8" autoloaderNamespaces[] = "ZFExt_" [staging : production] [testing : production] phpSettings.display_startup_errors = 1 phpSettings.display_errors = 1 resources.frontController.throwExceptions = 1 [development : production] phpSettings.display_startup_errors = 1 phpSettings.display_errors = 1 resources.frontController.throwExceptions = 1
C'état une rapide
introduction (enfin, vous pouvez toujours apprendre à lire rapidement) à
Zend_Application
. En tant que personne qui a
développée sa propre solution de son côté, je suis extrêmement content de
pouvoir l'adopter à la place. Cela me permet d'être sûr que d'autres
développeurs pourront suivre mon processsus de bootstrapping dans avoir à
apprendre une nouvelle approche (Parmi les centaines qui existent
probablement déjà). C'est la force d'enfin avoir une approche standard au
bootstrapping d'applications.
Vous verrez plus de
points en rapport avec Zend_Application
au fur et à
mesure que le livre avancera, puisqu'il y a plein d'objets et d'autres
portions de comportements que nous pourrons avoir à modifier dans nos
applications, et qu'utiliser le bootstrap pour effectuer ces
initialisations est la meilleure approche possible. Pour finir, notez que
Zend_Tool
, qui n'est pas encore couvert par ce
livre, utilise Zend_Application
pour gérer les
applications.