référence du
programmeur
iii
Guide de référence
du programmeur
iv
Guide de référence
du programmeur
v
Guide de référence
du programmeur
vi
Guide de référence
du programmeur
4.8. Zend_Cache_Backend_ZendServer_Disk et
Zend_Cache_Backend_ZendServer_ShMem ........................................ 241
5. Le gestionnaire de Cache ......................................................................... 241
Zend_Captcha ..................................................................................................... 245
1. Introduction .............................................................................................. 245
2. Opération Captcha ................................................................................... 245
3. Adaptateurs CAPTCHA ............................................................................ 246
3.1. Zend_Captcha_Word ..................................................................... 246
3.2. Zend_Captcha_Dumb .................................................................... 247
3.3. Zend_Captcha_Figlet ..................................................................... 247
3.4. Zend_Captcha_Image .................................................................... 247
3.5. Zend_Captcha_ReCaptcha ............................................................ 248
Zend_CodeGenerator ........................................................................................... 249
1. Introduction .............................................................................................. 249
1.1. Théorie ......................................................................................... 249
2. Exemples Zend_CodeGenerator ............................................................... 251
3. Zend_CodeGenerator Réference ............................................................... 255
3.1. Classes abstraites et interfaces ...................................................... 255
3.2. Classes CodeGenerator concrêtes ................................................. 256
Zend_Config ........................................................................................................ 262
1. Introduction .............................................................................................. 262
2. Aspect théorique ...................................................................................... 263
3. Zend_Config_Ini ....................................................................................... 264
4. Zend_Config_Xml ..................................................................................... 266
Zend_Config_Writer .............................................................................................. 271
1. Zend_Config_Writer .................................................................................. 271
Zend_Console_Getopt .......................................................................................... 274
1. Introduction .............................................................................................. 274
2. Déclarer les règles Getopt ........................................................................ 275
2.1. Déclarer des options avec la syntaxe courte .................................... 275
2.2. Déclarer des options avec la syntaxe longue ................................... 275
3. Extraire les options et les arguments ......................................................... 276
3.1. Manipuler les exceptions Getopt ..................................................... 276
3.2. Extraire les options par nom .......................................................... 277
3.3. Extraire les options ........................................................................ 277
3.4. Extraction des arguments sans option ............................................. 278
4. Configurer Zend_Console_Getopt ............................................................. 278
4.1. Ajouter des règles d'options ........................................................... 278
4.2. Ajouter des messages d'aide ......................................................... 279
4.3. Ajouter des alias aux options ......................................................... 279
4.4. Ajouter des listes d'arguments ........................................................ 279
4.5. Ajouter une configuration ............................................................... 280
Zend_Controller ................................................................................................... 282
1. Zend_Controller - Démarrage rapide ......................................................... 282
1.1. Introduction ................................................................................... 282
1.2. Démarrage rapide ......................................................................... 282
2. Fondations de Zend_Controller ................................................................. 286
3. Le contrôleur frontal (Front Controller) ....................................................... 289
3.1. Présentation générale .................................................................... 289
3.2. Méthodes principales ..................................................................... 290
3.3. Méthodes d'accès à l'environnement ............................................... 292
3.4. Paramètres du contrôleur frontal .................................................... 293
3.5. Étendre le contrôleur frontal ........................................................... 294
4. L'objet Requête ........................................................................................ 294
vii
Guide de référence
du programmeur
viii
Guide de référence
du programmeur
ix
Guide de référence
du programmeur
x
Guide de référence
du programmeur
xi
Guide de référence
du programmeur
xii
Guide de référence
du programmeur
xiii
Guide de référence
du programmeur
xiv
Guide de référence
du programmeur
xv
Guide de référence
du programmeur
xvi
Guide de référence
du programmeur
xvii
Guide de référence
du programmeur
xviii
Guide de référence
du programmeur
xix
Guide de référence
du programmeur
xx
Guide de référence
du programmeur
xxi
Guide de référence
du programmeur
xxii
Guide de référence
du programmeur
xxiii
Guide de référence
du programmeur
xxiv
Guide de référence
du programmeur
xxv
Guide de référence
du programmeur
3.3. Rechercher un produit Amazon spécifique avec son ASIN .............. 1217
3.4. Lancer des recherches de produits sur Amazon ............................. 1217
3.5. Utiliser l'API alternative de requêtes .............................................. 1218
3.6. Classes Zend_Service_Amazon ................................................... 1218
4. Zend_Service_Amazon_Ec2 ................................................................... 1224
4.1. Introduction ................................................................................. 1224
4.2. What is Amazon Ec2? ................................................................. 1224
4.3. Static Methods ............................................................................ 1224
5. Zend_Service_Amazon_Ec2: Instances ................................................... 1224
5.1. Instance Types ............................................................................ 1224
5.2. Running Amazon EC2 Instances .................................................. 1226
5.3. Amazon Instance Utilities ............................................................. 1227
6. Zend_Service_Amazon_Ec2: Windows Instances ..................................... 1229
6.1. Windows Instances Usage ........................................................... 1230
7. Zend_Service_Amazon_Ec2: Reserved Instances .................................... 1231
7.1. How Reserved Instances are Applied ............................................ 1231
7.2. Reserved Instances Usage ........................................................... 1231
8. Zend_Service_Amazon_Ec2: CloudWatch Monitoring ............................... 1232
8.1. CloudWatch Usage ...................................................................... 1232
9. Zend_Service_Amazon_Ec2: Amazon Machine Images (AMI) ................... 1234
9.1. AMI Information Utilities ............................................................... 1234
9.2. AMI Attribute Utilities ................................................................... 1235
10. Zend_Service_Amazon_Ec2: Elastic Block Stroage (EBS) ....................... 1236
10.1. Create EBS Volumes and Snapshots .......................................... 1237
10.2. Describing EBS Volumes and Snapshots .................................... 1237
10.3. Attach and Detaching Volumes from Instances ............................ 1238
10.4. Deleting EBS Volumes and Snapshots ........................................ 1239
11. Zend_Service_Amazon_Ec2: Elastic IP Addresses ................................. 1239
12. Zend_Service_Amazon_Ec2: Keypairs ................................................... 1240
13. Zend_Service_Amazon_Ec2: Regions and Availability Zones ................... 1241
13.1. Amazon EC2 Regions ................................................................ 1241
13.2. Amazon EC2 Availability Zones .................................................. 1242
14. Zend_Service_Amazon_Ec2: Security Groups ........................................ 1242
14.1. Security Group Maintenance ....................................................... 1242
14.2. Authorizing Access .................................................................... 1243
14.3. Revoking Access ....................................................................... 1244
15. Zend_Service_Amazon_S3 ................................................................... 1245
15.1. Introduction ............................................................................... 1245
15.2. Registering with Amazon S3 ....................................................... 1245
15.3. API Documentation .................................................................... 1245
15.4. Features .................................................................................... 1245
15.5. Getting Started .......................................................................... 1245
15.6. Bucket operations ...................................................................... 1246
15.7. Object operations ....................................................................... 1247
15.8. Data Streaming ......................................................................... 1249
15.9. Stream wrapper ......................................................................... 1249
16. Zend_Service_Amazon_Sqs .................................................................. 1250
16.1. Introduction ............................................................................... 1250
16.2. Registering with Amazon SQS .................................................... 1250
16.3. API Documentation .................................................................... 1250
16.4. Features .................................................................................... 1250
16.5. Getting Started .......................................................................... 1250
16.6. Queue operations ...................................................................... 1251
16.7. Message operations ................................................................... 1252
xxvi
Guide de référence
du programmeur
xxvii
Guide de référence
du programmeur
xxviii
Guide de référence
du programmeur
xxix
Guide de référence
du programmeur
xxx
Guide de référence
du programmeur
xxxi
Guide de référence
du programmeur
xxxii
Guide de référence
du programmeur
xxxiii
Guide de référence
du programmeur
xxxiv
Guide de référence
du programmeur
xxxv
Guide de référence
du programmeur
xxxvi
Partie I. Introduction
au Zend Framework
Table des matières
Présentation .................................................................................................................... 3
Installation ....................................................................................................................... 4
2
Présentation
Zend Framework (ZF) est un framework open-source destiné aux développements d'applications
web et de services web avec PHP5. Le Zend Framework est construit en utilisant 100% de
code orienté-objet. La structure des composants du Zend Framework est quelque peu unique ;
chaque composant est conçu avec de faibles dépendances envers les autres composants.
Cette architecture faiblement couplée permet aux développeurs d'utiliser les composants
individuellement. On appelle souvent ce type de conception "use-at-will".
Bien qu'ils puissent être utilisés individuellement, les composants de la librairie standard de
Zend Framework forment un framework d'application web puissant et extensible quand ils sont
combinés. Le ZF offre une robuste et performante implémentation du motif MVC, une abstraction
de base de données simple d'utilisation, et un composant de formulaire qui implémente un rendu
HTML, la validation et le filtrage des données, ainsi les développeurs peuvent consolider toutes
ces opérations en utilisant une interface orienté-objet facile d'utilisation. D'autres composants,
comme Zend_Auth ou Zend_Acl, fournissent l'authentification d'utilisateurs et l'autorisation
envers les solutions de stockage de crédits habituels. D'autres encore, implémentent des
librairies clientes pour simplifier l'accès aux services web disponibles les plus populaires.
Quelque soit le besoin de votre application, vous avez toutes les chances de trouver un
composant de Zend Framework qui peut être utilisé pour réduire drastiquement votre temps de
développement avec une base de tests solide.
Le sponsor principal du projet Zend Framework est Zend Technologies, mais un certain nombre
d'entreprises a contribué à des composants ou à des fonctionnalités significatives du framework.
Des entreprises comme Google, Microsoft et StrikeIron ont travaillé en partenariat avec Zend
pour fournir des interfaces vers des services web et d'autres technologies qu'ils souhaitaient
rendre disponible aux développeurs utilisant Zend Framework.
Zend Framework ne pourrait pas fournir et supporter toutes ces fonctionnalités sans l'aide de
la vibrante communauté du Zend Framework. Les membres de la communauté, incluant les
contributeurs, se rendent disponibles sur les listes de diffusion, canaux IRC, et autres forums.
Quelque soit la question que vous avez sur le Zend Framework, la communauté est toujours
disponible pour y répondre.
3
Installation
Veuillez vous reporter à l'annexe concernant la configuration système requise pour plus
d'informations.
Installer Zend Framework est extrêmement simple. Une fois que vous avez téléchargé et
décompressé le framework, vous devez ajouter le dossier "/library" de la distribution en début
de votre chemin d'inclusion ("include_path"). Vous pouvez bien entendu aussi déplacer la
librairie à tout autre position (partagée ou non) dans votre arborescence de fichiers.
• Téléchargement du dernier cliché nocturne : Pour ceux qui veulent être à l'avant-garde, les
clichés nocturnes représentent le dernier progrès de développement de Zend Framework.
Ces clichés sont empaquetés avec la documentation en anglais seulement ou dans toutes
les langues disponibles. Si vous prévoyez de travailler avec les derniers développements de
Zend Framework, considérez plutôt l'emploi d'un client subversion (SVN).
• Utilisation d'un client Subversion (SVN) : Zend Framework est un logiciel open-source,
et le référentiel Subversion utilisé pour son développement est disponible publiquement.
Considérer l'utilisation de SVN pour récupérer Zend Framework si vous utilisez déjà SVN pour
vos propres développements, si vous voulez contribuer à l'évolution du framework, ou si vous
désirez mettre à jour votre version du framework plus souvent que les sorties stables.
L'exportation est utile si vous souhaitez obtenir une révision particulière du framework sans
les dossiers .svn créé dans une copie de travail.
L'extraction d'une copie de travail est intéressante si vous contribuez à Zend Framework, et
une copie de travail peut être mise à jour à n'importe quel moment avec svn update et les
changements peuvent être livrés au référentiel SVN avec la commande svn commit.
Une définition externe est très pratique pour les développeurs utilisant déjà SVN pour gérer
les copies de travail de leurs applications.
Une fois votre copie de Zend Framework disponible, votre application nécessite d'avoir accès aux
classes du framework. Bien qu'il y ait plusieurs manières de réaliser ceci, votre include_path
de PHP doit contenir le chemin vers la bibliothèque de Zend Framework.
Zend fournit un tutoriel de démarrage rapide ("QuickStart") pour vous permettre de démarrer
rapidement. Ceci est une excellente manière pour commencer à apprendre le framework avec
une présentation de cas concrets que vous pourriez avoir à utiliser.
Puisque les composants de Zend Framework sont plutôt connectés de manière lâche, divers
composants peuvent être choisis pour un usage indépendant si nécessaire. Les chapitres
suivants documente l'utilisation de Zend Framework composant par composant.
4
Partie II. Apprendre Zend Framework
Table des matières
Démarrez rapidement avec Zend Framework .................................................................... 8
1. Zend Framework & MVC Introduction ................................................................... 8
1.1. Zend Framework ....................................................................................... 8
1.2. Model-View-Controller ............................................................................... 8
2. Create Your Project ........................................................................................... 10
2.1. Install Zend Framework ........................................................................... 10
2.2. Create Your Project ................................................................................. 10
2.3. The Bootstrap ......................................................................................... 11
2.4. Configuration ........................................................................................... 12
2.5. Action Controllers .................................................................................... 12
2.6. Views ..................................................................................................... 13
2.7. Checkpoint .............................................................................................. 15
3. Create A Layout ................................................................................................ 15
4. Create a Model and Database Table ................................................................... 18
5. Create A Form ................................................................................................... 28
6. Congratulations! ................................................................................................. 31
Chargement automatique avec Zend Framework ............................................................. 33
1. Introduction ........................................................................................................ 33
2. Architecture et buts ............................................................................................ 33
2.1. Convention de noms des classes ............................................................. 33
2.2. Conventions et architecture d'Autoload ..................................................... 33
3. Utilisation de base de l'autoloader ....................................................................... 34
4. Auto-chargement de resources ........................................................................... 36
5. Conclusion ......................................................................................................... 37
Les plugins dans Zend Framework ................................................................................. 38
1. Introduction ........................................................................................................ 38
2. Using Plugins .................................................................................................... 38
3. Conclusion ......................................................................................................... 40
Bien démarrer avec Zend_Layout ................................................................................... 42
1. Introduction ........................................................................................................ 42
2. Utiliser Zend_Layout .......................................................................................... 42
2.1. Layout Configuration ............................................................................... 42
2.2. Créer un script de layout ......................................................................... 43
2.3. Accéder à l'objet Layout .......................................................................... 43
2.4. Autres opérations .................................................................................... 44
3. Zend_Layout: Conclusions .................................................................................. 45
Bien démarrer avec Zend_View ...................................................................................... 46
1. Introduction ........................................................................................................ 46
2. Basic Placeholder Usage ................................................................................... 46
3. Standard Placeholders ....................................................................................... 49
3.1. Setting the DocType ................................................................................ 49
3.2. Specifying the Page Title ......................................................................... 50
3.3. Specifying Stylesheets with HeadLink ....................................................... 51
3.4. Aggregating Scripts Using HeadScript ...................................................... 52
4. View Placeholders: Conclusion ........................................................................... 54
Bien comprendre et utiliser les décorateurs Zend Form .................................................... 55
1. Introduction ........................................................................................................ 55
2. Les bases des décorateurs ................................................................................ 55
2.1. Aperçu du pattern décorateur ................................................................... 55
2.2. Créer votre premier décorateur ................................................................ 57
3. Chainer les décorateurs ..................................................................................... 58
6
Apprendre Zend Framework
7
Démarrez rapidement avec Zend
Framework
1. Zend Framework & MVC Introduction
1.1. Zend Framework
Zend Framework is an open source, object oriented web application framework for PHP 5.
Zend Framework is often called a 'component library', because it has many loosely coupled
components that you can use more or less independently. But Zend Framework also provides
an advanced Model-View-Controller (MVC) implementation that can be used to establish
a basic structure for your Zend Framework applications. A full list of Zend Framework
components along with short descriptions may be found in the components overview. This
QuickStart will introduce you to some of Zend Framework's most commonly used components,
including Zend_Controller, Zend_Layout, Zend_Config, Zend_Db, Zend_Db_Table,
Zend_Registry, along with a few view helpers.
Using these components, we will build a simple database-driven guest book application within
minutes. The complete source code for this application is available in the following archives:
• zip
• tar.gz
1.2. Model-View-Controller
So what exactly is this MVC pattern everyone keeps talking about, and why should you care?
MVC is much more than just a three-letter acronym (TLA) that you can whip out anytime you want
to sound smart; it has become something of a standard in the design of modern web applications.
And for good reason. Most web application code falls under one of the following three categories:
presentation, business logic, and data access. The MVC pattern models this separation of
concerns well. The end result is that your presentation code can be consolidated in one part of
your application with your business logic in another and your data access code in yet another.
Many developers have found this well-defined separation indispensable for keeping their code
organized, especially when more than one developer is working on the same application.
More Information
Let's break down the pattern and take a look at the individual pieces:
8
Démarrez rapidement
avec Zend Framework
• Model - This is the part of your application that defines its basic functionality
behind a set of abstractions. Data access routines and some business logic
can be defined in the model.
• View - Views define exactly what is presented to the user. Usually controllers
pass data to each view to render in some format. Views will often collect data
from the user, as well. This is where you're likely to find HTML markup in your
MVC applications.
9
Démarrez rapidement
avec Zend Framework
Of course there is more to be said about this critical pattern, but this should
give you enough background to understand the guestbook application we'll be
building.
After you have installed Zend Server, the Framework files may be found under /usr/local/
zend/share/ZendFramework on Mac OSX and Linux, and C:\Program Files\Zend
\ZendServer\share\ZendFramework on Windows. The include_path will already be
configured to include Zend Framework.
Alternately, you can Download the latest version of Zend Framework and extract the contents;
make a note of where you have done so.
Optionally, you can add the path to the library/ subdirectory of the archive to your php.ini's
include_path setting.
If you have problems setting up the zf command-line tool, please refer to the
manual.
Open a terminal (in Windows, Start -> Run, and then use cmd). Navigate to a directory where
you would like to start a project. Then, use the path to the appropriate script, and execute one
of the following:
# Unix:
% zf.sh create project quickstart
# DOS/Windows:
C:> zf.bat create project quickstart
Running this command will create your basic site structure, including your initial controllers and
views. The tree looks like the following:
10
Démarrez rapidement
avec Zend Framework
quickstart
|-- application
| |-- Bootstrap.php
| |-- configs
| | `-- application.ini
| |-- controllers
| | |-- ErrorController.php
| | `-- IndexController.php
| |-- models
| `-- views
| |-- helpers
| `-- scripts
| |-- error
| | `-- error.phtml
| `-- index
| `-- index.phtml
|-- library
|-- public
| `-- index.php
`-- tests
|-- application
| `-- bootstrap.php
|-- library
| `-- bootstrap.php
`-- phpunit.xml
At this point, if you haven't added Zend Framework to your include_path, we recommend
either copying or symlinking it into your library/ directory. In either case, you'll want to either
recursively copy or symlink the library/Zend/ directory of your Zend Framework installation
into the library/ directory of your project. On unix-like systems, that would look like one of
the following:
# Symlink:
% cd library; ln -s path/to/ZendFramework/library/Zend .
# Copy:
% cd library; cp -r path/to/ZendFramework/library/Zend .
Now that the project is created, the main artifacts to begin understanding are the bootstrap,
configuration, action controllers, and views.
// application/Bootstrap.php
11
Démarrez rapidement
avec Zend Framework
2.4. Configuration
While Zend Framework is itself configurationless, you often need to configure your application.
The default configuration is placed in application/configs/application.ini, and
contains some basic directives for setting your PHP environment (for instance, turning error
reporting on and off), indicating the path to your bootstrap class (as well as its class name), and
the path to your action controllers. It looks as follows:
; application/configs/application.ini
[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
includePaths.library = APPLICATION_PATH "/../library"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
[staging : production]
[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
Several things about this file should be noted. First, when using INI-style configuration, you
can reference constants directly and expand them; APPLICATION_PATH is actually a constant.
Additionally note that there are several sections defined: production, staging, testing, and
development. The latter three inherit settings from the "production" environment. This is a useful
way to organize configuration to ensure that appropriate settings are available in each stage of
application development.
An action controller should have one or more methods ending in "Action"; these methods
may then be requested via the web. By default, Zend Framework URLs follow the schema
/controller/action, where "controller" maps to the action controller name (minus the
"Controller" suffix) and "action" maps to an action method (minus the "Action" suffix).
Typically, you always need an IndexController, which is a fallback controller and which also
serves the home page of the site, and an ErrorController, which is used to indicate things
such as HTTP 404 errors (controller or action not found) and HTTP 500 errors (application errors).
// application/controllers/IndexController.php
12
Démarrez rapidement
avec Zend Framework
{
/* Initialize action controller here */
}
// application/controllers/ErrorController.php
switch ($errors->type) {
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
$this->view->exception = $errors->exception;
$this->view->request = $errors->request;
}
}
You'll note that (1) the IndexController contains no real code, and (2) the
ErrorController makes reference to a "view" property. That leads nicely into our next subject.
2.6. Views
Views in Zend Framework are written in plain old PHP. View scripts are placed in application/
views/scripts/, where they are further categorized using the controller names. In our case,
we have an IndexController and an ErrorController, and thus we have corresponding
index/ and error/ subdirectories within our view scripts directory. Within these subdirectories,
you will then find and create view scripts that correspond to each controller action exposed; in the
default case, we thus have the view scripts index/index.phtml and error/error.phtml.
View scripts may contain any markup you want, and use the <?php opening tag and ?> closing
tag to insert PHP directives.
The following is what we install by default for the index/index.phtml view script:
13
Démarrez rapidement
avec Zend Framework
a:link,
a:visited
{
color: #0398CA;
}
span#zf-name
{
color: #91BE3F;
}
div#welcome
{
color: #FFFFFF;
background-image: url(http://framework.zend.com/images/bkg_header.jpg);
width: 600px;
height: 400px;
border: 2px solid #444444;
overflow: hidden;
text-align: center;
}
div#more-information
{
background-image: url(http://framework.zend.com/images/bkg_body-bottom.gif);
height: 100%;
}
</style>
<div id="welcome">
<h1>Welcome to the <span id="zf-name">Zend Framework!</span><h1 />
<h3>This is your project's main page<h3 />
<div id="more-information">
<p>
<img src="http://framework.zend.com/images/PoweredBy_ZF_4LightBG.png" />
</p>
<p>
Helpful Links: <br />
<a href="http://framework.zend.com/">Zend Framework Website</a> |
<a href="http://framework.zend.com/manual/en/">Zend Framework
Manual</a>
</p>
</div>
</div>
The error/error.phtml view script is slightly more interesting as it uses some PHP
conditionals:
14
Démarrez rapidement
avec Zend Framework
</head>
<body>
<h1>An error occurred</h1>
<h2><?php echo $this->message ?></h2>
<h3>Stack trace:</h3>
<pre><?php echo $this->exception->getTraceAsString() ?>
</pre>
<h3>Request Parameters:</h3>
<pre><?php echo var_export($this->request->getParams(), 1) ?>
</pre>
<?php endif ?>
</body>
</html>
2.7. Checkpoint
At this point, you should be able to fire up your initial Zend Framework application. Create a virtual
host in your web server, and point its document root to your application's public/ subdirectory.
Make sure your host's name is in your DNS or hosts file, and then point your browser to it. You
should be able to see a welcome page at this point.
3. Create A Layout
You may have noticed that the view scripts in the previous sections were HTML fragments- not
complete pages. This is by design; we want our actions to return content only related to the action
itself, not the application as a whole.
Now we must compose that generated content into a full HTML page. We'd also like to have a
consistent look and feel for the application. We will use a global site layout to accomplish both
of these tasks.
There are two design patterns that Zend Framework uses to implement layouts: Two Step View
and Composite View. Two Step View is usually associated with the Transform View pattern; the
basic idea is that your application view creates a representation that is then injected into the
master view for final transformation. The Composite View pattern deals with a view made of one
or more atomic, application views.
In Zend Framework, Zend_Layout combines the ideas behind these patterns. Instead of each
action view script needing to include site-wide artifacts, they can simply focus on their own
responsibilities.
Occasionally, however, you may need application-specific information in your site-wide view
script. Fortunately, Zend Framework provides a variety of view placeholders to allow you to
provide such information from your action view scripts.
To get started using Zend_Layout, first we need to inform our bootstrap to use the Layout
resource. This can be done by adding the following line to your application/configs/
application.ini file, within the production section:
15
Démarrez rapidement
avec Zend Framework
; application/configs/application.ini
; application/configs/application.ini
[production]
; PHP settings we want to initialize
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
includePaths.library = APPLICATION_PATH "/../library"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
[staging : production]
[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
This directive tells your application to look for layout view scripts in application/layouts/
scripts. Create those directories now.
We also want to ensure we have an XHTML DocType declaration for our application. To enable
this, we need to add a resource to our bootstrap.
The simplest way to add a bootstrap resource is to simply create a protected method beginning
with the phrase _init. In this case, we want to initialize the doctype, so we'll create an
_initDoctype() method within our bootstrap class:
// application/Bootstrap.php
Within that method, we need to hint to the view to use the appropriate doctype. But where will
the view object come from? The easy solution is to initialize the View resource; once we have,
we can pull the view object from the bootstrap and use it.
To initialize the view resource, add the following line to your application/configs/
application.ini file, in the section marked production:
; application/configs/application.ini
16
Démarrez rapidement
avec Zend Framework
This tells us to initialize the view with no options (the '[]' indicates that the "view" key is an array,
and we pass nothing to it).
Now that we have a view, let's flesh out our _initDoctype() method. In it, we will first ensure
the View resource has run, fetch the view object, and then configure it:
// application/Bootstrap.php
Now that we've initialized Zend_Layout and set the Doctype, let's create our site-wide layout:
// application/layouts/scripts/layout.phtml
We grab our application content using the layout() view helper, and accessing the "content"
key. You may render to other response segments if you wish to, but in most cases, this is all
that's necessary.
Note also the use of the headLink() placeholder. This is an easy way to generate the HTML
for <link> elements, as well as to keep track of them throughout your application. If you need to
add additional CSS sheets to support a single action, you can do so, and be assured it will be
present in the final rendered page.
17
Démarrez rapidement
avec Zend Framework
Checkpoint
Now go to "http://localhost" and check out the source. You should see your
XHTML header, head, title, and body sections.
Now, let's consider what makes up a guestbook. Typically, they are simply a list of entries with
a comment, timestamp, and, often, email address. Assuming we store them in a database, we
may also want a unique identifier for each entry. We'll likely want to be able to save an entry,
fetch individual entries, and retrieve all entries. As such, a simple guestbook model API might
look something like this:
// application/models/Guestbook.php
class Application_Model_Guestbook
{
protected $_comment;
protected $_created;
protected $_email;
protected $_id;
18
Démarrez rapidement
avec Zend Framework
__get() and __set() will provide a convenience mechanism for us to access the individual
entry properties, and proxy to the other getters and setters. They also will help ensure that only
properties we whitelist will be available in the object.
find() and fetchAll() provide the ability to fetch a single entry or all entries.
Now from here, we can start thinking about setting up our database.
First we need to initialize our Db resource. As with the Layout and View resource,
we can provide configuration for the Db resource. In your application/configs/
application.ini file, add the following lines in the appropriate sections.
; application/configs/application.ini
[testing : production]
resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db"
[development : production]
resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
; application/configs/application.ini
[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
resources.view[] =
resources.db.adapter = "PDO_SQLITE"
resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db"
[staging : production]
[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db"
[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
Note that the database(s) will be stored in data/db/. Create those directories, and make them
world-writeable. On unix-like systems, you can do that as follows:
19
Démarrez rapidement
avec Zend Framework
On Windows, you will need to create the directories in Explorer and set the permissions to allow
anyone to write to the directory.
At this point we have a connection to a database; in our case, its a connection to a Sqlite database
located inside our application/data/ directory. So, let's design a simple table that will hold
our guestbook entries.
-- scripts/schema.sqlite.sql
--
-- You will need load your database schema with this SQL.
And, so that we can have some working data out of the box, lets create a few rows of information
to make our application interesting.
-- scripts/data.sqlite.sql
--
-- You can begin populating the database with the following SQL statements.
Now that we have both the schema and some data defined. Lets get a script together that we can
now execute to build this database. Naturally, this is not needed in production, but this script will
help developers build out the database requirements locally so they can have the fully working
application. Create the script as scripts/load.sqlite.php with the following contents:
// scripts/load.sqlite.php
/**
* Script for creating and loading database
*/
20
Démarrez rapidement
avec Zend Framework
// Initialize Zend_Application
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
// let the user know whats going on (we are actually creating a
// database here)
if ('testing' != APPLICATION_ENV) {
echo 'Writing Database Guestbook in (control-c to cancel): ' . PHP_EOL;
for ($x = 5; $x > 0; $x--) {
echo $x . "\r"; sleep(1);
}
}
// this block executes the actual statements that were loaded from
// the schema file.
try {
$schemaSql = file_get_contents(dirname(__FILE__) . '/schema.sqlite.sql');
// use the connection directly to load sql in batches
$dbAdapter->getConnection()->exec($schemaSql);
chmod($dbFile, 0666);
21
Démarrez rapidement
avec Zend Framework
if ('testing' != APPLICATION_ENV) {
echo PHP_EOL;
echo 'Database Created';
echo PHP_EOL;
}
if ($withData) {
$dataSql = file_get_contents(dirname(__FILE__) . '/data.sqlite.sql');
// use the connection directly to load sql in batches
$dbAdapter->getConnection()->exec($dataSql);
if ('testing' != APPLICATION_ENV) {
echo 'Data Loaded.';
echo PHP_EOL;
}
}
// generally speaking, this script will be run from the command line
return true;
Now, let's execute this script. From a terminal or the DOS command line, do the following:
Now we have a fully working database and table for our guestbook application. Our next few
steps are to build out our application code. This includes building a data source (in our case, we
will use Zend_Db_Table), and a data mapper to connect that data source to our domain model.
Finally we'll also create the controller that will interact with this model to both display existing
entries and process new entries.
We'll use a Table Data Gateway to connect to our data source; Zend_Db_Table provides
this functionality. To get started, lets create a Zend_Db_Table-based table class. First, create
the directory application/models/DbTable/. Then create and edit a file Guestbook.php
within it, and add the following contents:
// application/models/DbTable/Guestbook.php
/**
* This is the DbTable class for the guestbook table.
*/
class Application_Model_DbTable_Guestbook extends Zend_Db_Table_Abstract
{
/** Table name */
protected $_name = 'guestbook';
}
22
Démarrez rapidement
avec Zend Framework
Note the class prefix: Application_Model_DbTable. The class prefix for our module,
"Application", is the first segment, and then we have the component, "Model_DbTable"; the latter
is mapped to the models/DbTable/ directory of the module.
All that is truly necessary when extending Zend_Db_Table is to provide a table name and
optionally the primary key (if it is not "id").
Now let's create a Data Mapper. A Data Mapper maps a domain object to the database.
In our case, it will map our model, Application_Model_Guestbook, to our data source,
Application_Model_DbTable_Guestbook. A typical API for a data mapper is as follows:
// application/models/GuestbookMapper.php
class Application_Model_GuestbookMapper
{
public function save($model);
public function find($id, $model);
public function fetchAll();
}
In addition to these methods, we'll add methods for setting and retrieving the Table Data
Gateway. The final class, located in application/models/GuestbookMapper.php, looks
like this:
// application/models/GuestbookMapper.php
class Application_Model_GuestbookMapper
{
protected $_dbTable;
23
Démarrez rapidement
avec Zend Framework
Now it's time to update our model class slightly, to accomodate the data mapper. Just like the
data mapper contains a reference to the data source, the model contains a reference to the data
mapper. Additionally, we'll make it easy to populate the model by passing an array of data either
to the constructor or a setOptions() method. The final model class, located in application/
models/Guestbook.php, looks like this:
// application/models/Guestbook.php
class Application_Model_Guestbook
{
protected $_comment;
protected $_created;
protected $_email;
protected $_id;
protected $_mapper;
24
Démarrez rapidement
avec Zend Framework
25
Démarrez rapidement
avec Zend Framework
Lastly, to connect these elements all together, lets create a guestbook controller that will both
list the entries that are currently inside the database.
To create a new controller, open a terminal or DOS console, navigate to your project directory,
and enter the following:
# Unix-like systems:
% zf.sh create controller guestbook
# DOS/Windows:
C:> zf.bat create controller guestbook
26
Démarrez rapidement
avec Zend Framework
We'll use the "index" action as a landing page to view all guestbook entries.
Now, let's flesh out the basic application logic. On a hit to indexAction(), we'll display all
guestbook entries. This would look like the following:
// application/controllers/GuestbookController.php
And, of course, we need a view script to go along with that. Edit application/views/
scripts/guestbook/index.phtml to read as follows:
Checkpoint
27
Démarrez rapidement
avec Zend Framework
The "-e" switch allows you to specify the value to use for the constant
APPLICATION_ENV -- which in turn allows you to create a SQLite database for
each environment you define. Be sure to run the script for the environment you
choose for your application when deploying.
5. Create A Form
For our guestbook to be useful, we need a form for submitting new entries.
Our first order of business is to create the actual form class. First, create the directory
application/forms/. This directory will contain form classes for the application. Next, we'll
create a form class in application/forms/Guestbook.php:
// application/forms/Guestbook.php
28
Démarrez rapidement
avec Zend Framework
// Add a captcha
$this->addElement('captcha', 'captcha', array(
'label' => 'Please enter the 5 letters displayed below:',
'required' => true,
'captcha' => array(
'captcha' => 'Figlet',
'wordLen' => 5,
'timeout' => 300
)
));
The above form defines five elements: an email address field, a comment field, a CAPTCHA for
preventing spam submissions, a submit button, and a CSRF protection token.
Next, we will add a signAction() to our GuestbookController which will process the form
upon submission. To create the action and related view script, execute the following:
# Unix-like systems:
% zf.sh create action sign guestbook
# DOS/Windows:
C:> zf.bat create action sign guestbook
This will create a signAction() method in our controller, as well as the appropriate view script.
29
Démarrez rapidement
avec Zend Framework
Let's add some logic into our guestbook controller's sign action. We need to first check if we're
getting a POST or a GET request; in the latter case, we'll simply display the form. However, if we
get a POST request, we'll want to validate the posted data against our form, and, if valid, create
a new entry and save it. The logic might look like this:
// application/controllers/GuestbookController.php
if ($this->getRequest()->isPost()) {
if ($form->isValid($request->getPost())) {
$model = new Application_Model_Guestbook($form->getValues());
$model->save();
return $this->_helper->redirector('index');
}
}
$this->view->form = $form;
}
}
<?php
$this->form->setAction($this->url());
echo $this->form;
No one will be waxing poetic about the beauty of this form anytime soon. No
matter - form appearance is fully customizable! See the decorators section in the
reference guide for details.
Checkpoint
30
Démarrez rapidement
avec Zend Framework
6. Congratulations!
You have now built a very simple application using some of the most commonly used Zend
Framework components. Zend Framework makes many components available to you which
address most common requirements in web applications, including web services, search, PDF
31
Démarrez rapidement
avec Zend Framework
reading and writing, authentication, authorization, and much more. The Reference Guide is a
great place to find out more about the components you've used in this QuickStart as well as other
components. We hope you find Zend Framework useful and - more importantly - fun!
32
Chargement automatique avec Zend
Framework
1. Introduction
L'auto-chargement est un mécanisme qui élimine les inclusions de dépendances manuelles au
sein du code PHP. Le manuel sur l'autoload en PHPprécise qu'une fois qu'un autoloader a été
défini, "il est appelé automatiquement dans le cas où l'on tente d'utiliser une classe ou une
interface qui n'a pas encore été définie"
En utilisant l'auto-chargement, vous n'avez pas besoin de vous inquiéter du lieu où la classe
existe au sein du projet. Avec des autoloaders bien définis, la résolution du fichier contenant la
classe utilisée sera effectuée de manière transparente.
Aussi, l'autoloader chargeant la classe uniquement lorsque celle-ci est strictement nécessaire,
ceci peut avoir des effets très positifs sur les performances globales -- particulièrement si vous
prenez soin de supprimer tous les appels à require_once() avant votre déploiement.
2. Architecture et buts
2.1. Convention de noms des classes
Pour comprendre l'autochargement dans le Zend Framework, vous devez d'abord comprendre
la relation entre nom de classe et nom de fichier.
Zend Framework a emprunté une idée de PEAR, dans lequel les noms des classes ont une
relation 1:1 avec le système de fichiers. Simplement, le caractère underscore ("_") est remplacé
par un séparateur de dossier pour résoudre le chemin vers le fichier, puis le suffixe ".php"
est ajouté. Par exemple, une classe "Foo_Bar_Baz" va correspondre à "Foo/Bar/Baz.php"
sur le système de fichiers. La supposition est alors que PHP résoudra les fichier relativement
à l'include_path ce qui permet d'utiliser include() et require() pour chercher le fichier
relativement à l'include_path.
Aussi, conformément à PEAR et au PHP project, nous utilisons et vous recommandons d'utiliser
un préfixe à votre code. Cela signifie que toutes les classes que vous écrivez doivent partager un
préfixe unique, par exemple, dans Zend Framework le préfixe est "Zend_". Cette convention de
noms évite toute collision dans les noms des classes. Dans Zend Framework, nous utilisons la
notion "d'espace de noms" ("namespace"); attention à éviter la confusion avec l'implémentation
native des espaces de noms de PHP.
Zend Framework suit ces règles simples en interne et nos standards de code vous encouragent
à faire de même avec le code de vos propres librairies.
33
Chargement automatique
avec Zend Framework
• Correspondance d'espace de noms. Si l'espace de noms de la classe (son préfixe) n'est pas
dans une liste pré-enregistrée, retourner FALSE immédiatement. Ceci permet une optimisation
de la recherche ainsi que l'utilisation d'autres autoloaders ou d'un autoloader global par défaut.
Dans le cas le plus simple, vous incluez cette classe et l'instanciez. Comme
Zend_Loader_Autoloader est un singleton (car l'autoloader de la SPL est unique), nous
utilisons getInstance() pour en récupérer l'instance.
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance();
Par défaut, ceci va permettre de charger des classes dont le préfixe est "Zend_" ou "ZendX_",
si leurs fichiers sont dans votre include_path.
Que se passe-t-il si vous avez d'autres espaces de noms à charger? Le mieux et le plus simple
est alors d'utiliser la méthode registerNamespace() de l'instance. Vous pouvez lui passer
un préfixe simple, ou un tableau de préfixes:
require_once 'Zend/Loader/Autoloader.php';
$loader = Zend_Loader_Autoloader::getInstance();
$loader->registerNamespace('Foo_');
$loader->registerNamespace(array('Foo_', 'Bar_'));
$loader->setFallbackAutoloader(true);
34
Chargement automatique
avec Zend Framework
A l'heure de l'écriture de ces lignes, PHP 5.3 est sorti. Avec cette version, PHP
supporte maintenant officiellement les espaces de noms.
Cependant, Zend Framework date d'avant PHP 5.3, et donc les espaces de noms
PHP. Dans Zend Framework, lorsque nous parlons "d'espace de noms", nous
parlons d'une pratique consistant à préfixer le nom de la classe par un préfixe.
Par exemple, toutes les classes de Zend Framework commencent par "Zend_"
-- c'est notre espace de noms.
Si vous possédez votre propre autoloader et que vous voulez l'utiliser avec Zend Framework
-- peut être un autoloader provenant d'une autre librairie que vous utilisez -- vous pouvez
l'enregistrer grâce aux méthodes de Zend_Loader_Autoloader pushAutoloader() et
unshiftAutoloader(). Ces méthodes ajoutent des autoloaders à la fin ou au début de
la chaine utilisée avant l'exécution des mecanismes internes d'auto-chargement de Zend
Framewor. Cette approche a les avantages suivants:
• Chaque méthode prend un deuxième paramètre : un espace de noms qui indique que
l'autoloader passé ne doit être utilisé que pour charger des classes dans cet espace de noms
là. Si la classe n'est pas dans cet espace de noms, l'autoloader sera alors ignoré, ce qui peut
amener à des optimisations de performance.
• Si vous devez manipuler le registre de spl_autoload(), prenez garde si vous préciser des
fonctions de rappels sous forme de méthodes de classes car spl_autoload_functions()
ne retourne pas exactement leurs définitions. Zend_Loader_Autoloader ne souffre pas de
ce problème.
35
Chargement automatique
avec Zend Framework
Voici une liste de définitions de fonctions de rappel pour auto-chargement valides en PHP.
4. Auto-chargement de resources
En développant des applications, il est souvent difficile de regrouper certaines classes dans
une relation 1:1 avec le système de fichiers que recommande le Zend framework, ou alors
ça ne semble pas intuitif de le faire. Cela signifie que les classes ne seront pas trouvées par
l'autoloader.
Une ressource est juste un nom qui correspond à un espace de noms pour un composant (qui
est ajouté à l'espace de noms de l'autoloader) et un chemin (qui est relatif au chemin de base
de l'autoloader). Sous forme de code, vous feriez quelque chose comme:
Une fois le chargeur en place, il faut l'informer des différents types de ressources qu'il va avoir
à gérer. Ces types sont simplement des paires d'arbres et de préfixes.
path/to/some/resources/
|-- forms/
| `-- Guestbook.php // Foo_Form_Guestbook
|-- models/
| |-- DbTable/
| | `-- Guestbook.php // Foo_Model_DbTable_Guestbook
| |-- Guestbook.php // Foo_Model_Guestbook
| `-- GuestbookMapper.php // Foo_Model_GuestbookMapper
36
Chargement automatique
avec Zend Framework
préfixe "Form"), model (dans le sous-dossier "models", avec un préfixe "Model"), et dbtable (dans
le sous-dossier "models/DbTable", avec un préfixe "Model_DbTable"). Nous les définirons
comme ceci:
5. Conclusion
Zend Framework encourage l'utilisation de l'auto-chargement, et l'initialise même par défaut dans
Zend_Application. Nous espérons que ce tutoriel vous a apporté toutes les informations
sur l'utilisation de Zend_Loader_Autoloader, ainsi que sur son extension pour ajouter des
autochargeurs personnalisés.
Pour plus d'informations sur son utilisation, lisez les sections du manuel sur
Zend_Loader_Autoloader et Zend_Loader_Autoloader_Resource.
37
Les plugins dans Zend Framework
1. Introduction
Zend Framework makes heavy use of plugin architectures. Plugins allow for easy extensibility
and customization of the framework while keeping your code separate from Zend Framework's
code.
• Plugins are classes. The actual class definition will vary based on the component -- you may
need to extend an abstract class or implement an interface, but the fact remains that the plugin
is itself a class.
• Related plugins will share a common class prefix. For instance, if you have created a number
of view helpers, they might all share the class prefix "Foo_View_Helper_".
• Everything after the common prefix will be considered the plugin name or short name
(versus the "long name", which is the full classname). For example, if the plugin prefix is
"Foo_View_Helper_", and the class name is "Foo_View_Helper_Bar", the plugin name
will be simply "Bar".
• Plugin names are typically case sensitive. The one caveat is that the initial letter can often be
either lower or uppercase; in our previous example, both "bar" and "Bar" would refer to the
same plugin.
2. Using Plugins
Components that make use of plugins typically use Zend_Loader_PluginLoader to do their
work. This class has you register plugins by specifying one or more "prefix paths". The component
will then call the PluginLoader's load() method, passing the plugin's short name to it. The
PluginLoader will then query each prefix path to see if a class matching that short name exists.
Prefix paths are searched in LIFO (last in, first out) order, so it will match those prefix paths
registered last first -- allowing you to override existing plugins.
38
Les plugins dans Zend Framework
In this example, we will assume some validators have been written and placed in the
directory foo/plugins/validators/, and that all these classes share the class prefix
"Foo_Validate_"; these two bits of information form our "prefix path". Furthermore, let's
assume we have two validators, one named "Even" (ensuring a number to be validated is
even), and another named "Dozens" (ensuring the number is a multiple of 12). The tree
might look like this:
foo/
|-- plugins/
| |-- validators/
| | |-- Even.php
| | |-- Dozens.php
Now we can simply tell the element the short name of the validators we want to use. In the
following example, we're using a mix of standard validators ("NotEmpty", "Int") and custom
validators ("Even", "Dozens"):
$element->addValidator('NotEmpty')
->addValidator('Int')
->addValidator('Even')
->addValidator('Dozens');
When the element needs to validate, it will then request the plugin class from the
PluginLoader. The first two validators will resolve to Zend_Validate_NotEmpty and
Zend_Validate_Int, respectively; the next two will resolve to Foo_Validate_Even and
Foo_Validate_Dozens, respectively.
The plugin loader will look through each prefix path, checking to see if a file
matching the plugin name is found on that path. If the file is not found, it then
moves on to the next prefix path.
Once the stack of prefix paths has been exhausted, if no matching file has been
found, it will throw a Zend_Loader_PluginLoader_Exception.
39
Les plugins dans Zend Framework
One strength of the PluginLoader is that its use of a LIFO stack allows you to override existing
plugins by creating your own versions locally with a different prefix path, and registering that
prefix path later in the stack.
For example, let's consider Zend_View_Helper_FormButton (view helpers are one form
of plugin). This view helper accepts three arguments, an element name (also used as the
element's DOM identifier), a value (used as the button label), and an optional array of
attributes. The helper then generates HTML markup for a form input element.
Let's say you want the helper to instead generate a true HTML button element; don't want
the helper to generate a DOM identifier, but instead use the value for a CSS class selector;
and that you have no interest in handling arbitrary attributes. You could accomplish this in a
couple of ways. In both cases, you'd create your own view helper class that implements the
behavior you want; the difference is in how you would name and invoke them.
Our first example will be to name the element with a unique name:
Foo_View_Helper_CssButton, which implies the plugin name "CssButton". While this
certainly is a viable approach, it poses several issues: if you've already used the Button
view helper in your code, you now have to refactor; alternately, if another developer starts
writing code for your application, they may inadvertently use the Button view helper instead
of your new view helper.
So, the better example is to use the plugin name "Button", giving us the class name
Foo_View_Helper_Button. We then register the prefix path with the view:
Once done, anywhere you now use the "Button" helper will delegate to your custom
Foo_View_Helper_Button class!
3. Conclusion
Understanding the concept of prefix paths and overriding existing plugins will help you with your
understanding of many components within the framework. Plugins are used in a variety of places:
• Zend_Application: resources.
• Zend_Feed_Reader: plugins.
And several more places, besides. Learn the concepts early so you can leverage this important
extension point in Zend Framework.
40
Les plugins dans Zend Framework
Caveat
We'll note here that Zend_Controller_Front has a plugin system - but it does
not adhere to any of the guidelines offerred in this tutorial. The plugins registered
with the front controller must be instantiated directly and registered individually
with it. The reason for this is that this system predates any other plugin system
in the framework, and changes to it must be carefully weighed to ensure existing
plugins written by developers continue to work with it.
41
Bien démarrer avec Zend_Layout
1. Introduction
Dans une application utilisant les couches Zend Framework MVC, vos scripts de vue ne seront
que des blocs de HTML concernant l'action demandée. Par exemple, une action "/user/list"
mènerait vers un script de vue itérant sur les utilisateurs en présentant une liste:
<h2>Utilisateurs</h2>
<ul>
<?php if (!count($this->users)): ?>
<li>Pas d'utilisateurs</li>
<?php else: ?>
<?php foreach ($this->users as $user): ?>
<li>
<?php echo $this->escape($user->fullname) ?>
(<?php echo $this->escape($user->email) ?>)
</li>
<?php endforeach ?>
<?php endif ?>
</ul>
Comme c'est juste un bloc de code HTML, ce n'est pas une page valide, il manque le DOCTYPE
et la balise ouvrante HTML puis BODY. Quand seront-ils crées?
Dans les anciennes versions de Zend Framework, les développeurs créaient souvent des scripts
de vue "header" et "footer" qui servaient à cela. Ca fonctionnait certes, mais c'était difficile à
refactoriser, ou pour appeler du contenu provenant de plusieurs actions.
Le pattern Two-Step View solutionne beaucoup des problèmes indiqués. Avec, la vue
"application" est crée en premier, puis injectée dans une vue "page" ainsi présentée à l'utilisateur
client. la vue de page peut être imaginée comme un template global ou layout qui décrirait des
éléments communs utilisés au travers de multiples pages.
2. Utiliser Zend_Layout
L'utilisation classique de Zend_Layout est simple. En supposant que vous utilisez
Zend_Application, il suffit simplement de passer des options de configuration et créer un
script de layout.
application
|-- Bootstrap.php
|-- configs
| `-- application.ini
|-- controllers
|-- layouts
| `-- scripts
| |-- layout.phtml
42
Bien démarrer avec Zend_Layout
La première ligne indique où chercher les scripts de layout; la seconde donne le nom du script
à utiliser (l'extension est supposée ".phtml" par défaut).
<html>
<head>
<title>My Site</title>
</head>
<body>
<?php echo $this->layout()->content ?>
</body>
</html>
Dans l'exemple ci-dessus, un appel à l'aide de vue layout() y est effectué. Lorsque vous
activez l'instance de Zend_Layout, vous avez aussi accès à une aide d'acion et de vue qui
permettent d'accéder à l'instance de Zend_Layout; et vous pouvez ainsi appeler des méthodes
sur l'objet layout. Dans notre cas, nous récupérons une variable appelée $content, et nous
l'affichons. Par défaut, $content est peuplée du contenu de la vue rendue pour l'action en
cours. Sinon, tout ce que vous feriez dans un script de vue est valide dans un script de layout:
appel d'aides ou de méthodes sur la vue.
Maintenant, nous avons un script de layout fonctionnel et notre application sait où le trouver.
• Dans des scripts de vue: utilisez l'aide de vue layout(), qui retourne l'instance de
Zend_Layout enregistrée au moyen du plugin MVC.
Comme cela retourne l'objet de layout, vous pouvez appeler dessus toute méthode ou assigner
des variables.
• Dans vos contrôleurs: utilisez ici l'aide d'action layout(), qui agit comme l'aide de vue.
43
Bien démarrer avec Zend_Layout
Comme avec l'aide de vue, vous pouvez appeler dès lors n'importe quelle méthode de layout
ou lui assigner des variables.
$layout = Zend_Layout::getMvcInstance();
• Via le bootstrap: utilisez la ressource layout qui crée, configure et retourne l'objet
Zend_Layout.
$layout = $bootstrap->getResource('Layout');
Partout où vous avez accès à l'objet bootstrap, il s'agit de la méthode recommandée par
rapport à getMvcInstance().
• Affecter les variables de layout. Zend_Layout garde en mémoire les variables de vue
spécifiques à la layout, la clé $content en est un exemple. Vous pouvez assigner et récupérer
ces variables grâce à la méthode assign() ou en y accédant comme des attributs classiques.
// Affecter du contenu:
$layout->somekey = "foo"
$layout->disableLayout();
• Utiliser un autre script de layout: Si vous avez plusieurs scripts de layout pour votre application,
vous pouvez selectionner lequel rendre grâce à la méthode setLayout(). Précisez alors le
nom du script de layout, sans l'extension.
44
Bien démarrer avec Zend_Layout
Le script de layout doit se trouver dans le $layoutPath précisé via la configuration (en
bootstrap générallement). Zend_Layout utilisera le nouveau script à rendre.
3. Zend_Layout: Conclusions
Zend_Layout est une simple surcouche à Zend_View en proposant un pattern Two-Step View,
offrant la fléxibilité de créer un design applicatif dans lequel le contenu est injecté.
Si vous regardez de près les exemple, vous sentirez peut-être que les fonctionnalités sont
limitées : comment modifier le titre de la page, injecter un tag script optionnel ou même créer
une sidebar? Ces questions concernent le concept de "Composite View" -- et trouveront réponse
dans le chapitre suivant couvrant des "placeholders."
45
Bien démarrer avec Zend_View
1. Introduction
In the previous chapter, we looked at primarily the Two Step View pattern, which allows you to
embed individual application views within a sitewide layout. At the end of that chapter, however,
we discussed some limitations:
• How would you inject conditional scripts or stylesheets into the sitewide layout?
• How would you create and render an optional sidebar? What if there was some content that
was unconditional, and other content that was conditional for the sidebar?
These questions are addressed in the Composite View design pattern. One approach to that
pattern is to provide "hints" or content to the sitewide layout. In Zend Framework, this is achieved
through specialized view helpers called "placeholders." Placeholders allow you to aggregate
content, and then render that aggregate content elsewhere.
All placeholders operate in roughly the same way. They are containers, and thus allow you to
operate on them as collections. With them you can:
• Specify a string with which to prepend output of the collection when rendering.
• Specify a string with which to append output of the collection when rendering.
• Specify a string with which to separate items of the collection when rendering.
Typically, you will call the helper with no arguments, which will return a container on which you
may operate. You will then either echo this container to render it, or call methods on it to configure
or populate it. If the container is empty, rendering it will simply return an empty string; otherwise,
the content will be aggregated according to the rules by which you configure it.
As an example, let's create a sidebar that consists of a number of "blocks" of content. You'll likely
know up-front the structure of each block; let's assume for this example that it might look like this:
<div class="sidebar">
46
Bien démarrer avec Zend_View
<div class="block">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
consectetur aliquet odio ac consectetur. Nulla quis eleifend
tortor. Pellentesque varius, odio quis bibendum consequat, diam
lectus porttitor quam, et aliquet mauris orci eu augue.
</p>
</div>
<div class="block">
<ul>
<li><a href="/some/target">Link</a></li>
<li><a href="/some/target">Link</a></li>
</ul>
</div>
</div>
The content will vary based on the controller and action, but the structure will be the same. Let's
first setup the sidebar in a resource method of our bootstrap:
$view->placeholder('sidebar')
// "prefix" -> markup to emit once before all items in collection
->setPrefix("<div class=\"sidebar\">\n <div class=\"block\">\n")
// "separator" -> markup to emit between items in a collection
->setSeparator("</div>\n <div class=\"block\">\n")
// "postfix" -> markup to emit once after all items in a collection
->setPostfix("</div>\n</div>");
}
// ...
}
The above defines a placeholder, "sidebar", that has no items. It configures the basic markup
structure of that placeholder, however, per our requirements.
Now, let's assume for the "user" controller that for all actions we'll want a block at the top
containing some information. We could accomplish this in two ways: (a) we could add the content
to the placeholder directly in the controller's preDispatch() method, or (b) we could render a
view script from within the preDispatch() method. We'll use (b), as it follows a more proper
separation of concerns (leaving view-related logic and functionality within a view script).
47
Bien démarrer avec Zend_View
</ul>
<?php $this->placeholder('sidebar')->captureEnd() ?>
The above example makes use of the content capturing feature of placeholders. By default,
content is appended as a new item in the container, allowing us to aggregate content. This
example makes use of view helpers and static HTML in order to generate markup, and the content
is then captured and appended into the placeholder itself.
To invoke the above view script, we would write the following in our preDispatch() method:
$this->view->render('user/_sidebar.phtml');
// ...
}
// ...
}
Note that we're not capturing the rendered value; there's no need, as the entierty of that view
is being captured into a placeholder.
Now, let's assume our "view" action in that same controller needs to present some information.
Within the "user/view.phtml" view script, we might have the following snippet of content:
$this->placeholder('sidebar')
->append('<p>User: ' . $this->escape($this->username) . '</p>');
This example makes use of the append() method, and passes it some simple markup to
aggregate.
Finally, let's modify our layout view script, and have it render the placeholder.
<html>
<head>
<title>My Site</title>
</head>
<body>
<div class="content">
<?php echo $this->layout()->content ?>
</div>
<?php echo $this->placeholder('sidebar') ?>
</body>
</html>
For controllers and actions that do not populate the "sidebar" placeholder, no content will be
rendered; for those that do, however, echoing the placeholder will render the content according to
the rules we created in our bootstrap, and the content we aggregated throughout the application.
In the case of the "/user/view" action, and assuming a username of "matthew", we would get
content for the sidebar as follows (formatted for readability):
48
Bien démarrer avec Zend_View
<div class="sidebar">
<div class="block">
<h4>User Administration</h4>
<ul>
<li><a href="/user/list">List</a></li>
<li><a href="/user/create">Create</a></a></li>
</ul>
</div>
<div class="block">
<p>User: matthew</p>
</div>
</div>
There are a large number of things you can do by combining placeholders and layout scripts;
experiment with them, and read the relevant manual sections for more information.
3. Standard Placeholders
In the previous section, we learned about the placeholder() view helper, and how it can be
used to aggregate custom content. In this section, we'll look at some of the concrete placeholders
shipped with Zend Framework, and how you can use them to your advantage when creating
complex composite layouts.
Most of the shipped placeholders are for generating content for the <head> section of your layout
content -- an area you typically cannot manipulate directly via your application view scripts, but
one you may want to influence. As examples: you may want your title to contain certain content
on every page, but specific content based on the controller and/or action; you may want to specify
CSS files to load based on what section of the application you're in; you may need specific
JavaScript scripts loaded at different times; or you may want to set the DocType declaration.
Zend Framework ships with placeholder implementations for each of these situations, and
several more.
As an example, if you want to use the XHTML1 Strict DTD, you can simply specify:
$this->doctype('XHTML1_STRICT');
Among the other available mnemonics, you'll find these common types:
HTML5 HTML 5
49
Bien démarrer avec Zend_View
You can assign the type and render the declaration in a single call:
echo $this->doctype('XHTML1_STRICT');
However, the better approach is to assign the type in your bootstrap, and then render it in your
layout. Try adding the following to your bootstrap class:
Then, in your layout script, simply echo the helper at the top of the file:
This will ensure that your DocType-aware view helpers render the appropriate markup, ensure
that the type is set well before the layout is rendered, and provide a single location to change
the DocType.
At its simplest, the headTitle() helper allows you to aggregate content for the <title> tag;
when you echo it, it then assembles it based on the order in which segments are added. You
can control the order using prepend() and append(), and provide a separator to use between
segments using the setSeparator() method.
Typically, you should specify any segments common to all pages in your bootstrap, similar to how
we define the doctype. In this case, we'll define a _initPlaceholders() method for operating
on all the various placeholders, and specify an initial title as well as a separator.
50
Bien démarrer avec Zend_View
->setSeparator(' :: ');
}
// ...
}
In our example, we'll assume that all pages need to load the stylesheet located in "/styles/
site.css" (relative to the document root); we'll specify this in our _initPlaceholders()
bootstrap method.
51
Bien démarrer avec Zend_View
// ...
}
Within our layout view script, once again, we simply echo the placeholder:
Similar to the headLink() helper, headScript() provides the ability to append or prepend
scripts to the collection, and then echo the entire set. It provides the flexibility to specify
either script files themselves to load, or explicit JavaScript. You also have the option of
capturing JavaScript via captureStart()/captureEnd(), which allows you to simply inline
the JavaScript instead of requiring an additional call to your server.
In this example, we'll specify that a script, "/js/site.js" needs to be loaded on every page;
we'll update our _initPlaceholders() bootstrap method to do this.
52
Bien démarrer avec Zend_View
$this->bootstrap('View');
$view = $this->getResource('View');
$view->doctype('XHTML1_STRICT');
// ...
}
Within a view script, we might then add an extra script file to source, or capture some JavaScript
to include in our document.
Within our layout script, we then simply echo the placeholder, just as we have all the others:
InlineScript Variant
Many browsers will often block display of a page until all scripts and stylesheets
referenced in the <head> section have loaded. If you have a number of such
directives, this can impact how soon somebody can start actually viewing the
page.
One way around this is to emit your <script> tags just prior to closing the
<body> of your document. (This is a practice specifically recommend by the Y!
Slow project.)
53
Bien démarrer avec Zend_View
• You can render your headScript() tag whereever you like in your layout
script; just because the title references "head" does not mean it needs to be
rendered in that location.
54
Bien comprendre et utiliser les
décorateurs Zend Form
1. Introduction
Zend_Form utilise le pattern décorateur afin de générer le rendu des éléments. Contrairement au
pattern classique du décorateur, dans lequel se sont des objets qui sont passés, les décorateur
de Zend_Form implémentent un pattern strategy , et utilisent les méta-données contenues dans
l'élément ou le formulaire afin de créer une représentation de celles-ci.
Ne vous laissez pas surprendre par ces termes, les décorateurs de Zend_Form ne sont pas
terriblement difficiles et les mini-tutoriels qui suivent vont vous le prouver. Ils vont vous guider
pour les bases et les techniques avancées de décoration.
interface Window
{
public function isOpen();
public function open();
public function close();
}
55
Bien comprendre et utiliser
les décorateurs Zend Form
}
}
Une utilisation particulièrement pratique du pattern décorateur est pour tout ce qui concerne
la représentation des objets. Par exemple un objet "Personne" qui en lui-même n'a aucune
représentation textuelle. Grâce au pattern décorateur, vous pouvez créer un objet qui va agir
comme une Personne mais qui pourra aussi représenter textuellement cette Personne.
Dans cet exemple particulier, nous allons utiliser leduck typing plutôt qu'une interface explicite.
Ceci permet à notre implémentation d'être un peu plus fléxible tout en gardant l'utilisation de la
décoration intacte.
class Person
{
public function setFirstName($name) {}
public function getFirstName() {}
public function setLastName($name) {}
public function getLastName() {}
public function setTitle($title) {}
public function getTitle() {}
}
class TextPerson
{
protected $_person;
56
Bien comprendre et utiliser
les décorateurs Zend Form
Dans cet exemple, nous passons une instance Person au constructeur de TextPerson. Grâce
à la surcharge des méthodes, nous pouvons continuer d'appeler les méthodes de Person --
affecter un nom, un prénom, ... -- mais nous pouvons en plus récupérer une représentation sous
forme de chaine grâce à __toString().
Imaginons une situation dans laquelle nous souhaitons simplement rendre un élément comme
un tag html text avec un libéllé(label). Juste la base, nous verrons plus tard la gestion des erreurs
et les éventuels autres tags html. Un tel décorateur pourrait ressembler à ça:
57
Bien comprendre et utiliser
les décorateurs Zend Form
return $markup;
}
}
<label for="bar[foo]">Foo</label>
<input id="bar-foo" name="bar[foo]" type="text" value="test"/>
Nous pourrions aussi ranger cette classe dans un dossier de librairie, il faut alors informer
l'élément du chemin vers ce dossier, et ensuite faire référence au décorateur comme
"SimpleInput":
Ceci permet de partager du code entre projets et ouvre aussi la possibilité d'étendre dans le
futur les classes rangées.
Dans le chapitre suivant, nous allons voir comment combiner les décorateurs afin de créer un
affichage par morceaux (composite).
Pour la plupart des éléments, les décorateurs suiovants sont chargés par défaut:
• ViewHelper: utilise une aide de vue pour rendre l'élément balise de formulaire à proprement
parlé.
• Errors: utilise l'aide de vue FormErrors pour afficher les erreurs de validation éventuelles.
58
Bien comprendre et utiliser
les décorateurs Zend Form
• Label: rend l'intitulé de l'élément en utilisant l'aide de vue FormLabel (et en encapsulant le
tout dans un tag <dt>).
Notez bien que chaque décorateur na qu'une petite tâche particulière et opère sur une partie
spécifique des données de l'élément auquel il est rattaché, le décorateur Errors récupère les
messages de validation de l'élément et les rend, le décorateur Label rend simplement le libéllé.
Ceci fait que chaque décorateur est très petit, réutilisable, et surtout testable.
Cet argument $content vient de là aussi : chaque décorateur travaille avec sa méthode
render() sur un contenu (générallement généré par le décorateur immédiatement précédent
dans la pile globale) et embellit ce contenu en lui rajoutant ou en lui faisant précéder des
informations. Il peut aussi remplacer totallement son contenu.
Ainsi, pensez au mécanisme des décorateurs comme la conception d'un oignon de l'intérieur
vers l'exterieur.
59
Bien comprendre et utiliser
les décorateurs Zend Form
Ok, ca semble bon mais il y a un problème : le dernier décorateur va l'emporter. Vous allez vous
retrouver avec comme seul rendu, celui du dernier décorateur.
Pour faire fonctionner le tout comme il se doit, concaténez simplement le contenu précédent
$content avec le contenu généré:
Le problème avec cette approche est que vous ne pouvez pas choisir où se place
le contenu du décorateur en question. Heureusement, un mécanisme standard existe;
Zend_Form_Decorator_Abstract possède le concept de place et définit des constantes
pour le régler. Aussi, il permet de préciser un séparateur à placer entre les 2. Voyons celà:
$placement = $this->getPlacement();
$separator = $this->getSeparator();
switch ($placement) {
case self::PREPEND:
return $markup . $separator . $content;
case self::APPEND:
default:
return $content . $separator . $markup;
}
}
}
60
Bien comprendre et utiliser
les décorateurs Zend Form
$placement = $this->getPlacement();
$separator = $this->getSeparator();
switch ($placement) {
case self::APPEND:
return $markup . $separator . $content;
case self::PREPEND:
default:
return $content . $separator . $markup;
}
}
}
Notez que dans l'exemple ci-dessus, nous intervertissons les comportements par défaut avec
append et prepend.
Comment ça fonctionne? et bien nous appelons render(), l'élément va alors commencer une
itération sur tous ses décorateurs, en appelant render() sur chacun. Il va passer une chaine
vide comme contenu pour le premier décorateur, et le rendu de chaque décorateur va servir de
contenu pour le suivant, ainsi de suite:
• chaine vide ('') est passée au décorateur SimpleInput, qui génère un tag de formulaire
de type input qu'il ajoute à la chaine vide: <input id="bar-foo" name="bar[foo]" type="text"
value="test"/>.
• Ce contenu généré est alors passé comme contenu original pour le décorateur SimpleLabel
qui génère un libéllé et le place avant le contenu original avec comme séparateur PHP_EOL, ce
qui donne: <label for="bar-foo">\n<input id="bar-foo" name="bar[foo]" type="text" value="test"/
>.
Mais attendez une minute! Et si nous voulions que le libéllé soit rendu après le tag de formulaire
pour une raison quelconque? Vous souvenez-vous de l'option "placement"? Vous pouvez la
préciser comme option de décorateur, et le plus simple est alors de la passer à la création de
l'élément:
61
Bien comprendre et utiliser
les décorateurs Zend Form
Notez que passer des options vous oblige à préciser le nom du décorateur dans un tableau en
tant que premier élément, le deuxième élément est un tableau d'options.
Grâce à cette technique, vous pouvez avoir plusieurs décorateurs dont chacun s'occupe de
rendre une petite partie d'un élément; et c'est en utilisant plusieurs décorateurs et en les chainant
correctement que vous obtiendrez un rendu complet : l'oignon final.
• C'est plus complexe qu'un rendu simple. Vous devez faire attention à chaque décorateur mais
en plus à l'ordre dans lequel ils agissent.
• Ca consomme plus de ressources. Plus de décorateurs, plus d'objets, multipliés par le nombre
d'éléments dans un formulaire et la consommation en ressources augmente. La mise en cache
peut aider.
• Réutilisabilité. Vous pouvez créer des décorateurs complètement réutilisables car vous ne
vous souciez pas du rendu final, mais de chaque petit bout de rendu.
• Fléxibilité. Il est en théorie possible d'arriver au rendu final voulu très exactement, et ceci avec
une petite poignée de décorateurs.
Les exemples ci-dessus montrent l'utilisation de décorateurs au sein même d'un objet
Zend_Form et nous avons vu comment les décorateurs jouent les uns avec les autres pour
arriver au rendu final. Afin de pouvoir les utiliser de manière indépendante, la version 1.7 a
ajouté des méthodes fléxibles rendant les formulaires ressemblant au style Rail. Nous allons
nous pencher sur ce fait dans la section suivante.
Une fois des décorateurs enregistrés, vous pouvez les récupérer via leur nom depuis l'élément.
Revoyons l'exemple précédent:
62
Bien comprendre et utiliser
les décorateurs Zend Form
$decorator = $element->getDecorator('SimpleInput');
echo $decorator->render('');
C'est simple et ça peut l'être encore plus; ré-écrivons le tout sur une seule ligne:
echo $element->getDecorator('SimpleInput')->render('');
Pas mauvais, mais toujours un peu compliqué. Pour simplifier, une notation raccourcie a été
introduite dans Zend_Form en 1.7: vous pouvez rendre n'importe quel décorateur enregistré en
appelant une méthode de la forme renderDecoratorName(). Ceci effectue le rendu et fait en
sorte que $content soit optionnel ce qui simplifie l'utilisation:
echo $element->renderSimpleInput();
Beaucoup de développeurs ont des besoins très précis en affichage des formulaires. Ils préfèrent
avoir un contrôle complet sur tout l'affichage plutôt que d'utiliser une solution automatisée
qui peut s'écarter de leur but initial. Dans d'autres cas, les formulaires peuvent demander un
affichage extrêmement spécifique, en groupant des éléments alors que d'autres doivent pouvoir
être invisibles avant que l'on n'effectue telle action sur la page, etc.
63
Bien comprendre et utiliser
les décorateurs Zend Form
));
$this->addElement('text', 'title', array(
'label' => 'Title: ',
));
$this->addElement('text', 'dateOfBirth', array(
'label' => 'Date of Birth (DD/MM/YYYY): ',
));
$this->addElement('text', 'email', array(
'label' => 'Your email address: ',
));
$this->addElement('password', 'password', array(
'label' => 'Password: ',
));
$this->addElement('password', 'passwordConfirmation', array(
'label' => 'Confirm Password: ',
));
}
}
Nous n'utilisons pas de validateurs ou de filtres ici, car ils n'ont rien à voir avec
le rendu visuel qui nous interesse. En réalité, il y en aurait.
Maintenant réfléchissons au rendu visuel du formulaire. Une communalité concernant les nom
et prénom est qu'on les affiche l'un à coté de l'autre, à coté de leur titre, si présent. Les dates,
si elles n'utilisent pas Javascript, affichent souvent des champs séparés pour chaque segment
de la date.
Utilisons la possibilité de rendre des décorateurs un par un pour accomplir notre tâche. D'abord,
notez qu'aucun décorateur spécifique n'a été renseigné dans les éléments. Rappelons donc les
décorateurs par défaut de la plupart des éléments:
• ViewHelper: utilise une aide de vue pour rendre l'élément balise de formulaire à proprement
parlé.
• Errors: utilise l'aide de vue FormErrors pour afficher les erreurs de validation éventuelles.
• Label: rend l'intitulé de l'élément en utilisant l'aide de vue FormLabel (et en encapsulant le
tout dans un tag <dt>).
Nous vous rappelons aussi que vous pouvez accéder à tout élément individuellement en tant
qu'attribut du formulaire représentant son nom.
<?php
$form = $this->form;
// Enlève le <dt> depuis l'intitulé
foreach ($form->getElements() as $element) {
$element->getDecorator('label')->setOption('tag', null);
}
?>
64
Bien comprendre et utiliser
les décorateurs Zend Form
Si vous utilisez le script ci-dessus, vous verrez un code HTML ressemblant à ceci:
<div class="element">
<label for="dateOfBirth" tag="" class="optional">Date of Birth
(DD/MM/YYYY):</label>
<input type="text" name="dateOfBirth[day]" id="dateOfBirth-day"
value="" size="2" maxlength="2"/>
/
<input type="text" name="dateOfBirth[month]" id="dateOfBirth-month"
value="" size="2" maxlength="2"/>
/
<input type="text" name="dateOfBirth[year]" id="dateOfBirth-year"
value="" size="4" maxlength="4"/>
</div>
65
Bien comprendre et utiliser
les décorateurs Zend Form
<div class="element">
<label for="password" tag="" class="optional">Password:</label>
<input type="password" name="password" id="password" value=""/>
</div>
<div class="element">
<label for="passwordConfirmation" tag="" class="" id="submit"
value="Save"/>
</form>
Ca peut ne pas ressembler à quelque chose de terminé, mais avec un peu de CSS, cela
peut ressembler exactement à ce que vous cherchez. Le point important ici, c'est que le
formulaire a été généré en utilisant de la décoration manuelle personnalisée (ainsi que l'utilisation
d'échappement avec htmlentities).
Grâce à cette partie du tutoriel, vous devriez être à l'aise avec les possibilité de rendu de
Zend_Form. Dans la section suivante, nous verrons comment monter un élément de date grâce
à des éléments et des décorateur uniques assemblés main.
<div class="element">
<?php echo $form->dateOfBirth->renderLabel() ?>
<?php echo $this->formText('dateOfBirth[day]', '', array(
'size' => 2, 'maxlength' => 2)) ?>
/
<?php echo $this->formText('dateOfBirth[month]', '', array(
'size' => 2, 'maxlength' => 2)) ?>
/
<?php echo $this->formText('dateOfBirth[year]', '', array(
'size' => 4, 'maxlength' => 4)) ?>
</div>
5.1. L'élément
Les questions à se poser sur le fonctionnement de l'élément sont:
Les deux première questions se positionnent sur l'élément de formulaire lui-même, comment
vont fonctionner les méthodes setValue() et getValue()? L'autre question nous suggère
de nous questionner sur comment récupérer les segments représentant la date, ou comment
les affecter dans l'élément?
La solution est de surcharger la méthode setValue() dans l'élément pour proposer sa propre
logique. Dans le cas de notre exemple, notre élément devrait avoir trois comportements distincts:
66
Bien comprendre et utiliser
les décorateurs Zend Form
• Si un timestamp entier est utilisé, il doit aider à la détermination des entités jour, mois, année.
• Si une chaine est utilisée, elle devrait être transformée en timestamp, et cette valeur sera
utiliser pour déterminer les entités jour, mois, année.
• Si un tableau contenant les clés jour, mois, année est utilisé, alors les valeurs doivent être
stockées.
En interne, les jour, mois et année seront stockés distinctement. Lorsque la valeur de l'élément
sera demandée, nous récupèrerons une chaine formatée et normalisée. Nous surchargerons
getValue() pour assembler les segments élémentaires composant la date.
67
Bien comprendre et utiliser
les décorateurs Zend Form
->setYear(date('Y', $value));
} elseif (is_string($value)) {
$date = strtotime($value);
$this->setDay(date('d', $date))
->setMonth(date('m', $date))
->setYear(date('Y', $date));
} elseif (is_array($value)
&& (isset($value['day'])
&& isset($value['month'])
&& isset($value['year'])
)
) {
$this->setDay($value['day'])
->setMonth($value['month'])
->setYear($value['year']);
} else {
throw new Exception('Valeur de date invalide');
}
return $this;
}
Cette classe est fléxible : nous pouvons affecter les valeurs par défaut depuis une base de
données et être certains qu'elles seront stockées correctement. Aussi, la valeur peut être affectée
depuis un tableau provenant des entrées du formulaire. Enfin, nous avons tous les accesseurs
distincts pour chaque segment de la date, un décorateur pourra donc créer l'élément comme
il le voudra.
5.2. Le décorateur
Toujours en suivant notre exemple, imaginons que nous voulions que notre utilisateur saisissent
chaque segment jour, mois, année séparément. Heureusement, PHP permet d'utiliser la notation
tableau pour créer des éléments, ainsi nous pourrons capturer ces trois valeurs en une seule et
nous crérons un élément Zend_Form traitant avec des valeurs en tableau.
68
Bien comprendre et utiliser
les décorateurs Zend Form
$view = $element->getView();
if (!$view instanceof Zend_View_Interface) {
// Nous utilisons des aides de vue, si aucune vue n'existe
// nous ne rendons rien
return $content;
}
$day = $element->getDay();
$month = $element->getMonth();
$year = $element->getYear();
$name = $element->getFullyQualifiedName();
$params = array(
'size' => 2,
'maxlength' => 2,
);
$yearParams = array(
'size' => 4,
'maxlength' => 4,
);
switch ($this->getPlacement()) {
case self::PREPEND:
return $markup . $this->getSeparator() . $content;
case self::APPEND:
default:
return $content . $this->getSeparator() . $markup;
}
}
}
Il faut maintenant préciser à notre élément d'utiliser notre décorateur par défaut. Pour ceci, il
faut informer l'élément du chemin vers notre décorateur. Nous pouvons effectuer ceci par le
constructeur:
// ...
}
Notez que l'on fait cela en constructeur et non dans la méthode init(). Ceci pour deux raisons.
D'abord, ceci permet d'étendre dans le futur notre élément afin d'y ajouter de la logique dans
69
Bien comprendre et utiliser
les décorateurs Zend Form
init sans se soucier de l'appel à parent::init(). Ensuite, celà permet aussi de redéfinir
le décorateur par défaut Date dans le futur si celà devient nécessaire, via le constructeur ou
la méthode init.
$decorators = $this->getDecorators();
if (empty($decorators)) {
$this->addDecorator('Date')
->addDecorator('Errors')
->addDecorator('Description', array(
'tag' => 'p',
'class' => 'description'
))
->addDecorator('HtmlTag', array(
'tag' => 'dd',
'id' => $this->getName() . '-element'
))
->addDecorator('Label', array('tag' => 'dt'));
}
}
// ...
}
$d = new My_Form_Element_Date('dateOfBirth');
$d->setLabel('Date de naissance: ')
->setView(new Zend_View());
Si vous affichez cet élément, vous obtiendrez ce rendu (avec quelques modifications concernant
la mise en page du manuel et sa lisibilité):
70
Bien comprendre et utiliser
les décorateurs Zend Form
5.3. Conclusion
Nous avons maintenant un élément qui peut rendre de multiples champs de formulaire, et les
traiter comme une seule entité -- la valeur dateOfBirth sera passée comme un tableau à
l'élément et celui-ci créra les segments de date appropriés et retournera une valeur normalisée.
Aussi, nous pouvons toujours utiliser des décorateurs différents avec l'élément. Si nous avions
voulu utiliser un décorateur Dojo DateTextBox -- qui accepte et retourne des chaines -- we
aurions pu, sans modification sur l'élément lui-même.
Enfin, vous avez une API uniforme pour décrire un élement se composant se plusieurs segments
distincts.
6. Conclusion
Les décorateur de formulaire sont un système qui peut prendre du temps à maitriser. A première
vue, ils semblent complexes et rudes. Mais les différents sujets traités dans ce chapitre vous
aident à comprendre leur fonctionnement et vous montrent des manières de faire pour les utiliser
efficacement dans vos formulaires.
71
Bien démarrer avec Zend_Session,
Zend_Auth, et Zend_Acl
1. Fabrique une application Multi-Utilisateurs avec Zend
Framework
1.1. Zend Framework
Lorsque le web a été crée, il s'agissait d'un média permettant de consulter des documents
statiques. La demande de contenu a cru, le nombre d'internautes aussi et les sites webs sont
devenus des applications tournant sur de grosses plateformes.
HTTP est le protocole du web: sans état, des requêtes/réponses à courte durée de vie. Ce
protocole a été crée comme cela pour assurer le web tel qu'on l'entendait avant : servir du
contenu statique et c'est ce design qui a fait du web un immense succès. C'est aussi ce design
qui mène à des notions que les développeurs veulent utiliser dans leurs applications.
Nous utilisons le terme "client" et pas utilisateur. Les applications web deviennent
des fournisseurs de services. Ceci signifie que les "gens", les utilisateurs
humains avec des navigateurs web ne sont pas les seuls à consommer
l'application et ses services. Beaucoup d'autres applications web consomment
elles-mêmes des ressources sur une application via des technologies comme
REST, SOAP, ou XML-RPC. On voit bien qu'on ne peut parler d'utilisateur, nous
traitons donc les utilisateurs humains des utilisateurs machines sous le même
nom : des "clients" web.
Dans les chapitres qui suivent, nous nous attaquerons à ces problèmes que sont
l'authentification, l'identification et les détails. Nous allons découvrir trois composants:
Zend_Session, Zend_Auth, et Zend_Acl; nous montrerons des exemples concrets et des
possibilités d'extension.
72
Bien démarrer avec Zend_Session,
Zend_Auth, et Zend_Acl
Interagir avec l'application web c'est en fait faire la somme de toutes les requêtes que celle-ci
reçoit. Et comme il y a beaucoup de clients, il y a beaucoup de requête, et le moyen d'associer
une requête à un client est appelé "session".
En PHP, le problème des sessions a été résolu au travers de l'extension session qui utilise un
système de persistance, typiquement basé sur des cookies et un stockage local des variables
dans $_SESSION. Dans Zend Framework, le composant Zend_Session ajoute de la valeur au
système de session de PHP notamment une manipulation objet.
C'est générallement une bonne pratique que de démarrer sa session en bootstrap, cependant
la première création d'un objet Zend_Session_Namespace démarrera la session par défaut.
Comme vous le remarquez, les options utilisées sont les mêmes que celles que reconnait ext/
session (l'extension session de PHP). Le chemin de stockage des session par exemple. Les
fichiers ini peuvent utiliser des constantes, nous réutilisons APPLICATION_PATH pour calculer
le chemin relatif vers un dossier arbitraire sensé stocker les sessions.
La plupart des composants de Zend Framework utilisant les sessions n'ont rien besoin de plus.
Dès lors, vous pouvez utiliser un composant faisant appel à la session, ou manipuler la session
vous-même au travers d'un ou plusieurs objets Zend_Session_Namespace.
Zend_Session_Namespace est une classe qui guide ses données vers $_SESSION. La
classe s'appelle Zend_Session_Namespace car elle crée des espaces de noms au sein de
$_SESSION, autorisant plusieurs composants ou objets à stocker des valeurs sans se marcher
dessus. Nous allons voir dans l'exemple qui suit comment créer un simple compteur de session
qui commence à 1000 et se remet à zéro après 1999.
$mysession = Zend_Session_Namespace('mysession');
if (!isset($mysession->counter)) {
$mysession->counter = 1000;
} else {
$mysession->counter++;
}
Comme vous le remarquez, l'objet de session utilise les méthodes magiques __get, __set,
__isset, et __unset pour proposer une API intuitive. Les informations stockées dans notre
exemple le sont en réalité dans $_SESSION['mysession']['counter'].
73
Bien démarrer avec Zend_Session,
Zend_Auth, et Zend_Acl
resources.session.saveHandler.class = "Zend_Session_SaveHandler_DbTable"
resources.session.saveHandler.options.name = "session"
resources.session.saveHandler.options.primary.session_id = "session_id"
resources.session.saveHandler.options.primary.save_path = "save_path"
resources.session.saveHandler.options.primary.name = "name"
resources.session.saveHandler.options.primaryAssignment.sessionId = "sessionId"
resources.session.saveHandler.options.primaryAssignment.sessionSavePath = "sessionSavePath"
resources.session.saveHandler.options.primaryAssignment.sessionName = "sessionName"
resources.session.saveHandler.options.modifiedColumn = "modified"
resources.session.saveHandler.options.dataColumn = "session_data"
resources.session.saveHandler.options.lifetimeColumn = "lifetime"
Un identifiant peut lui aussi être un "login" tout banal, mais pourquoi pas un numéro de membre,
une adresse email... le secret, lui, est donc souvent un mot de passe sous forme de chaine de
caractères.
Zend_Auth effectue deux tâches. D'abord elle doit récupérer un adaptateur d'authentification
afin de déclencher le processus d'authentification, puis si celui-ci est correct, elle doit faire
persister ces informations entre requêtes. Pour assurer cette persistance, Zend_Auth utilise un
Zend_Session_Namespace, mais en général vous n'aurez pas besoin d'agir sur cet objet.
C'est une table qui inclue des champs nom, password et aussi grain de sel. Le grain de sel est
utilisé pour améliorer la sécurité contre les attaques par force brute qui cibleraient l'alogithme de
hashage du mot de passe. Plus d'informations sur le grain de sel.
74
Bien démarrer avec Zend_Session,
Zend_Auth, et Zend_Acl
// localisé à application/forms/Auth/Login.php
$this->addElement(
'text', 'username', array(
'label' => 'Username:',
'required' => true,
'filters' => array('StringTrim'),
));
}
}
if ($loginForm->isValid()) {
$adapter->setIdentity($loginForm->getValue('username'));
$adapter->setCredential($loginForm->getValue('password'));
75
Bien démarrer avec Zend_Session,
Zend_Auth, et Zend_Acl
$result = $auth->authenticate($adapter);
if ($result->isValid()) {
$this->_helper->FlashMessenger('Successful Login');
$this->redirect('/');
return;
}
$this->view->loginForm = $loginForm;
Le script de vue est quant à lui enfantin, il sera logé dans application/views/scripts/
auth/login.phtml:
$this->form->setAction($this->url());
echo $this->form;
Et voila! Avec ce scénario de base, vous pouvez étendre les possibilités et répondre à vos
besoins précis. Tous les adaptateurs Zend_Auth se trouvent décrits dans le guide de réference.
Dans Zend Framework, le composant Zend_Acl vous propose de créer ces trois entités
remarquables, de les associer et de les interroger dans le futur.
Nous allons faire une démonstration avec un modèle simple. On peut le relier avec notre système
d'ACL en implémentant Zend_Acl_Role_Interface. La méthode getRoleId() retournera
"guest" lorsque l'ID est inconnu, ou l'ID du rôle lorsque celui-ci aura été affecté. Cette valeur
76
Bien démarrer avec Zend_Session,
Zend_Auth, et Zend_Acl
peut provenir de n'importe où, probablement qu'elle proviendra d'une définition faite en base de
données.
return $this->_aclRoleId;
}
}
Le concept des utilisateurs ayant des rôles est simple à comprendre, mais l'application peut
consommer plusieurs modèles et en retrouver des "ressources" qui seront consommables par
les rôles. Nous utiliserons simplement des billets de blog comme ressources dans nos exemples,
et comme les ressources sont des objets, nous ferons en sorte que l'ID d'un billet blog soir
'blogPost', naturellement cette valeur peut être calculée dynamiquement en fonction du besoin.
Maintenant que nous avons au minimum un rôle et une ressource, définissons règles qui les
lient. Ces règles seront lues lorsque le système recevra une requête d'acl demandant ce qu'il
est possible de faire avec tel rôle, telle ressource et éventuellement tel privilège.
// ajout de ressources
$acl->addResource('blogPost');
Les règles ci-dessus sont très simples: deux rôles "guest"(invité) et "owner" (propriétaire), et une
ressource "blogPost"(billet). Les invités sont autorisés à voir les billets, les propriétaires peuvent
poster et publier des billets. Pour requêter le système, procédez alors comme suit:
77
Bien démarrer avec Zend_Session,
Zend_Auth, et Zend_Acl
Comme vous pouvez le voir le système répond comme il faut dans la mesure où les invités
peuvent lire les billets mais seuls les propriétaires peuvent en ajouter. Cependant ce système
peut sembler manquer de dynamisme. Comment vérifier qu'un utilisateur spécifique est bien
propriétaire d'un billet spécifique avant de l'autoriser à le publier ? Autrement dit, on veut s'assurer
que seuls les propriétaires des billets peuvent publier ceux-ci, et pas ceux des autres.
C'est là qu'entrent en jeu les assertions. Les assertions sont des vérifications supplémentaires à
effectuer en même temps que la vérification de la règle d'acl. Ce sont des objets. Utilisons notre
exemple avec une assertion:
// vérifions que qui que ce soit, il modifie uniquement ses propres billets
if ($user->id != null && $blogPost->ownerUserId == $user->id) {
78
Bien démarrer avec Zend_Session,
Zend_Auth, et Zend_Acl
return true;
} else {
return false;
}
}
}
Pour faire intervenir l'assertion dans les ACL, nous les utilisons comme ceci:
// remplacez ceci:
// $acl->allow('owner', 'blogPost', 'publish');
// par cela:
$acl->allow('owner',
'blogPost',
'publish',
new OwnerCanPublishBlogPostAssertion());
Maintenant, dès que l'ACL est consultée pour savoir si un propriétaire peut publier un billet, cette
assertion sera vérifiée. Elle s'assure que sauf si le rôle est 'publisher' le propriétaire a bien écrit le
billet. Dans cet exemple, nous vérifions pour savoir si l'attribut ownerUserId du billet correspond
à l'identifiant de l'utilisateur en question.
79
Bien démarrer avec
Zend_Search_Lucene
1. Introduction à Zend_Search_Lucene
Le composant Zend_Search_Lucene est prévu pour fournir une solution de recherche full-text
1
prête à l'emploi. Il ne nécessite aucunes extensions PHP ni que des logiciels supplémentaires
soient installés, et peut être utilisé tout de suite après l'installation du Framework Zend.
Chaque document est un ensemble de champs : paires <nom, valeur> où le nom et la valeur sont
2
des chaînes UTF-8 . N'importe quel sous ensemble de champs de document peut être marqué
comme "indexé" pour inclure des données de champ durant le processus d'indexation de texte.
Les valeurs de champs peuvent être indexées segmentées durant l'indexation. Si un champ
n'est pas segmenté, alors la valeur du champ est stockée comme un seul terme ; autrement,
l'analyseur courant est utilisé pour la segmentation.
Les valeurs de champs sont stockés optionnellement au sein de l'index. Ceci permet aux
données originale du champ d'être récupérée pendant la recherche. C'est le seul moyen
d'associer les résultats de recherche avec les données originales (l'ID interne du document peut
avoir changé après une optimisation d'index ou une auto-optimisation).
Ce qui doit être gardé en mémoire, c'est que l'index Lucene n'est pas une base de données. Il ne
fournit pas un mécanisme de sauvegarde de l'index à l'exception de la sauvegarde du répertoire
du système de fichier. Il ne fournit pas de mécanisme transactionnel bien que soient supportés
la mise à jour concurrente d'index ainsi que que la mise à jour et la lecture concurrente. Il n'est
pas comparable aux bases de données en terme de vitesse de récupération de données.
80
Bien démarrer avec
Zend_Search_Lucene
• De ne pas utiliser l'index Lucene comme du stockage car cela réduirait les performance de
récupération de résultat de recherche. Stocker uniquement les identifiants de documents
(chemin de documents, URLs, identifiant unique de base données) et associer les données
au sein de l'index. Ex. titre, annotation, categorie, information de langue, avatar. (Note : un
champs peut être inclu dans l'indexation, mais pas stocké, ou stocké, mais pas indexé).
• D'écrire des fonctionalités qui peuvent reconstruire intégralement l'index, si il a été corrompu
pour une raison ou pour une autre.
Les documents individuels dans l'index peuvent avoir des ensemble de champs totalement
différents. Le même champ dans différents documents n'a pas besoin d'avoir les mêmes
attributs. Ex. un champs peu être indexé pour l'un des documents mais sauté pour l'indexation
d'un autre. Le même principe s'applique au stockage, à la segmentation, ou traitement de valeur
de champ comme chaîne binaire.
Un index est stocké dans un ensemble de fichier au sein d'un seul répertoire.
Un index est un ensemble indépendant de segments dans lesquels sont stockées des
informations au sujet d'un sous-ensemble de documents indexés. Chaque segment a son propre
dictionnaire de terme, son propre index de dictionnaire de terme, et son propre stockage de
3
document (valeur de champ stocké) . Toutes les informations de segments sont stockées dans
un fichier _xxxxx.cfs, où xxxxx est le nom d'un segment.
Dès qu'un fichier de segment d'index est créé, il ne peut être mis à jour. De nouveaux documents
sont ajoutés à de nouveaux segments. Les documents supprimés sont seulement marqués
comme supprimés dans un fichier facultatif <segmentname>.del.
D'un autre coté, utiliser plusieurs segments (avoir un document par segment est un cas
exceptionnel) augmente le temps de recherche :
• La récupération d'un terme depuis le dictionnaire est effectué pour chaque segment ;
• Le dictionnaire de terme de l'index est préchargé pour chaque segment (ce processus occupe
la plupart du temps de recherche pour de simples requêtes et nécessite aussi de la mémoire
supplémentaire).
81
Bien démarrer avec
Zend_Search_Lucene
La mise à jour de la liste de segments s'effectue de manière atomique. Ceci donne la capacité
d'ajouter de nouveaux documents simultanément, d'effectuer des optimisations d'index, et de
chercher à travers l'index.
• MaxBufferedDocs (Le nombre minimal de documents requis avant que les documents mis en
mémoire tampon soit écrits dans un nouveau segment) ;
• MaxMergeDocs (Le plus grand nombre de documents fusionnés par une opération
d'optimisation) ; et
• MergeFactor (qui détermine la fréquence à laquelle les indices de segments sont fusionnés
par les opérations d'auto-optimisation).
Si nous ajoutons un documents par exécution de script, MaxBufferedDocs n'est finalement pas
utilisé (seul un segment avec un seul document est créé à la fin de l'exécution du script, moment
auquel démarre le processus d'auto-optimisation).
$index = Zend_Search_Lucene::create($indexPath);
$index = Zend_Search_Lucene::open($indexPath);
4. Indexation
L'indexation s'effectue en ajoutant un nouveau document à un index existant ou à un nouvel
index :
$index->addDocument($doc);
La seconde méthode est de le charger depuis un fichier HTML ou Microsoft Office 2007 :
82
Bien démarrer avec
Zend_Search_Lucene
$doc = Zend_Search_Lucene_Document_Html::loadHTML($htmlString);
$doc = Zend_Search_Lucene_Document_Docx::loadDocxFile($path);
$doc = Zend_Search_Lucene_Document_Pptx::loadPptFile($path);
$doc = Zend_Search_Lucene_Document_Xlsx::loadXlsxFile($path);
Si un document est chargé depuis l'un des formats supportés, il peut quand même être étendu
manuellement avec des champs définis par l'utilisateur.
Vous pourriez avoir besoin d'une configuration d'indexation à la demande (quelque chose
comme le système OLTP). Sur de test systèmes, vous ajoutez généralement un document par
requête utilisateur. De cette manière, l'option MaxBufferedDocs n'affectera pas le système. D'un
autre coté, MaxMergeDocs est vraiment utile, car il vous permet de limiter le temps d'exécution
maximum du script. MergeFactor doit être définis par une valeur qui conserve un équilibre
entre le temps moyen d'indexation (il est aussi affecté par temps d'optimisation moyen) et les
performance de recherche (le niveau d'optimisation dépend du nombre de segments).
Si vous allez surtout effectuer des mises à jour d'index par lot, votre configuration devrait
utiliser une option MaxBufferedDocs définis à la valeur maximum supporté par la quantité de
mémoire disponible. MaxMergeDocs et MergeFactor doivent être définis à des valeurs réduisant
5
au maximum le recours à l'auto-optimisation . Les optimisations complètes d'index doivent être
appliquées après l'indexation.
$index->optimize();
Dans certaines configuration, il est plus efficace d'effectuer une série de mise à jour de l'index en
organisant une file de requête de mise à jour et de traiter plusieurs requête de mise à jour dans
une seule exécution de script. Ceci réduit la charge d'ouverture de l'index et permet d'utiliser le
tampon de document de l'index.
5. Recherche
La recherche s'effectue en utilisant la méthode find() :
$hits = $index->find($query);
foreach ($hits as $hit) {
printf("%d %f %s\n", $hit->id, $hit->score, $hit->title);
}
Cet exemple montre l'utilisation de deux propriétés particulières des résultats de recherche - id
et score.
id est un identifiant interne de document utilisé dans un index Lucene. Il peut être utilisé pour un
certains nombre d'opérations, tels que la suppression d'un document de l'index :
5
Une limite additionnelle est le nombre maximum de gestionnaire de fichiers supporter par le système d'exploitation pour les opérations
concurrente d'ouverture
83
Bien démarrer avec
Zend_Search_Lucene
$index->delete($id);
$doc = $index->getDocument($id);
Le champ score est un score de résultat. Les résultats de recherche sont triés par score
(meilleurs résultats en premier).
Il est aussi possible de trier l'ensemble de résultats en fonction d'une valeur de champ spécifique.
Voir la documentation Zend_Search_Lucene pour plus de détails sur cette possibilité.
Cette exemple montre aussi la possibilité d'accéder à des champs stockés (ex : $hit->title).
Les champs de documents stockés sont chargés lors du premier accès à l'une des propriété du
résultat autre que id ou score, et la valeur du champ correspondant est retournée.
Ceci cause une ambiguïté car les documents ont leurs propres champs id ou score par
conséquence, il n'est pas recommendé d'utiliser ces noms de champs dans les documents
stockés. Cependant, ils peuvent être accédé via la méthode : getDocument()
$id = $hit->getDocument()->id;
$score = $hit->getDocument()->score;
6. Requêtes supportées
Zend_Search_Lucene et Lucene Java supportent un langage de requête puissant. Il permet
de rechercher des termes individuels, des phrases, des ensembles de termes ; en utilisant des
jokers ou des recherches floues ; en combinant des requêtes à l'aide d'opérateurs booléens et
ainsi de suite.
Une description détaillée du langage de requête peut être trouvé dans la documentation du
composant Zend_Search_Lucene.
hello
84
Bien démarrer avec
Zend_Search_Lucene
hello dolly
Recherche deux mots. Les deux mots sont facultatifs, au moins l'un des deux doit être
présent dans le résultat
+hello dolly
+hello -dolly
Recherche avec deux mots ; "hello" est requis, 'dolly' est interdit. En d'autres termes, si le
document contient "hello", mais contient aussi le mot "dolly", il ne sera pas retourné dans
l'ensemble de résultats.
"hello dolly"
Recherche la phrase "The Right Way" au sein du champ title et le mot "go" dans la propriété
text.
Exemple 18. Effectuer des recherches dans des champs en particulier aussi bien
que dans le document complet
Recherche la phrase "The Right Way" dans la propriété title et le mot "go" dans tous les
champs du document.
Exemple 19. Effectuer des recherches dans des champs en particulier aussi bien
que dans le document complet (Alternatif)
title:Do it right
Recherche le mot "Do" dans la propriété title et les mots "it" and "right" dans tous les champs ;
si l'un d'entre eux correspond, le document correspondra à un résultat de recherche.
85
Bien démarrer avec
Zend_Search_Lucene
te?t
Recherche les mots correspondants au motif "te?t", où "?" est n'importe quel caractère
unique.
test*
Recherche les mots correspondants au motif "test*", où "*" est n'importe quelle séquence
de 0 caractère ou plus.
mod_date:[20020101 TO 20030101]
title:{Aida to Carmen}
roam~
Requête booléenne.
Toutes les requêtes supportées peuvent être construites via l'API de construction de requêtes
de Zend_Search_Lucene. De plus l'analyse et la construction de requêtes peuvent être
combinées :
$userQuery = Zend_Search_Lucene_Search_QueryParser::parse($queryStr);
$query = new Zend_Search_Lucene_Search_Query_Boolean();
$query->addSubquery($userQuery, true /* required */);
$query->addSubquery($constructedQuery, true /* required */);
86
Bien démarrer avec
Zend_Search_Lucene
Ne récupérez pas tous les documents si vous avez seulement besoin de travailler sur une partie.
Parcourez les résultats de recherche et stockez l'ID du document (et éventuellement son score)
afin de récupérer les documents depuis l'index pendant la prochaine exécution du script.
$cacheId = md5($query);
if (!$resultSet = $cache->load($cacheId)) {
$hits = $index->find($query);
$resultSet = array();
foreach ($hits as $hit) {
$resultSetEntry = array();
$resultSetEntry['id'] = $hit->id;
$resultSetEntry['score'] = $hit->score;
$resultSet[] = $resultSetEntry;
}
$cache->save($resultSet, $cacheId);
}
$publishedResultSet = array();
for ($resultId = $startId; $resultId < $endId; $resultId++) {
$publishedResultSet[$resultId] = array(
'id' => $resultSet[$resultId]['id'],
'score' => $resultSet[$resultId]['score'],
'doc' => $index->getDocument($resultSet[$resultId]['id']),
);
}
87
Bien démarrer avec Zend_Paginator
1. Introduction
Let's say you're creating a blogging application that will be home to your vast collection of blog
posts. There is a good chance that you do not want all of your blog posts to appear on one
single page when someone visits your blog. An obvious solution would be to only display a
small number of blog posts on the screen at a time, and allow the user to browse through the
different pages, much like your favorite search engine shows you the result of your search query.
Zend_Paginator is designed to help you achieve the goal of dividing collections of data in
smaller, more manageable sets more easily, with more consistency, and with less duplicate code.
Zend_Paginator uses Adapters to support various data sources and ScrollingStyles to support
various methods of showing the user which pages are available. In later sections of this text we
will have a closer look at what these things are and how they can help you to make the most
out of Zend_Paginator.
Before going in-depth, we will have a look at some simple examples first. After these simple
examples, we will see how Zend_Paginator supports the most common use-case; paginating
database results.
This introduction has given you a quick overview of Zend_Paginator. To get started and to
have a look at some code snippets, let's have a look at some simple examples.
2. Simple Examples
In this first example we won't do anything spectacular, but hopefully it will give you a good idea
of what Zend_Paginator is designed to do. Let's say we have an array called $data with the
numbers 1 to 100 in it, which we want to divide over a number of pages. We can use the static
factory() method in the Zend_Paginator class to get a Zend_Paginator object with our
array in it.
We're already almost done! The $paginator variable now contains a reference to the Paginator
object. By default it is setup to display 10 items per page. To display the items for the currently
active page, all you need to do is iterate over the Paginator object with a foreach loop. The
currently active page defaults to the first page if it's not explicitly specified. We will see how you
can select a specific page later on. The snippet below will display an unordered list containing
the numbers 1 to 10, which are the numbers on the first page.
?><ul><?php
88
Bien démarrer avec Zend_Paginator
?></ul>
Now let's try and render the items on the second page. You can use the
setCurrentPageNumber() method to select which page you want to view.
?><ul><?php
?></ul>
As expected, this little snippet will render an unordered list with the numbers 11 to 20 in it.
These simple examples demonstrate a small portion of what can be achieved with
Zend_Paginator. However, a real application rarely reads its data from a plain array, so the
next section is dedicated to showing you how you can use Paginator to paginate the results of a
database query. Before reading on, make sure you're familiar with the way Zend_Db_Select
works!
In the database examples we will look at a table with blog posts called 'posts'. The 'posts' table
has four columns: id, title, body, date_created. Let's dive right in and have a look at a simple
example.
?><ul><?php
// Render each the title of each post for the current page in a list-item
foreach ($paginator as $item) {
echo '<li>' . $item->title . '</li>';
}
89
Bien démarrer avec Zend_Paginator
?></ul>
As you can see, this example is not that different from the previous one. The only difference is
that you pass a Zend_Db_Select object to the Paginator's factory() method, rather than
an array. For more details on how the database adapter makes sure that your query is being
executed efficiently, see the Zend_Paginator chapter in the reference manual on the DbSelect
and DbTableSelect adapters.
La vue partiel est un bout de vue qui rend juste les contrôles de la pagination comme les boutons
suivant et précédent. Le design de la vue partielle est libre, il vous faudra simplement un objet
Zend_View. Commencez donc par créer un nouveau script de vue dans le dossier des scripts
de vue. Vous pouvez l'appeler comme vous voulez, nous l'appellerons "controls.phtml" de notre
coté. Le manuel comporte des exemples de tels scripts, en voici un.
90
Bien démarrer avec Zend_Paginator
Il faut maintenant indiquer à Zend_Paginator la vue partielle à utiliser. Ajoutez ceci à votre
bootstrap:
Zend_View_Helper_PaginationControl::setDefaultViewPartial('controls.phtml');
La dernière étape est la plus simple. Passez un objet Paginator à un script de vue (PAS
'controls.phtml'!). Ensuite, demandez simplement l'affichage de l'objet Paginator lui-même. Ceci
va faire intervenir l'aide de vue PaginationControl. Dans l'exemple qui suit, l'objet Paginator a
été affecté comme variable de vue 'paginator'. Ne vous inquiétez pas si vous ne comprenez pas
totalement le fonctionnement, les sections suivantes le détaillent.
Pour décider quels numéros de page afficher, le paginateur utilise des styles de défilement. Le
style par défaut est "Sliding", qui ressemble à la présentation des résultats de Yahoo! Un style
ressemblant à Google est "Elastic". Le style par défaut se règle au moyen de la méthode statique
setDefaultScrollingStyle(), ou lors du rendu du paginateur dans le script de vue mais
ceci nécessite un appel manuel à l'aide de vue.
In the following examples we will ignore the best practice implementation of using a Service
Layer to keep the example simple and easier to understand. Once you get familiar with using
Service Layers, it should be easy to see how Paginator can fit in with the best practice approach.
Lets start with the controller. The sample application is simple, and we'll just put everything in
the IndexController and the IndexAction. Again, this is for demonstration purposes only. A real
application should not use controllers in this manner.
// Create a select object which fetches blog posts, sorted decending by date of crea
$select = $db->select()->from('posts')->sort('date_created DESC');
// Read the current page number from the request. Default to 1 if no explicit page n
91
Bien démarrer avec Zend_Paginator
$paginator->setCurrentPageNumber($this->_getParam('page', 1));
The following view script is the index.phtml view script for the IndexController's indexAction. The
view script can be kept simple. We're assuming the use of the default ScrollingStyle.
<ul>
<?php
// Render each the title of each post for the current page in a list-item
foreach ($this->paginator as $item) {
echo '<li>' . $item->title . '</li>';
}
?>
</ul>
<?php echo $this->paginator; ?>
Now navigate to your project's index and see Paginator in action. What we have discussed in
this tutorial is just the tip of the iceberg. The reference manual and API documentation can tell
you more about what you can do with Zend_Paginator.
92
Partie III. Guide de
référence Zend Framework
Table des matières
Zend_Acl ..................................................................................................................... 124
1. Introduction ...................................................................................................... 124
1.1. A propos des ressources ....................................................................... 124
1.2. A propos des rôles ................................................................................ 124
1.3. Créer la Liste de Contrôle d'Accès ......................................................... 125
1.4. Registre des rôles ................................................................................. 126
1.5. Définir les Contrôles d'Accès .................................................................. 127
1.6. Interroger les ACL ................................................................................. 127
2. Affiner les Contrôles d'Accès ............................................................................ 128
2.1. Mieux définir les Contrôles d'Accès ........................................................ 128
2.2. Retirer les Contrôles d'Accès ................................................................. 130
3. Utilisation avancée ........................................................................................... 130
3.1. Rendre les données ACL persistantes .................................................... 130
3.2. Écrire des règles ACL conditionnelles avec des assertions ....................... 131
Zend_Amf .................................................................................................................... 132
1. Introduction ...................................................................................................... 132
2. Zend_Amf_Server ............................................................................................ 132
2.1. Connecting to the Server from Flex ........................................................ 134
2.2. Error Handling ....................................................................................... 136
2.3. AMF Responses .................................................................................... 136
2.4. Typed Objects ....................................................................................... 136
2.5. Resources ............................................................................................. 138
2.6. Connecting to the Server from Flash ...................................................... 138
2.7. Authentication ....................................................................................... 140
Zend_Application .......................................................................................................... 142
1. Introduction ...................................................................................................... 142
2. Zend_Application démarrage rapide .................................................................. 142
2.1. Utiliser Zend_Tool ................................................................................. 142
2.2. Ajouter Zend_Application à votre existant ................................................ 144
2.3. Ajouter et créer des ressources .............................................................. 145
2.4. Aller plus loin avec Zend_Application ...................................................... 147
3. Théorie générale .............................................................................................. 147
3.1. Bootstrapping ........................................................................................ 148
3.2. Resource Plugins .................................................................................. 152
4. Exemples ......................................................................................................... 153
5. Fonctionnalités principales ................................................................................ 156
5.1. Zend_Application ................................................................................... 156
5.2. Zend_Application_Bootstrap_Bootstrapper .............................................. 160
5.3. Zend_Application_Bootstrap_ResourceBootstrapper ................................ 161
5.4. Zend_Application_Bootstrap_BootstrapAbstract ....................................... 162
5.5. Zend_Application_Bootstrap_Bootstrap ................................................... 165
5.6. Zend_Application_Resource_Resource ................................................... 166
5.7. Zend_Application_Resource_ResourceAbstract ....................................... 166
6. Plugins de ressources disponibles .................................................................... 168
6.1. Zend_Application_Resource_Cachemanager ........................................... 168
6.2. Zend_Application_Resource_Db ............................................................. 169
6.3. Zend_Application_Resource_Frontcontroller ............................................ 170
6.4. Zend_Application_Resource_Layout ....................................................... 171
6.5. Zend_Application_Resource_Locale ....................................................... 171
6.6. Zend_Application_Resource_Log ............................................................ 172
6.7. Zend_Application_Resource_Multidb ....................................................... 173
94
Guide de référence
Zend Framework
95
Guide de référence
Zend Framework
96
Guide de référence
Zend Framework
97
Guide de référence
Zend Framework
98
Guide de référence
Zend Framework
99
Guide de référence
Zend Framework
100
Guide de référence
Zend Framework
5.
Consommer un flux Atom ................................................................................. 576
6.
Consommer une entrée Atom particulière .......................................................... 577
7.
Modifier la structure du flux ou des entrées ....................................................... 577
8.
Classes personnalisées pour les flux et entrées ................................................. 578
9.
Zend_Feed_Reader .......................................................................................... 580
9.1. Introduction ........................................................................................... 580
9.2. Importing Feeds .................................................................................... 580
9.3. Retrieving Underlying Feed and Entry Sources ........................................ 581
9.4. Cache Support and Intelligent Requests ................................................. 582
9.5. Locating Feed URIs from Websites ........................................................ 583
9.6. Attribute Collections ............................................................................... 584
9.7. Retrieving Feed Information ................................................................... 585
9.8. Retrieving Entry/Item Information ............................................................ 588
9.9. Extending Feed and Entry APIs ............................................................. 591
10. Zend_Feed_Writer .......................................................................................... 595
10.1. Introduction ......................................................................................... 595
10.2. Architecture ......................................................................................... 595
10.3. Getting Started .................................................................................... 596
10.4. Setting Feed Data Points ..................................................................... 597
10.5. Setting Entry Data Points ..................................................................... 599
11. Zend_Feed_Pubsubhubbub ............................................................................ 601
11.1. What is Pubsubhubbub? ...................................................................... 601
11.2. Architecture ......................................................................................... 602
11.3. Zend_Feed_Pubsubhubbub_Publisher .................................................. 602
11.4. Zend_Feed_Pubsubhubbub_Subscriber ................................................ 603
Zend_File .................................................................................................................... 611
1. Zend_File_Transfer .......................................................................................... 611
1.1. Adaptateurs supportés par Zend_File_Transfer ....................................... 612
1.2. Options de Zend_File_Transfer .............................................................. 612
1.3. Vérification des fichiers .......................................................................... 613
1.4. Informations complémentaires sur les fichiers .......................................... 613
1.5. Progress for file uploads ........................................................................ 615
2. Validateurs pour Zend_File_Transfer ................................................................. 617
2.1. Utiliser les validateurs avec Zend_File_Transfer ...................................... 618
2.2. Validateur Count ................................................................................... 620
2.3. Validateur Crc32 ................................................................................... 621
2.4. Validateur ExcludeExtension .................................................................. 621
2.5. Validateur ExcludeMimeType ................................................................. 622
2.6. Validateur Exists ................................................................................... 623
2.7. Validateur Extension .............................................................................. 623
2.8. Validateur FilesSize ............................................................................... 624
2.9. Validateur ImageSize ............................................................................. 625
2.10. Validateur IsCompressed ..................................................................... 626
2.11. Validateur IsImage ............................................................................... 626
2.12. Validateur Hash ................................................................................... 626
2.13. Validateur Md5 .................................................................................... 627
2.14. Validateur MimeType ........................................................................... 627
2.15. Validateur NotExists ............................................................................. 629
2.16. Validateur Sha1 ................................................................................... 629
2.17. Validateur Size .................................................................................... 629
2.18. Validateur WordCount .......................................................................... 630
3. Filtres pour Zend_File_Transfer ........................................................................ 631
3.1. Utiliser les filtres avec Zend_File_Transfer .............................................. 631
3.2. Filtre Decrypt ........................................................................................ 632
101
Guide de référence
Zend Framework
102
Guide de référence
Zend Framework
103
Guide de référence
Zend Framework
104
Guide de référence
Zend Framework
105
Guide de référence
Zend Framework
106
Guide de référence
Zend Framework
107
Guide de référence
Zend Framework
108
Guide de référence
Zend Framework
109
Guide de référence
Zend Framework
110
Guide de référence
Zend Framework
111
Guide de référence
Zend Framework
112
Guide de référence
Zend Framework
113
Guide de référence
Zend Framework
114
Guide de référence
Zend Framework
115
Guide de référence
Zend Framework
116
Guide de référence
Zend Framework
117
Guide de référence
Zend Framework
118
Guide de référence
Zend Framework
119
Guide de référence
Zend Framework
120
Guide de référence
Zend Framework
121
Guide de référence
Zend Framework
122
Guide de référence
Zend Framework
123
Zend_Acl
1. Introduction
Zend_Acl fournit une implémentation légère et flexible de listes de contrôle d'accès (ACL) pour
la gestion de privilèges. En général, une application peut utiliser ces ACL pour contrôler l'accès
à certains objets par d'autres objets demandeurs.
Dit simplement, les rôles demandent l'accès à des ressources. Par exemple, si une personne
demande l'accès à une voiture, alors la personne est le rôle demandeur et la voiture est la
ressource, puisque l'accès à la voiture est soumis à un contrôle.
Grâce à la définition et à la mise en oeuvre d'une ACL, une application peut contrôler comment
les objets demandeurs (rôles) reçoivent l'accès (ou non) à des objets protégés (ressources).
Zend_Acl fournit une structure en arbre à laquelle plusieurs ressources (ou "zone sous contrôle
d'accès") peuvent être ajoutées. Puisque les ressources sont sauvées dans cet arbre, elles
peuvent être organisées du général (via la racine de l'arbre) jusqu'au particulier (via les feuilles
de l'arbre). Les requêtes envers une ressource spécifique vont automatiquement entraîner la
recherche de règles sur ses parents au sein de la structure hiérarchique des ressources, ce qui
permet un héritage simple des règles. Par exemple, si une règle par défaut doit être appliquée
à tous les bâtiments d'une ville, on pourra simplement assigner la règle à la ville elle-même, au
lieu de la répéter à tous les bâtiments. Mais certains bâtiments peuvent nécessiter des règles
spécifiques, et ceci peut se faire aisément avec Zend_Acl en assignant les règles nécessaires
à chaque bâtiment de la ville qui nécessite une exception. Une ressource peut hériter d'un seul
parent ressource, qui hérite lui même de son propre parent, et ainsi de suite.
Zend_Acl supporte aussi des privilèges pour chaque ressource (par exemple : "créer", "lire",
"modifier", "supprimer"), et le développeur peut assigner des règles qui affectent tous les
privilèges ou seuls certains privilèges d'une ressource.
Dans Zend_Acl, un rôle peut hériter de un ou plusieurs rôles. Ceci permet de supporter
l'héritage de règles à travers plusieurs rôles. Par exemple, un rôle utilisateur, comme "Éric", peut
124
Zend_Acl
Bien que la possibilité d'hériter de plusieurs rôles soit très utile, l'héritage multiple introduit aussi
un certain degré de complexité. L'exemple ci-dessous illustre l'ambiguïté et la manière dont
Zend_Acl la résout.
Le code ci-dessous définit trois rôles de base - "guest", "member", et "admin" - desquels
d'autres rôles peuvent hériter. Ensuite, un rôle identifié par "someUser" est créé et hérite des
trois autres rôles. L'ordre selon lequel ces rôles apparaissent dans le tableau $parents est
important. Lorsque cela est nécessaire Zend_Acl recherche les règles d'accès définies non
seulement pour le rôle demandé (ici "someUser"), mais aussi pour les autres rôles desquels
le rôle recherché hérite (ici "guest", "member", et "admin") :
$acl->addRole(new Zend_Acl_Role('guest'))
->addRole(new Zend_Acl_Role('member'))
->addRole(new Zend_Acl_Role('admin'));
$acl->add(new Zend_Acl_Resource('someResource'));
$acl->deny('invite', 'someResource');
$acl->allow('membre', 'someResource');
Si Zend_Acl continuait à examiner toutes les règles de tous les rôles parents, il trouverait
que "someResource" est interdit d'accès à "someResource". Ceci introduit une ambiguïté
puisque maintenant "someUser" est à la fois autorisé et interdit d'accès à "someResource",
puisqu'il hérite de règles opposées de ses différents parents.
Zend_Acl résout cette ambiguïté en arrêtant la recherche de règles d'accès dès qu'une
première règle est découverte. Dans notre exemple, puisque le rôle "member" est examiné
avant le rôle "guest", le résultat devrait afficher "autorisé".
125
Zend_Acl
pour une Gestion de Contenus (CMS) qui comporte plusieurs niveaux de groupes au sein d'une
grande variété de zones. Pour créer un nouvel objet ACL, nous créons une nouvelle instance
d'ACL sans paramètres :
Pour cet exemple, Zend_Acl_Role est utilisé, mais n'importe quel objet qui implémente
Zend_Acl_Role_Interface est acceptable. Ces groupes peuvent être ajoutés au registre
des rôles comme suit :
126
Zend_Acl
$acl->addRole(new Zend_Acl_Role('administrateur'));
En conséquence, on peut définir un nombre assez complexe de règles avec un nombre minimal
de code. Pour définir les permissions comme définies ci-dessus :
/*
ce qui précède peut aussi être écrit :
$acl->allow('invité', null, 'voir');
*/
Les valeurs NULL dans les appels allow() ci-dessus sont utilisées pour indiquer que les règles
s'appliquent à toutes les ressources.
127
Zend_Acl
echo $acl->isAllowed('administrateur') ?
"autorisé" : "refusé";
// autorisé car administrateur est autorisé pour tout
Pour l'exemple du CMS, nous avons déterminé que bien que le groupe "Staff" couvre les besoins
de la plupart des utilisateurs, un groupe "Marketing" est nécessaire. Ce groupe doit avoir accès à
la newsletter et aux dernières news dans le CMS. Le groupe va recevoir la possibilité de publier
et d'archiver à la fois des newsletters et des news.
De plus, il a été demandé que le groupe "Staff" puisse voir les nouveaux textes, mais
pas les nouvelles news. Enfin, il devrait être impossible pour tout le monde (y compris les
administrateurs) d'archiver un contenu qui n'aurait une durée de vie que de 1 ou 2 jours.
En premier lieu, nous modifions le registre des rôles pour refléter ces changements. Nous avons
dit que le groupe "Marketing" a les même permissions de base que "Staff". Donc nous créons
"marketing" pour qu'il hérite des permissions de "staff".
Ensuite, notez que les contrôles d'accès plus haut font référence à des ressources (ex.
"newsletters", "dernières news", "annonces"). Maintenant, nous ajoutons ces Ressources :
128
Zend_Acl
// newsletter
$acl->addResource(new Zend_Acl_Resource('newsletter'));
// news
$acl->addResource(new Zend_Acl_Resource('news'));
// dernières news
$acl->addResource(new Zend_Acl_Resource('latest'), 'news');
// annonces
$acl->addResource(new Zend_Acl_Resource('announcement'), 'news');
Ensuite c'est simplement une manière de définir ces règles spécifiques sur les parties cibles de
l'ACL :
On peut maintenant interroger les ACL sur base des dernières modifications :
129
Zend_Acl
Les privilèges peuvent être modifiés de manière incrémentielle comme indiqué au dessus, mais
une valeur NULL pour les privilèges écrase ces modifications incrémentielles.
3. Utilisation avancée
3.1. Rendre les données ACL persistantes
Zend_Acl a été conçu pour ne pas nécessiter de technologie spécifique comme une base
de données ou un serveur de cache pour conserver les données ACL. Son implémentation
PHP permet de créer des outils d'administration basés sur Zend_Acl assez facilement. De
nombreuses situations nécessitent une certaine forme de maintenance ou de gestion des ACL,
et Zend_Acl fournit les méthodes pour définir et interroger les règles d'accès d'une application.
Le stockage des données ACL est dès lors laissé aux bons soins du développeur, dans la mesure
où les cas d'utilisation peuvent grandement varier d'un cas à l'autre. Puisque Zend_Acl est
sérialisable, les objets ACL peuvent être sérialisés avec la fonction serialize() de PHP, et le
résultat peut être stocké n'importe où le développeur le désire : fichier, base de donnée, cache.
130
Zend_Acl
Lorsqu'une classe d'assertion est disponible, le développeur doit fournir une instance de cette
classe lorsqu'il assigne une règle conditionnelle. Une règle qui est créée avec une assertion
s'applique uniquement dans les cas où l'assertion retourne une valeur TRUE.
Le code ci-dessus crée une règle conditionnelle qui autorise l'accès à tous les privilèges, sur
tout et pour tout le monde, sauf lorsque l'adresse IP de la requête fait partie de la liste noire.
Si une requête provient d'une adresse IP qui n'est pas considérée comme "propre", alors la
règle d'autorisation ne s'applique pas. Puisque la règle s'applique à tous les rôles, toutes les
Ressources, et tous les privilèges, une IP "sale" aboutira à un refus d'accès. Ceci constitue un
cas spécial, et il faut bien noter que tous les autres cas (donc, si un rôle, une ressource ou un
privilège est défini pour la règle), une assertion qui échoue aboutit à une règle qui ne s'applique
pas et ce sont alors les autres règles qui servent à déterminer si l'accès est autorisé ou non.
La méthode assert() d'un objet d'assertion reçoit l'ACL, le rôle, la ressource et le privilège
auquel une requête d'autorisation (c.-à-d., isAllowed()) s'applique, afin de fournir un contexte
à la classe d'assertion pour déterminer ses conditions lorsque cela est nécessaire.
131
Zend_Amf
1. Introduction
Zend_Amf fournit le support pour l' Action Message Format(AMF) d'Adobe, permettant la
communication entre le Flash Playerd'Adobe et PHP. De manière spécifique, il fournit une
implémentation serveur pour gérer les requêtes envoyées par le Flash Player au serveur et fait
correspondre ces requêtes à des objets, à des méthodes de classe et à des callbacks arbitraires.
La spécification AMF3est librement disponible, et sert de référence pour les types de messages
qui peuvent être envoyés entre le Flash Player et le serveur.
2. Zend_Amf_Server
Zend_Amf_Server provides an RPC-style server for handling requests made from the Adobe
Flash Player using the AMF protocol. Like all Zend Framework server classes, it follows the
SoapServer API, providing an easy to remember interface for creating servers.
132
Zend_Amf
Let's assume that you have created a class Foo with a variety of public methods. You may
create an AMF server using the following code:
You could also mix and match multiple classes and functions. When doing so, we suggest
namespacing each to ensure that no method name collisions occur; this can be done by
simply passing a second string argument to either addFunction() or setClass():
$server->addDirectory(dirname(__FILE__) .'/../services/');
$server->addDirectory(dirname(__FILE__) .'/../package/');
When calling remote services your source name can have underscore ("_") and dot (".")
directory delimiters. When an underscore is used PEAR and Zend Framework class naming
conventions will be respected. This means that if you call the service com_Foo_Bar the
server will look for the file Bar.php in the each of the included paths at com/Foo/Bar.php.
If the dot notation is used for your remote service such as com.Foo.Bar each included
path will have com/Foo/Bar.php append to the end to autoload Bar.php
All AMF requests sent to the script will then be handled by the server, and an AMF response
will be returned.
// Function to attach:
/**
133
Zend_Amf
// Attached class
class World
{
/**
* @param string $name
* @param string $greeting
* @return string
*/
public function hello($name, $greeting = 'Hello')
{
return $greeting . ', ' . $name;
}
}
Say, for instance, you have created your server and placed it in the server.php file in your
application root, and thus the URI is http://example.com/server.php. In this case, you
would modify your services-config.xml file to set the channel endpoint uri attribute to this
value.
If you have never created a service-config.xml file you can do so by opening your project
in your Navigator window. Right click on the project name and select 'properties'. In the Project
properties dialog go into 'Flex Build Path' menu, 'Library path' tab and be sure the 'rpc.swc' file
is added to your projects path and Press Ok to close the window.
You will also need to tell the compiler to use the service-config.xml to find the
RemoteObject endpoint. To do this open your project properties panel again by right clicking
on the project folder from your Navigator and selecting properties. From the properties popup
select 'Flex Compiler' and add the string: -services "services-config.xml". Press Apply then
OK to return to update the option. What you have just done is told the Flex compiler to look to the
services-config.xml file for runtime variables that will be used by the RemotingObject class.
We now need to tell Flex which services configuration file to use for connecting to our remote
methods. For this reason create a new 'services-config.xml' file into your Flex project src
folder. To do this right click on the project folder and select 'new' 'File' which will popup a new
window. Select the project folder and then name the file 'services-config.xml' and press
finish.
Flex has created the new services-config.xml and has it open. Use the following example
text for your services-config.xml file. Make sure that you update your endpoint to match
that of your testing server. Make sure you save the file.
134
Zend_Amf
There are two key points in the example. First, but last in the listing, we create an AMF channel,
and specify the endpoint as the URL to our Zend_Amf_Server:
<channel-definition id="zend-endpoint"
<endpoint uri="http://example.com/server.php"
class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
Notice that we've given this channel an identifier, "zend-endpoint". The example create a service
destination that refers to this channel, assigning it an ID as well -- in this case "zend".
Within our Flex MXML files, we need to bind a RemoteObject to the service. In MXML, this might
be done as follows:
<mx:RemoteObject id="myservice"
fault="faultHandler(event)"
showBusyCursor="true"
destination="zend">
Here, we've defined a new remote object identified by "myservice" bound to the service
destination "zend" we defined in the services-config.xml file. We then call methods on it
in our ActionScript by simply calling "myservice.<method>". As an example:
myservice.hello("Wade");
myservice.world.hello("Wade");
For more information on Flex RemoteObject invocation, visit the Adobe Flex 3 Help site.
135
Zend_Amf
When in production mode, only the exception code will be returned. If you disable production
mode -- something that should be done for testing only -- most exception details will be returned:
the exception message, line, and backtrace will all be attached.
$server->setProduction(false);
$server->setProduction(true);
One area to be especially careful with is PHP errors themselves. When the display_errors INI
directive is enabled, any PHP errors for the current error reporting level are rendered directly
in the output -- potentially disrupting the AMF response payload. We suggest turning off the
display_errors directive in production to prevent such problems
In this example, we add a 'foo' MessageHeader with the value 'bar' to the response prior
to returning it.
$response = $server->handle();
$response->addAmfHeader(new Zend_Amf_Value_MessageHeader('foo', true, 'bar'))
echo $response;
Zend_Amf provides three methods for mapping ActionScript and PHP objects.
• First, you may create explicit bindings at the server level, using the setClassMap() method.
The first argument is the ActionScript class name, the second the PHP class name it maps to:
136
Zend_Amf
• Second, you can set the public property $_explicitType in your PHP class, with the value
representing the ActionScript class to map to:
class Contact
{
public $_explicitType = 'ContactVO';
}
• Third, in a similar vein, you may define the public method getASClassName() in your PHP
class; this method should return the appropriate ActionScript class:
class Contact
{
public function getASClassName()
{
return 'ContactVO';
}
}
Although we have created the ContactVO on the server we now need to make its corresponding
class in AS3 for the server object to be mapped to.
Right click on the src folder of the Flex project and select New -> ActionScript File. Name the
file ContactVO and press finish to see the new file. Copy the following code into the file to finish
creating the class.
package
{
[Bindable]
[RemoteClass(alias="ContactVO")]
public class ContactVO
{
public var id:int;
public var firstname:String;
public var lastname:String;
public var email:String;
public var mobile:String;
public function ProductVO():void {
}
}
}
The class is syntactically equivalent to the PHP of the same name. The variable names are
exactly the same and need to be in the same case to work properly. There are two unique AS3
meta tags in this class. The first is bindable which makes fire a change event when it is updated.
The second tag is the RemoteClass tag which defines that this class can have a remote object
mapped with the alias name in this case ContactVO. It is mandatory that this tag the value that
was set is the PHP class are strictly equivalent.
[Bindable]
private var myContact:ContactVO;
137
Zend_Amf
myContact = ContactVO(event.result);
}
The following result event from the service call is cast instantly onto the Flex ContactVO. Anything
that is bound to myContact will be updated with the returned ContactVO data.
2.5. Resources
Zend_Amf provides tools for mapping resource types returned by service classes into data
consumable by ActionScript.
In order to handle specific resource type, the user needs to create a plugin class named after the
resource name, with words capitalized and spaces removed (so, resource type "mysql result"
becomes MysqlResult), with some prefix, e.g. My_MysqlResult. This class should implement
one method, parse(), receiving one argument - the resource - and returning the value that
should be sent to ActionScript. The class should be located in the file named after the last
component of the name, e.g. MysqlResult.php.
The directory containing the resource handling plugins should be registered with Zend_Amf type
loader:
Zend_Amf_Parse_TypeLoader::addResourceDirectory(
"My",
"application/library/resources/My"
);
For detailed discussion of loading plugins, please see the plugin loader section.
Default directory for Zend_Amf resources is registered automatically and currently contains
handlers for "mysql result" and "stream" resources.
Trying to return unknown resource type (i.e., one for which no handler plugin exists) will result
in an exception.
138
Zend_Amf
flex. The following example can also be used from a Flex AS3 file. We will reuse the same
Zend_Amf_Server configuration along with the World class for our connection.
Open Flash CS and create and new Flash File (ActionScript 3). Name the document
ZendExample.fla and save the document into a folder that you will use for this example.
Create a new AS3 file in the same directory and call the file Main.as. Have both files open in your
editor. We are now going to connect the two files via the document class. Select ZendExample
and click on the stage. From the stage properties panel change the Document class to Main. This
links the Main.as ActionScript file with the user interface in ZendExample.fla. When you run
the Flash file ZendExample the Main.as class will now be run. Next we will add ActionScript
to make the AMF call.
We now are going to make a Main class so that we can send the data to the server and display
the result. Copy the following code into your Main.as file and then we will walk through the code
to describe what each element's role is.
package {
import flash.display.MovieClip;
import flash.events.*;
import flash.net.NetConnection;
import flash.net.Responder;
We first need to import two ActionScript libraries that perform the bulk of the work. The first
is NetConnection which acts like a by directional pipe between the client and the server. The
second is a Responder object which handles the return values from the server related to the
success or failure of the call.
import flash.net.NetConnection;
import flash.net.Responder;
In the class we need three variables to represent the NetConnection, Responder, and the
gateway URL to our Zend_Amf_Server installation.
139
Zend_Amf
In the Main constructor we create a responder and a new connection to the Zend_Amf_Server
endpoint. The responder defines two different methods for handling the response from the server.
For simplicity I have called these onResult and onFault.
In the onComplete function which is run as soon as the construct has completed we send the
data to the server. We need to add one more line that makes a call to the Zend_Amf_Server
World->hello function.
When we created the responder variable we defined an onResult and onFault function to handle
the response from the server. We added this function for the successful result from the server.
A successful event handler is run every time the connection is handled properly to the server.
The onFault function, is called if there was an invalid response from the server. This happens
when there is an error on the server, the URL to the server is invalid, the remote service or
method does not exist, and any other connection related issues.
Adding in the ActionScript to make the remoting connection is now complete. Running the
ZendExample file now makes a connection to Zend Amf. In review you have added the required
variables to open a connection to the remote server, defined what methods should be used when
your application receives a response from the server, and finally displayed the returned data to
output via trace().
2.7. Authentication
Zend_Amf_Server allows you to specify authentication and authorization hooks to control
access to the services. It is using the infrastructure provided by Zend_Auth and Zend_Acl
components.
The adapter should use properties _username and _password from the parent
Zend_Amf_Auth_Abstract class in order to authenticate. These values are set by the server
using setCredentials() method before call to authenticate() if the credentials are
received in the AMF request headers.
140
Zend_Amf
The identity returned by the adapter should be an object containing property role for the ACL
access control to work.
If the authentication result is not successful, the request is not proceseed further and failure
message is returned with the reasons for failure taken from the result.
$server->setAuth(new My_Amf_Auth());
If the ACL object is set, and the class being called defines initAcl() method, this method will
be called with the ACL object as an argument. The class then can create additional ACL rules
and return TRUE, or return FALSE if no access control is required for this class.
After ACL have been set up, the server will check if access is allowed with role set by the
authentication, resource being the class name (or NULL for function calls) and privilege being
the function name. If no authentication was provided, then if the anonymous role was defined,
it will be used, otherwise the access will be denied.
141
Zend_Application
1. Introduction
Zend_Application propose une interface de lancement (bootstrap) pour vos applications,
permettant la réutilisabilité des ressources utilisées, la vérification de dépendances et des
classes de bootstrap basées sur des modules. Ce composant s'occupe aussi de configurer
l'environnement PHP et propose l'autoload par défaut.
Si vous souhaitez utiliser Zend_Tool pour créer votre projet, continuez votre lecture. Si vous
ajoutez Zend_Application à un projet existant, vous devriez passer à la suite.
newproject
|-- application
| |-- Bootstrap.php
| |-- configs
| | `-- application.ini
| |-- controllers
| | |-- ErrorController.php
| | `-- IndexController.php
| |-- models
| `-- views
| |-- helpers
| `-- scripts
| |-- error
| | `-- error.phtml
| `-- index
| `-- index.phtml
|-- library
|-- public
| `-- index.php
`-- tests
|-- application
| `-- bootstrap.php
142
Zend_Application
|-- library
| `-- bootstrap.php
`-- phpunit.xml
[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
includePaths.library = APPLICATION_PATH "/../library"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
[staging : production]
[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
/** Zend_Application */
require_once 'Zend/Application.php';
143
Zend_Application
Créez maintenant votre configuration. Pour ce tutoriel, nous utilisons une syntaxe INI, bien sûr
une syntaxe XML ou PHP est utilisable aussi. Créez donc le fichier application/configs/
application.ini, et ajoutez lui ce contenu :
[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
includePaths.library = APPLICATION_PATH "/../library"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
[staging : production]
[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
144
Zend_Application
dirname(dirname(__FILE__)) . '/library',
get_include_path(),
)));
/** Zend_Application */
require_once 'Zend/Application.php';
Notez que l'environnement applicatif est défini dans une constante "APPLICATION_ENV". Nous
recommandons la spécification d'un tel paramètre dans la configuration générale du serveur web.
Pour Apache, vous pouvez utiliser .htaccess si votre serveur le permet. Nous recommandons
un fichier public/.htaccess avec le contenu suivant :
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
Apprenez mod_rewrite
Les règles de réécriture ci-dessus autorisent l'accès à tout fichier existant dans
l'hôte virtuel. S'il existe des fichiers que vous ne voulez pas exposer, utilisez des
règles plus restrictives. Le site web d'Apache vous permettra d'en apprendre plus
au sujet de mod_rewrite.
Nous allons voir ici comment créer et configurer des ressources. D'abord un layout, puis nous
personnaliserons un objet de vue.
Une ressource assez standard proposée par Zend_Application est "layout". Cette ressource
attend une configuration qu'elle fera suivre immédiatement à Zend_Layout.
Pour l'utiliser, vous devrez modifier votre fichier de configuration comme suit:
[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
145
Zend_Application
[staging : production]
[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
Maintenant passons à la vue. Nous voulons un DocType HTML et une valeur de titre par défaut
à utiliser dans la partie "head" du HTML. Nous pouvons ordonner ceci en éditant la classe
Bootstrap et en ajoutant une méthode.
// Ajoutons là au ViewRenderer
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(
'ViewRenderer'
);
$viewRenderer->setView($view);
146
Zend_Application
Cette méthode va être lancée automatiquement lors du bootstrap et configurera une vue.
3. Théorie générale
Monter une application MVC configurée et prête à être lancée requière de plus en plus de code
au fur et à mesure de l'ajout de fonctionnalités : monter une base de données, configurer la vue
et ses aides, les layouts, enregistrer des plugins, des aides d'action et bien plus encore...
Aussi, vous réutiliserez souvent le même code dans vos tests, dans une tâche cron ou encore
un service. Il est certes possible d'inclure le script de bootstrap dans de tels cas, mais souvent
des variables seront dépendantes de l'environnement. Par exemple, vous n'aurez pas besoin
de MVC dans une tâche cron, ou alors vous aurez juste besoin de l'accès à la base de données
dans un script de service.
• L'environnement courant
Les options de bootstrap incluent le chemin vers le fichier contenant la classe de bootstrap, et
optionnellement :
147
Zend_Application
Les options peuvent être un tableau, un objet Zend_Config, ou le chemin vers un fichier de
configuration.
3.1. Bootstrapping
Zend_Application's second area of responsibility is executing the application bootstrap.
Bootstraps minimally need to implement Zend_Application_Bootstrap_Bootstrapper,
which defines the following API:
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();
}
This API allows the bootstrap to accept the environment and configuration from the application
object, report the resources its responsible for bootstrapping, and then bootstrap and run the
application.
Besides this functionality, there are a number of other areas of concern you should familiarize
yourself with.
To bootstrap a single resource method, use the bootstrap() method, and pass it the name of
the resource. The name will be the method name minus the _init prefix.
To bootstrap several resource methods, pass an array of names. Too bootstrap all resource
methods, pass nothing.
148
Zend_Application
// ...
}
$bootstrap->bootstrap('foo');
$bootstrap->bootstrap(array('foo', 'bar'));
$bootstrap->bootstrap();
If your bootstrap should be capable of using resource plugins, you will need to implement
an additional interface, Zend_Application_Bootstrap_ResourceBootstrapper. This
interface defines an API for locating, registering, and loading resource plugins:
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();
}
Resource plugins basically provide the ability to create resource intializers that can be re-used
between applications. This allows you to keep your actual bootstrap relatively clean, and to
introduce new resources without needing to touch your bootstrap itself.
Zend_Application_Bootstrap_BootstrapAbstract (and
Zend_Application_Bootstrap_Bootstrap by extension) implement this interface as well,
allowing you to utilize resource plugins.
To utilize resource plugins, you must specify them in the options passed to the application object
and/or bootstrap. These options may come from a configuration file, or be passed in manually.
Options will be of key to options pairs, with the key representing the resource name. The resource
name will be the segment following the class prefix. For example, the resources shipped with
Zend Framework have the class prefix "Zend_Application_Resource_"; anything following
this would be the name of the resource. As an example,
149
Zend_Application
This indicates that the "FrontController" resource should be used, with the options specified.
If you begin writing your own resource plugins, or utilize third-party resource plugins, you
will need to tell your bootstrap where to look for them. Internally, the bootstrap utilizes
Zend_Loader_PluginLoader, so you will only need to indicate the common class prefix an
path pairs.
Just like resource methods, you use the bootstrap() method to execute resource plugins.
Just like with resource methods, you can specify either a single resource plugin, multiple plugins
(via an array), or all plugins. Additionally, you can mix and match to execute resource methods
as well.
// Execute one:
$bootstrap->bootstrap('FrontController');
// Execute several:
$bootstrap->bootstrap(array('FrontController', 'Foo'));
For maximum flexibility, this registry is referred to as a "container" internally; its only requirements
are that it is an object. Resources are then registered as properties named after the resource
name. By default, an instance of Zend_Registry is used, but you may also specify any
other object you wish. The methods setContainer() and getContainer() may be used
150
Zend_Application
return $view;
}
}
Please note that the registry and also the container is not global. This
means that you need access to the bootstrap in order to fetch resources.
Zend_Application_Bootstrap_Bootstrap provides some convenience for this: during its
run() execution, it registers itself as the front controller parameter "bootstrap", which allows you
to fetch it from the router, dispatcher, plugins, and action controllers.
As an example, if you wanted access to the view resource from above within your action
controller, you could do the following:
At the same time, some resources may depend on other resources being executed. To
solve these two issues, Zend_Application_Bootstrap_BootstrapAbstract provides a
simple, effective mechanism for dependency tracking.
151
Zend_Application
As noted previously, all resources -- whether methods or plugins -- are bootstrapped by calling
bootstrap($resource), where $resource is the name of a resource, an array of resources,
or, left empty, indicates all resources should be run.
If a resource depends on another resource, it should call bootstrap() within its code to ensure
that resource has been executed. Subsequent calls to it will then be ignored.
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();
}
The interface defines simply that a resource plugin should accept options to the constructor,
have mechanisms for setting and retrieving options, have mechanisms for setting and retrieving
the bootstrap object, and an initialization method.
As an example, let's assume you have a common view intialization you use in your applications.
You have a common doctype, CSS and JavaScript, and you want to be able to pass in a base
document title via configuration. Such a resource plugin might look like this:
152
Zend_Application
$viewRenderer =
Zend_Controller_Action_HelperBroker::getStaticHelper(
'ViewRenderer'
);
$viewRenderer->setView($view);
$this->_view = $view;
}
return $this->_view;
}
}
As long as you register the prefix path for this resource plugin, you can then use it in your
application. Even better, because it uses the plugin loader, you are effectively overriding the
shipped "View" resource plugin, ensuring that your own is used instead.
4. Exemples
La classe de bootstrap elle-même sera typiquement minimaliste ; souvent, elle s'agira
simplement d'une extension vide de la classe de bootstrap de base :
; APPLICATION_PATH/configs/application.ini
[production]
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
153
Zend_Application
[testing : production]
[development : production]
Cependant, si de l'initialisation personnalisée est nécessaire, alors vous avez 2 choix. D'abord
vous pouvez écrire des méthodes préfixées par _init pour ajouter du code au bootstrap. De telles
méthodes seront appelées par bootstrap(), et peuvent être appelées comme si elles étaient
publiques, par : bootstrap<resource>(). Elles peuvent accepter un tableau d'options.
Si votre méthode de ressource retourne une valeur, elle sera stockée dans un conteneur du
bootstrap. Ceci peut être utile quand différentes ressources ont besoin d'interagir (comme une
ressource s'injectant elle-même dans une autre). La méthode getResource() peut être utilisée
pour récupérer ces valeurs.
L'exemple ci-dessous montre une méthode de ressource pour l'initialisation d'un objet requête.
Il utilise le traqueur de dépendances (il dépend de la ressource de contrôleur frontal), récupère
une ressource à partir du bootstrap, et retourne une valeur à stocker dans le bootstrap.
Notez l'appel à bootstrap() ; Ceci permet de s'assurer que le contrôleur frontal a bien été
initialisé avant d'appeler cette méthode.
Une autre option consiste à utiliser des ressources de bootstrap. Les plugins de ressources sont
des objets qui s'occupent d'initialisations spéciales, elles peuvent être spécifiées :
• en les activant spécifiquement via des appels de méthodes sur l'objet de bootstrap.
class My_Bootstrap_Resource_View
154
Zend_Application
extends Zend_Application_ResourceAbstract
{
public function init()
{
$view = new Zend_View($this->getOptions());
Zend_Dojo::enableView($view);
$view->doctype('XHTML1_STRICT');
$view->headTitle()->setSeparator(' - ')->append('My Site');
$view->headMeta()->appendHttpEquiv('Content-Type',
'text/html; charset=utf-8');
$view->dojo()->setDjConfigOption('parseOnLoad', true)
->setLocalPath('/js/dojo/dojo.js')
->registerModulePath('../spindle', 'spindle')
->addStylesheetModule('spindle.themes.spindle')
->requireModule('spindle.main')
->disable();
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(
'ViewRenderer'
);
$viewRenderer->setView($view);
return $view;
}
}
Pour dire au bootstrap d'utiliser cette classe, vous devrez fournir le nom de la classe pour ce
plugin de ressource, ou une combinaison préfixe / chemin de chargeur de plugin (plugin loader)
et le nom court du plugin de ressource ("view") :
class My_Bootstrap_Resource_Layout
extends Zend_Application_ResourceAbstract
{
public function init()
{
// Assurons nous que la vue est initialisée...
155
Zend_Application
$this->getBootstrap()->bootstrap('view');
// ...
}
}
En usage normal, vous instancierez votre application, lancerez le bootstrap, puis l'application :
Pour un script personnalisé, vous auriez peut être besoin de ne lancer que des ressources
spécifiques :
Plutôt que d'utiliser la méthode bootstrap() pour appeler les méthodes internes, vous pouvez
surcharger :
5. Fonctionnalités principales
Ici vous trouverez une documentation type API concernant les composants coeurs de
Zend_Application.
5.1. Zend_Application
Zend_Application est la classe de base du composant et le point d'entrée de votre
application Zend Framework. Ses buts sont multiples : configurer l'environnement PHP (incluant
l'autoloading) et exécuter le bootstrap de votre application.
156
Zend_Application
Option Description
autoloaderNamespaces Tableau d'espaces de noms à enregistrer dans
Zend_Loader_Autoloader.
bootstrap Soit une chaîne vers le fichier contenant la
classe de bootstrap, soit un tableau avec les
clés 'path' et 'class' menant vers le bootstrap.
Notez que les noms des options ne sont pas sensibles à la casse.
• $options :
optionnel.
• String : chemin
vers un fichier
Zend_Config à
charger pour la
configuration de
votre application.
$environment
sera utilisé pour
déterminer la
section de
configuration à
charger depuis le
fichier.
• Array : tableau
associatif de
données de
configuration pour
votre application.
• Zend_Config :
un instance
157
Zend_Application
158
Zend_Application
• $class : optionnel.
Si $path est
une chaîne, $class
doit être indiqué
et doit être une
chaîne représentant
le nom d'une classe
contenue dans le
fichier représenté
par le chemin.
getBootstrap() NULL | N/A Récupère l'instance du
bootstrap enregistrée.
Zend_Application_Bootstrap_Bootstrapper
bootstrap() Void N/A Appelle la méthode
bootstrap() du
159
Zend_Application
5.2. Zend_Application_Bootstrap_Bootstrapper
Zend_Application_Bootstrap_Bootstrapper est l'interface de base que toutes les
classes de bootstrap doivent implémenter. Les fonctionnalités apportées sont la configuration,
l'identification des ressources, le bootstrap (d'une ressource ou de l'application entière), et le
lancement (dispatching) de l'application.
160
Zend_Application
5.3. Zend_Application_Bootstrap_ResourceBootstrapper
Zend_Application_Bootstrap_ResourceBootstrapper est une interface utilisée
lorsqu'une classe de bootstrap chargera une ressource externe, ce qui signifie que les
ressources peuvent ne pas être définies comme de simples méthodes, mais via des classes
"plugins". Cette interface devrait être utilisée avec Zend_Application_Bootstrap_Bootstrapper ;
Zend_Application_Bootstrap_BootstrapAbstract implémente cette fonctionnalité.
161
Zend_Application
5.4. Zend_Application_Bootstrap_BootstrapAbstract
Zend_Application_Bootstrap_BootstrapAbstract est une classe abstraite
qui propose les fonctionnalités de base d'un bootstrap classique.
Elle implémente à la fois Zend_Application_Bootstrap_Bootstrapper et
Zend_Application_Bootstrap_ResourceBootstrapper.
Deux options
supplémentaires
spéciales peuvent
aussi être utilisée.
pluginPaths spécifie
des préfixes de chemin
vers les plugins ; on
attend ici un tableau
de paires préfixes,
chemins. resources
permet de spécifier un
plugin à utiliser.
162
Zend_Application
163
Zend_Application
164
Zend_Application
5.5. Zend_Application_Bootstrap_Bootstrap
Zend_Application_Bootstrap_Bootstrap est une implémentation concrète de
Zend_Application_Bootstrap_BootstrapAbstract. Ces caractéristiques principales sont
l'enregistrement de la ressource Front Controller, et la méthode run() qui vérifie d'abord la
présence d'un module par défaut dans le contrôleur frontal, avant de lancer le dispatching.
Dans la plupart des cas, vous étendrez cette classe dans vos bootstraps, ou encore vous
utiliserez cette classe en lui fournissant une liste de plugins à utiliser.
appnamespace = "Application"
Ou en XML :
<appnamespace>Application</appnamespace>
165
Zend_Application
5.6. Zend_Application_Resource_Resource
Zend_Application_Resource_Resource est une interface implémentée par les plugins
de ressources lorsqu'ils sont utilisés par des classes de bootstrap implémentant
Zend_Application_Bootstrap_ResourceBootstrapper. Les classes de plugins de
ressources doivent accepter de la configuration, doivent pouvoir être lancées ("bootstrapées")
et doivent utiliser un pattern strategy pour initialiser la ressource.
5.7. Zend_Application_Resource_ResourceAbstract
Zend_Application_Resource_ResourceAbstract est une classe abstaite implementant
Zend_Application_Resource_Resource, c'est un bon point de départ pour créer vos propres
plugins de ressources.
Note: Cette classe abstraite n'implémente pas la méthode init(); elle doit donc être
implémentée par les extensions concrêtes de cette classe.
166
Zend_Application
D'abord, si vos classes de plugins existent dans un chemin précis, vous pouvez alors y faire
référence simplement par leur nom court -- la portion du nom de la classe située après le
préfixe de classe. Par exemple, la classe "Zend_Application_Resource_View" peut être
référencée simplement via "View" car le préfixe "Zend_Application_Resource" est déja
enregistré. Vous pouvez aussi utiliser le nom long de classe complet :
Quoiqu'il en soit, vous pouvez lancer (bootstrap) la ressource ou la récupérer via son nom court:
$bootstrap->bootstrap('view');
$view = $bootstrap->getResource('view');
Ensuite, si aucun chemin précis n'est enregistré, il reste possible de passer ses plugins de
ressources via leur nom de classe complet :
167
Zend_Application
// Alors que ceci charge une classe spécifiquement via son nom:
'My_Resource_View' => array(),
),
));
$bootstrap->bootstrap('My_Resource_View');
$view = $bootstrap->getResource('My_Resource_View');
La troisième méthode découle des deux précédentes. Il est possible de donner un nom court
à n'importe quelle classe. Ajoutez une variable publique $_explicitType dans la classe du
plugin, sa valeur sera alors utilisée comme nom court pour référencer le plugin dans le bootstrap.
Définissons par exemple notre propre vue :
Nous pouvons dès lors lancer cette ressource (bootstrap) ou la récupérer via le nom "My_View":
$bootstrap->bootstrap('My_View');
$view = $bootstrap->getResource('My_View');
Grâce à ses différentes manières de faire, vous pouvez redéfinir des plugins existants, en ajouter
ou encore les mixer pour accomplir des tâches d'initialisation complexes.
6.1. Zend_Application_Resource_Cachemanager
Zend_Application_Resource_Cachemanager peut être utilisé pour configurer un jeu
d'ensemble d'options Zend_Cache permettant de paramétrer des caches à chargement tardifs
("lazy loading") avec Zend_Cache_Manager
Comme le gestionnaire de cache est un mécanisme à chargement tardif, les options sont
traduites en modèle d'options utilisé pour instancier un objet de cache à la demande.
168
Zend_Application
resources.cachemanager.database.frontend.name = Core
resources.cachemanager.database.frontend.options.lifetime = 7200
resources.cachemanager.database.frontend.options.automatic_serialization = true
resources.cachemanager.database.backend.name = File
resources.cachemanager.database.backend.options.cache_dir = "/path/to/cache"
Ensuite reécupérer ce cache à partir du gestionnaire est aussi simple que d'accéder à
l'instance du gestionnaire et d'appeler $cacheManager->getCache('database');.
6.2. Zend_Application_Resource_Db
Zend_Application_Resource_Db initialisera un adaptateur Zend_Db basé sur les options
qui lui seront fournis. Par défaut, il spécifiera aussi cet adaptateur comme adaptateur par défaut
à utiliser avec Zend_Db_Table. Si vous souhaitez utiliser simultanément de multiples bases de
données, vous pouvez utiliser la plugin de ressource Multidb.
Voici un exmple de configuration INI qui peut-être utilisé pour initialiser une ressource de
base de données.
[production]
resources.db.adapter = "pdo_mysql"
resources.db.params.host = "localhost"
resources.db.params.username = "webuser"
resources.db.params.password = "XXXXXXX"
resources.db.params.dbname = "test"
resources.db.isDefaultTableAdapter = true
Comme tout plugin de ressource, vous pouvez extraire votre plugin de ressource
de votre fichier d'initialisation :
$resource = $bootstrap->getPluginResource('db');
169
Zend_Application
Une fois que vous avez l'objet ressource, vous pouvez récupérer l'adaptateur de
base de données en utilisant la méthode getDbAdapter() :
$db = $resource->getDbAdapter();
6.3. Zend_Application_Resource_Frontcontroller
Probablement la ressource que vous allez le plus communément charger avec
Zend_Application sera la ressource de contrôleur frontal qui fournit la possibilité de
configurer Zend_Controller_Front. Cette ressource permet de spécifier n'importe quel
paramètre du contrôleur frontal, de spécifier les plugins à initialiser, et bien plus...
Les clés de configuration disponibles incluent les suivantes et sont sensibles à la casse :
• moduleDirectory : un dossier dans lequel tous les modules peuvent être trouvés.
Si une clé non-connue est fournie, elle sera enregistrée comme paramètre du contrôleur frontal
en la fournissant à setParam().
[production]
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.frontController.moduleControllerDirectoryName = "actions"
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.frontController.defaultControllerName = "site"
resources.frontController.defaultAction = "home"
resources.frontController.defaultModule = "static"
resources.frontController.baseUrl = "/subdir"
resources.frontController.plugins.foo = "My_Plugin_Foo"
resources.frontController.plugins.bar = "My_Plugin_Bar"
resources.frontController.env = APPLICATION_ENV
170
Zend_Application
Une fois la ressource de contrôleur frontal initialisée, vous pouvez récupérer l'instance via
la propriété $frontController de votre initialiseur.
$bootstrap->bootstrap('frontController');
$front = $bootstrap->frontController;
6.4. Zend_Application_Resource_Layout
Zend_Application_Resource_Layout peut être utilisé pour configurer Zend_Layout. Les
options de configurations sont les mêmes que celles de Zend_Layout.
resources.layout.layout = "NomDuLayoutParDefaut"
resources.layout.layoutPath = "/chemin/vers/layouts"
6.5. Zend_Application_Resource_Locale
Zend_Application_Resource_Locale can be used to set an application-wide locale which
is then used in all classes and components which work with localization or internationalization.
There are basically three usecases for the Locale Resource Plugin. Each of them should be used
depending on the applications need.
This detection works because your client sends the wished language within his HTTP request.
Normally the clients browser sends the languages he wants to see, and Zend_Locale uses
this information for detection.
• The user could have manually set a locale which does not exist
In both cases Zend_Locale will fallback to other mechanism to detect the locale:
• When a locale has been set which does not exist, Zend_Locale tries to downgrade this string.
When, for example, en_ZZ is set it will automatically be degraded to en. In this case en will
be used as locale for your application.
• When the locale could also not be detected by downgrading, the locale of your environment
(web server) will be used. Most available environments from Web Hosters use en as locale.
• When the systems locale could not be detected Zend_Locale will use it's default locale, which
is set to en per default.
171
Zend_Application
For more informations about locale detection take a look into this chapter on Zend_Locale's
automatic detection.
The following snippet shows how to set a own default locale which will be used when the
client does not send a locale himself.
In this case this single locale will be used and the automatic detection is turned off.
The following snippet shows how to set a single locale for your entire application.
6.6. Zend_Application_Resource_Log
Zend_Application_Resource_Log permet d'instancier une instance Zend_Log avec
une nombre quelconque de rédacteurs. La configuration sera fournie à la méthode
Zend_Log::factory() vous permettant de spécifier les combinaisons de rédacteurs et
de filtres. L'instance de journalisation peut ensuite être récupérée à partir du bootstrap afin
d'enregistrer les événements.
Ci-dessous, vous avez un extrait de fichier INI montrant comment configurer la ressource
de journalisation.
resources.log.stream.writerName = "Stream"
resources.log.stream.writerParams.stream = APPLICATION_PATH "/../data/logs/application.l
resources.log.stream.writerParams.mode = "a"
resources.log.stream.filterName = "Priority"
resources.log.stream.filterParams.priority = 4
Pour plus d'informations concernant les options disponibles, vous pouvez consulter la
documentation de Zend_Log::factory().
172
Zend_Application
6.7. Zend_Application_Resource_Multidb
Zend_Application_Resource_Multidb est utilisé pour initialiser de multiples connexions
vers des bases de données. Vous pouvez utiliser les mêmes options qu'avec le plugin de
ressource Db. Cependant, pour spécifier une connexion par défaut, vous pouvez aussi utiliser
la directive 'default'.
[production]
resources.multidb.db1.adapter = "pdo_mysql"
resources.multidb.db1.host = "localhost"
resources.multidb.db1.username = "webuser"
resources.multidb.db1.password = "XXXX"
resources.multidb.db1.dbname = "db1"
resources.multidb.db2.adapter = "pdo_pgsql"
resources.multidb.db2.host = "example.com"
resources.multidb.db2.username = "dba"
resources.multidb.db2.password = "notthatpublic"
resources.multidb.db2.dbname = "db2"
resources.multidb.db2.default = true
Lorsque vous utilisez ce plugin de ressource, vous aurez sans doute besoin de récupérer un
adaptateur spécifique. Ceci peut être réalisé en utilisant la méthode getDb(). La méthode
getDb() retourne l'instance d'une classe qui étend Zend_Db_Adapter_Abstract. Si
vous n'avez pas activé un adaptateur par défaut, une exception sera levée lorsque vous
appelerez cette méthode sans lui fournir de paramètre.
$resource = $bootstrap->getPluginResource('multidb');
$db1 = $resource->getDb('db1');
$db2 = $resource->getDb('db2');
$defaultDb = $resource->getDb();
Ci-dessous vous avez un exemple qui suppose que le plugin de ressource Multidb a été
configuré avec l'exemple INI ci-dessus :
$resource = $bootstrap->getPluginResource('multidb');
$db2 = $resource->getDefaultDb();
173
Zend_Application
6.8. Zend_Application_Resource_Mail
Zend_Application_Resource_Mail peut être utilisé pour instancier un transport pour
Zend_Mail ou pour paramétrer le nom par défaut et l'adresse, ainsi que le nom et l'adresse
de réponse par défaut.
Ci-dessous, vous avez un extrait d'un fichier INI montrant comment configurer le plugin de
ressource Mail.
resources.mail.transport.type = smtp
resources.mail.transport.host = "smtp.example.com"
resources.mail.transport.auth = login
resources.mail.transport.username = myUsername
resources.mail.transport.password = myPassword
resources.mail.transport.register = true ; True by default
resources.mail.defaultFrom.email = john@example.com
resources.mail.defaultFrom.name = "John Doe"
resources.mail.defaultReplyTo.email = Jane@example.com
resources.mail.defaultReplyTo.name = "Jane Doe"
6.9. Zend_Application_Resource_Modules
Zend_Application_Resource_Modules est utilisé pour initialiser les modules de votre
application. Si votre module possède un fichier Bootstrap.php à sa racine, et que celui-ci
contient une classe nommée Module_Bootstrap (où "Module" est le nom du module), alors
celle-ci sera utiliser pour lancer votre module.
Puisque la ressource Modules ne prend pas d'argument par défaut, pour l'activer via la
configuration, vous devez créer un tableau vide. Since the Modules resource does not take any
arguments by default, in order to enable it via configuration, you need to create it as an empty
array. En configuration de type INI cela ressemblerait à ceci :
resources.modules[] =
<resources>
<modules>
<!-- Emplacement pour s'assurer qu'un tableau est créé -->
<placeholder />
</modules>
</resources>
$options = array(
'resources' => array(
174
Zend_Application
Par exemple, supposons que vous possédiez un module appelé "news". Voici des exemples
INI et XML de fichiers de configuration pour ce module.
[production]
news.resources.db.adapter = "pdo_mysql"
news.resources.db.params.host = "localhost"
news.resources.db.params.username = "webuser"
news.resources.db.params.password = "XXXXXXX"
news.resources.db.params.dbname = "news"
<?xml version="1.0"?>
<config>
<production>
<news>
<resources>
<db>
<adapter>pdo_mysql</adapter>
<params>
<host>localhost</host>
<username>webuser</username>
<password>XXXXXXX</password>
<dbname>news</dbname>
</params>
<isDefaultAdapter>true</isDefaultAdapter>
</db>
</resources>
</news>
</production>
</config>
Il peut être utile de pouvoir récupérer l'objet bootstrap de votre module, pour en exécuter par
exemple des méthodes spécifiques, ou encore pour en récupérer l'autoloader. La méthode
getExecutedBootstraps() peut être utilisée dans ce cas là, elle s'applique sur un objet
ressource de modules.
$resource = $bootstrap->getPluginResource('modules');
$moduleBootstraps = $resource->getExecutedBootstraps();
$newsBootstrap = $moduleBootstraps['news'];
175
Zend_Application
6.10. Zend_Application_Resource_Navigation
Zend_Application_Resource_Navigation peut être utilisé pour configurer une instance
de Zend_Navigation. Les options de configurations sont les mêmes que celles de
Zend_Navigation.
6.11. Zend_Application_Resource_Router
Zend_Application_Resource_Router est utilisé pour configurer le routeur enregistré
grâce aux options du contrôleur frontal. Les options sont identiques à celles de
Zend_Controller_Router_Route.
Voici l'exemple d'un fichier INI qui configure une ressource de type routeur.
resources.router.routes.route_id.route = "/login"
resources.router.routes.route_id.defaults.module = "user"
resources.router.routes.route_id.defaults.controller = "login"
resources.router.routes.route_id.defaults.action = "index"
6.12. Zend_Application_Resource_Session
Zend_Application_Resource_Session est utilisé pour configurer Zend_Session et
éventuellement un support de sauvegarde sessions (SaveHandler).
Pour créer un support de sauvegarde session, passez la clé saveHandler (case insensitive) à la
ressource. La valeur d'une telle option peut être :
• Array : avec les clés "class" et optionnellement "options", indiquant une classe à instancier
(implémentant Zend_Session_SaveHandler_Interface) et un tableau d'options à
passer à son constructeur.
176
Zend_Application
Toute autre option non reconnue sera alors passée à Zend_Session::setOptions() pour
configurer Zend_Session.
6.13. Zend_Application_Resource_View
Zend_Application_Resource_View peut être utilisée pour configurer une instance
Zend_View instance. Les clés de configurations sont celles de Zend_View.
Voici un extrait de configuration INI montrant comment configurer une ressource de vue.
resources.view.encoding = "UTF-8"
resources.view.basePath = APPLICATION_PATH "/views/scripts"
177
Zend_Auth
1. Introduction
Zend_Auth fournit une API pour l'authentification et inclut des adaptateurs concrets
d'authentification pour les cas les plus courants.
Zend_Auth est uniquement concerné par le processus d'authentification et non pas par
le processus d'autorisation. L'authentification est définie de manière lâche (souple) afin de
déterminer si une entité donnée est bien celle qu'elle prétend être (c.-à-d. identification), sur la
base d'identifiants fournis. L'autorisation, l'action de décider si une entité donnée peut accéder
à d'autres entités et / ou exécuter des opérations sur celles-ci ne fait pas partie des prérogatives
de Zend_Auth. Pour plus d'informations sur les autorisations et le contrôle d'accès via Zend
Framework, voyez Zend_Acl.
1.1. Adaptateurs
Un adaptateur Zend_Auth est utilisé pour authentifier via un service particulier d'authentification,
comme LDAP, RDBMS ou un stockage basé sur des fichiers. Les différents adaptateurs peuvent
posséder des options et des comportements très divers. Cependant, quelques méthodes de
base leur sont communes. Par exemple, accepter des éléments d'authentification (incluant
une identité prétendue), authentifier et retourner un résultat sont des éléments communs aux
adaptateurs Zend_Auth.
178
Zend_Auth
// ...
}
/**
* Réalise une tentative d'authentification
*
* @throws Zend_Auth_Adapter_Exception Si l'authentification
* ne peut pas être réalisée
* @return Zend_Auth_Result
*/
public function authenticate()
{
// ...
}
}
1.2. Résultats
Les adaptateurs Zend_Auth retournent une instance de Zend_Auth_Result via
authenticate() de manière à présenter les résultats d'une tentative d'authentification. Les
adaptateurs alimentent l'objet Zend_Auth_Result lors de sa construction, de manière à ce que
les quatre méthodes suivantes fournissent de base un lot d'opérations communes aux résultats
des adaptateurs Zend_Auth :
Zend_Auth_Result::SUCCESS
Zend_Auth_Result::FAILURE
Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND
Zend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUS
Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID
Zend_Auth_Result::FAILURE_UNCATEGORIZED
179
Zend_Auth
switch ($resultat->getCode()) {
case Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND:
/** l'identifiant n'existe pas **/
break;
case Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID:
/** mauvaise authentification **/
break;
case Zend_Auth_Result::SUCCESS:
/** authentification acceptée **/
break;
default:
/** autres cas **/
break;
}
HTTP est un protocole sans état, cependant, des techniques telles que les cookies ou les
sessions ont été développées de manière à faciliter le maintien d'un contexte lors de multiples
requêtes dans les applications Web.
180
Zend_Auth
/**
* @todo Paramètrage de l'adaptateur d'authentification :
* $authAdaptateur
*/
181
class MonStockage implements Zend_Auth_Storage_Interface
{
Pour utiliser une classe de stockage d'identité persistante autre que
/** Zend_Auth
Zend_Auth_Storage_Session, le développeur commence par implémenter
* Retourne true si et seulement si le stockage est vide
Zend_Auth_Storage_Interface
* :
* @throws Zend_Auth_Storage_Exception S'il est impossible de déterminer
Exemple
* 50. Utiliser une classe de stockage personnalisée
si le stockage est vide
* @return boolean
*/
public function isEmpty()
{
/**
* @todo implémentation
*/
}
/**
* Retourne le contenu du stockage
*
* Comportement à définir si le stockage est vide.
*
* @throws Zend_Auth_Storage_Exception Si la lecture du stockage
* est impossible
* @return mixed
*/
public function read()
{
/**
* @todo implémentation
*/
}
/**
* Ecrit $contents dans le stockage
*
* @param mixed $contents
* @throws Zend_Auth_Storage_Exception Si l'écriture de $contents
* est impossible
* @return void
*/
public function write($contents)
{
/**
* @todo implementation
*/
}
/**
* RAZ du stockage
*
* @throws Zend_Auth_Storage_Exception Si la remise à zéro (RAZ)
* est impossible
* @return void
*/
// Définit function
public la classeclear()
personnalisée à utiliser
{
Zend_Auth::getInstance()->setStorage(new MonStockage());
/**
Ensuite la *classe
@todopersonnalisée est invoquée, avant la requête d'authentification, avec
implementation
/**
Zend_Auth::setStorage()
*/ :
* @todo Paramètrage de l'adaptateur d'authentification :
* } $authAdaptateur
*/
}
// Authentification, sauvegarde et
// persistance du résultat en cas de succès.
$result = Zend_Auth::getInstance()->authenticate($authAdaptateur);
182
Zend_Auth
if (!$resultat->isValid()) {
// Echec de l'authentification ; afficher pourquoi
foreach ($resultat->getMessages() as $message) {
echo "$message\n";
}
} else {
// Authentification réussie ; l'identité ($identifiant) est
// stockée dans la session
// $resultat->getIdentity() === $auth->getIdentity()
// $resultat->getIdentity() === $identifiant
}
Une fois la tentative d'authentification réalisée, tel que montré ci-dessus, il est très simple de
vérifier si une identité correctement authentifiée existe :
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
// l'identité existe ; on la récupère
$identite = $auth->getIdentity();
}
Zend_Auth::getInstance()->clearIdentity();
Quand l'utilisation automatique du stockage persistant n'est pas appropriée, le développeur peut
simplement contourner l'utilisation de la classe Zend_Auth en utilisant directement une classe
adaptateur. L'usage direct d'une classe adaptateur implique de configurer et préparer l'objet
adaptateur et d'appeler ensuite sa méthode authenticate(). Les détails spécifiques à un
adaptateur sont décrits dans la documentation de chacun d'entre-eux. L'exemple suivant utilise
directement MonAdaptateurAuth :
183
Zend_Auth
if (!$resultat->isValid()) {
// échec de l'authentification ; afficher pourquoi
foreach ($resultat->getMessages() as $message) {
echo "$message\n";
}
} else {
// Authentification réussie
// $resultat->getIdentity() === $identifiant
}
• tableName : il s'agit du nom de la table dans la base de données qui contient les crédits
d'authentification, et envers laquelle la requête d'authentification sera réalisée.
• identityColumn : il s'agit du nom de la colonne dans la table utilisée pour représenter l'identité.
La colonne d'identité doit contenir une valeur unique, comme un "username" ou une adresse
émail.
• credentialTreatment : dans la plupart des cas, les mots de passe et autres données sensibles
sont cryptés, hachés, encodés, masqués, ou sinon traités à travers une fonction ou un
algorithme. En spécifiant un traitement paramétrable de chaîne avec cette méthode, comme
MD5(?) ou PASSWORD(?), un développeur peut appliquer un code SQL arbitraire sur les
données de crédit fournies. Comme ces fonctions sont spécifiques à chaque gestionnaire de
base de données (SGBD), vérifiez le manuel de la base de données pour vérifier la disponibilité
de ces fonctions dans votre système.
184
Zend_Auth
Avec
// une connexion
Insertion de base de données et des données disponibles dans la table,
des données
$dbAdapter->query($sqlInsert);
une instance de Zend_Auth_Adapter_DbTable peut être créée. Les valeurs d'options
de
// configuration
Configure une peuvent être fournies
instance avec desauparamètres
constructeurdeouconstructeur
en tant que paramètres
... aux
méthodes de réglage après l'instanciation :
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter,
'users',
'username',
'password');
En plus
// de la disponibilité
Réalise la requêtede la méthode getIdentity()
d'authentification, pour récupérer
et sauvegarde l'objet du résultat
le résultat
$result = $authAdapter->authenticate();
d'authentification, Zend_Auth_Adapter_DbTable supporte aussi la récupération de la
// Affiche l'identité
ligne
echo de la table qui a réussi l'authentification
$result->getIdentity() . "\n\n"; :
/* Affiche:
my_username
Array
(
[id] => 1
[username] => my_username
[password] => my_password
[real_name] => My Real Name
Puisque la ligne de la table contient la valeur de crédit, il est important de garantir ces valeurs
)
*/
contre l'accès fortuit.
185
Zend_Auth
if ($result->isValid()) {
/* ... */
} else {
/* ... */
Ceci étant dit, Zend_Auth_Adapter_DbTable possède des mécanismes qui sont construits
de telle sorte qu'ils peuvent être démultipliés pour ajouter des contrôles supplémentaires au
moment de l'authentification pour résoudre quelques problèmes communs d'utilisateur.
186
Zend_Auth
'utilisateurs',
'login',
'password',
'MD5(?) AND actif = "TRUE"');
Un autre scénario possible est l'implantation d'un mécanisme de "salting". "Salting" est un terme
se référant une technique qui peut fortement améliorer la sécurité de votre application. C'est
basé sur l'idée que concaténer une chaîne aléatoire à tout mot de passe rend impossible la
réussite d'une attaque de type "brute force" sur la base de données en utilisant des valeurs
préalablement hashées issues d'un dictionnaire.
Par conséquent nous devons modifier notre table pour stocker notre chaîne de "salt" aléatoire :
$dbAdapter->query($sqlAlter);
Voici une méthode simple pour générer une chaîne aléatoire pour chaque utilisateur à leur
enregistrement :
187
Zend_Auth
3. Authentification "Digest"
3.1. Introduction
L'authentification "Digest" est une méthode d'authentification HTTP qui améliore
l'authentification basique en fournissant un moyen d'authentifier sans avoir à transmettre le mot
de passe en clair sur le réseau.
Cet adaptateur permet l'authentification en utilisant un fichier texte contenant des lignes
comportant les éléments de base d'une authentification Digest :
• hachage MD55 d'un identifiant, domaine et mot de passe, séparés par des caractères deux-
points.
Les éléments ci-dessus sont séparés par des caractères deux-points, comme dans l'exemple
suivant (dans lequel le mot de passe est "unMotdepasse") :
unUtilisateur:Un domaine:3b17d7f3a9374666e892cbce58aa724f
3.2. Spécifications
L'adaptateur d'authentification Digest, Zend_Auth_Adapter_Digest requiert plusieurs
paramètres d'entrée :
3.3. Identité
L'adaptateur d'authentification Digest retourne un objet Zend_Auth_Result, lequel a été
alimenté avec l'identité sous la forme d'un tableau ayant pour clés realm (domaine) et username
188
Zend_Auth
(identifiant). Les valeurs respectives associées à ces clés correspondent aux valeurs définies
avant l'appel à authenticate().
$resultat = $adaptateur->authenticate();
$identite = $resultat->getIdentity();
print_r($identite);
/*
Array
(
[realm] => Un domaine
[username] => unUtilisateur
)
*/
Caractéristiques principales :
• Propose tous les des schémas de challenge, le client peut répondre avec le schéma qu'il
supporte ;
• Inclus le support d'authentification de type fichier, et fournit une interface pour créer son propre
support, comme une base de données.
4.2. Fonctionnement
Cette adaptateur utilise 2 sous-composants, la classe d'authentification HTTP elle-même et
des "Résolveurs." La classe d'authentification HTTP encapsule la logique de commande des
authentifications Basic et Digest. Elle utilise aussi un résolveur pour chercher les identifiants sur
un disque (fichier texte par défaut), et les analyser. Ils sont alors comparés aux valeurs envoyées
par le client pour déterminer une éventuelle correspondance.
189
Zend_Auth
4.4. Résolveurs
Le travail du résolveur consiste à récupérer un nom d'utilisateur ("username") et un nom
d'authentification ("realm") et retourner des identifiants. L'authentification Basic s'attend à
recevoir une version encodée Base64 du mot de passe ("password"). L'authentification Digest,
elle, attend un hash du "username", du "realm", et du "password" (séparés par des deux-points).
Actuellement le seul algorithme de hash supporté est MD5.
190
Zend_Auth
texte est inclue avec cet adaptateur, mais n'importe quelle classe peut être écrite, grâce à
l'interface.
<username>:<realm>:<credentials>\n
$path = 'files/passwd.txt';
$resolver = new Zend_Auth_Adapter_Http_Resolver_File($path);
ou
$path = 'files/passwd.txt';
$resolver = new Zend_Auth_Adapter_Http_Resolver_File();
$resolver->setFile($path);
$config = array(
'accept_schemes' => 'basic digest',
'realm' => 'My Web Site',
'digest_domains' => '/members_only /my_account',
'nonce_timeout' => 3600,
);
Ce tableau va permettre d'accepter les modes Basic ou Digest et demandera une authentification
pour les zones du site situées sous /members_only et /my_account. La valeur du "real" est
en général affichée par le navigateur dans la boite de dialogue. Le paramètre nonce_timeout,
fonctionne comme expliqué plus haut.
Comme nous supportons les 2 modes Basic et Digest, nous avons besoin de deux résolveurs
différents :
191
Zend_Auth
$adapter->setBasicResolver($basicResolver);
$adapter->setDigestResolver($digestResolver);
$adapter->setRequest($request);
$adapter->setResponse($response);
$result = $adapter->authenticate();
if (!$result->isValid()) {
// Mauvais username/password, ou action annulée
}
5. LDAP Authentication
5.1. Introduction
Zend_Auth_Adapter_Ldap supports web application authentication with LDAP services. Its
features include username and domain name canonicalization, multi-domain authentication, and
failover capabilities. It has been tested to work with Microsoft Active Directory and OpenLDAP,
but it should also work with other LDAP service providers.
5.2. Usage
To incorporate Zend_Auth_Adapter_Ldap authentication into your application quickly, even
if you're not using Zend_Controller, the meat of your code should look something like the
following:
$username = $this->_request->getParam('username');
$password = $this->_request->getParam('password');
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($adapter);
if ($log_path) {
$messages = $result->getMessages();
192
Zend_Auth
$logger->addWriter(new Zend_Log_Writer_Stream($log_path));
$filter = new Zend_Log_Filter_Priority(Zend_Log::DEBUG);
$logger->addFilter($filter);
Of course, the logging code is optional, but it is highly recommended that you use a logger.
Zend_Auth_Adapter_Ldap will record just about every bit of information anyone could want
in $messages (more below), which is a nice feature in itself for something that has a history of
being notoriously difficult to debug.
The Zend_Config_Ini code is used above to load the adapter options. It is also optional. A
regular array would work equally well. The following is an example application/config/
config.ini file that has options for two separate servers. With multiple sets of server options
the adapter will try each, in order, until the credentials are successfully authenticated. The names
of the servers (e.g., 'server1' and 'server2') are largely arbitrary. For details regarding the options
array, see the Server Options section below. Note that Zend_Config_Ini requires that any
values with "equals" characters (=) will need to be quoted (like the DNs shown below).
[production]
ldap.log_path = /tmp/ldap.log
With servers in different domains, this configuration illustrates multi-domain authentication. You
can also have multiple servers in the same domain to provide redundancy.
Note that in this case, even though OpenLDAP has no need for the short NetBIOS style domain
name used by Windows, we provide it here for name canonicalization purposes (described in
the Username Canonicalization section below).
193
Zend_Auth
The $options parameter is required and must be an array containing one or more sets of
options. Note that it is an array of arrays of Zend_Ldap options. Even if you will be using only
one LDAP server, the options must still be within another array.
Below is print_r() output of an example options parameter containing two sets of server
options for LDAP servers s0.foo.net and dc1.w.net (the same options as the above INI
representation):
Array
(
[server2] => Array
(
[host] => dc1.w.net
[useStartTls] => 1
[accountDomainName] => w.net
[accountDomainNameShort] => W
[accountCanonicalForm] => 3
[baseDn] => CN=Users,DC=w,DC=net
)
The information provided in each set of options above is different mainly because AD does not
require a username be in DN form when binding (see the bindRequiresDn option in the Server
Options section below), which means we can omit a number of options associated with retrieving
the DN for a username being authenticated.
The names of servers (e.g. 'server1' and 'server2' shown above) are largely arbitrary, but for the
sake of using Zend_Config, the identifiers should be present (as opposed to being numeric
indexes) and should not contain any special characters used by the associated file formats (e.g.
the '.' INI property separator, '&' for XML entity references, etc).
194
Zend_Auth
With multiple sets of server options, the adapter can authenticate users in multiple domains and
provide failover so that if one server is not available, another will be queried.
When the authenticate() method is called, the adapter iterates over each
set of server options, sets them on the internal Zend_Ldap instance, and
calls the Zend_Ldap::bind() method with the username and password being
authenticated. The Zend_Ldap class checks to see if the username is qualified
with a domain (e.g., has a domain component like alice@foo.net or FOO
\alice). If a domain is present, but does not match either of the server's
domain names (foo.net or FOO), a special exception is thrown and caught
by Zend_Auth_Adapter_Ldap that causes that server to be ignored and the
next set of server options is selected. If a domain does match, or if the user
did not supply a qualified username, Zend_Ldap proceeds to try to bind with
the supplied credentials. if the bind is not successful, Zend_Ldap throws a
Zend_Ldap_Exception which is caught by Zend_Auth_Adapter_Ldap and
the next set of server options is tried. If the bind is successful, the iteration
stops, and the adapter's authenticate() method returns a successful result.
If all server options have been tried without success, the authentication fails,
and authenticate() returns a failure result with error messages from the last
iteration.
Name Description
host The hostname of LDAP server that these
options represent. This option is required.
port The port on which the LDAP server is listening.
If useSsl is TRUE, the default port value is 636.
If useSsl is FALSE, the default port value is 389.
useStartTls Whether or not the LDAP client should
use TLS (aka SSLv2) encrypted transport.
A value of TRUE is strongly favored in
production environments to prevent passwords
from be transmitted in clear text. The default
value is FALSE, as servers frequently require
that a certificate be installed separately
after installation. The useSsl and useStartTls
options are mutually exclusive. The useStartTls
option should be favored over useSsl but not all
servers support this newer mechanism.
195
Zend_Auth
Name Description
useSsl Whether or not the LDAP client should use
SSL encrypted transport. The useSsl and
useStartTls options are mutually exclusive, but
useStartTls should be favored if the server
and LDAP client library support it. This value
also changes the default port value (see port
description above).
username The DN of the account used to perform
account DN lookups. LDAP servers that
require the username to be in DN form
when performing the "bind" require this option.
Meaning, if bindRequiresDn is TRUE, this
option is required. This account does not need
to be a privileged account; an account with
read-only access to objects under the baseDn
is all that is necessary (and preferred based on
the Principle of Least Privilege).
password The password of the account used to perform
account DN lookups. If this option is not
supplied, the LDAP client will attempt an
"anonymous bind" when performing account
DN lookups.
bindRequiresDn Some LDAP servers require that the username
used to bind be in DN form like CN=Alice
Baker,OU=Sales,DC=foo,DC=net (basically all
servers except AD). If this option is TRUE, this
instructs Zend_Ldap to automatically retrieve
the DN corresponding to the username being
authenticated, if it is not already in DN form,
and then re-bind with the proper DN. The
default value is FALSE. Currently only Microsoft
Active Directory Server (ADS) is known not
to require usernames to be in DN form when
binding, and therefore this option may be
FALSE with AD (and it should be, as retrieving
the DN requires an extra round trip to the
server). Otherwise, this option must be set
to TRUE (e.g. for OpenLDAP). This option
also controls the default acountFilterFormat
used when searching for accounts. See the
accountFilterFormat option.
baseDn The DN under which all accounts being
authenticated are located. This option is
required. if you are uncertain about the
correct baseDn value, it should be sufficient
to derive it from the user's DNS domain
using DC= components. For example, if the
user's principal name is alice@foo.net,
a baseDn of DC=foo,DC=net should
work. A more precise location (e.g.,
196
Zend_Auth
Name Description
OU=Sales,DC=foo,DC=net) will be more
efficient, however.
accountCanonicalForm A value of 2, 3 or 4 indicating the form to
which account names should be canonicalized
after successful authentication. Values are
as follows: 2 for traditional username style
names (e.g., alice), 3 for backslash-style
names (e.g., FOO\alice) or 4 for principal
style usernames (e.g., alice@foo.net). The
default value is 4 (e.g., alice@foo.net). For
example, with a value of 3, the identity returned
by Zend_Auth_Result::getIdentity()
(and Zend_Auth::getIdentity(), if
Zend_Auth was used) will always be FOO
\alice, regardless of what form Alice
supplied, whether it be alice, alice@foo.net,
FOO\alice, FoO\aLicE, foo.net\alice,
etc. See the Account Name Canonicalization
section in the Zend_Ldap documentation for
details. Note that when using multiple sets
of server options it is recommended, but not
required, that the same accountCanonicalForm
be used with all server options so that the
resulting usernames are always canonicalized
to the same form (e.g., if you canonicalize
to EXAMPLE\username with an AD server
but to username@example.com with an
OpenLDAP server, that may be awkward for
the application's high-level logic).
accountDomainName The FQDN domain name for which the
target LDAP server is an authority (e.g.,
example.com). This option is used to
canonicalize names so that the username
supplied by the user can be converted
as necessary for binding. It is also
used to determine if the server is an
authority for the supplied username (e.g., if
accountDomainName is foo.net and the user
supplies bob@bar.net, the server will not be
queried, and a failure will result). This option is
not required, but if it is not supplied, usernames
in principal name form (e.g., alice@foo.net)
are not supported. It is strongly recommended
that you supply this option, as there are many
use-cases that require generating the principal
name form.
accountDomainNameShort The 'short' domain for which the target
LDAP server is an authority (e.g., FOO).
Note that there is a 1:1 mapping
between the accountDomainName and
accountDomainNameShort. This option should
197
Zend_Auth
Name Description
be used to specify the NetBIOS domain
name for Windows networks, but may also be
used by non-AD servers (e.g., for consistency
when multiple sets of server options with
the backslash style accountCanonicalForm).
This option is not required but if it is not
supplied, usernames in backslash form (e.g.,
FOO\alice) are not supported.
accountFilterFormat The LDAP search filter used to search for
accounts. This string is a printf()-style
expression that must contain one '%s' to
accomodate the username. The default value
is '(&(objectClass=user)(sAMAccountName=
%s))', unless bindRequiresDn is set to
TRUE, in which case the default is
'(&(objectClass=posixAccount)(uid=%s))'. For
example, if for some reason you wanted
to use bindRequiresDn = true with AD
you would need to set accountFilterFormat
= '(&(objectClass=user)(sAMAccountName=
%s))'.
optReferrals If set to TRUE, this option indicates to the LDAP
client that referrals should be followed. The
default value is FALSE.
If you enable useStartTls = TRUE or useSsl = TRUE you may find that the LDAP
client generates an error claiming that it cannot validate the server's certificate.
Assuming the PHP LDAP extension is ultimately linked to the OpenLDAP
client libraries, to resolve this issue you can set "TLS_REQCERT never" in
the OpenLDAP client ldap.conf (and restart the web server) to indicate to
the OpenLDAP client library that you trust the server. Alternatively, if you are
concerned that the server could be spoofed, you can export the LDAP server's
root certificate and put it on the web server so that the OpenLDAP client can
validate the server's identity.
198
Zend_Auth
In practice, index 0 should be displayed to the user (e.g., using the FlashMessenger helper),
index 1 should be logged and, if debugging information is being collected, indexes 2 and higher
could be logged as well (although the final message always includes the string from index 1).
199
Zend_Auth
domains are explicitly checked, but this may not be true of a future
implementation that discovers the domain at runtime, or if an alternative adapter
is used (e.g., Kerberos). In general, account name ambiguity is known to be the
source of security issues, so always try to use qualified account names.
For OpenLDAP or a generic LDAP server using a typical posixAccount style schema, the
following options are noteworthy:
200
Zend_Auth
6. Authentification OpenID
6.1. Introduction
Zend_Auth_Adapter_OpenId permet l'authentification à travers un serveur distant OpenID.
Une telle authentification attend que l'utilisateur fournisse à l'application Web son identifiant
OpenID. L'utilisateur est alors redirigé vers un fournisseur de services OpenID, afin de s'identifier
en rapport avec l'application Web utilisée. Un mot de passe ou un autre procédé est utilisé, et
celui-ci n'est jamais connu de l'application Web originale.
L'identité OpenID est juste une URI qui pointe vers une page avec des informations décrivant
le serveur à utiliser et des informations sur l'utilisateur. Pour plus d'informations, consultez le
site officiel OpenID.
6.2. Spécifications
Comme toute autre classe adaptateur de Zend_Auth, Zend_Auth_Adapter_OpenId
implémente Zend_Auth_Adapter_Interface, qui définit une seule méthode :
authenticate(). Elle est utilisée pour l'authentification elle-même, une fois que l'objet est
prêt. La préparation d'un objet OpenID nécessite quelques options à passer à Zend_OpenId.
<?php
$status = "";
$auth = Zend_Auth::getInstance();
if ((isset($_POST['openid_action']) &&
$_POST['openid_action'] == "login" &&
!empty($_POST['openid_identifier'])) ||
isset($_GET['openid_mode']) ||
isset($_POST['openid_mode'])) {
$result = $auth->authenticate(
new Zend_Auth_Adapter_OpenId(@$_POST['openid_identifier']));
if ($result->isValid()) {
201
Zend_Auth
Il est possible de personnaliser le processus, pour par exemple demander une redirection
du serveur OpenID vers l'application, sur une page différente de la première. Ceci
peut être fait avec des objets personnalisés Zend_OpenId_Consumer_Storage ou
Zend_Controller_Response. Vous pouvez aussi utiliser le procédé "Simple Registration"
pour récupérer les informations au sujet de l'utilisateur, en provenance du serveur
OpenID. Toutes ces possibilités sont écrites et détaillées dans le chapitre concernant
Zend_OpenId_Consumer.
202
Zend_Barcode
1. Introduction
Zend_Barcode fournit une manière générique de générer des code-barres. Le composant
Zend_Barcode est divisé en deux sous-composants : les objets code-barres et les générateurs
de rendu. Les objets permettent de créer les code-barres indépendamment du support de rendu.
Les générateurs de rendu vous permettent de tracer les code-barres en fonction sur le support.
5. Booléen indiquant si le générateur automatique d'erreur est activé. Si une exception intervient,
l'objet code-barres fourni sera remplacé par un objet représentant l'erreur (optionnel par défaut
vaut TRUE)
203
Zend_Barcode
Vous pouvez fournir un objet Zend_Config à la fabrique afin de créer les objets souhaités.
L'exemple suivant est fonctionnellement équivalent au précédent.
204
Zend_Barcode
Exemple 56. Effectuer le rendu d'un code-barres avec l'objet générateur de rendu
3. Zend_Barcode Objects
Barcode objects allow you to generate barcodes independently of the rendering support. After
generation, you can retrieve the barcode as an array of drawing instructions that you can provide
to a renderer.
Objects have a large number of options. Most of them are common to all objects. These options
can be set in four ways:
205
Zend_Barcode
// Case 1: constructor
$barcode = new Zend_Barcode_Object_Code39($options);
// Case 2: setOptions()
$barcode = new Zend_Barcode_Object_Code39();
$barcode->setOptions($options);
// Case 3: setConfig()
$config = new Zend_Config($options);
$barcode = new Zend_Barcode_Object_Code39();
$barcode->setConfig($config);
206
Zend_Barcode
// In your bootstrap:
Zend_Barcode_Object::setBarcodeFont('my_font.ttf');
// or:
207
Zend_Barcode
Zend_Barcode::render(
'code39',
'image',
array(
'text' => 'ZEND-FRAMEWORK',
'font' => 3
)
); // will use the 3rd GD internal font
208
Zend_Barcode
3.3.1. Zend_Barcode_Object_Error
This barcode is a special case. It is internally used to automatically render an exception caught
by the Zend_Barcode component.
3.3.2. Zend_Barcode_Object_Code25
• Length: variable
3.3.3. Zend_Barcode_Object_Code25interleaved
This barcode extends Zend_Barcode_Object_Code25 (Code 2 of 5), and has the same
particulars and options, and adds the following:
209
Zend_Barcode
3.3.4. Zend_Barcode_Object_Ean2
This barcode extends Zend_Barcode_Object_Ean5 (EAN 5), and has the same particulars
and options, and adds the following:
• Name: EAN-2
• Length: 2 characters
3.3.5. Zend_Barcode_Object_Ean5
This barcode extends Zend_Barcode_Object_Ean13 (EAN 13), and has the same particulars
and options, and adds the following:
• Name: EAN-5
• Length: 5 characters
210
Zend_Barcode
3.3.6. Zend_Barcode_Object_Ean8
This barcode extends Zend_Barcode_Object_Ean13 (EAN 13), and has the same particulars
and options, and adds the following:
• Name: EAN-8
3.3.7. Zend_Barcode_Object_Ean13
• Name: EAN-13
3.3.8. Zend_Barcode_Object_Code39
• Name: Code 39
211
Zend_Barcode
• Length: variable
3.3.9. Zend_Barcode_Object_Identcode
3.3.10. Zend_Barcode_Object_Itf14
• Name: ITF-14
212
Zend_Barcode
3.3.11. Zend_Barcode_Object_Leitcode