Escolar Documentos
Profissional Documentos
Cultura Documentos
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
3.3.12. Zend_Barcode_Object_Planet
3.3.13. Zend_Barcode_Object_Postnet
213
Zend_Barcode
3.3.14. Zend_Barcode_Object_Royalmail
• Checksum: mandatory
• Length: variable
3.3.15. Zend_Barcode_Object_Upca
3.3.16. Zend_Barcode_Object_Upce
214
Zend_Barcode
4. Zend_Barcode Renderers
Renderers have some common options. These options can be set in four ways:
// Case 1
$renderer = new Zend_Barcode_Renderer_Pdf($options);
// Case 2
$renderer = new Zend_Barcode_Renderer_Pdf();
$renderer->setOptions($options);
// Case 3
$config = new Zend_Config($options);
$renderer = new Zend_Barcode_Renderer_Pdf();
$renderer->setConfig($config);
// Case 4
$renderer = new Zend_Barcode_Renderer_Pdf();
$renderer->setTopOffset(10);
215
Zend_Barcode
An additional getter exists: getType(). It returns the name of the renderer class without the
namespace (e.g. Zend_Barcode_Renderer_Image returns "image").
4.2. Zend_Barcode_Renderer_Image
The Image renderer will draw the instruction list of the barcode object in an image resource. The
component requires the GD extension. The default width of a module is 1 pixel.
216
Zend_Barcode
4.3. Zend_Barcode_Renderer_Pdf
The PDF renderer will draw the instruction list of the barcode object in a PDF document. The
default width of a module is 0.5 point.
217
Zend_Cache
1. Introduction
Zend_Cache fournit un moyen générique de mettre en cache des données.
Le cache dans Zend Framework est réalisé via les frontends alors que les caches
d'enregistrements sont stockés grâce à des adaptateurs de backend (File, Sqlite, Memcache...)
grâce à un système souple d'ID et de balises. En les utilisant, il est simple de supprimer des types
spécifiques d'enregistrements par la suite (par exemple: "supprime tous les enregistrements de
cache marqués avec une balise donnée").
$frontendOptions = array(
'lifetime' => 7200, // temps de vie du cache de 2 heures
'automatic_serialization' => true
);
$backendOptions = array(
// Répertoire où stocker les fichiers de cache
'cache_dir' => './tmp/'
);
218
Zend_Cache
Exemple 61. Mettre en cache un résultat de requête sur une base de données
Maintenant que nous avons un frontend, nous pouvons mettre en cache tout type de
données (nous avons activé la sérialisation). Par exemple nous pouvons mettre en cache le
résultat d'une requête de base de données coûteuse. Après qu'il soit mis en cache, il n'y a
plus besoin de se connecter à la base de données. Les enregistrements récupérés depuis
le cache sont désérialisés.
$cache->save($result, 'myresult');
} else {
// il y a un cache
echo "Ceci est issu du cache !\n\n";
print_r($result);
219
Zend_Cache
Nous marquons les sections dans lesquelles nous voulons un cache de sortie en ajoutant de
la logique conditionnelle, en encapsulant la section entre les méthodes start() et end()
(cela ressemble au premier exemple et est le coeur de la stratégie de mise en cache).
A l'intérieur, affichez vos données comme d'habitude toutes les sorties seront misent en
cache quand la méthode end() est appelée. A la prochaine exécution, la section complète
sera évitée, au profit de la récupération de son cache (si le cache est encore valide).
$frontendOptions = array(
// temps de vue du cache de 30 secondes
'lifetime' => 30,
// par défaut
'automatic_serialization' => false
);
$cache = Zend_Cache::factory('Output',
'File',
$frontendOptions,
$backendOptions);
Notez que nous affichons le résultat de time() deux fois ; c'est dans un but de
démonstration. Essayez de lancer la page et de la rafraîchir plusieurs fois ; vous allez
constater que le premier nombre ne change pas alors que le second change à chaque
actualisation. C'est parce que le premier nombre a été mis en cache et sauvegardé. Après
30 secondes ("lifeTime" a été mis à 30 secondes), le premier nombre devrait de nouveau
correspondre au second nombre parce que le cache a expiré -- seulement pour être mis
en cache de nouveau. Vous devriez essayer ce code dans votre navigateur ou dans une
console.
2. Aspect théorique
Il y a trois concepts clés dans Zend_Cache. Le premier est l'identifiant unique (une chaîne)
qui est utilisé pour identifier les enregistrements de cache. Le second est la directive "lifeTime"
220
Zend_Cache
vue dans les exemples ; elle définit combien de temps la ressource de cache est considérée
comme à jour. Le troisième est l'exécution conditionnelle, ainsi chaque partie de votre code
peut être évitée entièrement, pour améliorer methodname performances. La fonction principale
du frontend (Zend_Cache_Core::get()) est toujours faite pour retourner FALSE en cas de
cache manquant, si cela donne du sens à la nature d'un frontend. Cela permet aux utilisateurs
d'entourer des parties de code qu'ils veulent mettre en cache (et éviter) dans une instruction
if(){ ... } où la condition est une méthode Zend_Cache. A la fin de ces blocs, vous devez
sauvegarder ce que vous avez généré (par exemple Zend_Cache_Core::save()).
Le "Cache hit" est un terme pour une condition quand l'enregistrement de cache
est trouvé, valide, et à jour (en d'autres mots, qu'il n'a pas encore expiré). Le
"Cache miss" est tout le reste. Lorsque un "Cache miss" survient, vous devez
générer vos données (comme vous le feriez normalement) et les mettre en cache.
Lorsque vous avez un "Cache hit", le backend récupère pour vous et de façon
transparente, les enregistrements.
Dans les exemples suivants, nous nous assurerons que la variable $cache utilise une instance
de frontend valide, et que vous comprenez comment passer des paramètres à vos backends.
221
Zend_Cache
$cache->save($grande_donnees,
'monIDUnique',
array('tagA', 'tagB', 'tagC'));
$cache->remove('idAEffacer');
Pour effacer / invalider plusieurs identifiants de caches en une seule opération, vous pouvez
utiliser la méthode clean(). Par exemple, pour supprimer tous les caches :
Si vous voulez effacer les caches correspondant aux balises "tagA" et "tagC" :
$cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG,
array('tagA', 'tagC'));
Si vous voulez effacer les caches ne correspondant pas aux balises "tagA" et "tagC" :
$cache->clean(Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG,
array('tagA', 'tagC'));
Si vous voulez effacer les caches correspondant aux balises "tagA" ou "tagC" :
$cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG,
array('tagA', 'tagC'));
222
Zend_Cache
223
Zend_Cache
3.1.3. Exemples
224
Zend_Cache
Si vous stocker uniquement des chaînes de caractères dans le cache (parce qu'avec l'option
"automatic_serialization", il est possible de stocker des booléens), vous pouvez utiliser une
construction plus compact comme :
if (!($data = $cache->load($id))) {
// cache absent
$data = '';
for ($i = 0; $i < 10000; $i++) {
$data = $data . $i;
}
$cache->save($data);
Si vous voulez cacher des blocs multiples ou des instances de données, l'idée reste la même :
// block 1
if (!($data = $cache->load($id1))) {
// cache absent
$data = '';
for ($i=0;$i<10000;$i++) {
$data = $data . $i;
}
$cache->save($data);
}
echo($data);
// block 2
if (!($data = $cache->load($id2))) {
// cache missed
$data = '';
for ($i=0;$i<10000;$i++) {
$data = $data . '!';
}
$cache->save($data);
}
echo($data);
225
Zend_Cache
Si vous voulez cacher des valeurs "spéciales" (des booléens avec l'option
"automatic_serialization") ou des chaînes vides, vous ne pouvez pas utiliser la construction
compacte montrée ci-dessus. Vous devez tester de manière formelle l'état du cache.
// cache absent
$cache->save($data);
// [...]
// cache absent
$cache->save($data);
} else {
// lecture du cache
$data = $cache->load($id);
3.2. Zend_Cache_Frontend_Output
3.2.1. Introduction
Zend_Cache_Frontend_Output est un frontend capturant la sortie. Il utilise la bufferisation
de sortie de PHP pour capturer tout ce qui passe entre les méthodes start() et end().
3.2.3. Exemples
Un exemple est donnée dans le manuel, tout au début. Le voici avec des changements mineurs :
226
Zend_Cache
Utiliser cette forme est assez simple pour définir une mise de cache de sortie dans vos projets
déjà en production, avec peu de refactorisation de code.
3.3. Zend_Cache_Frontend_Function
3.3.1. Introduction
Zend_Cache_Frontend_Function met en cache les résultats des appels de fonction. Elle a
une seule méthode principale appelée call() qui prend un nom de fonction et des paramètres
pour l'appel dans un tableau.
3.3.3. Exemples
Utiliser la fonction call() est la même chose qu'utiliser call_user_func_array() en PHP :
$cache->call('veryExpensiveFunc', $params);
227
Zend_Cache
3.4. Zend_Cache_Frontend_Class
3.4.1. Introduction
Zend_Cache_Frontend_Class est différent de Zend_Cache_Frontend_Function parce
qu'elle permet de mettre en cache les objets et les méthodes statiques.
3.4.3. Exemples
Par exemple, pour mettre en cache des appels statiques :
class test {
// Méthode statique
public static function foobar($param1, $param2) {
echo "foobar_output($param1, $param2)";
return "foobar_return($param1, $param2)";
}
// [...]
$frontendOptions = array(
'cached_entity' => 'test' // Le nom de la classe
);
// [...]
// l'appel caché
$res = $cache->foobar('1', '2');
228
Zend_Cache
class test {
// [...]
$frontendOptions = array(
'cached_entity' => new test() // Une instance de la classe
);
// [...]
3.5. Zend_Cache_Frontend_File
3.5.1. Introduction
Zend_Cache_Frontend_File est un frontend piloté par la modification d'un "fichier maître".
C'est vraiment intéressant, par exemple, dans les problématiques de configuration ou de
templates. Il est également possible d'utiliser plusieurs fichiers maîtres.
Par exemple, vous avez un fichier de configuration XML qui est analysé par une
fonction, celle-ci retourne un "objet de configuration" (comme avec Zend_Config). Avec
Zend_Cache_Frontend_File, vous pouvez stocker l'objet de configuration dans le cache
(pour éviter d'analyser le fichier de configuration XML chaque fois) mais avec une sorte de forte
dépendance au fichier maître. Ainsi si le fichier XML de configuration est modifié, le cache est
immédiatement invalide.
229
Zend_Cache
3.5.3. Exemples
L'utilisation de ce frontend est la même que celle de Zend_Cache_Core. Il n'y a pas besoin
d'exemple spécifique - la seule chose à faire est de définir le master_file lors de l'utilisation de
la fabrique.
3.6. Zend_Cache_Frontend_Page
3.6.1. Introduction
Zend_Cache_Frontend_Page est comme Zend_Cache_Frontend_Output mais créé pour
une page complète. Il est impossible d'utiliser Zend_Cache_Frontend_Page pour mettre en
cache un bloc unique.
D'un autre côté, le "cache ID", est calculé automatiquement avec $_SERVER['REQUEST_URI']
et (en fonction des options) $_GET, $_POST, $_SESSION, $_COOKIE, $_FILES. De plus, vous
avez seulement une méthode pour appeler (start()) parce que l'appel à end() est totalement
automatique lorsque la page est terminé.
Pour le moment, ceci n'est pas implémenté mais nous prévoyons d'ajouter un système de
condition HTTP pour économiser de la bande passante (le système émettra un en-tête "HTTP
304 Not Modified" si le cache est trouvé, et si le navigateur a déjà la bonne version).
230
Zend_Cache
• (boolean, FALSE
par défaut)
cache_with_get_variables
:
si TRUE, le cache
est toujours activé
même s'il y a des
variables dans le
tableau $_GET
• (boolean, FALSE
par défaut)
cache_with_post_variables
:
si TRUE, le cache
est toujours activé
même s'il y a des
variables dans le
tableau $_POST
• (boolean, FALSE
par défaut)
cache_with_session_variables
:
si TRUE, le cache
est toujours activé
s'il y a des variables
dans le tableau
$_SESSION
• (boolean, FALSE
par défaut)
cache_with_files_variables
:
si TRUE, le cache est
toujours activé s'il y
a des variables dans
le tableau $_FILES
• (boolean, FALSE
par défaut)
cache_with_cookie_variables
:
si TRUE, le cache
est toujours activé
s'il y a des variables
dans le tableau
$_COOKIE
231
Zend_Cache
232
Zend_Cache
3.6.3. Exemples
$cache->start();
// si le cache est trouvé, le résultat est envoyé au navigateur
// et le script s'arrête là
233
Zend_Cache
Un exemple plus complexe qui montre un moyen pour obtenir une gestion centralisée du cache
dans un fichier d'amorçage (pour utiliser avec Zend_Controller par exemple)
$frontendOptions = array(
'lifetime' => 7200,
'debug_header' => true, // pour le déboguage
'regexps' => array(
// met en cache la totalité d'IndexController
'^/$' => array('cache' => true),
$cache->start();
// si nous trouvons un cache, le résultat est envoyé au navigateur,
// et le script s'arrête là
234
Zend_Cache
$cache->start();
// [...]
if ($unTest) {
$cache->cancel();
// [...]
}
// [...]
4.1. Zend_Cache_Backend_File
Ces backends (étendus) stockent les enregistrements de cache dans des fichiers (dans un
dossier choisi).
235
Zend_Cache
236
Zend_Cache
4.2. Zend_Cache_Backend_Sqlite
Ce backend (étendu) stocke les enregistrements de cache dans une base de donnée SQLite.
4.3. Zend_Cache_Backend_Memcached
Ce backend (étendu) stocke les enregistrements de cache dans un serveur memcached.
Memcached est un système de cache en mémoire distribuée, de haute performance. Pour utiliser
ce backend, vous devez avoir un démon memcached et l'extension PECL memcache.
Attention : avec ce backend, les balises ("tags") ne sont pas supportées pour le moment comme
l'argument "doNotTestCacheValidity=true".
237
Zend_Cache
4.4. Zend_Cache_Backend_Apc
Ce backend (étendu) stocke les enregistrements de cache en mémoire partagée grâce à
l'extension APC (Alternative PHP Cache) qui est requise pour utiliser ce backend.
Attention: avec ce backend, les balises ("tags") ne sont pas supportées pour le moment comme
l'argument "doNotTestCacheValidity=true".
238
Zend_Cache
4.5. Zend_Cache_Backend_Xcache
Ce backend stocke ces enregistrements de cache dans la mémoire partagée à travers l'extension
XCache(qui est bien sûr nécessaire pour utiliser ce backend).
Attention : avec ce backend, les balises ("tags") ne sont pas supportées pour le moment comme
l'argument "doNotTestCacheValidity=true".
4.6. Zend_Cache_Backend_ZendPlatform
Ce backend utilise l'API de cache de contenu de la Zend Platform. Naturellement, pour utiliser
ce backend, vous devez avoir installé une Zend Platorm.
Ce backend supporte les balises ("tags") mais ne supporte pas le mode de nettoyage
CLEANING_MODE_NOT_MATCHING_TAG.
Spécifiez ce backend en utilisant un séparateur de mot - "-", ".", " " ou "_" - entre les mots "Zend"
et "Platform" quand vous utilisez la méthode Zend_Cache::factory() :
4.7. Zend_Cache_Backend_TwoLevels
Ce backend (étendu) est un hybride. Il stocke les enregistrements de cache dans deux autres
backends : un rapide (mais limité) comme Apc, Memcache... et un plus "lent" comme File, Sqlite...
Spécifiez ce backend avec un séparateur de mots - "-", ".", " ", ou "_" - entre les mots "Two" et
"Levels" quand vous utilisez la méthode Zend_Cache::factory() :
239
Zend_Cache
240
Zend_Cache
4.8. Zend_Cache_Backend_ZendServer_Disk et
Zend_Cache_Backend_ZendServer_ShMem
Ces backends utilisent les fonctionnalités de mise en cache de Zend Serverpour stocker les
données.
Attention : avec ces backends ne supportent pas les balises ("tags") pour le moment de même
que l'argument "doNotTestCacheValidity=true".
Ces backends fonctionnent seulement dans l'environnement de Zend Server pour les pages
requêtées à travers HTTP ou HTTPS et ne fonctionnent pas pour les scripts exécutés en ligne
de commande.
5. Le gestionnaire de Cache
Une application comporte par nature plusieurs caches de types différents fonctions du contrôleur
ou du modèle accédé. Afin de faciliter la création et la manipulation des options de Zend_Cache
au plus tôt (par exemple en bootstrap), Zend_Cache_Manager a été créée. Cette classe est
accompagnée de Zend_Application_Resource_Cachemanager pour tout ce qui concerne
le bootstrap et Zend_Controller_Action_Helper_Cache afin d'accéder aux caches depuis
les contrôleurs et autres aides d'action.
$dbCache = array(
'frontend' => array(
'name' => 'Core',
'options' => array(
'lifetime' => 7200,
'automatic_serialization' => true
)
),
'backend' => array(
'name' => 'Core',
'options' => array(
241
Zend_Cache
$manager->setCacheTemplate('database', $dbCache);
/**
* Partout ailleurs où le gestionnaire de cache est accessible...
*/
$databaseCache = $manager->getCache('database');
$frontendOptions = array(
'lifetime' => 7200,
'automatic_serialization' => true
);
$backendOptions = array(
'cache_dir' => '/path/to/cache'
);
$dbCache = Zend_Cache::factory('Core',
'File',
$frontendOptions,
$backendOptions);
/**
* Partout ailleurs où le gestionnaire de cache est accessible...
*/
$databaseCache = $manager->getCache('database');
Si vous n'êtes pas sûr si le gestionnaire possède en lui un template de configuration ou un objet
de cache déja enregistré, vérifiez celà grâce à Zend_Cache_Manager::hasCache().
$dbCache = array(
'frontend' => array(
'name' => 'Core',
'options' => array(
'lifetime' => 7200,
'automatic_serialization' => true
)
),
'backend' => array(
'name' => 'Core',
'options' => array(
'cache_dir' => '/path/to/cache'
)
)
);
$manager->setCacheTemplate('database', $dbCache);
242
Zend_Cache
/**
* Partout ailleurs où le gestionnaire de cache est accessible...
*/
if ($manager->hasCache('database')) {
$databaseCache = $manager->getCache('database');
} else {
// Créer un cache à la main puisque non trouvé dans le gestionnaire
}
Dans certains cas, vous pouvez avoir défini un certain de cas d'utilisation avec
Zend_Cache_Manager, mais vous avez besoin de préciser un option dans un cas particulier.
Il est alors possible de modifier la configuration d'un template de cache après l'avoir saisie, ceci
au moyen de Zend_Cache_Manager::setTemplateOptions().
$dbCache = array(
'frontend' => array(
'name' => 'Core',
'options' => array(
'lifetime' => 7200,
'automatic_serialization' => true
)
),
'backend' => array(
'name' => 'Core',
'options' => array(
'cache_dir' => '/path/to/cache'
)
)
);
$manager->setCacheTemplate('database', $dbCache);
/**
* Partout ailleurs où le gestionnaire de cache est accessible...
* Ici nous changeons le support de stockage vers Memcache plutôt que ce
* qu'il était avant : File.
*/
$fineTuning = array(
'backend' => array(
'name' => 'Memcached',
'options' => array(
'servers' => array(
array(
'host' => 'localhost',
'port' => 11211,
'persistent' => true,
'weight' => 1,
'timeout' => 5,
'retry_interval' => 15,
'status' => true,
'failure_callback' => ''
)
)
)
)
);
243
Zend_Cache
$manager->setTemplateOptions('database', $fineTuning);
$databaseCache = $manager->getCache('database');
244
Zend_Captcha
1. Introduction
CAPTCHA est l'acronyme anglais de "Completely Automated Public Turing test to tell Computers
and Humans Apart" [N.D.T. : test de Turing automatisé permettant de différencier un utilisateur
humain d'un ordinateur], il est utilisé comme contre-réponse pour s'assurer que l'émetteur de
l'information est un humain et pas un processus automatisé. Typiquement, un captcha est utilisé
lors de la soumission d'un formulaire quand des utilisateurs authentifiés ne sont pas nécessaires
mais si vous désirez empêcher la soumission de spam.
Les captchas peuvent prendre un variété de formes, incluant poser des questions de logique,
présenter des polices de travers, et présenter des images en demandant ce qu'elle représente.
Zend_Captcha a pour but de fournir une variété de backends qui peuvent être utilisés de
manière autonome ou en conjonction avec Zend_Form.
2. Opération Captcha
Tous les adaptateurs CAPTCHA implémentent Zend_Captcha_Adapter, qui ressemble à
ceci :
245
Zend_Captcha
// Requête original :
$captcha = new Zend_Captcha_Figlet(array(
'name' => 'foo',
'wordLen' => 6,
'timeout' => 300,
));
$id = $captcha->generate();
echo $captcha->render($view);
3. Adaptateurs CAPTCHA
Les adaptateurs suivants sont fournis dans Zend Framework par défaut.
3.1. Zend_Captcha_Word
Zend_Captcha_Word est un adaptateur abstrait qui sert de classe de base pour la plupart
des autres adaptateurs CAPTCHA. Il fournit des mutateurs permettant de spécifier la taille
du mot le TTL de session, l'objet d'espace de noms de session à utiliser, et la classe
d'espace de noms de session à utiliser pour la persistance si vous ne souhaitez pas utiliser
Zend_Session_Namespace pour la persistance. Zend_Captcha_Word encapsule toute la
logique de validation.
Par défaut la taille du mot est de 8 caractères, le timeout de session est de 5 minutes et l'objet
d'espace de nom de session utilisé est Zend_Session_Namespace (avec l'espace de nom
("Zend_Form_Captcha_<captcha ID>").
• getWord() retourne le mot généré utilisé avec le CAPTCHA. Il sera généré pour vous si
aucun n'existe déjà.
246
Zend_Captcha
Tous les CAPTCHA Word vous autorisent à passer un tableau d'options au constructeur, ou à
setOptions() (un objet Zend_Config peut être utilisé avec setConfig()). Par défaut, les
clés timeout wordLen et sessionClass seront utilisées.
3.2. Zend_Captcha_Dumb
L'adaptateur Zend_Captch_Dumb propose une chaine aléatoire qui doit être ressaisie,
mais inversée. Ce n'est pas une solution CAPTCHA idéale (un robot peut la détourner), il
devrait être utilisé comme solution de remplacement extrême, ou pour les tests. Il étend
Zend_Captcha_Word.
3.3. Zend_Captcha_Figlet
L'adaptateur Zend_Captcha_Figlet utilise Zend_Text_Figlet pour présenter un captcha.
Seuls les caractères alphabétiques sont utilisables.
Les options passées au constructeur le seront pour l'objet Zend_Text_Figlet que l'adaptateur
va utiliser. Voyez la documentation de Zend_Text_Figletpour plus d'informations.
3.4. Zend_Captcha_Image
L'adaptateur Zend_Captcha_Image prend le mot généré et le transforme en image
difficile à analyser pour un programme informatique (robot). Pour cela, il nécessite
l'extension GD, compilée avec le support TrueType et Freetype. Actuellement, l'adaptateur
Zend_Captcha_Image ne génère que des images PNG.
247
Zend_Captcha
Toutes les options ci-dessus peuvent aussi être passées en constructeur. Supprimer la partie
"set" de leur méthodes, et passez leur première lettre en minuscule pour avoir les clés du tableau
d'options que le constructeur utilise. ("suffix", "height", "imgUrl", etc...).
3.5. Zend_Captcha_ReCaptcha
L'adaptateur Zend_Captcha_ReCaptcha utilise Zend_Service_ReCaptcha pour générer des
CAPTCHA. Les méthodes suivantes lui sont propres :
248
Zend_CodeGenerator
1. Introduction
Zend_CodeGenerator est un outils permettant de générer du code PHP en utilisant une
interface orientée objet. Il peut générer ou mettre à jour du code. Il est aussi possible d'étendre
ces classes afin de changer le langage de référence pour générer du Javascript, des hôtes
virtuels Apache ..., par exemple.
1.1. Théorie
Dans la plupart des cas, vous créerez une instance du générateur de code, et vous le configurez.
Pour afficher le code généré, un simple echo suffira, ou l'appel à sa méthode generate().
<?php
class World
{
249
Zend_CodeGenerator
Il est aussi possible de mettre à jour un code existant, par exemple, ajouter une méthode
à une classe. Dans ce cas, vous devez inspecter le code existant en utilisant la réflexion,
puis ajouter une nouvelle méthode. Zend_CodeGenerator rend ceci très simple en utilisant
Zend_Reflection.
Par exemple, imaginons que nous avons sauvegardé le code de l'exemple ci-dessus dans un
fichier "World.php" que nous avons alors inclus. Nous pourrions dès lors agir comme suit :
$class = Zend_CodeGenerator_Php_Class::fromReflection(
new Zend_Reflection_Class('World')
);
<?php
class World
{
250
Zend_CodeGenerator
2. Exemples Zend_CodeGenerator
Exemple 63. Génération de classes PHP
L'exemple suivant génère le code d'une classe avec son bloc de commentaires PHPDoc.
Le résultat est :
/**
* Sample generated class
*
* This is a class generated with Zend_CodeGenerator.
*
* @version $Rev:$
* @license New BSD
*
*/
class Foo
{
251
Zend_CodeGenerator
Exemple 64. Générer des classes PHP avec des attributs de classe
Suivant l'exemple précédant, nous ajoutons maintenant des attributs à la classe.
$foo = new Zend_CodeGenerator_Php_Class();
$docblock = new Zend_CodeGenerator_Php_Docblock(array(
'shortDescription' => 'Sample generated class',
'longDescription' => 'This is a class generated with Zend_CodeGenerator.',
'tags' => array(
array(
'name' => 'version',
'description' => '$Rev:$',
),
array(
'name' => 'license',
'description' => 'New BSD',
),
),
));
$foo->setName('Foo')
->setDocblock($docblock)
->setProperties(array(
array(
'name' => '_bar',
'visibility' => 'protected',
'defaultValue' => 'baz',
),
array(
'name' => 'baz',
'visibility' => 'public',
'defaultValue' => 'bat',
),
array(
'name' => 'bat',
'const' => true,
'defaultValue' => 'foobarbazbat',
),
));
echo $foo->generate();
Le résultat sera :
/**
* Sample generated class
*
* This is a class generated with Zend_CodeGenerator.
*
* @version $Rev:$
* @license New BSD
*
*/
class Foo
{
252
->setProperties(array(
array(
'name' => '_bar',
'visibility' => 'protected',
Zend_CodeGenerator
'defaultValue' => 'baz',
),
array(
'name'
Exemple 65. => 'baz',
Générer des classes PHP avec des méthodes
'visibility' => 'public',
'defaultValue' => 'bat',
),
array(
'name' => 'bat',
'const' => true,
'defaultValue' => 'foobarbazbat',
),
))
->setMethods(array(
// Method passed as array
array(
'name' => 'setBar',
'parameters' => array(
array('name' => 'bar'),
),
'body' => '$this->_bar = $bar;' . "\n" . 'return $this;',
'docblock' => new Zend_CodeGenerator_Php_Docblock(array(
'shortDescription' => 'Set the bar property',
/** 'tags' => array(
* Sample generated class
new Zend_CodeGenerator_Php_Docblock_Tag_Param(array(
* 'paramName' => 'bar',
* This is a class generated with Zend_CodeGenerator.
'datatype' => 'string'
* )),
* @version $Rev:$ new Zend_CodeGenerator_Php_Docblock_Tag_Return(array(
* @license New BSD 'datatype' => 'string',
*/ )),
class Foo ),
{ )),
),
protected
// Method$_bar = 'baz';
passed as concrete instance
new Zend_CodeGenerator_Php_Method(array(
Le résultat sera :
public $baz
'name'= 'bat';
=> 'getBar',
'body' => 'return $this->_bar;',
const bat = 'foobarbazbat';
'docblock' => new Zend_CodeGenerator_Php_Docblock(array(
'shortDescription' => 'Retrieve the bar property',
/** 'tags' => array(
* Set the bar property
new Zend_CodeGenerator_Php_Docblock_Tag_Return(array(
* 'datatype' => 'string|null',
* @param string)), bar
* @return string
),
*/ )),
public
)),function setBar($bar)
{
));
$this->_bar = $bar;
return $this;
echo $foo->generate();
}
/**
* Retrieve the bar property
*
* @return string|null
*/
public function getBar()
{
return $this->_bar;
}
253
Zend_CodeGenerator
/**
* Sample generated class
*
* This is a class generated with Zend_CodeGenerator.
*
* @version $Rev:$
* @license New BSD
*/
class Foo
{
/**
* Set the bar property
*
* @param string bar
* @return string
*/
public function setBar($bar)
{
$this->_bar = $bar;
return $this;
}
/**
* Retrieve the bar property
*
* @return string|null
*/
public function getBar()
{
return $this->_bar;
}
define('APPLICATION_ENV', 'testing');
254
Zend_CodeGenerator
Vous pouvez ajouter du code PHP à n'importe quel fichier PHP existant à condition d'utiliser
la réflexion sur celui-ci afin de l'analyser. La méthode fromReflectedFileName() va
vous y aider.
$generator = Zend_CodeGenerator_Php_File::fromReflectedFileName($path);
$body = $generator->getBody();
$body .= "\n\$foo->bar();";
file_put_contents($path, $generator->generate());
Exemple 68. Ajouter du code à une classe PHP existante en utilisant la réflexion
Vous pouvez aussi ajouter du code à une classe existante. Utilisez fromReflection()
pour transformer la classe en objet Reflection. Ajoutez ensuite des méthodes, des attributs,
puis régénérez le code de la classe modifiée :
$generator = Zend_CodeGenerator_Php_Class::fromReflection(
new Zend_Reflection_Class($class)
);
$generator->setMethod(array(
'name' => 'setBaz',
'parameters' => array(
array('name' => 'baz'),
),
'body' => '$this->_baz = $baz;' . "\n" . 'return $this;',
'docblock' => new Zend_CodeGenerator_Php_Docblock(array(
'shortDescription' => 'Set the baz property',
'tags' => array(
new Zend_CodeGenerator_Php_Docblock_Tag_Param(array(
'paramName' => 'baz',
'datatype' => 'string'
)),
new Zend_CodeGenerator_Php_Docblock_Tag_Return(array(
'datatype' => 'string',
)),
),
)),
));
$code = $generator->generate();
3. Zend_CodeGenerator Réference
3.1. Classes abstraites et interfaces
3.1.1. Zend_CodeGenerator_Abstract
La classe de base de CodeGenerator dont toutes les classes héritent. Elle propose l'API
suivante :
255
Zend_CodeGenerator
Le constructeur appelle _init() (restée vide, à écrire dans les classes concrètes), puis passe
le paramètre $options à setOptions(), et enfin appelle _prepare() (encore une fois, vide,
à écrire dans les classes concrètes).
Comme partout dans Zend Framework, setOptions() compare les clés du tableau passé
comme paramètre à des setters de la classe, et passe donc la valeur à la bonne méthode si
trouvée.
3.1.2. Zend_CodeGenerator_Php_Abstract
Zend_CodeGenerator_Php_Abstract étend Zend_CodeGenerator_Abstract et ajoute
des méthodes permettant de savoir si le contenu a changé et aussi le nombre d'indentation à
utiliser avant chaque ligne de code à générer. L'API est la suivante :
3.1.3. Zend_CodeGenerator_Php_Member_Abstract
Zend_CodeGenerator_Php_Member_Abstract est une classe de base pour générer des
propriétés ou des méthodes de classe, et propose des accesseurs et des mutateurs pour créer
la visibilité, l'abstraction, la staticité ou la finalité. L'API est la suivante :
256
Zend_CodeGenerator
3.2.2. Zend_CodeGenerator_Php_Class
Voici l'API :
La méthode setProperty() accepte soit un tableau qui peut être utilisé pour générer
une instance de Zend_CodeGenerator_Php_Property, soit directement une instance de
Zend_CodeGenerator_Php_Property. setMethod() se manipule de la même manière, et
utilise une instance de Zend_CodeGenerator_Php_Method.
257
Zend_CodeGenerator
3.2.3. Zend_CodeGenerator_Php_Docblock
Zend_CodeGenerator_Php_Docblock est utilisée pour générer des éléments PHP arbitraire,
y compris les commentaires de description longs ou courts.
Les tags annotation doivent être spécifiés via setTag() ou setTags() qui prennent en
paramètre un objet Zend_CodeGenerator_Php_Docblock_Tag ou un tableau qui permettra
sa construction.
Voici l'API :
3.2.4. Zend_CodeGenerator_Php_Docblock_Tag
Zend_CodeGenerator_Php_Docblock_Tag est utilisée pour créer des tags d'annotation
PHP DocBlck. Les tags doivent posséder un nom (la partie qui suit immédiatement le '@') et une
description (ce qui suit le tag).
Voici l'API :
class Zend_CodeGenerator_Php_Docblock_Tag
extends Zend_CodeGenerator_Php_Abstract
{
public static function fromReflection(
Zend_Reflection_Docblock_Tag $reflectionTag
)
public function setName($name)
public function getName()
public function setDescription($description)
public function getDescription()
public function generate()
}
3.2.5. Zend_CodeGenerator_Php_DocBlock_Tag_Param
Zend_CodeGenerator_Php_DocBlock_Tag_Param est une version spéciale de
Zend_CodeGenerator_Php_DocBlock_Tag, et permet de représenter un paramètre d'une
méthode. Le nom du tag est donc connu, mais des informations additionnelles sont requises :
le nom du paramètre et le type de données qu'il représente.
class Zend_CodeGenerator_Php_Docblock_Tag_Param
258
Zend_CodeGenerator
extends Zend_CodeGenerator_Php_Docblock_Tag
{
public static function fromReflection(
Zend_Reflection_Docblock_Tag $reflectionTagParam
)
public function setDatatype($datatype)
public function getDatatype()
public function setParamName($paramName)
public function getParamName()
public function generate()
}
3.2.6. Zend_CodeGenerator_Php_DocBlock_Tag_Return
Zend_CodeGenerator_Php_Docblock_Tab_Return est une variante qui permet de
modéliser la valeur de retour d'une méthode. Dans ce cas, le nom du tag est connu ('return')
mais pas le type de retour.
Voici l'API :
class Zend_CodeGenerator_Php_Docblock_Tag_Param
extends Zend_CodeGenerator_Php_Docblock_Tag
{
public static function fromReflection(
Zend_Reflection_Docblock_Tag $reflectionTagReturn
)
public function setDatatype($datatype)
public function getDatatype()
public function generate()
}
3.2.7. Zend_CodeGenerator_Php_File
Zend_CodeGenerator_Php_File est utilisée pour générer le contenu complet d'un fichier
PHP. Le fichier peut contenir des classes, du code PHP ou encore des commentaires PHPDoc.
Pour ajouter des classes, vous devrez soit passer un tableau d'informations à
passer au constructeur de Zend_CodeGenerator_Php_Class, soit un objet de cette
dernière classe directement. Idem concernant les commentaires PHPDoc et la classe
Zend_CodeGenerator_Php_Docblock
259
Zend_CodeGenerator
3.2.8. Zend_CodeGenerator_Php_Member_Container
Zend_CodeGenerator_Php_Member_Container est utilisée en interne par
Zend_CodeGenerator_Php_Class pour garder une trace des attributs et des méthodes de
classe. Ceux-ci sont indéxés par nom.
3.2.9. Zend_CodeGenerator_Php_Method
Zend_CodeGenerator_Php_Method est utilisée pour décrire une méthode d'une classe,
et va générer son code, et ses éventuels commentaires PHPDoc. La visibilité et
le statut (abstraite, finale, statique) peuvent être spécifiées par la classe parente
Zend_CodeGenerator_Php_Member_Abstract. Enfin, il est aussi possible de spécifier les
paramètres de la méthodes, et sa valeur de retour.
class Zend_CodeGenerator_Php_Method
extends Zend_CodeGenerator_Php_Member_Abstract
{
public static function fromReflection(
Zend_Reflection_Method $reflectionMethod
)
public function setDocblock(Zend_CodeGenerator_Php_Docblock $docblock)
public function getDocblock()
public function setFinal($isFinal)
public function setParameters(Array $parameters)
public function setParameter($parameter)
public function getParameters()
public function setBody($body)
public function getBody()
public function generate()
}
3.2.10. Zend_CodeGenerator_Php_Parameter
Zend_CodeGenerator_Php_Parameter est utilisée pour décrire un paramètre de méthode.
Chacun doit avoir une position (sinon l'ordre de leur enregistrement sera utilisé par défaut), une
valeur par défaut, un type et enfin un nom.
260
Zend_CodeGenerator
Voici l'API :
En interne setDefaultValue() convertit aussi les valeurs qui peuvent être exprimées en PHP
dans le conteneur.
3.2.11. Zend_CodeGenerator_Php_Property
Zend_CodeGenerator_Php_Property est utilisée pour décrire une propriété, nous
entendons par là une variable ou une constante. Une valeur par défaut peut alors
être spécifiée. La visibilité de la propriété peut être définie par la classe parente,
Zend_CodeGenerator_Php_Member_Abstract.
Voici l'API :
class Zend_CodeGenerator_Php_Property
extends Zend_CodeGenerator_Php_Member_Abstract
{
public static function fromReflection(
Zend_Reflection_Property $reflectionProperty
)
public function setConst($const)
public function isConst()
public function setDefaultValue($defaultValue)
public function getDefaultValue()
public function generate()
}
261
Zend_Config
1. Introduction
Zend_Config est conçu pour simplifier l'accès et l'utilisation des données de configuration dans
les applications. Il fournit une interface utilisateur basée sur des propriétés d'objets imbriquées.
Les données de configuration peuvent venir de sources variées supportant une organisation
hiérarchique des données. Actuellement Zend_Config fournit des adaptateurs pour les
données de configuration qui sont stockées dans des fichier textes avec Zend_Config_Ini
et Zend_Config_Xml.
Comme illustré dans l'exemple ci-dessus, Zend_Config fournit une syntaxe de propriétés
d'objets imbriquées pour accéder aux données de configuration passées à son constructeur.
Avec l'accès de type orienté-objet aux données, Zend_Config a aussi la méthode get() qui
retournera la valeur par défaut si l'élément n'existe pas. Par exemple :
262
Zend_Config
Il est souvent souhaitable d'utiliser une fichier de configuration en pur PHP. Le code suivant
illustre comment ceci peut être facilement réalisé :
// config.php
return array(
'webhost' => 'www.example.com',
'database' => array(
'adapter' => 'pdo_mysql',
'params' => array(
'host' => 'db.example.com',
'username' => 'dbuser',
'password' => 'secret',
'dbname' => 'mydatabase'
)
)
);
// Lecture de la configuration
$config = new Zend_Config(require 'config.php');
2. Aspect théorique
Les données de configuration sont transmis au constructeur de Zend_Config sous la forme
d'un tableau associatif, qui peut être multidimensionnel, afin de supporter une organisation
des données du général vers le spécifique. Les classes d'adaptateur concrètes permettent de
construire le tableau associatif pour le constructeur de Zend_Config à partir du système de
stockage des données de configuration. Dans certaines utilisations spécifiques, les scripts de
l'utilisateur peuvent fournir de tels tableaux directement au constructeur de Zend_Config, sans
employer une classe d'adaptateur.
Zend_Config implémente les interfaces Countable et Iterator afin de faciliter l'accès simple aux
données de configuration. Ainsi, on peut employer la fonction count() et des constructions de
PHP telles que foreach sur des objets Zend_Config.
Par défaut, les données de configuration fournies par Zend_Config ne sont pas modifiables
(lecture seule), et une affectation (par exemple, $config->database->host = 'example.com';)
lèvera une exception. Ce comportement par défaut peut cependant être surchargé par le
constructeur pour permettre la modification des valeurs de données. De plus, quand les
modifications sont autorisées, Zend_Config supporte l'effacement de valeurs (c'est-à-dire
unset($config->database->host)). La méthode readOnly() peut être utilisée pour
déterminer si les modifications sont autorisés pour un objet Zend_Config donné et la méthode
setReadOnly() peut être utilisée pour empêcher toute nouvelle modification d'un objet
Zend_Config qui aurait été créé en autorisant le modifications.
263
Zend_Config
Les classes d'adaptateur héritent de la classe de Zend_Config ce qui permet d'utiliser ses
fonctionnalités.
Les classes d'adaptateurs Zend_Config supporte un modèle d'héritage simple qui permet à
des données de configuration d'être héritées d'une section de données de configuration dans
d'autres. Ceci afin de réduire ou d'éliminer le besoin de reproduire des données de configuration
pour différents cas. Une section héritante peut également surchargée les valeurs dont elle hérite
de sa section parente. Comme l'héritage des classes PHP, une section peut hériter d'une section
parent, qui peut hériter d'une section grand-parent, et ainsi de suite, mais l'héritage multiple (c.-
à-d., la section C héritant directement des sections parents A et B) n'est pas supporté.
Si vous avez deux objets Zend_Config, vous pouvez les fusionner en un objet unique en
utilisant la fonction merge(). Par exemple, prenons $config et $localConfig, vous pouvez
fusionner $localConfig dans $config en utilisant $config->merge($localConfig);. Les
éléments de $localConfig surchargeront les éléments nommés de la même manière dans
$config.
L'objet Zend_Config qui réalise la fusion doit avoir été construit en autorisant
les modifications, en fournissant TRUE en tant que second paramètre du
constructeur. La méthode setReadOnly() peut être utilisée pour empêcher
toute nouvelle modification après la fin de la fusion.
3. Zend_Config_Ini
Zend_Config_Ini permet aux développeurs de stocker des données de configuration dans
le format familier d'un fichier INI et de les lire dans l'application en employant une syntaxe
de propriétés d'objet imbriquées. Le format INI est spécialisé pour fournir à la fois la capacité
d'avoir une hiérarchie des données de configuration et permettre l'héritage entre les sections de
données de configuration. La hiérarchie des données de configuration est supportée grâce au
fractionnement des clés à l'aide de points (.). Une section peut être étendue ou héritée d'une
autre section en suivant le nom de section avec le caractère de deux points (:) et le nom de la
section de laquelle des données doivent être héritées.
264
Zend_Config
Séparateur de clé
Par défaut, le séparateur de clé est le caractère point (.), cependant cela peut
être changé en changeant la clé nestSeparator de $options en construisant
l'objet Zend_Config_Ini. Par exemple :
$options['nestSeparator'] = ':';
$options = new Zend_Config_Ini('/chemin/vers/config.ini',
'test',
$options);
Cet exemple illustre une utilisation de base de Zend_Config_Ini pour le chargement des
données de configuration à partir d'un fichier INI. Dans cet exemple il y a des données
de configuration pour un environnement de production et pour un environnement de test.
Puisque les données de configuration de l'environnement de test sont très semblables à
celles de la production, la section de test hérite de la section de production. Dans ce cas,
la décision est arbitraire et pourrait avoir été écrite réciproquement, avec la section de
production héritant de la section de test, bien que ceci ne doit pas être le cas pour des
situations plus complexes. Supposons, que les données suivantes de configuration sont
contenues dans /chemin/vers/config.ini :
Ensuite, supposons que le développeur ait besoin des données de configuration de test
issues du fichier INI. Il est facile de charger ces données en indiquant le fichier INI et la
section de test :
265
Zend_Config
Paramètres Notes
à ce paramètre chargera toutes les
sections. Alternativement, un tableau
de noms de section peut être fourni
pour charger des sections multiples.
$options (par défault FALSE) Tableau d'options. Les clés suivantes
sont supportées :
4. Zend_Config_Xml
Zend_Config_Xml permet aux développeurs de stocker des données de configuration dans
un format simple XML et de les lire grâce à une syntaxe de propriétés d'objets imbriquées. Le
nom de l'élément racine du fichier XML n'a pas d'importance et peut être nommé arbitrairement.
Le premier niveau des éléments XML correspond aux sections des données de configuration.
Le format XML supporte l'organisation hiérarchique par l'emboîtement des éléments XML à
l'intérieur des éléments de niveau section. Le contenu d'un élément XML de niveau le plus bas
correspond aux données de configuration. L'héritage des sections est supportée par un attribut
spécial de XML nommé extends, et la valeur de cet attribut correspond à la section de laquelle
des données doivent être héritées.
Type retourné
266
Zend_Config
Cet exemple illustre une utilisation de base de Zend_Config_Xml pour le chargement des
données de configuration à partir d'un fichier XML. Dans cet exemple il y a des données
de configuration pour un environnement de production et pour un environnement de test.
Puisque les données de configuration de l'environnement de test sont très semblables à
celles de la production, la section de test hérite de la section de production. Dans ce cas,
la décision est arbitraire et pourrait avoir été écrite réciproquement, avec la section de
production héritant de la section de test, bien que ceci ne doit pas être le cas pour des
situations plus complexes. Supposons, que les données suivantes de configuration sont
contenues dans /chemin/vers/config.xml :
<?xml version="1.0"?>
<configdata>
<production>
<webhost>www.example.com</webhost>
<database>
<adapter>pdo_mysql</adapter>
<params>
<host>db.example.com</host>
<username>dbuser</username>
<password>secret</password>
<dbname>dbname</dbname>
</params>
</database>
</production>
<test extends="production">
<database>
<params>
<host>dev.example.com</host>
<username>devuser</username>
<password>devsecret</password>
</params>
</database>
</test>
</configdata>
267
Zend_Config
<?xml version="1.0"?>
<configdata>
<production webhost="www.example.com">
<database adapter="pdo_mysql">
<params host="db.example.com"
username="dbuser"
password="secret"
dbname="dbname"/>
</database>
</production>
<staging extends="production">
<database>
<params host="dev.example.com"
username="devuser"
password="devsecret"/>
</database>
</staging>
</configdata>
<?xml version="1.0"?>
<configdata>
<production>
<webhost>www.example.com</webhost>
<database>
<adapter value="pdo_mysql"/>
<params>
<host value="db.example.com"/>
<username value="dbuser"/>
<password value="secret"/>
<dbname value="dbname"/>
</params>
</database>
</production>
<staging extends="production">
<database>
<params>
<host value="dev.example.com"/>
<username value="devuser"/>
<password value="devsecret"/>
</params>
</database>
</staging>
</configdata>
268
Zend_Config
$string = <<<EOT
<?xml version="1.0"?>
<config>
<production>
<db>
<adapter value="pdo_mysql"/>
<params>
<host value="db.example.com"/>
</params>
</db>
</production>
<staging extends="production">
<db>
<params>
<host value="dev.example.com"/>
</params>
</db>
</staging>
</config>
EOT;
Zend_Config possède son propre espace de noms XML, qui ajoute des
fonctionnalités additionnelles lors du processus d'analyse. Pour tirer avantage
de celui-ci, vous devez définir l'espace de noms avec l'URI http://
framework.zend.com/xml/zesnd-config-xml/1.0/ dans votre noeud
racine de configuration.
Avec l'espace de noms activé, vous pouvez maintenant utiliser les constantes
PHP à l'intérieur de vos fichiers de configuration. De plus l'attribut extends a été
déplacé dans ce nouvel espace de noms et déprécié de l'espace de noms NULL.
Il en sera complétement effacé dans Zend Framework 2.0.
$string = <<<EOT
<?xml version="1.0"?>
<config xmlns:zf="http://framework.zend.com/xml/zend-config-xml/1.0/">
<production>
<includePath>
<zf:const zf:name="APPLICATION_PATH"/>/library</includePath>
<db>
<adapter value="pdo_mysql"/>
<params>
<host value="db.example.com"/>
</params>
</db>
</production>
269
Zend_Config
<staging zf:extends="production">
<db>
<params>
<host value="dev.example.com"/>
</params>
</db>
</staging>
</config>
EOT;
define('APPLICATION_PATH', dirname(__FILE__));
$config = new Zend_Config_Xml($string, 'staging');
270
Zend_Config_Writer
1. Zend_Config_Writer
Zend_Config_Writer vous donne la possibilité d'écrire des fichiers de configuration à partir
d'objets Zend_Config. Il fonctionne avec des adaptateurs détachés du système et est donc
très simple à utiliser. Par défaut Zend_Config_Writer embarque trois adaptateurs, qui
fonctionnent tous sur la base de fichiers. Vous instanciez un rédacteur ("writer") avec des
options spécifiques, qui peuvent être filename et config. Ensuite vous pouvez appeler la
méthode write() du rédacteur et le fichier de configuration est créé. Vous pouvez aussi fournir
$filename et $config directement à la méthode write(). Actuellement les rédacteurs
suivants sont embarqués avec Zend_Config_Writer :
• Zend_Config_Writer_Array
• Zend_Config_Writer_Ini
• Zend_Config_Writer_Xml
Le rédacteur INI possède deux modes de rendu en fonction des sections. Par défaut la
configuration de premier niveau est toujours écrite dans des noms de section. En appelant
$writer->setRenderWithoutSections();, toutes les options sont écrites dans l'espace de noms
global du fichier INI et aucune section n'est appliquée.
271
Zend_Config_Writer
$config->setExtend('staging', 'production');
$config->production->db = array();
$config->production->db->hostname = 'localhost';
$config->production->db->username = 'production';
$config->staging->db = array();
$config->staging->db->username = 'staging';
// b)
$writer = new Zend_Config_Writer_Xml();
$writer->setConfig($config)
->setFilename('config.xml')
->write();
// c)
$writer = new Zend_Config_Writer_Xml();
$writer->write('config.xml', $config);
Ceci créera un fichier de configuration XML avec les sections "production" et "staging", où
"staging" étend "production".
// Ecrire le fichier
$writer = new Zend_Config_Writer_Ini(array('config' => $config,
'filename' => 'config.ini'));
$writer->write();
272
Zend_Config_Writer
Pour tous les rédacteurs à base de fichiers (INI, XML et tableau PHP), en interne la méthode
render() est utilisée pour construire la chaîne de configuration. Cette méthode peut être
utilisée en dehors de la classe si vous souhaitez accéder à une représentation textuelle de vos
données de configuration.
273
Zend_Console_Getopt
1. Introduction
La classe Zend_Console_Getopt aide les applications lancées en ligne de commande à
analyser les options et arguments.
Les utilisateurs peuvent spécifier des arguments en ligne de commande quand ils exécutent votre
application. Ces arguments ont du sens dans l'application : changer son comportement, choisir
des ressources, ou spécifier des paramètres. Beaucoup d'options ont une signification usuelle,
par exemple "--verbose" permet la production d'informations supplémentaires dans beaucoup
d'applications. D'autres options peuvent avoir un sens qui est différent pour chaque application.
Par exemple, "-c" prend des sens différents lorsqu'il est utilisé dans grep, ls, et tar.
Nous avons ci-dessous quelques définitions de termes. L'usage commun des termes varie, mais
cette documentation utilisera les définitions ci-dessous.
• "argument" : une chaîne de caractères qui apparaît dans la ligne de commande après le nom
de la commande. Les arguments peuvent être des options ou bien peut apparaître sans option,
appeler des ressources sur lesquelles la commande agit.
• "option" : un argument qui signifie que la commande va changer son comportement par défaut
d'une manière quelconque.
• "flag" (drapeau) : la première partie d'une option, identifie le but de l'option. Un drapeau est
précédé conventionnellement par un ou deux tirets ("-" or "--"). Un drapeau court comporte
un caractère unique. Un drapeau long est une chaîne de plusieurs caractères. Un tiret simple
précède un drapeau court ou un groupe de drapeaux courts. Un tiret double précède un
drapeau long. Les drapeaux longs ne peuvent pas être groupés.
• "parameter" (paramètre) : la seconde partie d'une option ; une donnée qui peut accompagner
un drapeau, si c'est applicable à l'option donnée. Par exemple, beaucoup de commandes
acceptent "--verbose", mais typiquement cette option n'a aucun paramètre. Cependant, une
option comme "--user a presque toujours besoin d'un paramètre à sa suite.
Un paramètre peut être donné comme un argument séparé après un argument de drapeau,
ou comme faisant partie de la même chaîne de caractères, séparé du drapeau par le
symbole égal ("="). La dernière forme est autorisée seulement avec des drapeaux longs. Par
exemple, -u username, --user username, et --user=username sont des formats supportés
par Zend_Console_Getopt.
• "cluster" (groupe) : les drapeaux courts peuvent être combinés dans une chaîne de caractère
unique précédée par un tiret simple. Par exemple, " ls -1str" emploie un groupe de quatre
drapeaux courts. Cette commande est équivalente à " ls -1 -s -t -r". Seuls les drapeaux courts
peuvent être groupés. Vous ne pouvez pas faire un groupe de drapeaux longs.
Par exemple, dans "mysql --user=root mabase", "mysql" est la commande, "--user=root" est
une option, "--user" est un drapeau, "root" est un paramètre de l'option, et "mabase" est un
argument mais pas une option dans notre définition.
Zend_Console_Getopt fournit une interface pour déclarer quels drapeaux sont valides pour
votre application, produit une erreur et un message s'ils emploient un drapeau invalide, et
transmet à votre application les drapeaux spécifiés par l'utilisateur.
274
Zend_Console_Getopt
Le troisième argument du constructeur peut contenir des options de configuration pour adapter
le comportement de Zend_Console_Getopt. Voir la section Ajouter une configuration pour la
référence des options disponibles.
La syntaxe courte est limitée aux drapeaux courts (1 seul caractère). Les alias, les types des
paramètres, et les messages d'aide ne sont pas supportés dans la syntaxe courte.
275
Zend_Console_Getopt
comme pour la syntaxe courte, la syntaxe longue emploie un tableau associatif comme premier
argument du constructeur pour déclarer les options.
La clé de chaque élément du tableau associatif est une chaîne avec un format qui nomme le
drapeau, avec tous ses alias, séparés par le symbole "|". Après la série des alias, si l'option exige
un paramètre, il y a un symbole égal ("=") avec une lettre qui représente le type du paramètre :
• "=w" pour un paramètre de type mot (une chaîne de caractère qui ne contient pas d'espace).
La valeur de chaque élément dans le tableau associatif est un message d'aide pour décrire à
l'utilisateur comment employer votre programme.
Dans l'exemple ci-dessus, il y a trois options. "--abricot" et "-a" sont des alias l'un pour l'autre et
l'option ne prend pas de paramètres. "--banane" et "-b" sont des alias l'un pour l'autre et l'option
prend un paramètre obligatoire de type entier. Enfin, "--pear" et "-p" sont des alias l'un pour
l'autre et l'option prend un paramètre optionnel de type chaîne de caractère.
L'analyse des données est reportée jusqu'à ce que vous invoquiez pour la première fois l'objet
Zend_Console_Getopt pour découvrir si une option était renseignée, l'objet exécute alors
son analyse. Ceci permet plusieurs appels de méthode pour configurer les options, arguments,
messages d'aide, et les options de configuration, avant que l'analyse ne soit lancée.
276
Zend_Console_Getopt
try {
$opts = new Zend_Console_Getopt('abp:');
$opts->parse();
} catch (Zend_Console_Getopt_Exception $e) {
echo $e->getUsageMessage();
exit;
}
• Le paramètre d'option n'a pas le bon type. Par exemple, une chaîne de caractères non-
numérique quand un nombre entier a été exigé.
De manière alternative, vous pouvez employer la fonction magique __get() pour rechercher
la valeur d'une option comme si c'était une variable de membre de classe. La méthode magique
__isset() est également implémentée.
Si vos options sont déclarées avec des alias, vous pouvez employer n'importe quel alias de
l'option dans les méthodes ci-dessus.
• Comme pour une chaîne de caractères : employez la méthode toString(). Les options
sont retournées sous la forme d'une chaîne de caractère où les paires drapeau=valeur sont
séparées par des espaces. La valeur d'une option qui n'a pas de paramètre est la chaîne
"TRUE".
277
Zend_Console_Getopt
• Comme un tableau : employez la méthode toArray(). Les options sont retournées dans
un tableau de chaînes de caractères indexé par des nombres, les chaînes de drapeau sont
suivies par les chaînes de paramètres éventuels.
Dans toutes les méthodes de déchargement ci-dessus, la chaîne du drapeau est la première
chaîne dans la liste des alias correspondants. Par exemple, si les noms d'alias d'option étaient
déclarés comme "verbose|v", alors la première chaîne, "verbose", est employé comme nom de
l'option. Le nom du drapeau d'option n'inclut pas le tiret précédent.
4. Configurer Zend_Console_Getopt
4.1. Ajouter des règles d'options
Vous pouvez ajouter de nouvelles règles d'option en complément de celles indiquées dans le
constructeur de Zend_Console_Getopt, en utilisant la méthode addRules(). L'argument
d'addRules() est identique au premier argument du constructeur de classe. C'est soit une
chaîne dans le format d'options de syntaxe courte, soit un tableau associatif dans le format
d'options de syntaxe longue. Voir "Déclarer les règles Getopt" pour les détails sur la syntaxe de
déclaration des options.
L'exemple au-dessus montre comment ajouter l'option "--verbose" avec l'alias "-v" à
un ensemble d'options définies dans l'appel au constructeur. Notez que vous pouvez
mélanger des options de syntaxe courte et de syntaxe longue dans la même instance de
Zend_Console_Getopt.
278
Zend_Console_Getopt
Si vous déclarez des options avec des alias, vous pouvez employer n'importe quel alias comme
clé du tableau associatif.
La méthode setHelp() est la seule manière de définir des messages d'aide si vous déclarez
les options en utilisant la syntaxe courte.
Un alias ne peut être déclaré qu'une seule fois. Si vous essayez de redéfinir un alias, une
Zend_Console_Getopt_Exception est levée.
Dans l'exemple ci-dessus, après leurs déclarations, "-a", "--apple" et "--apfel" sont des alias les
un pour les autres. En outre "-p" et "--pear" sont des alias l'un pour l'autre.
La méthode setAliases() est la seule manière de définir des alias si vous déclarez les options
en utilisant la syntaxe courte.
279
Zend_Console_Getopt
Les options actuellement supportées ont des définitions de constantes dans la classe. Les
options, leurs constantes (avec des valeurs littérales entre parenthèses) sont énumérées ci-
dessous :
Plus d'options de configuration pourront être ajoutées en tant que futurs perfectionnements de
cette classe.
280
Zend_Console_Getopt
Les deux arguments de la méthode setOption() sont un nom d'option de configuration et une
valeur d'option.
281
Zend_Controller
1. Zend_Controller - Démarrage rapide
1.1. Introduction
Zend_Controller est le coeur du système MVC de Zend Framework. MVC équivaut à
Modèle-Vue-Contrôleuret est un motif de conception ("design pattern") visant à séparer la
logique d'application de la logique d'affichage. Zend_Controller_Front implémente un
modèle de contrôleur frontal ("Front Controller"), dans lequel toutes les demandes sont arrêtées
par le contrôleur frontal et distribuées vers différents contrôleurs d'action ("Action Controller")
basés sur l'URL demandé.
Le système Zend_Controller a été construit dans un souci d'extensibilité, soit par sous-
classement des classes existantes, en écrivant les nouvelles classes qui implémentent les
diverses interfaces et les classes abstraites qui forment la base de la famille de contrôleur
des classes, soit par écriture de plugins ou d'aides d'action afin d'utiliser ou d'augmenter les
fonctionnalités du système.
La première étape est de créer votre disposition de système de fichiers. La disposition typique
est la suivante :
application/
controllers/
IndexController.php
models/
views/
scripts/
index/
index.phtml
helpers/
filters/
html/
.htaccess
index.php
Dans votre serveur Web, faîtes pointer votre "document root" vers le dossier html du système
de fichiers ci-dessus.
282
Zend_Controller
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
Connaître mod_rewrite
Les règles de réécriture ci-dessus autorisent l'accès à tous fichiers sous la racine
de votre hôte virtuel. Si vous avez des fichiers que vous ne souhaitez pas
exposer, vous pouvez souhaiter restreindre ces règles. Allez sur le site d'Apache
pour en apprendre davantage concernant mod_rewrite.
Si vous avez IIS 7.0, utilisez ceci pour votre configuration de réécriture :
Les règles ci-dessus vont redirigées toute requête vers des ressources existantes (liens
symboliques, fichiers non vides, ou répertoires non-vides), tout autre requête sera renvoyée vers
le contrôleur frontal.
Les règles de réécriture ci-dessus sont pour Apache ; pour des exemples
de règles de réécriture pour d'autres serveurs Web, reportez-vous à la
documentation du routeur.
283
Zend_Controller
Zend_Controller_Front::run('/chemin/vers/application/controllers');
Ceci va instancier et distribuer le contrôleur frontal, qui conduira les demandes vers les
contrôleurs d'action.
De la même manière, la valeur de l'action correspond à une méthode de la classe contrôleur. Par
défaut, la valeur est écrit en minuscule, et le mot Action est ajouté. Ainsi, dans notre exemple
ci-dessus, l'action components devient componentsAction, et la méthode finale appelée est
RoadmapController::componentsAction().
Créons maintenant un contrôleur d'action par défaut et une méthode d'action. Comme noté
auparavant, le contrôleur et l'action par défaut sont tous les deux nommées index. Ouvrez le
fichier application/controllers/IndexController.php, et entrez ce qui suit :
/** Zend_Controller_Action */
Par défaut, l'aide d'action ViewRendererest activé. Ceci signifie qu'en définissant simplement
une méthode d'action et un script de vue correspondant, vous obtiendrez automatiquement le
rendu du contenu. Par défaut, Zend_View est utilisé en tant que couche Vue dans le MVC. Le
ViewRenderer utilise le nom du contrôleur (par exemple, index) et le nom de l'action courante
(par exemple, index) pour déterminer le modèle à afficher. Par défaut, le fichier modèle se
termine par l'extension .phtml, cela signifie que dans l'exemple ci-dessus, le modèle index/
index.phtml sera rendu. De plus, le ViewRenderer suppose automatiquement que le dossier
views situé au même niveau que le dossier des contrôleurs est le dossier de base des vues,
et que les scripts de vues sont dans le sous-dossier views/scripts/. Ainsi le modèle rendu
sera trouvé dans application/views/scripts/index/index.phtml.
284
Zend_Controller
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>My first Zend Framework App</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
/** Zend_Controller_Action */
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Erreur</title>
</head>
<body>
<h1>Erreur apparue</h1>
<p>Une erreur est apparue ; veuillez rééssayer plus tard.</p>
</body>
</html>
• http://exemple.fr/
• http://exemple.fr/index
• http://exemple.fr/index/index
285
Zend_Controller
Vous êtes maintenant prêt à créer de nouveaux contrôleurs et de nouvelles méthodes d'action.
Félicitations !
2. Fondations de Zend_Controller
Le système Zend_Controller est conçu pour être léger, modulaire, et extensible. Il est de
conception minimaliste pour permettre la flexibilité et la liberté aux utilisateurs tout en fournissant
assez de structure de sorte que les systèmes établis autour de Zend_Controller partagent
certaines conventions communes et dispositions semblables de code.
Le diagramme suivant décrit le déroulement des opérations, et ce qui suit décrit en détail les
interactions :
286
Zend_Controller
287
Zend_Controller
288
Zend_Controller
289
Zend_Controller
3.2.1. getInstance()
getInstance() est utilisé pour récupérer une instance du contrôleur frontal. Comme le
contrôleur frontal implémente un motif de Singleton, c'est aussi le seul moyen possible pour
instancier un objet unique de contrôleur frontal.
$front = Zend_Controller_Front::getInstance();
Quelques exemples :
290
Zend_Controller
Vous pouvez récupérer les réglages courants des répertoires du contrôleur en utilisant
getControllerDirectory() ; ceci retournera un tableau des paires modules/chemins.
Chaque module doit être dans son propre dossier, ce dossier étant un miroir du dossier
du module "default" en terme de structure - c'est-à-dire, qu'il doit contenir un sous-dossier
"controllers" au minimum, et typiquement un sous-dossier "views" puis d'autres sous-dossiers.
Ensuite, si vous souhaitez déterminer le chemin vers un module en particulier ou vers le module
courant, vous pouvez appeler getModuleDirectory(), en fournissant optionnellement le
nom du module spécifique que vous recherchez.
3.2.4. dispatch()
dispatch(Zend_Controller_Request_Abstract $request = null,
Zend_Controller_Response_Abstract $response = null) fait le gros travail du
contrôleur frontal. Il peut facultativement prendre un objet de requêteet/ou un objet de réponse,
permettant ainsi au développeur de fournir des objets personnalisés.
Si aucun objet de requête ou de réponse ne lui sont fournis, dispatch() vérifiera s'il existe des
objets précédemment enregistrés et utilisera ceux-là ou des objets par défaut pour les utiliser
dans son processus (dans les deux cas, le mode HTTP sera utilisé par défaut).
De la même manière, dispatch() vérifie s'il existe des objets routeuret distributeurinscrits, et
instancie des versions par défaut si aucun n'est trouvé.
• le routage
• la distribution
• la réponse
Le routage a lieu exactement une fois, utilisant les valeurs de l'objet de requête quand
dispatch() est appelé. La distribution a lieu dans une boucle ; une demande peut soit indiquer
des actions multiples à distribuer, soit le contrôleur ou un plugin peuvent remettre à zéro l'objet
de requête et ainsi forcer la distribution d'actions supplémentaires. Quand tout est réalisé, le
contrôleur frontal retourne la réponse.
291
Zend_Controller
3.2.5. run()
Zend_Controller_Front::run($path) est une méthode "raccourci", statique, prenant
simplement un chemin vers un répertoire contenant des contrôleurs. Elle récupère l'instance
de contrôleur frontal (via getInstance()), enregistre le chemin fourni par l'intermédiaire de
setControllerDirectory(), et finalement réalise la distribution.
Fondamentalement, run() est une méthode de convenance qui peut être employée pour les
installations de sites qui n'exigent pas la personnalisation de l'environnement du contrôleur
frontal.
• resetInstance() peut être utilisé pour effacer tous les réglages courants. Son but principal
est pour les tests, mais elle peut également être employée pour des instances où vous
souhaitez enchaîner ensemble les contrôleurs frontaux multiples.
Lors de la recherche d'un objet routeur, cela vérifie d'abord si un objet est présent, et sinon,
instancie le routeur par défaut ("rewrite router").
• (set|get)BaseUrl() vous permet d'indiquer l'URL de base à écarter lors du routage des
requêtes et de rechercher la valeur courante. La valeur est fournie à l'objet de requête juste
avant le routage.
Lors de la recherche d'un objet distributeur, cela vérifie d'abord si un objet est présent, et
sinon, instancie le distributeur par défaut.
292
Zend_Controller
• setParams(array $params) vous permet de régler des paramètres multiples en une seule
fois en utilisant un tableau associatif.
Il y a plusieurs paramètres prédéfinis qui peuvent être réglés et qui ont des utilisations spécifiques
dans la chaîne d'expédition :
293
Zend_Controller
Voir Section 12.3, « Différents types d'exceptions que vous pouvez rencontrer » pour plus
d'informations concernant l'utilisation de ce réglage.
return self::$_instance;
}
}
Typiquement, vous n'aurez pas besoin de sous-classer le contrôleur frontal à moins que vous ne
deviez ajouter une nouvelle fonctionnalité (par exemple, un plugin d'autoloader, ou une manière
d'indiquer des chemins d'aide d'action). Quelques exemples où vous pouvez vouloir changer le
comportement peuvent inclure modifier comment des répertoires de contrôleur sont stockés, ou
quel routeur ou distributeur par défaut sont employés.
4. L'objet Requête
4.1. Introduction
L'objet requête est un objet simple de valeur qui est passé entre Zend_Controller_Front
et le routeur, le distributeur, et les classes de contrôleur. Il empaquette les noms du module
demandé, du contrôleur, de l'action, et des paramètres facultatifs, aussi bien que le reste de
l'environnement de requête, que ce soit le HTTP, du CLI, ou de PHP-GTK.
294
Zend_Controller
• Les paramètres accessibles par l'action sont dans un tableau associatif de paires clés
et valeurs qui sont récupérables par getParams() et modifiables par setParams(), ou
individuellement par getParam() et setParam().
L'objet requête est passé au contrôleur frontal, ou si aucun n'est fourni, il est instancié au début
du processus de distribution, avant que le routage ne se produise. Il est passé à travers chaque
objet dans la chaîne de distribution.
De plus, l'objet requête est particulièrement utile pour les tests. Le développeur peut simuler
l'environnement de requête, y compris le module, le contrôleur, l'action, les paramètres, l'URI,
etc., et passe l'objet requête au contrôleur frontal pour tester une application. Une fois appairés
avec l'objet réponse, les tests unitaires élaboré et précis d'application MVC deviennent possible.
Données superglobales
En accédant à des données superglobales par
Zend_Controller_Request_Http en tant que propriétés publiques de
membre, il est nécessaire de maintenir dans l'esprit que le nom de propriété
(clé du tableau des superglobales) est assorti à une superglobale dans un ordre
spécifique de priorité : 1. GET, 2. POST, 3. COOKIE, 4. SERVER, 5. ENV.
Des superglobales spécifiques peuvent être accédées en utilisant une méthode publique comme
alternative. Par exemple, la valeur directe $_POST['user'] peut être accédée en appelant
getPost('user') sur l'objet de requête. Ceci inclue getQuery() pour rechercher des
éléments $$_GET, et getHeader() pour récupérer les en-têtes de la requête.
295
Zend_Controller
Vous pouvez également placer des paramètres d'utilisateur dans l'objet de requête en utilisant
setParam() et récupérer ces derniers en utilisant plus tard getParam(). Le routeur se sert de
cette fonctionnalité pour faire correspondre des paramètres de l'URI de la requête dans l'objet
requête.
Si vous souhaitez rechercher seulement dans les valeurs que vous avez
paramétrées avec setParam(), utilisez getUserParam().
Caprices d'Apache
Si vous utilisez le gestionnaire 404 d'Apache pour passer les requêtes
entrantes au contrôleur frontal, ou si vous utilisez le drapeau PT avec les
règles de ré-écriture, $_SERVER['REDIRECT_URL'] contient l'URI dont vous
avez besoin, pas $_SERVER['REQUEST_URI']. Si vous employez une telle
installation et obtenez un routage invalide, vous devriez employer la classe
Zend_Controller_Request_Apache404 au lieu de la classe HTTP par
défaut pour votre objet de requête :
296
Zend_Controller
Par exemple, si vous maintenez votre index.php dans un sous-répertoire du serveur Web
appelé /projects/myapp/index.php, la base d'URL (rewrite base) devrait être réglé à /
projects/myapp. Cette chaîne sera alors dépouillée du début du chemin avant de calculer tous
les routes correspondantes. Ceci libère de la nécessité de l'ajouter au début de n'importe laquelle
de vos routes. Une route 'user/:username' correspondra à des URI comme http://
localhost/projects/myapp/user/martel et http://example.com/user/martel.
Si la base de l'URL est détectée de manière inexacte vous pouvez la surcharger avec
votre propre chemin de base grâce à la méthode setBaseUrl() soit de la classe de
Zend_Controller_Request_Http, soit de la classe de Zend_Controller_Front. La
méthode la plus facile est de la régler dans Zend_Controller_Front, qui le transmets dans
l'objet de requête. Exemple d'utilisation pour régler une base d'URL personnalisée :
/**
* Distribue la requête avec une base d'URL réglé
* avec Zend_Controller_Front.
*/
$router = new Zend_Controller_Router_Rewrite();
$controller = Zend_Controller_Front::getInstance();
$controller->setControllerDirectory('./application/controllers')
->setRouter($router)
->setBaseUrl('/projects/myapp'); // affecte la base d'url
$response = $controller->dispatch();
• isGet()
• isPost()
• isPut()
• isDelete()
• isHead()
• isOptions()
La principale utilisation est lors de la création des architectures MVC de type REST.
297
Zend_Controller
Actuellement, on sait que cet en-tête est fourni par défaut par les bibliothèques JS suivantes :
• Yahoo! UI Library
• jQuery
• MochiKit
La plupart des librairies AJAX vous permettent d'envoyer vos propres en-têtes de requête HTTP ;
si votre librairie n'envoie pas cet en-tête, ajoutez le simplement afin d'être sûr que la méthode
isXmlHttpRequest() fonctionne dans votre cas.
/**
* @param string $value
* @return self
*/
public function setControllerName($value);
/**
* @return string
*/
public function getActionName();
/**
* @param string $value
* @return self
*/
public function setActionName($value);
/**
* @return string
*/
public function getControllerKey();
/**
* @param string $key
* @return self
*/
public function setControllerKey($key);
/**
* @return string
*/
public function getActionKey();
298
Zend_Controller
/**
* @param string $key
* @return self
*/
public function setActionKey($key);
/**
* @param string $key
* @return mixed
*/
public function getParam($key);
/**
* @param string $key
* @param mixed $value
* @return self
*/
public function setParam($key, $value);
/**
* @return array
*/
public function getParams();
/**
* @param array $array
* @return self
*/
public function setParams(array $array);
/**
* @param boolean $flag
* @return self
*/
public function setDispatched($flag = true);
/**
* @return boolean
*/
public function isDispatched();
}
Étendez cette classe, ou une de ses dérivés, quand vous avez besoin de la classe de requête
pour interagir avec un environnement spécifique afin de récupérer des données pour les utiliser
dans les tâches ci-dessus. Les exemples incluent l'environnement HTTP, un environnement CLI,
ou un environnement PHP-GTK.
5. Routeur Standard
5.1. Introduction
Zend_Controller_Router_Rewrite est le routeur par défaut du framework. Le routage
consiste à analyser l'URI définie (la partie après l'URL de base) et la décomposer en valeurs
299
Zend_Controller
déterminant quels module, contrôleur et action doivent recevoir la requête. Ces valeurs sont
encapsulées dans un objet de requête Zend_Controller_Request_Http qui est alors injecté
dans Zend_Controller_Dispatcher_Standard pour y être traité Le routage n'est effectué
qu'une seule fois par requête : juste avant que le premier contrôleur ne soit traité (distribué)
RewriteEngine on
RewriteRule !\.(js|ico|gif|jpg|png|css|html)$ index.php
ou (recommandé) :
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
Le routeur de réécriture peut aussi être utilisé avec un serveur Web IIS (versions <= 7.0) si
Isapi_Rewrite a été installée comme une extension Isap avec la règle suivante :
IIS Isapi_Rewrite
Lorsque IIS est utilisé, $_SERVER['REQUEST_URI'] n'existera pas ou vaudra
une chaîne vide. Dans ce cas, Zend_Controller_Request_Http essaiera
d'utiliser la valeur de $_SERVER['HTTP_X_REWRITE_URL'], initialisée par
l'extension Isapi_Rewrite.
IIS 7.0 introduit un moodule de réécriture d'URL natif, et il peut être configuré comme ceci :
300
Zend_Controller
</rules>
</rewrite>
</system.webServer>
</configuration>
url.rewrite-once = (
".*\?(.*)$" => "/index.php?$1",
".*\.(js|ico|gif|jpg|png|css|html)$" => "$0",
"" => "/index.php"
)
/* Créer un routeur */
$router = $frontctrl->getRouter();
// retourne un routeur de réécriture par défaut
$router->addRoute(
'user',
new Zend_Controller_Router_Route('user/:username',
array('controller' => 'user',
'action' => 'info'))
);
$router->addRoute('user',
new Zend_Controller_Router_Route('user/:username'));
Le routeur de réécriture est fourni avec six types de route, dont une spéciale :
• Zend_Controller_Router_Route
• Zend_Controller_Router_Route_Static
• Zend_Controller_Router_Route_Regex
• Zend_Controller_Router_Route_Hostname
• Zend_Controller_Router_Route_Chain
• Zend_Controller_Router_Rewrite *
Chaque route peut être utilisée plusieurs fois pour créer un chaîne de routes représentant un
schéma de routage personnalisé. La route du module, en revanche, ne devrait être utilisée
qu'une seule fois, elle est en générale la route la plus générique (par défaut). Chaque route sera
définie un peu plus tard.
Le premier paramètre de addRoute est le nom de la route. Il sera utilisé plus tard pour la
sélectionner (par exemple pour générer un URL. Le deuxième paramètre étant l'objet route lui-
même.
301
Zend_Controller
L'utilisation la plus plausible du nom de la route est illustrée dans l'aide vue "url" :
Le routage consiste simplement à itérer toutes les routes reçues et à les faire correspondre
à l'URI de la requête courante. Dès qu'une correspondance est établie, les variables sont
injectées dans l'objet Zend_Controller_Request utilisé après dans le distributeur et dans les
contrôleurs. Si aucune correspondance n'est trouvée, la route suivante dans la pile est analysée.
Si vous devez déterminer quelle route a été trouvée, vous pouvez utilisez la méthode
getCurrentRouteName(), qui vous retournera l'identifiant utilisé pour enregistrer la route
dans le routeur. Si vous souhaitez récupérer l'objet de la route actuelle, vous pouvez utiliser
getCurrentRoute().
Pile LIFO
Les routes sont analysées dans l'ordre LIFO : dernière fournie, première
analysée. Veillez à définir les routes les génériques en premier donc.
Paramètres de la requête
Les paramètres de la requête proviennent de l'utilisateur,
ou des routes définies. Ils seront plus tard accessibles
via Zend_Controller_Request::getParam() ou la méthode
Zend_Controller_Action::_getParam().
Il y a trois valeurs spéciales qui peuvent être utilisées dans la définition de vos routes : - "module",
"controller" et "action" -. Ces valeurs sont utilisées par Zend_Controller_Dispatcher pour
trouver les contrôleurs et action à distribuer.
Valeurs spéciales
Le nom de ces valeurs peut être changé dans
Zend_Controller_Request_Http avec les méthodes setControllerKey
et setActionKey.
// Considérons :
$ctrl->setControllerDirectory(
array(
'default' => '/path/to/default/controllers',
302
Zend_Controller
Module seulement:
http://example/news
module == news
Module + controller:
http://example/blog/archive
module == blog
controller == archive
Si vous ne souhaitez pas cette route par défaut, créez en une et stocker la avec le nom
"default" (écrasement), ou supprimez la route avec removeDefaultRoutes() :
Si ça n'était pas le cas, vous pouvez spécifier votre propre base URL dans
Zend_Controller_Request_Http en appelant setBaseUrl() (voyez Base de l'URL et
sous-dossiers) :
303
Zend_Controller
$request->setBaseUrl('/~user/application-root/');
$router->setGlobalParam('lang', 'en');
Imaginons une application ayant besoin de posséder une page en décrivant l'auteur. Nous
voulons que lorsque le navigateur pointe vers http://domaine.fr/auteur/martel, la
page d'informations en question puisse apparaître, au sujet de "martel". La route pour une telle
URL pourrait être :
$router->addRoute('user', $route);
Cette route exemple devrait être utilisée lorsque le navigateur pointe vers http://
domaine.fr/auteur/martel, et dans un tel cas, tous les paramètres de la requête seront
injectés dans l'objet Zend_Controller_Request et seront accessibles à travers votre
ProfileController. Les variables retournées par cet exemple peuvent être représentées
par le tableau suivant :
304
Zend_Controller
$values = array(
'username' => 'martel',
'controller' => 'profile',
'action' => 'userinfo'
);
$username = $this->_getParam('username');
}
La définition des routes peut contenir un ou plusieurs caractères spéciaux - des jokers -
représentés par le symbole '*'. Il est utilisé pour collecter des paramètres. L'exemple suivant
représente plus ou moins le comportement par défaut de la route "Module" :
Chaque variable dynamique dans la définition des routes peut avoir une valeur par défaut. C'est
à cela que sert le second paramètre du constructeur de Zend_Controller_Router_Route.
Il s'agit d'un tableau avec comme clés les noms des variables, et comme valeurs, leurs valeurs
par défaut :
L'exemple ci-dessus établira une correspondance avec les URL comme http://
domaine.fr/archive/2005 et http://exemple.fr/archive. Dans ce dernier cas, la
variable de l'année (annee) aura la valeur 2006.
L'exemple ci-dessus injecte ainsi un paramètre représentant une année (annee). Si aucune
information de contrôleur ou d'actions n'est présente, alors ceux par défaut seront utilisés (ils
sont définis dans Zend_Controller_Dispatcher_Abstract). Pour que l'exemple soit plus
intuitif, spécifions des paires contrôleur et action par défaut dans notre route :
305
Zend_Controller
Avec une telle définition de route, comme ci-dessus, le routeur n'établira une correspondance
que si la variable "annee" contient une donnée numérique : http://domaine.fr/
archive/2345. Une URL comme http://exemple.annee/archive/test ne sera pas
captée (matchée) par cette route, et le contrôle sera passé à la route suivante, etc.
5.7.2. Zend_Controller_Router_Route_Static
Les exemples ci-dessus utilisent des routes dynamiques - routes qui contiennent des motifs pour
chercher des correspondances. Seulement, parfois, une route particulière est marquée en dur,
et mettre en marche le moteur d'expression régulière serait inutile. La réponse à cette situation
est l'utilisation de routes statiques :
306
Zend_Controller
• controller
• action
$front->setParam('useDefaultControllerAlways', true);
5.7.3. Zend_Controller_Router_Route_Regex
En plus des routes par défaut, et statique, les routes exprimées par expression régulière sont
acceptées. Ces routes sont plus puissantes que les autres, mais aussi plus complexes à mettre
en oeuvre et un peu plus lentes en matière d'analyse.
Comme les routes standards, cette route doit être initialisée avec une définition et des valeurs
par défaut. Créons par exemple avec une route "archive" en utilisant les routes par expressions
régulières :
Chaque motif d'expression régulière sera injecté dans l'objet de requête. Avec l'exemple
ci-dessus, en utilisant http://domain.com/archive/2006, la tableau résultat devrait
ressembler à :
$values = array(
1 => '2006',
'controller' => 'archive',
'action' => 'show'
);
307
Zend_Controller
Cette route ne fonctionnera pas encore tout à fait comme la route standard, car la valeur par
défaut pour "year" n'est pas indiquée. Attention par contre, vous risquez d'avoir un problème
avec les slashs finaux même si nous déclarons une valeur par défaut pour "year" et que celui-
ci est facultatif. La solution consiste à traiter ce slash, mais sans le capturer :
Nous voyons apparaître tout de même un problème : gérer des chiffres, comme clés pour les
paramètres n'est pas très intuitif. C'est là qu'entre en jeu le troisième paramètre du constructeur
de Zend_Controller_Router_Route_Regex. Il accepte un tableau faisant correspondre les
numéros des paramètres et leur nom respectif :
$values = array(
'year' => '2006',
'controller' => 'archive',
308
Zend_Controller
// OU
Si vous inversez comme dans le deuxième cas de l'exemple ci-dessus, la clé alors reçue
par l'objet de requête ne représente plus un chiffre, mais le nom du paramètre. Vous pouvez
évidemment mixer les comportements :
$values = array(
'year' => '2006',
2 => 10,
'controller' => 'archive',
'action' => 'show'
);
Étant donné que les route par expression régulière ne sont pas facilement réversible, vous
devrez préparer le motif vous-même dans le but d'utiliser l'aide de vue "url". Ce chemin inverse
doit être défini comme une chaîne traitable par la fonction sprintf() de PHP, et définie en
quatrième paramètre du constructeur de la route Regex :
Quels sont donc les avantages des routes par expressions régulières (Regex) ? C'est que vous
pouvez décrire n'importe quelle URL avec. Imaginez un blog, vous voulez créer des URLs du
309
Zend_Controller
Comme vous le voyez, ce type de route ajoute une solution flexible concernant la gestion des
URLs et leur routage.
5.7.4. Zend_Controller_Router_Route_Hostname
Zend_Controller_Router_Route_Hostname est la route par nom d'hôte du framework.
Elle fonctionne de la même manière que la route standard, mais elle utilise le nom d'hôte de
l'URL appelé au lieu du chemin.
Utilisons l'exemple d'une route standard et regardons ce que cela donnerais en utilisant le nom
d'hôte. Au lieu d'appeler l'utilisateur par le chemin, nous voulons être capable d'appeler http://
toto.users.example.com pour voir les informations concernant l'utilisateur "toto"
$router->addRoute('user', $hostnameRoute->chain($plainPathRoute);
Les routes par nom d'hôtes peuvent, mais ne devraient pas être utilisées comme ceci.
La raison à cela est que qu'une route par nom d'hôte seule ne correspondra à aucun
chemin. Donc vous devez donc chaîner le chemin d'une route à une route par nom
d'hôte. Ceci est réalisé comme dans l'exemple ci-dessous en appelant $hostnameRoute-
>chain($pathRoute);. En faisant ceci, $hostnameRoute n'est pas modifié, mais une
nouvelle route (Zend_Controller_Router_Route_Chain) est retournée, qui peut ainsi être
fournie au routeur.
310
Zend_Controller
5.7.5. Zend_Controller_Router_Route_Chain
Zend_Controller_Router_Route_Chain est une route permettant le chainage d'autres
routes. Ceci permet de chainer des routes hostnames à des routes de chemin, ou de multiples
routes de chemin entre elles, par exemple. Le chainage se configure via des méthodes ou un
fichier de configuration.
Le chainage utilise le slash comme séparateur par défaut entre les routes. Pour utiliser un
séparateur différent, procédez comme suite:
Pour chainer les route grâce à un fichier de configuration, il faut considérer des paramètres
additionnels. L'approche la plus simple consiste à utiliser les paramètres de la section chains.
Il s'agit simplement d'une liste de routes qui seront chainées à la route parente. Ce n'est ni le
parent, ni un des enfants qui sera ajouté au routeur, mais bien le résultat de la chaine générale.
Le nom de cette chaine dans le routeur sera constitué du nom de la route parente séparé du
nom des enfants par un tiret (-) par défaut. Voici un exemple:
<routes>
311
Zend_Controller
<www type="Zend_Controller_Router_Route_Hostname">
<route>www.example.com</route>
<chains>
<language type="Zend_Controller_Router_Route">
<route>:language</route>
<reqs language="[a-z]{2}">
<chains>
<index type="Zend_Controller_Router_Route_Static">
<route></route>
<defaults module="default" controller="index" action="index" />
</index>
<imprint type="Zend_Controller_Router_Route_Static">
<route>imprint</route>
<defaults module="default" controller="index" action="index" />
</imprint>
</chains>
</language>
</chains>
</www>
<users type="Zend_Controller_Router_Route_Hostname">
<route>users.example.com</route>
<chains>
<profile type="Zend_Controller_Router_Route">
<route>:username</route>
<defaults module="users" controller="profile" action="index" />
</profile>
</chains>
</users>
<misc type="Zend_Controller_Router_Route_Static">
<route>misc</route>
</misc>
</routes>
Autre manière de faire : utiliser les nom des routes directement. Cela ne peut se faire que pour
le niveau racine:
<routes>
<www type="Zend_Controller_Router_Route_Chain">
<route>www.example.com</route>
</www>
<language type="Zend_Controller_Router_Route">
<route>:language</route>
<reqs language="[a-z]{2}">
</language>
<index type="Zend_Controller_Router_Route_Static">
<route></route>
<defaults module="default" controller="index" action="index" />
</index>
<imprint type="Zend_Controller_Router_Route_Static">
<route>imprint</route>
<defaults module="default" controller="index" action="index" />
</imprint>
<www-index type="Zend_Controller_Router_Route_Chain">
<chain>www, language, index</chain>
312
Zend_Controller
</www-index>
<www-imprint type="Zend_Controller_Router_Route_Chain">
<chain>www, language, imprint</chain>
</www-imprint>
</routes>
On peut aussi passer un tableau à chain plutôt que les noms de route séparés par des virgules:
<routes>
<www-index type="Zend_Controller_Router_Route_Chain">
<chain>www</chain>
<chain>language</chain>
<chain>index</chain>
</www-index>
<www-imprint type="Zend_Controller_Router_Route_Chain">
<chain>www</chain>
<chain>language</chain>
<chain>imprint</chain>
</www-imprint>
</routes>
// Ajoute la configuration
$router->addConfig($config);
5.7.6. Zend_Rest_Route
Le composant Zend_Rest contient une route RESTful pour
Zend_Controller_Router_Rewrite. Cette route permet un schéma de routage fonction de
la méthode HTTP et de l'URI afin d'y faire correspondre un module, contrôleur, et action. Le
tableau suivant vous donne un aperçu du schéma de routage en fonction de l'URI.
313
Zend_Controller
Pour activer Zend_Rest_Route pour une application entière, construisez en un objet sans
paramètre spécifique et ajoutez le comme route par défaut dans le contrôleur frontal:
$front = Zend_Controller_Front::getInstance();
$restRoute = new Zend_Rest_Route($front);
$front->getRouter()->addRoute('default', $restRoute);
Pour activer Zend_Rest_Route pour des modules spécifiques, construisez l'objet avec comme
troisième paramètre, un tableau de noms de modules :
$front = Zend_Controller_Front::getInstance();
$restRoute = new Zend_Rest_Route($front, array(), array('product'));
$front->getRouter()->addRoute('rest', $restRoute);
Pour activer Zend_Rest_Route pour des contrôleurs spécifiques, construisez l'objet avec
comme troisième paramètre, un tableau de noms de contrôleurs en correspondance avec des
noms de modules.
$front = Zend_Controller_Front::getInstance();
$restRoute = new Zend_Rest_Route($front, array(), array(
'product' => array('ratings')
));
$front->getRouter()->addRoute('rest', $restRoute);
To use Zend_Rest_Route from an INI config file, use a route type parameter and set the config
options:
routes.rest.type = Zend_Rest_Route
routes.rest.defaults.controller = object
routes.rest.mod = project,user
314
Zend_Controller
The 'type' option designates the RESTful routing config type. The 'defaults' option is used to
specify custom default module, controller, and/or actions for the route. All other options in the
config group are treated as RESTful module names, and their values are RESTful controller
names. The example config defines Mod_ProjectController and Mod_UserController as RESTful
controllers.
5.7.6.3. Zend_Rest_Controller
Pour vous aidez à utiliser des contrôleurs avec Zend_Rest_Route, faites les étendre
Zend_Rest_Controller. Zend_Rest_Controller définit les 5 opérations RESTful les plus
connues sous forme de méthodes abstraites.
• getAction() - Devrait récupérer des données d'une ressource définie par URI et les passer
à la vue.
• putAction() - Devrait accepter une ressource indentifiée par URI et la faire persister (la
sauvegarder).
[production]
routes.archive.route = "archive/:year/*"
routes.archive.defaults.controller = archive
routes.archive.defaults.action = show
routes.archive.defaults.year = 2000
routes.archive.reqs.year = "\d+"
routes.news.type = "Zend_Controller_Router_Route_Static"
routes.news.route = "news"
routes.news.defaults.controller = "news"
routes.news.defaults.action = "list"
routes.archive.type = "Zend_Controller_Router_Route_Regex"
routes.archive.route = "archive/(\d+)"
routes.archive.defaults.controller = "archive"
routes.archive.defaults.action = "show"
315
Zend_Controller
routes.archive.map.1 = "year"
; OU: routes.archive.map.year = 1
Ce fichier INI peut être lu dans grâce à un objet Zend_Config comme suit :
Nous indiquons au routeur d'utiliser la section "routes" du fichier INI. Chaque clé de
premier niveau représente le nom de la route, ainsi nous avons dans l'exemple ci dessus
"archive" et "news". Chaque route attend alors au moins une entrée "route" avec une
ou plusieurs entrées "defaults" ; optionnellement nous pouvons rajouter des paramètres
obligatoires. Tout ceci correspond aux trois arguments fournis par l'objet implémentant
Zend_Controller_Router_Route_Interface. Une entrée optionnelle "type" peut être
utilisée pour indiquer le type de classe de routage à utiliser, il s'agit par défaut de
Zend_Controller_Router_Route. Dans l'exemple au dessus, la route "news" va utiliser
Zend_Controller_Router_Route_Static.
Cependant, si vous voulez utiliser votre propre logique de routage, une interface est disponible.
Zend_Controller_Router_Interface ne définit qu'une seule méthode :
interface Zend_Controller_Router_Interface
{
/**
* @param Zend_Controller_Request_Abstract $request
* @throws Zend_Controller_Router_Exception
* @return Zend_Controller_Request_Abstract
*/
public function route(Zend_Controller_Request_Abstract $request);
}
Le processus de routage n'intervient qu'une fois : lorsque la requête est reçue par le système. Le
routeur doit alors déterminer un contrôleur, une action et de paramètres optionnel et les spécifier
dans un objet de requête, qui est ensuite passé au distributeur. Si il n'est pas possible de router
une requête, alors l'objet de requête devrait être laissé tel-quel.
6. Le distributeur
6.1. Vue d'ensemble
La distribution est le processus de récupération de l'objet requête,
Zend_Controller_Request_Abstract, d'extraction du nom de module, du nom de
contrôleur, du nom d'action, et des paramètres facultatifs qui s'y trouvent, et enfin
d'instanciation du contrôleur et de l'appel d'une action de ce contrôleur. Si le module,
le contrôleur, ou l'action ne sont pas trouvés, il emploiera des valeurs par défaut pour
eux. Zend_Controller_Dispatcher_Standard indique index pour le contrôleur et
l'action par défaut et default pour le module par défaut, mais permet au développeur
316
Zend_Controller
de changer ces valeurs par défaut pour chacun en utilisant les méthodes respectives
setDefaultController(), setDefaultAction(), et setDefaultModule().
Le module "Default"
Quand vous créez des applications modulaires, vous pouvez constater que
vous voulez aussi que votre module par défaut ait son espace de noms
(dans la configuration par défaut, le module "default" n'a pas d'espace
de noms). A partir de la version 1.5.0, vous pouvez spécifier le paramètre
prefixDefaultModule à TRUE soit dans le contrôleur frontal soit dans le
distributeur :
// Dans le distributeur :
$dispatcher->setParam('prefixDefaultModule', true);
Ceci vous permet de ré-utiliser un module existant en tant que module par défaut
d'une application.
La distribution se produit dans une boucle dans le contrôleur frontal. Avant que le distribution
ne se produise, le contrôleur frontal détermine la route de la requête pour récupérer les valeurs
spécifiées par l'utilisateur pour le module, le contrôleur , l'action , et les paramètres optionnels.
Il entre alors dans la boucle d'expédition, et distribue la requête.
Au début de chaque itération, il régle un drapeau dans l'objet requête indiquant que l'action a été
distribuée. Si une action ou un plugin pre/postDispatch remet à zéro ce drapeau, la boucle
de distribution continue et tente de distribuer la nouvelle requête. En changeant le contrôleur et/
ou l'action dans la requête et en effaçant le drapeau de distribution, le développeur peut définir
une chaîne de requêtes à réaliser.
La méthode du contrôleur d'action qui contrôle cette distribution est _forward() ; appelez
cette méthode à partir de pre/postDispatch() ou d'une méthode d'action, en fournissant
une action, un contrôleur, un module, et optionnellement des paramètres additionnels que vous
souhaitez passer à la nouvelle action :
317
Zend_Controller
Le distributeur a besoin de plusieurs données afin de réaliser son travail - il doit connaître le
format des noms d'actions et de contrôleur, où chercher les fichiers de classe des contrôleurs,
savoir si le nom de module fourni est valide, et il a besoin d'une API pour déterminer si une
requête donnée est distribuable suivant les informations disponibles.
interface Zend_Controller_Dispatcher_Interface
{
/**
* Formate une chaîne en un nom de classe de contrôleur
*
* @param string $unformatted
* @return string
*/
public function formatControllerName($unformatted);
/**
* Formate une chaîne en un nom de méthode d'action
*
* @param string $unformatted
* @return string
*/
public function formatActionName($unformatted);
/**
* Détermine si une requête est distribuable
*
* @param Zend_Controller_Request_Abstract $request
* @return boolean
*/
public function isDispatchable(
Zend_Controller_Request_Abstract $request);
/**
* Règle un paramètre utilisateur
* (via le contrôleur frontal, ou pour un usage local)
*
* @param string $name
* @param mixed $value
* @return Zend_Controller_Dispatcher_Interface
*/
public function setParam($name, $value);
/**
* Règle un tableau de paramètres utilisateur
*
* @param array $params
* @return Zend_Controller_Dispatcher_Interface
318
Zend_Controller
*/
public function setParams(array $params);
/**
* Récupère un paramètre utilisateur unique
*
* @param string $name
* @return mixed
*/
public function getParam($name);
/**
* Récupère tous les paramètres utilisateur
*
* @return array
*/
public function getParams();
/**
* Efface le tableau des paramètres utilisateur,
* ou un paramètre utilisateur unique :
*
* @param null|string|array single key or
* array of keys for params to clear
* @return Zend_Controller_Dispatcher_Interface
*/
public function clearParams($name = null);
/**
* Règle l'objet réponse à utiliser, s'il existe
*
* @param Zend_Controller_Response_Abstract|null $response
* @return void
*/
public function setResponse(
Zend_Controller_Response_Abstract $response = null);
/**
* Récupère l'objet réponse, s'il existe
*
* @return Zend_Controller_Response_Abstract|null
*/
public function getResponse();
/**
* Ajoute un dossier de contrôleur dans le tableau
* des dossiers de contrôleurs
*
* @param string $path
* @param string $args
* @return Zend_Controller_Dispatcher_Interface
*/
public function addControllerDirectory($path, $args = null);
/**
* Règle le(s) dossier(s) où les fichiers de contrôleurs
* sont stockés
*
* @param string|array $dir
* @return Zend_Controller_Dispatcher_Interface
319
Zend_Controller
*/
public function setControllerDirectory($path);
/**
* Retourne le(s) dossier(s) où les fichiers de contrôleurs
* sont stockés
*
* @return array
*/
public function getControllerDirectory();
/**
* Distribue une requête vers un (module/)contrôleur/action.
*
* @param Zend_Controller_Request_Abstract $request
* @param Zend_Controller_Response_Abstract $response
* @return Zend_Controller_Request_Abstract|boolean
*/
public function dispatch(Zend_Controller_Request_Abstract $request,
Zend_Controller_Response_Abstract $response);
/**
* Informe si un module donné est valide
*
* @param string $module
* @return boolean
*/
public function isValidModule($module);
/**
* Retourne le nom du module par défaut
*
* @return string
*/
public function getDefaultModule();
/**
* Retourne le nom du contrôleur par défaut
*
* @return string
*/
public function getDefaultControllerName();
/**
* Retourne le nom de l'action par défaut
*
* @return string
*/
public function getDefaultAction();
}
Cependant, dans la plupart des cas, vous devriez simplement étendre la classe abstraite
Zend_Controller_Dispatcher_Abstract, dans laquelle chacune de ces méthodes a déjà
été définie, ou Zend_Controller_Dispatcher_Standard pour modifier une fonctionnalité
du distributeur standard.
Les raisons possibles au sous-classement du distributeur incluent un désir d'employer une classe
ou un schéma différent de nommage des classes et/ou des méthodes dans vos contrôleurs
d'action, ou un désir d'employer un paradigme de distribution différent tel que la distribution
320
Zend_Controller
de fichiers de classe d'action dans des dossiers de contrôleur (au lieu de la distribution des
méthodes de classes).
7. Contrôleurs d'action
7.1. Introduction
Zend_Controller_Action est une classe abstraite que vous pouvez utiliser avec le
contrôleur frontal quand vous construisez un site Web basé sur le modèle de conception Modèle-
Vues-Contrôleurs (MVC).
Pour utiliser Zend_Controller_Action, vous devez la sous-classer dans vos propres classes
de contrôleurs d'action (ou la sous-classer pour créer votre propre classe de base pour vos
contrôleurs d'action). L'opération la plus basique est de la sous-classer, et de créer vos
méthodes d'action qui correspondent aux différentes actions que vous souhaitez gérer. La
gestion du routage et de la distribution des Zend_Controller va rechercher automatiquement
les méthodes dont le nom termine par 'Action' dans votre classe et les considérer comme des
actions potentiellement valides de votre contrôleur.
Il y a d'autres fonctionnalités qui peuvent être utilisées, comme personnaliser l'initialisation des
actions, gérer les actions par défaut quand aucune action ou une action invalide est fournie,
avoir la possibilité de hook ("détournement") en pre et post-dispatch, et une variété de méthodes
d'aides (helper). Ce chapitre fournit une vue d'ensemble des fonctionnalités du contrôleur
d'action.
Par défaut, le contrôleur frontal active l'aide d'action ViewRenderer. Cette aide
s'occupe de l'injection automatique de l'objet de vue dans vos contrôleurs, ainsi
que du rendu de cette vue. Vous pouvez la désactiver au sein de vos contrôleurs
par l'une ou l'autre des actions suivantes :
321
Zend_Controller
$this->_helper->viewRenderer->setNoRender(true);
// Global :
$this->_helper->removeHelper('viewRenderer');
Vous pouvez simplement désactiver le rendu pour une vue individuelle grâce au
drapeau noRender du ViewRenderer :
322
Zend_Controller
7.4. Accesseurs
Un certain nombre d'objets et de variables sont enregistrés avec l'objet et chacun possède des
méthodes accesseurs.
• Objet Requête : getRequest() peut être utilisé pour récupérer l'objet de requête utilisé pour
appeler l'action.
• Objet Réponse : getResponse() peut être utilisé pour récupérer l'objet de réponse
assemblant la réponse finale. Quelques appels typiques peuvent ressembler à ceci :
$this->getResponse()->setHeader('Content-Type', 'text/xml');
$this->getResponse()->appendBody($content);
323
Zend_Controller
• Paramètres de requêtes : l'objet de requête rassemble les paramètres de requête, comme les
paramètres _GET ou __POST, ou les paramètres utilisateurs spécifiés dans le chemin d'URL.
Pour récupérer ceux-ci utilisez _getParam($key) ou _getAllParams(). Vous pouvez
aussi régler ces paramètres en utilisant _setParam() ; ceci est pratique quand vous redirigez
vers des actions additionnelles.
Pour tester si un paramètre existe ou non (pratique pour les branchements logiques), utilisez
_hasParam($key).
// Au lieu de :
if ($this->_hasParam('id') {
$id = $this->_getParam('id');
} else {
$id = 1;
}
applicationOrModule/
controllers/
IndexController.php
views/
scripts/
index/
index.phtml
324
Zend_Controller
helpers/
filters/
En d'autres termes, les scripts de vues sont supposés être dans le sous-répertoire /views/
scripts/, et le sous-répertoire /views/ est censé contenir les fonctionnalités soeurs (aides
[helpers], filtres [filters]). En déterminant le script de vue et son chemin, le répertoire /views/
scripts/ est utilisé comme chemin de base, avec des dossiers nommés par le nom de
contrôleur fournissant ainsi la hiérarchie des scripts de vues.
render() effectue le rendu d'un script de vue. Si aucun argument n'est fourni, la méthode
suppose que le script requêté est [controller]/[action].phtml (où .phtml est la valeur
de la propriété $viewSuffix). Fournir une valeur pour $action va effectuer le rendu du
script dans le sous-dossier /[controller]/. Pour surcharger l'utilisation du sous-dossier /
[controller]/, fournissez la valeur TRUE à $noController. Enfin, les scripts sont rendus
dans l'objet réponse ; si vous souhaitez effectuer le rendu dans un segment nomméspécifique
de l'objet réponse, fournissez une valeur à $name.
Quelques exemples :
325
Zend_Controller
• _redirect($url, array $options = array()) : redirige vers une autre page. Cette
méthode prend un URL et un jeu d'options optionnel. Par défaut, il exécute une redirection
de type HTTP 302.
• : avec ou sans sortie immédiate. Si appelée, la méthode fermera proprement toute session
ouverte et réalisera la redirection.
Vous pouvez régler cette option de manière globale dans le contrôleur en utilisant
l'accesseur setRedirectExit().
• prependBase : ajoute ou non l'URL de base enregistré dans l'objet requête à l'URL produit.
Vous pouvez régler cette option de manière globale dans le contrôleur en utilisant
l'accesseur setRedirectPrependBase().
• code : fournit le code HTTP à utiliser pour la redirection. Par défaut, un HTTP 302 est utilisé ;
tout code compris entre 301 et 306 peut être utilisé.
Vous pouvez régler cette option de manière globale dans le contrôleur en utilisant
l'accesseur setRedirectCode().
En plus de la création de fonctionnalité utile pour vos applications Web, vous pouvez aussi
constater que vous répétez souvent la même installation ou les mêmes méthodes utilitaires dans
vos contrôleurs divers ; s'il en est ainsi, créer une classe de contrôleur de base commune qui
étend Zend_Controller_Action peut résoudre une telle redondance.
326
Zend_Controller
Si une requête vers un contrôleur est faite en incluant une méthode d'action indéfinie,
Zend_Controller_Action::__call() sera invoqué. __call() est, bien sûr, la
méthode magique de PHP pour la surcharge de méthode.
Vous pouvez surcharger cette fonctionnalité si vous souhaitez exécuter d'autres opérations.
Par exemple, si vous souhaitez afficher un message d'erreur, vous pouvez écrire quelque
chose comme ceci :
Une autre possibilité est de rediriger vers une page de contrôleur par défaut :
327
Zend_Controller
surchargées dans le but de personnaliser vos contrôleurs. Par exemple, si vous stockez votre
objet de vue dans le registre, vous pouvez vouloir modifier votre méthode initView() avec
une code comme celui-ci :
return $this->view;
}
}
Il y a de nombreuses manières d'utiliser les aides d'action. Les aides d'action utilisent le système
de gestionnaire ("Broker"), similaire aux gestionnaires vus pour les Zend_View_Helper, et les
Zend_Controller_Plugin. Les aides d'action (comme les aides de vue Zend_View_Helper)
peuvent être chargées et appelées à la demande, ou elles peuvent être instanciées au début de
la requête ("bootstrap") ou au moment de la création des contrôleurs d'action (init()). Pour
mieux comprendre ceci, reportez vous à la section d'utilisation ci-dessous.
• L'utilisation explicite de getHelper(). Passez lui simplement un nom, et l'objet d'aide est
retourné :
$flashMessenger = $this->_helper->getHelper('FlashMessenger');
$message = 'Nous avons fait quelquechose lors de la dernière requête';
$flashMessenger->addMessage($message);
328
Zend_Controller
$flashMessenger = $this->_helper->FlashMessenger;
$message = 'Nous avons fait quelquechose lors de la dernière requête';
$flashMessenger->addMessage($message);
• Enfin, la plupart des aides d'action implémente la méthode direct() qui va appeler une
méthode spécifique par défaut dans l'aide. Dans l'exemple de FlashMessenger, ceci appelle
addMessage() :
Vous pouvez vouloir aussi instancier les aides explicitement. Vous pourriez avoir besoin de ceci
si vous utilisez l'aide hors du contexte du contrôleur d'action, ou si vous souhaitez fournir une
aide au gestionnaire d'aides à utiliser pour une action quelconque. L'instanciation se fait comme
toute autre classe PHP.
Zend_Controller_Action_HelperBroker::addHelper($helper);
Bien sûr, instancier et fournir des aides au gestionnaire est coûteux en temps et en ressource
donc deux méthodes existent pour automatiser les choses légèrement : addPrefix() et
addPath().
• addPrefix() prend un préfixe de classe et l'utilise pour déterminer le chemin des dossiers
dans lesquels les classes d'aides ont été définies. Ceci suppose que le préfixe de la classe
respecte la convention de nommage de Zend Framework.
Puisque ces méthodes sont statiques, elles peuvent être appelées en tout point du déroulement
du contrôleur pour ajouter dynamiquement les aides nécessaires.
329
Zend_Controller
Pour déterminer si une aide existe dans le gestionnaire d'aide, utilisez hasHelper($name), où
$name est le nom raccourci de l'aide (sans le préfixe) :
Il existe aussi deux méthodes statiques pour récupérer les aides issues du gestionnaire d'aide :
getExistingHelper() et getStaticHelper(). getExistingHelper() récupérera une
aide seulement si elle a précédemment été invoquée par ou explicitement enregistrée dans le
gestionnaire d'aides; la méthode lèvera une exception sinon. getStaticHelper() réalise la
même chose que getExistingHelper(), mais tentera d'instancier l'aide si elle n'a pas déjà
été enregistrée dans la pile des aides. getStaticHelper() est un bon choix pour récupérer
les aides que vous voulez configurer.
Les deux méthodes prennent un unique paramètre, $name, qui est le nom court de l'aide (c'est-
à-dire sans le préfixe).
8.4.1. ActionStack
L'aide ActionStack vous permet d'empiler les requêtes dans le plugin de contrôleur frontal
ActionStack, vous aidant effectivement à créer une liste d'actions à exécuter durant la requête.
330
Zend_Controller
Cette aide vous permet d'ajouter des actions, soit en spécifiant de nouveaux objets de requêtes,
soit en paramétrant action / controller / module.
Exemple 89. Ajouter une tâche en utilisant une action, un contrôleur et un module
331
Zend_Controller
Parfois la nature OO d'un objet de requête a plus de sens ; vous pouvez alors fournir l'objet
à l'aide ActionStack.
8.4.2. AutoComplete
Beaucoup de librairies javascript AJAX propose une fonctionnalité dite d'auto-complétion. Une
liste de résultats possibles est chargée au fur et à mesure que l'utilisateur saisit. L'aide
AutoComplete est destinée à simplifier le retour de ces valeurs vers la librairie Javascript.
// Ou :
$response = $this->_helper
->autoCompleteDojo
->sendAutoCompletion($data);
332
Zend_Controller
• Envoie la réponse.
Dojo n'a pas une fonctionnalité d'auto-complétion, mais deux : ComboBox et FilteringSelect.
Dans les deux cas, elle demande une structure de données qui implémente QueryReadStore ;
voyez la documentation de dojo.data
Dans Zend Framework, vous pouvez passer un simple tableau indexé à l'aide
AutoCompleteDojo, elle retournera une réponse JSON compatible avec la structure de données
Dojo :
333
{
Voyons le javascript
protected nécessaire. Dojo est une librairie complète pour la création de javascript
$_form;
dojo.provide("custom.TestNameReadStore");
OO, un peu comme
L'auto-complétion avecZend Framework
Dojo via MVCpour PHP. plusieurs
requière Il est possible
choses de : créer des un
générer pseudo-
objet
dojo.declare("custom.TestNameReadStore",
Ensuite, créons le formulaire sur lequelvous
nous souhaitons une auto-complétion : répertoire
namespaces
formulaire
public en utilisant
surfunction l'arborescence
ledojox.data.QueryReadStore,
ComboBox sur
getForm() lequel des
Zend_Controller répertoires.
voulez Nous allons créer
de l'auto-complétion, un
un contrôleur avec
"custom"
{
une action aupour
mêmeservir
{ niveau
les que le
résultats, répertoire
la Dojo.
création d'un A l'intérieur,
QueryReadStorenous allons
à créer
connecter un
à fichier
l'action
Cette classe
javascript, if estfetch:function
(nullune simple
===
TestNameReadStore.js,
et la génération du javascript extension
$this->_form)
à utiliser de {QueryReadStore,
avec
pour
(request) le{contenu
initialiser suivantqui
: est une
l'auto-complétion classe
coté abstraite.
serveur.
NousExemple
définissonsrequire_once
simplement 'Zend/Form.php';
une
request.serverQueryméthode de
= { requête, et on lui assigne
test:request.query.name notre
}; élément
91. AutoCompletion
$this->_form
avec Dojo en utilisant MVC
= new Zend_Form();
"test". return this.inherited("fetch", arguments);
$this->_form->setMethod('get')
}
}); ->setAction($this->getRequest()->getBaseUrl()
. '/test/process')
->addElements(array(
'test' => array('type' => 'text', 'options' => array(
'filters' => array('StringTrim'),
'dojoType' => array('dijit.form.ComboBox'),
'store' => 'testStore',
'autoComplete' => 'false',
'hasDownArrow' => 'true',
'label' => 'Your input:',
)),
'go' => array('type' => 'submit',
'options' => array('label' => 'Go!'))
class TestController extends Zend_Controller_Action
Ici,
{
nous créons simplement
)); un formulaire avec des méthodes "test" et "go". La méthode
"test"//ajoute
}
... plusieurs attributs Dojo spéciaux : dojoType, store, autoComplete, et
Ajoutons une méthode
hasDownArrow.
return dojoType pour afficherpour
est utilisé
$this->_form; le indiquer
formulaire, et une entrée
la création pour traiteretl'auto-
d'une ComboBox, nous
complétion
allons/**
} :
la relier au conteneur de données ("store") de "testStore". Mettre "autoComplete" à
}
FALSE*ditLanding
à Dojo de ne pas sélectionner automatiquement la première valeur, mais de plutôt
page
montrer */une liste de valeurs possibles. Enfin, "hasDownArrow" crée une flèche bas comme
public
sur les select function
box. indexAction()
{
$this->view->form = $this->getForm();
}
$matches = array();
<?php //foreach
configuration de l'entrepôt
($this->getData() de données
as $datum) { : ?>
<div dojoType="custom.TestNameReadStore"
if (0 === strpos($datum, $match)) jsId="testStore"
{
Voyons maintenant notre$this->baseUrl()
url="<?php echo
script de vue. Nous devons configurer notre entrepôt de données,
$matches[] = $datum; ?>/unit-test/autocomplete/format/ajax"
puis
Dansrendre le formulaire,
autocompleteAction(),et s'assurer
requestMethod="get"></div>
} quevérifions
nous les librairies
que Dojo
nousappropriées
avons bien sont
une bien chargées
requête post,
(ainsi
et un que }notre
paramètre entrepôt).
"format" Voici
avec le
la script
valeur de vue
"ajax".: Ensuite, nous vérifions la présence d'un
paramètre "test", et
duleformulaire
comparons avec: ?>nos données. (getData() retourne des données
<?php //$this->_helper->autoCompleteDojo($matches);
rendu
quelconques).
<?php Enfin, nous envoyons
} echo $this->form ?> nos résultats à notre aide AutoCompletion.
<?php
} // configuration des CSS de Dojo dans le head HTML : ?>
<?php $this->headStyle()->captureStart() ?>
@import "<?php echo $this->baseUrl()
?>/javascript/dijit/themes/tundra/tundra.css";
@import "<?php echo $this->baseUrl() ?>/javascript/dojo/resources/dojo.css";
<?php $this->headStyle()->captureEnd() ?>
<?php // configuration de javascript pour charger
// les librairies Dojo dans le head HTML : ?>
<?php $this->headScript()
->setAllowArbitraryAttributes(true)
->appendFile($this->baseUrl() . '/javascript/dojo/dojo.js',
'text/javascript',
array('djConfig' => 'parseOnLoad: true'))
Nous pouvons->captureStart() ?>
dès lors faire fonctionner l'auto-complétion Dojo.
djConfig.usePlainJson=true;
dojo.registerModulePath("custom","../custom");
Notez les appels aux aides de vue comme headStyle et headScript ; celles-ci sont des
dojo.require("dojo.parser");
emplacements réservés, que nous pouvons ensuite utiliser pour effectuer le rendu dans la
dojo.require("dojox.data.QueryReadStore");
section "head" du HTML de votre script
dojo.require("dijit.form.ComboBox"); 334 de layout.
dojo.require("custom.TestNameReadStore");
<?php $this->headScript()->captureEnd() ?>
Zend_Controller
Pour l'activer, vous devez indiquer à votre contrôleur quelles actions répondent à quel contexte.
Si une requête d'entrée indique un contexte valide pour une action, alors l'aide d'action en
charge :
• Va changer le suffixe de la vue à rendre, il faudra donc créer une vue par contexte.
/**
* Liste les news
*/
public function listAction()
{
}
/**
* Affiche une new particulière
*/
public function viewAction()
{
}
}
Imaginons que nous voulions que listAction() soit aussi accessible au format XML. Plutôt
que de créer une autre action, nous pouvons lui indiquer qu'elle doit retourner du XML :
335
Zend_Controller
// ...
}
• De changer le suffixe de vue vers "xml.phtml" (ou un autre suffixe si vous en utilisez un
personnalisé "xml.[votre suffixe]").
Il est donc nécessaire de créer un nouveau script de vue, "news/list.xml.phtml", qui créera
et rendra le XML.
Pour savoir si la requête doit utiliser un contexte switch, l'aide vérifie un jeton dans l'objet
de requête. Par défaut, l'aide va chercher le paramètre de requête "format", ceci peut être
changé. Ceci signifie que dans la plupart des cas, pour changer le contexte d'une réponse, il
faut simplement injecter un paramètre "format" à la requête:
• Via l'URL : /news/list/format/xml (le routeur par défaut utilise les paires clés et valeurs
fournies après l'action)
ContextSwitch vous permet d'écrire des contextes, ceux-ci spécifient le suffixe de vue qui
change, les en-têtes de réponse à modifier, et les fonctions de rappel éventuelles.
Par défaut cependant, aucun script de vue n'est nécessaire, il va simplement sérialiser en
JSON toutes les variables de vues, et les envoyer en tant que réponse.
$this->_helper->contextSwitch()->setAutoJsonSerialization(false);
Vous pouvez créer vos propres contextes d'action. Par exemple pour retourner du YAML, du
PHP sérialisé, ou encore du RSS ou du ATOM. ContextSwitch est là pour cela.
336
Zend_Controller
• suffix : Le préfixe qui va s'ajouter au suffixe de vue. Il sera utiliser par le ViewRenderer.
• callbacks : un tableau dont les clés peuvent être "init" ou "post", et les valeurs représentent des
noms de fonctions PHP valides, qui seront utilisées pour initialiser ou traiter la fin du contexte.
Les fonctions d'initialisation interviennent lorsque le contexte est détecté par ContextSwitch.
Par exemple dans le contexte intégré JSON, la fonction désactive le ViewRenderer lorsque la
sérialisation automatique JSON est activée.
• getContext($context) : retourne un contexte par son nom. Le retour est un tableau qui
a la même syntaxe que celui utilisé par addContext().
• getContexts() : retourne tous les contextes. Le tableau de retour est de la forme contexte
=> spécifications.
Il existe deux mécanismes pour créer et affecter des contextes. Vous pouvez créer des tableaux
dans vos contrôleurs, ou utiliser plusieurs méthodes de ContextSwitch pour les assembler.
La méthode principale pour ajouter des contextes à des actions est addActionContext().
Elle attend 2 arguments, l'action et le contexte (ou un tableau de contextes). Par exemple,
considérons la classe suivante :
337
Zend_Controller
Imaginons que nous voulions ajouter un contexte XML à l'action "list", et deux contextes XML et
JSON à l'action "comments". Nous pourrions faire ceci dans la méthode init() :
Cette syntaxe est simplement moins pratique et plus prompte aux erreurs.
Pour construire vos contextes, les méthodes suivantes vous seront utiles :
Passer la valeur TRUE comme contexte marquera tous les contextes comme disponibles pour
cette action.
Une valeur vide pour $context désactivera tous les contextes donnés à cette action.
338
Zend_Controller
Dans certains cas, vous voudriez forcer un contexte pour une action ; par exemple vous pouvez
vouloir seulement le contexte XML si la permutation de contexte est active. Passez le alors à
initContext() :
$contextSwitch->initContext('xml');
Voici quelques méthodes qui peuvent être utilisées pour changer le comportement de l'aide
ContextSwitch :
$contextSwitch->setAutoJsonSerialization(false);
$contextSwitch->initContext();
339
Zend_Controller
Passer une valeur vide au suffixe aura pour effet de n'utiliser que le suffixe du ViewRenderer.
Chaque contexte peut posséder plusieurs en-têtes, addHeader() ajoute des en-têtes dans
une pile, pour un contexte donné.
Si l'en-tête $header spécifié pour le contexte existe déjà, une exception sera alors levée.
340
Zend_Controller
• getCurrentContext() est utilisée pour savoir quel contexte a été détecté (si c'est le cas).
Cette méthode retourne NULL si aucune permutation de contexte a été détectée, ou si elle est
appelée avant initContext().
L'aide AjaxContext étend l'aide de permutation de contexte ContextSwitch, donc toutes les
fonctionnalités de ContextSwitch s'y retrouvent. Il y a cependant quelques différences :
Cette aide utilise une propriété de contrôleur d'action différente pour déterminer les contextes,
$ajaxable. Vous pouvez avoir différents contextes utilisés avec les requêtes AJAX ou HTTP.
Les différentes méthodes ActionContext() de AjaxContext vont écrire dans cette propriété.
Enfin, AjaxContext ajoute un contexte, HTML. Dans ce contexte, le suffixe de vue est
"ajax.phtml". Il n'y a pas d'en-tête particulier ajouté à la réponse.
341
Zend_Controller
Dans l'exemple qui suit, nous autorisons les actions "view", "form", et "process" à répondre
aux requêtes AJAX. Dans les actions, "view" et "form", nous retournerons des portions de
HTML ; dans "process", nous retournerons du JSON.
8.4.4. FlashMessenger
8.4.4.1. Introduction
L'aide FlashMessenger vous permet de fournir les messages dont l'utilisateur pourrait
avoir besoin dans la requête suivante. Pour accomplir ceci, FlashMessenger utilise
Zend_Session_Namespace pour stocker les messages à retrouver dans la prochaine
requête. C'est généralement une bonne idée si vous planifiez d'utiliser Zend_Session ou
Zend_Session_Namespace, que vous initialisez avec Zend_Session::start() dans votre
fichier d'amorçage. (Reportez vous à la documentation de Zend_Sessionpour plus de détails sur
son utilisation.)
342
Zend_Controller
L'exemple ci-dessous vous montre l'utilisation du flash messenger dans sa forme la plus basique.
Quand l'action /some/my est appelée, il ajoute le message "Sauvegarde réalisée !". Une requête
suivante vers l'action /some/my-next-request le retrouvera (ainsi que le détruira).
8.4.5. JSON
Les réponses JSON sont les réponses de choix dans une architecture de type AJAX qui attend
des données structurées. JSON peut être immédiatement interprété du coté du client, ce qui
rend la tâche plus simple et plus rapide.
• Par défaut, retourne immédiatement la réponse, sans attendre la fin de l'exécution de l'action.
343
Zend_Controller
Son utilisation est toute simple, appelez le depuis le gestionnaire d'aides (Broker), ou appelez
une de ses méthodes encodeJson() ou sendJson() :
// ou...
$this->_helper->json->sendJson($data);
Si vous avez besoin des layouts séparés pour les réponses JSON, pour par
exemple générer vos réponses JSON dans un contexte particulier, chaque
méthode de l'aide JSON accepte un second paramètre booléen. A TRUE, les
layouts resteront activées :
$this->_helper->json($data, true);
Si vous souhaitez faire ceci, vous devez un tableau en tant que second argument.
Ceci vous permet aussi de combiner avec les autres options, comme l'option
keepLayouts. Toutes ces options sont alors fournies à Zend_Json::encode().
$this->_helper->json($data, array(
'enableJsonExprFinder' => true,
'keepLayouts' => true,
));
344
Zend_Controller
8.4.6. Redirector
8.4.6.1. Introduction
L'aide Redirector vous permet d'utiliser un objet de redirection qui remplit tous les
besoins de votre application, nécessaires à une redirection vers une nouvelle URL. Il fournit
de nombreux avantages par rapport à la méthode _redirect(), comme la capacité de
préconfigurer le comportement du site dans l'objet Redirector ou d'utiliser l'interface
intégrée gotoSimple($action, $controller, $module, $params) similaire à
Zend_Controller_Action::_forward().
Redirector possède une certain nombre de méthodes qui peuvent être utilisées pour affecter
le comportement de la redirection :
• setCode() peut être utilisée pour paramétrer le code HTTP de la réponse à utiliser pendant
la redirection.
• setExit() peut être utilisée pour forcer un exit() juste après la redirection. Par défaut
ceci vaut TRUE.
• setGotoRoute() peut être utilisée pour paramétrer une URL basée sur une route
enregistrée. Fournissez un tableau de paires clé/valeur et une route nommée, et elle
assemblera une URL suivant le type de la route et sa définition.
• setGotoUrl() peut être utilisée pour paramétrer l'URL par défaut à utiliser si aucune n'est
fournie à gotoUrl(). Accepte une chaîne unique correspondant à une URL.
• setPrependBase() peut être utilisée pour ajouter une chaîne au début de l'URL de
base de l'objet requête pour les URLs spécifiées avec setGotoUrl(), gotoUrl(), ou
gotoUrlAndExit().
• setUseAbsoluteUri() peut être utilisée pour forcer le Redirector à utiliser des URI
absolus pour la redirection. Quand cette option est choisie, elle utilise les valeurs de
$_SERVER['HTTP_HOST'], $_SERVER['SERVER_PORT'], et $_SERVER['HTTPS'] pour
former un URI complet à partir de l'URL spécifiée par une des méthodes de redirection.
Cette option est inactive par défaut, mais pourra être activée par défaut dans les prochaines
releases.
De plus, il y a une variété de méthodes dans le Redirector pour réaliser les redirections
actuelles :
• gotoUrl() utilise setGotoUrl() (URL sous forme de chaîne) pour construire une
URL et réaliser une redirection.
345
Zend_Controller
Enfin, vous pouvez déterminer l'URL de la redirection courante à tout moment en utilisant
getRedirectUrl().
Cet exemple surcharge de multiples options, incluant le réglage du code de statut HTTP à
utiliser dans la redirection ("303"), le retrait du exit par défaut après la redirection, et la
définition d'une URL par défaut à utiliser lors d'une redirection.
346
Zend_Controller
Cet exemple suppose que vous utilisez les paramètres par défaut, ce qui inclut que toute
redirection sera suivie d'un exit() immédiat.
// EXEMPLE ALTERNATIF
class AlternativeController extends Zend_Controller_Action
{
/**
* Redirector - défini pour l'auto-complétion
*
* @var Zend_Controller_Action_Helper_Redirector
*/
protected $_redirector = null;
$url = '/my-controller/my-action/param1/test/param2/test2';
$this->_redirector->gotoUrl($url);
return;
// jamais atteint puisque les paramètres par défaut
// sont à goto et exit
}
}
347
Zend_Controller
348
Zend_Controller
L'exemple suivant utilise la méthode assemble() du routeurpour créer une URL basée
sur un tableau associatif de paramètres fournis. Il suppose que la route suivante a été
enregistrée :
En donnant un tableau avec l'année réglée à 2006, le mois à 4, le jour à 24 et l'ID à 42, il
construira l'URL /blog/2006/4/24/42.
8.4.7. ViewRenderer
8.4.7.1. Introduction
• Élimine le besoin d'instancier un objet de vue dans ses contrôleurs. Ceci devient automatique.
• Configure automatiquement les chemins vers les scripts de vue, les aides, et les filtres, en
se basant sur le module actuel et associe le nom du module comme préfixe de classe pour
les aides et les filtres.
• Créer un objet de vue général accessible pour tous les contrôleurs et donc pour toutes les
actions disptachées.
349
Zend_Controller
• Donne accès aux paramètres configurant le chemin de base (base path) et le chemin des
scripts (script path), de la vue.
$viewRenderer =
Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
$viewRenderer->setView($view)
->setViewSuffix('php');
8.4.7.2. API
Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
Voici un exemple :
350
Zend_Controller
...
Le ViewRenderer définit de même des accesseurs pour configurer la vue qu'il encapsule :
• setScriptAction($name) peut être utilisée pour spécifier le script de vue d'une action
à rendre. $name doit être le nom de l'action sans le suffixe (et sans le nom du contrôleur
sauf si noController a été activé). Si vous n'utilisez pas cette méthode elle cherchera
le script de vue correspondant au nom de l'action en cours (issue de l'objet de requête).
getScriptAction() retourne la valeur actuelle de ce paramètre.
• initView($path, $prefix, $options) doit être appelée pour configurer la vue : son
"base path", le préfixe de classe pour les filtres et aides, et des options matérialisées par :
neverRender, noRender, noController, scriptAction, et responseSegment.
351
Zend_Controller
Il est aussi possible de personnaliser les chemins utilisés pour déterminer le base path (chemin
de base) de la vue ainsi que le script path (chemin vers les scripts de vue). Des méthodes le
permettent, utilisez les options suivantes avec :
• :suffix est utilisée pour le suffixe du script de vue. setViewSuffix() permet aussi de
le modifier.
352
Zend_Controller
Par défaut, les règles de l'inflecteur sont statiques autant pour le suffixe et le répertoire module,
que pour la cible. Ceci permet au ViewRenderer de modifier ces valeurs dynamiquement.
Le ViewRenderer utilise certaines règles par défaut pour chercher ses scripts
de vue, voyez plutôt :
Enfin, l'API ViewRenderer vous propose aussi des méthodes pour déterminer les scripts de
vue, et rendre la vue. Celles-ci se décomposent en :
Une fois rendue, la vue utilise noRender pour éviter un double rendu
automatisé.
353
Zend_Controller
Les délimiteurs apparaissant dans les modules, contrôleurs ou actions seront remplacés par
des tirets ("-"). Ainsi pour un un contrôleur "foo.bar" et une action "baz:bat", il résultera un
chemin de vue "foo-bar/baz-bat.phtml".
354
Zend_Controller
8.4.7.3. Exemples
...
355
Zend_Controller
Notez dans le second exemple, le module est celui par défaut, mais comme un
séparateur de chemin (tiret bas ou "_") est donné, alors le contrôleur distribué
devient Bar_BazController, dans Bar/BazController.php.
Il peut être nécessaire dans certains cas de désactiver manuellement le rendu automatique
de vue effectué par ViewRenderer. Par exemple, si le contrôleur doit retourner une sortie
spéciale, comme XML ou JSON. Deux options s'offrent à vous : setNeverRender()) et
setNoRender().
356
Zend_Controller
Il peut arriver que vous éprouviez le besoin de rendre un script de vue différent de
celui correspondant à l'action en cours de distribution. Par exemple, un contrôleur qui
possède deux actions ajout et édition, qui sont susceptibles toutes les deux de rendre le
même script de vue. Utilisez alors setScriptAction(), setRender(), ou appelez l'aide
ViewRenderer directement :
// continue le processus...
}
}
Si vous désirez modifier l'objet de vue absorbé par ViewRenderer, pour par exemple
ajouter un chemin vers des aides spécifique, ou spécifier l'encodage, vous pourriez par
exemple récupérer l'objet de vue depuis le ViewRenderer, ou dans un contrôleur.
357
Zend_Controller
Dans certains cas, il peut être nécessaire d'utiliser un chemin absolu, fixe. Par exemple
si vous ne donnez accès à vos graphistes qu'à un seul dossier, en utilisant un moteur de
template tel que Smarty.
Pour ceci, imaginons que le base path soit fixé à "/opt/vendor/templates", et que vous
voulez que vos scripts de vues soit référencés par ":moduleDir/:controller/:action.:suffix";
si le paramètre noController est activé, vous désirez utiliser le dossier plus haut
":action.:suffix". Enfin, vous désirez un suffixe en "tpl" :
/**
* Dans le fichier de démarrage :
*/
Exemple 102. Rendu de plusieurs scripts de vue dans une même action
Afin de rendre plusieurs scripts de vue dans une même action, appelez tout simplement
plusieurs fois render() :
358
Zend_Controller
• init(), déclenchée par le gestionnaire d'aides à l'instanciation, peut être utilisée pour
déclencher l'initialisation dans l'aide ; ceci peut être pratique pour remettre dans l'état initial
quand de multiples contrôleurs utilisent la même aide dans des actions enchaînées.
• postDispatch() est déclenchée quand une action a été distribuée - même si un plugin
preDispatch() a évité l'action. Principalement utile pour le nettoyage.
• getName() récupère le nom de l'aide. Elle récupère la portion du nom de la classe qui suit
le dernier tiret bas ("_"), ou le nom de la classe entier sinon. Pour exemple, si la classe est
nommée Zend_Controller_Action_Helper_Redirector, elle retourne Redirector ;
une classe nommée FooMessage retournera FooMessage.
Vous pouvez optionnellement inclure une méthode direct() dans votre classe d'aide. Si
définie, ceci vous permet de traiter l'aide comme une méthode du gestionnaire, dans le but de
faciliter un usage unique de l'aide. Pour exemple, l'aide Redirectordéfinit direct() comme un
alias de goto(), vous permettant d'utiliser l'aide comme ceci :
Une fois que vous avez créé vos propres classes d'aide, vous pouvez en fournir l'accès comme
décrit dans les sections ci-dessus.
9. Objet de réponse
9.1. Utilisation
L'objet de réponse équilibre la balance du modèle MVC avec l'objet de requête. Son
but est de collecter du contenu et des en-têtes, afin d'être rendue. De plus, le
contrôleur frontal passe les exceptions qu'il a rencontré à l'objet de réponse, vous
offrant une possibilité élégante de les gérer. Ce comportement peut être changé avec
Zend_Controller_Front::throwExceptions(true) :
$front->throwExceptions(true);
$response->sendResponse();
$front->returnResponse(true);
$response = $front->dispatch();
359
Zend_Controller
Vous ne devriez pas afficher du contenu directement dans un contrôleur. Empiler plutôt les
affichages dans l'objet de réponse :
Grâce à cela, tous les en-têtes sont envoyés d'un coup, juste avant l'affichage du contenu.
Si vous utilisez des vues dans vos action, vous n'avez pas besoin d'ajouter le
rendu de la vue dans la réponse, car Zend_Controller_Action::render()
le fait par défaut.
Si une exception est ajoutée par le contrôleur frontal à la réponse, utilisez isException() pour
vérifier ceci, et récupérez les exceptions avec getException(). Vous pourriez par exemple
créer un objet de réponse d'erreur, comme un 404, et journaliser l'exception, etc.
Pour prendre la main sur l'objet de réponse, demandez le au contrôleur frontal via un accesseur,
ou commandez lui de vous retourner la celle-ci lors après la distribution.
// du code ici
// enfin, affichage:
$response->sendResponse();
Par défaut, les messages d'exceptions ne sont pas affichés. Utilisez renderExceptions()
si vous le voulez. Aussi, vous pouvez activer leur rendu grâce à
Zend_Controller_Front::throwExceptions() :
$response->renderExceptions(true);
$front->dispatch($request, $response);
// ou:
$front->returnResponse(true);
$response = $front->dispatch();
$response->renderExceptions();
$response->sendResponse();
// ou:
360
Zend_Controller
$front->throwExceptions(true);
$front->dispatch();
• canSendHeaders($throw = false) est utilisée pour déterminer si les en-têtes n'ont pas
déjà été envoyés. Si le paramètre optionnel $throw est à TRUE, alors une exception sera
envoyée si c'est le cas. L'attribut headersSentThrowsException permet aussi de gérer
ce comportement.
Avant d'affecter un en-tête, cette méthode utilise canSendHeaders() pour voir si à ce point
l'en-tête peut être envoyé.
En interne, cette méthode appelle setHeader() avec $replace à TRUE, pour s'assurer de
l'unicité de cet en-tête.
• getHeaders() retourne un tableau contenant tous les en-têtes. Chaque élément est un
tableau "nom" 'valeur.
• setRawHeader() s'utilise pour affecter un en-tête brut, n'utilisant pas la syntaxe clé/valeur,
comme un statut.
De plus, des accesseurs spéciaux sont disponibles pour manipuler le code de réponse HTTP :
setHttpResponseCode() et getHttpResponseCode().
Par exemple, vous pourriez utiliser l'évènement preDispatch() pour rajouter un bandeau de
header au corps de la réponse, et l'évènement postDispatch() pour en ajouter un bandeau
de footer :
361
Zend_Controller
$response->prepend('header', $view->render('header.phtml'));
}
$response->append('footer', $view->render('footer.phtml'));
}
}
// un contrôleur d'action
class MyController extends Zend_Controller_Action
{
public function fooAction()
{
$this->render();
}
}
Un appel à /my/foo dans ce cas là, générera un objet de réponse ressemblant à ceci :
array(
'header' => ..., // contenu du segment header
'default' => ..., // corps, contenu de MyController::fooAction()
'footer' => ... // contenu du segment footer
);
Lorsque ceci est rendu, ça l'est dans l'ordre dans lequel les segments sont rangés dans la
réponse.
• clearBody($name = null) va vider le contenu du segment qui lui est passé en paramètre
via $name. Par défaut, il vide tout le tableau (détruit tous les segments).
362
Zend_Controller
Vous pouvez dériver l'objet de réponse pour plusieurs raisons, incluant la volonté de modifier le
retour de la sortie, pour ne pas envoyer d'en-têtes dans un environnement de requête CLI ou
PHP-GTK, la gestion de templates, etc.
10. Plugins
10.1. Introduction
L'architecture MVC de Zend Framework propose l'injection de plugins de code, qui vont intervenir
à différents niveaux dans le processus complet. Le contrôleur frontal enregistre des plugins, et
363
Zend_Controller
utilise un gestionnaire de plugins ("plugin broker"), qui va se charger de faire intervenir chaque
plugin, à chacun des instants clés à votre disposition.
Les instants clés sont des méthodes événementielles définies dans la classe abstraite
Zend_Controller_Plugin_Abstract, dont tous les plugins doivent hériter :
• routeShutdown() est appelée après que le routeur aie fini de router la requête.
• preDispatch() est appelée avant qu'une action ne soit distribuée par le distributeur.
Cette méthode permet un filtrage ou un proxy. En jouant sur la requête à
ce niveau là, vous êtes capable de changer le processus, et en vous aidant
de Zend_Controller_Request_Abstract::setDispatched(true)), vous supprimez
l'ordre de distribution de celle-ci.
• postDispatch() est appelée après qu'une action n'ait été distribuée par le distributeur.
Cette méthode permet un filtrage ou un proxy. En jouant sur la requête à ce
niveau là, vous êtes capable de changer le processus, et en vous aidant de
Zend_Controller_Request_Abstract::setDispatched(false)), vous ordonnez
une redistribution de celle-ci.
364
Zend_Controller
$this->getResponse()
->appendBody("<p>routeStartup() appelée</p>\n");
}
$front = Zend_Controller_Front::getInstance();
$front->setControllerDirectory('/path/to/controllers')
->setRouter(new Zend_Controller_Router_Rewrite())
->registerPlugin(new MyPlugin());
$front->dispatch();
Si aucune autre action ne génère une sortie (typiquement, un rendu de vue), alors le résultat
suivant devrait s'afficher :
<p>routeStartup() appelée</p>
<p>routeShutdown() appelée</p>
<p>dispatchLoopStartup() appelée</p>
<p>preDispatch() appelée</p>
<p>postDispatch() appelée</p>
<p>dispatchLoopShutdown() appelée</p>
365
Zend_Controller
Enregistrez vos plugins où vous voulez dans votre code, mais faites attention de
ne pas leur faire sauter de méthodes, selon l'endroit où vous les enregistrez.
10.5.1. ActionStack
Le plugin ActionStack vous permet de gérer une pile de requêtes en opérant en
postDispatch. Si un forward (un appel à une autre action) est détecté, alors le plugin n'agira
pas. Dans le cas contraire cependant, sa pile est analysée (en ordre LIFO : dernier empilé,
premier dépilé) et une nouvelle action est distribuée. Ce plugin est commandé par l'aide d'action
du même nom ActionStack
La méthode forward(), elle, est directe : elle attend un objet de requête qu'elle passe
immédiatement au contrôleur frontal en redemandant un jeton de distribution.
10.5.2. Zend_Controller_Plugin_ErrorHandler
Zend_Controller_Plugin_ErrorHandler est un plugin intégré par défaut pour gérer les
exceptions levées par votre application, il sert à gérer les exceptions envoyées par l'application,
366
Zend_Controller
en particulier celles concernant des contrôleurs ou des actions manquants. C'est une manière
rejoignant la section Exceptions MVC.
• Intercepter les exceptions envoyées si un contrôleur ou une action ne peuvent être trouvés
Globalement, ErrorHandler sert à gérer les erreurs HTTP 404 ou 500. Attention, le plugin n'est
pas destiné à intervenir sur les exceptions envoyées dans d'autres plugins. Des effets de bords
peuvent apparaître, veillez à les gérer.
• setErrorHandler() est un raccourci des trois précédantes. Passez un tableau avec les
clés "module", "controller", or "action", et leurs valeurs appropriées.
Ce comportement fonctionne aussi avec le constructeur du plugin. Celui-ci agit comme un proxy
vers setErrorHandler().
Si une exception arrive lorsque le plugin agit, alors celui-ci ordonne au contrôleur frontal de
renvoyer l'exception, et relance la dernière exception enregistrée dans l'objet de réponse.
Une fois que vous possédez l'objet contenant l'exception, inspectez son type avec $errors-
>type;. Des constantes sont à votre disposition :
• Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE, indique
qu'aucune route correspondante n'a été trouvée.
367
Zend_Controller
• Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER, indique un
contrôleur non trouvé.
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:
// erreur 404 -- contrôleur ou action introuvable
$this->getResponse()->setRawHeader('HTTP/1.1 404 Not Found');
Enfin, il est possible de récupérer l'exception ayant menée au contrôleur d'erreur. Ceci afin de
l'analyser. L'attribut exception de l'objet error_handler le permet :
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:
// erreur 404 -- contrôleur ou action introuvable
$this->getResponse()->setRawHeader('HTTP/1.1 404 Not Found');
// ...
368
Zend_Controller
new Zend_Log(
new Zend_Log_Writer_Stream(
'/tmp/applicationException.log')
);
$log->debug($exception->getMessage()
. "\n"
. $exception->getTraceAsString());
break;
}
}
Si vous décomposez vos processus en plusieurs actions ou plusieurs appels à render(), il est
possible que la réponse contienne déjà des éléments. Ceci peut introduire un mélange entre le
rendu attendu et le contenu de l'erreur.
Si vous désirez rendre votre contrôleur d'erreur dans ce contenu, alors il n'y a rien à faire de
spécial. En revanche, il peut aussi être judicieux de vider totalement la réponse afin de rendre
le contrôleur d'erreurs. Procédez alors comme suit :
$this->getResponse()->clearBody();
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Zend_Controller_Plugin_ErrorHandler());
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Zend_Controller_Plugin_ErrorHandler(array(
'module' => 'mystuff',
'controller' => 'static',
'action' => 'error'
)));
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin($plugin);
Pour utiliser le plugin de gestion d'erreurs, un contrôleur d'erreurs est requis. En voici un
exemple :
369
Zend_Controller
{
$errors = $this->_getParam('error_handler');
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:
// 404 error -- controller or action not found
$this->getResponse()->setRawHeader('HTTP/1.1 404 Not Found');
$content =<<<EOH
<h1>Erreur !</h1>
<p>Page introuvable.</p>
EOH;
break;
default:
// application error
$content =<<<EOH
<h1>Erreur !</h1>
<p>Une erreur innatendue est survenue</p>
EOH;
break;
}
$this->view->content = $content;
}
}
10.5.3. Zend_Controller_Plugin_PutHandler
Zend_Controller_Plugin_PutHandler fournit un plugin intégré pour la gestion du corps
des requêtes PUT en tant que paramètres de requête, tout comme le corps d'une requête
POST. Il va inspecter la requête et, s'il s'agit d'une requête PUT, va utiliser la fonction parse_str
pour découper le contenu brut de la requête PUT en un tableau de paramètres qui est ensuite
enregistré dans l'objet de requête. Par exemple :
title=Hello&body=World
Pour recevoir les paramètres "title" et "body" comme des paramètres de requête habituels, vous
devez enregistrer le plugin :
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Zend_Controller_Plugin_PutHandler());
Ensuite vous pouvez accéder aux paramètres du corps de la requête PUT par leur nom à
l'intérieur de votre contrôleur :
...
public function putAction()
{
$title = $this->getRequest()->getParam('title'); // $title = "Hello"
$body = $this->getRequest()->getParam('body'); // $body = "World"
}
370
Zend_Controller
...
docroot/
index.php
application/
default/
controllers/
IndexController.php
FooController.php
models/
views/
scripts/
index/
foo/
helpers/
filters/
blog/
controllers/
IndexController.php
models/
views/
scripts/
index/
helpers/
filters/
news/
controllers/
IndexController.php
ListController.php
models/
views/
scripts/
index/
list/
helpers/
filters/
Ci dessus, le nom du module est utilisé comme préfixe pour les contrôleurs qu'il
possède. Il y a donc trois contrôleurs : "Blog_IndexController", "News_IndexController",
et "News_ListController". Deux contrôleurs dans le module par défaut sont aussi définis,
"IndexController" et "FooController". Ceux-ci ne possèdent pas le nom du module dans leur nom.
Cet exemple d'arborescence conseillée sera utilisé dans ce chapitre.
371
Zend_Controller
$front->setControllerDirectory(array(
'default' => '/path/to/application/controllers',
'blog' => '/path/to/application/blog/controllers'
));
$front->addControllerDirectory('/path/to/application/news/controllers',
'news');
Enfin, si vous avez un dossier spécial regroupant tous vos modules, indiquez le grâce à
addModuleDirectory() :
/**
* Sur cet exemple :
* application/
* modules/
* default/
* controllers/
* foo/
* controllers/
* bar/
* controllers/
*/
$front->addModuleDirectory('/path/to/application/modules');
Dans le code ci dessus, vous déclarez 3 modules en une méthodes. Celle-ci s'attend à la
structure comprenant les modules default, foo, et bar, chacun devant posséder un dossier
controllers
/**
* Le dossier des contrôleurs s'appelle désormais 'con'
* application/
* modules/
* default/
* con/
* foo/
* con/
* bar/
* con/
372
Zend_Controller
*/
$front->setModuleControllerDirectoryName('con');
$front->addModuleDirectory('/path/to/application/modules');
• :module/:controller/:action/*
• :controller/:action/*
Ainsi, elle va trouver les contrôleurs et actions, avec ou sans module les précédant. Un module
ne sera trouvé que si sa clé existe dans le tableau d'options passé au contrôleur frontal ou au
distributeur.
Si vous voulez renvoyer directement vers le contrôleur par défaut du module "default", passez
le paramètre useDefaultControllerAlways à TRUE dans le contrôleur frontal :
$front->setParam('useDefaultControllerAlways', true);
Cependant, les exceptions peuvent contenir des messages ou des traces plutôt sensibles pour
le système, comme des requêtes SQL, l'emplacement de certains fichiers ... Pour vous aider à
protéger votre site, par défaut, Zend_Controller_Front attrape toutes les exceptions et les
enregistre dans l'objet de réponse ; et bien entendu, par défaut, cet objet de réponse n'affiche
pas ces exceptions.
• Par défaut, le plugin error handlerest présent, et activé. Ce plugin a été conçu pour gérer :
373
Zend_Controller
ErrorHandler agit dans le postDispatch(), et analyse si une exception a été levée (en
gérant son type). Si c'est le cas, alors le plugin renvoie un jeton vers un contrôleur de gestion
des erreurs.
Ce contrôleur couvrira la majorité des cas d'utilisation. Il parvient à gérer les cas "contrôleur
absent", "action absente", ou "autre cas".
• Zend_Controller_Front::throwExceptions()
En passant la valeur TRUE à cette méthode, vous indiquez au contrôleur frontal que vous
souhaitez qu'il vous retourne les exceptions qu'il rencontre. Ainsi, il ne les ajoutera pas à la
réponse, et il ne fera pas intervenir le plugin "Error handler". Exemple :
$front->throwExceptions(true);
try {
$front->dispatch();
} catch (Exception $e) {
// A vous de gérer ici
}
Cette méthode vous permet d'utiliser une gestion personnalisée des exceptions dans votre
application, de manière simple.
• Zend_Controller_Response_Abstract::renderExceptions()
• Zend_Controller_Front::returnResponse() et
Zend_Controller_Response_Abstract::isException().
$front->returnResponse(true);
$response = $front->dispatch();
if ($response->isException()) {
$exceptions = $response->getException();
// Gestion des exceptions ici
} else {
$response->sendHeaders();
$response->outputBody();
}
374
Zend_Controller
Comme exemples :
$front->setParam('useDefaultControllerAlways', true);
// ou
$dispatcher->setParam('useDefaultControllerAlways', true);
Lorsque ceci est injecté, le distributeur utilisera le contrôleur par défaut s'il s'aperçoit qu'il
ne peut distribuer un contrôleur spécifique, plutôt que de renvoyer une exception. Méfiez
vous des moteurs de recherche qui n'aiment pas que plusieurs URI pointent sur un même
contenu. En effet, avec ce paramètre activé, les utilisateurs orthographiant mal votre site,
seront redirigés vers la page d'accueil de celui-ci, ce qui peut aboutir à du "duplicate
content" (contenu dupliqué).
Cet exemple intercepte les actions non existantes, et redirige vers l'action principale du
contrôleur actuel.
375
Zend_Controller
return $action;
}
}
L'exemple précédant vérifie si l'action existe dans le contrôleur demandé. Si ce n'est pas le
cas, il redéfinit l'action en spécifiant celle par défaut.
Cette méthode permet de changer l'action avant la distribution. Attention une fois encore aux
erreurs de syntaxes dans l'URL, qui devraient mener vers une page d'erreur quelconque.
• Utilisez Zend_Controller_Action::preDispatch() ou
Zend_Controller_Plugin_Abstract::preDispatch() pour identifier les actions
invalides.
L'utilisation d'un plugin offre une flexibilité supplémentaire : Si tous vos contrôleurs n'héritent
pas de la même classe, plutôt que de dupliquer du code, un plugin va agir indépendamment
de vos contrôleurs. En preDispatch(), il agit avant ceux-ci.
Par exemple :
class My_Controller_PreDispatchPlugin
extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$front = Zend_Controller_Front::getInstance();
$dispatcher = $front->getDispatcher();
$class = $dispatcher->getControllerClass($request);
if (!$controller) {
$class = $dispatcher->getDefaultControllerClass($request);
}
$r = new ReflectionClass($class);
$action = $dispatcher->getActionMethod($request);
376
Zend_Controller
if (!$r->hasMethod($action)) {
$defaultAction = $dispatcher->getDefaultAction();
$controllerName = $request->getControllerName();
$response = $front->getResponse();
$response->setRedirect('/' . $controllerName
. '/' . $defaultAction);
$response->sendHeaders();
exit;
}
}
}
Dans cet exemple, nous vérifions si l'action demandée existe dans le contrôleur distribué.
Si ce n'est pas le cas, nous exécutons une redirection immédiate.
377
Zend_Currency
1. Introduction à Zend_Currency
Zend_Currency est une partie du coeur de l'i18n ("internationalisation") de Zend Framework.
Elle permet le traitement de toutes les questions liées à la monnaie, à la représentation de
l'argent, son formatage, les services d'échange et les calculs.
Zend_Currency a l'avantage que les représentations de monnaie qui ont déjà été définies
peuvent être réutilisés. Vous pouvez ainsi deux représentations différentes de la même
monnaie.
Zend_Currency vous permet de faire des calculs avec les valeurs de monnaie. De plus il
vous fournit une interface pour l'échange de devises.
• Méthodes additionnelles
2. Utiliser Zend_Currency
2.1. Utilisation de base
La manière la plus simple consiste à se reposer sur la locale de l'utilisateur. Lorsque vous créez
une instance de Zend_Currency sans préciser d'options, la locale du client sera alors utilisée.
Imaginons un client dont la locale est "en_US" dans son navigateur. Dans ce cas,
Zend_Currency détectera automatiquement la monnaie à utiliser.
L'objet crée va alors contenir une monnaie "US Dollar" car il s'agit de la monnaie affectée aux
USA. D'autres options ont aussi été affectées comme le signe "$" ou l'abbréviation "USD".
378
Zend_Currency
Zend_Currency supporte aussi l'utilisation d'une locale globale. Mettez une instance de
Zend_Locale dans le registre comme montré ci-après. Dans un tel cas, l'option locale n'est
plus obligatoire pour chaque instance et la même locale sera utilisée partout, tout le temps.
// dans le bootstrap
$locale = new Zend_Locale('de_AT');
Zend_Registry::set('Zend_Locale', $locale);
Zend_Currency connait tous les noms, abbréviations et signes des monnaies mais il peut
s'avérer nécessaire de devoir remplacer la représentation visuelle d'une monnaie.
• Script:
Vous pouvez définit les scripts à utiliser pour le rendu des chiffres des monnaies. Vous
trouverez des détails sur les scripts dans le chapitre de Zend_Locale concernant Les
systèmes de conversion des nombres.
379
Zend_Currency
Le nombre qui représente la somme est par défaut formatté via les valeurs que fournit la
locale en cours. Par exemple, la virgule ',' est utilisée pour séparer les milliers dans la langue
anglaise, mais en français il s'agit du séparateur des décimales.
La liste suivante précise les options disponibles qui peuvent être passées en constructeur ou via
la méthode setFormat(), sous forme de tableau.
• format: Précise le format pour représenter les nombres. Ce format inclut par exemple le
séparateur des milliers. Vous pouvez vous reposer sur la locale en passant un identifiant
de locale, ou définir un format manuellement. Si aucun format n'est précisé, la locale dans
Zend_Currency sera utilisée. Voyez le chapitre sur le formattage des nombres.
• locale: Définit la locale à utiliser pour cette monnaie. Elle sera utilisée pour les paramètres par
défaut si il faut les utiliser. Notez que si vous ne passez pas de locale vous-même, elle sera
alors détectée de manière automatique, ce qui pourrait créer des problèmes.
• position: Définit la position de la monnaie. Pour plus d'informations, voyez cette section.
• precision: Définit la précision à utiliser pour représenter la monnaie. La valeur par défaut
dépend de la locale et vaut la plupart du temps 2.
• script: Indique le script à utiliser pour représenter les chiffres. Souvent par défaut 'Latn', qui
inclut les chiffres de 0 à 9. Les autres scripts comme 'Arab' utilisent d'autres chiffres. Voyez
Le chapitre sur les système numérraires pour plus de détails.
• value: Indique le montant (la valeur de la monnaie). S'utilise avec l'option service.
Beaucoup d'options sont donc ajustables, et la plupart trouvent leurs valeurs par défaut dans les
représentations normalisées de la monnaie utilisée.
380
Zend_Currency
Constante Description
USE_SYMBOL Le symbole de la monnaie sera rendu. Pour
l'Euro : '€'
USE_SHORTNAME L'abbréviation sera utilisée pour le rendu
visuel. L'Euro aura 'EUR' comme abbréviation
par exemple. La plupart des abbréviations
tiennent sur 3 caractères.
USE_NAME Le nom complet de la monnaie sera utilisé.
Pour le dollar américain : "US Dollar".
Imaginons que le client utilise la locale "en_US". Sans autre option, la valeur de monnaie
retournée ressemblerait à ceci:
Sans le paramètre display, le signe de la monnaie sera utilisé lors du rendu visuel. Si la
monnaie n'a pas de signe, son abbréviation sera utilisée à la place.
Toutes les monnaies ne possèdent pas forcément un signe. Ceci signifie que
s'il n'y a pas de signe par défaut pour la monnaie, et que vous spécifiez
manuellement de rendre un signe, alors le rendu de la monnaie sera nul car le
signe serait alors une chaine vide.
Pour changer des options concernant les monnaies, voyez le paragraphe ci-après.
381
Zend_Currency
Imaginons que le client utilise la locale "en_US". Nous ne voulons pas des paramètres par
défaut, mais nous voulons préciser manuellement notre propre description. Ceci s'applique
au moyen d'une option simple:
Ainsi, utiliser l'option sign peut se faire en évitant de toucher à display, nul besoin
de passer cette dernière à 'USE_SYMBOL'.
Constante Description
STANDARD Affiche le symbole de la monnaie dans une
position standard, conforme à la locale
RIGHT Affiche le symbole de la monnaie à droite de
sa valeur
LEFT Affiche le symbole de la monnaie à gauche de
sa valeur
382
Zend_Currency
Imaginons que le client utilise la locale "en_US". Sans option, la valeur retournée serait par
exemple:
En utilisant la valeur par défaut, le symbole pourrait être retourné à gauche ou à droite de
la valeur de monnaie. Voyons comment fixer cette position:
Notez que dans le deuxième exemple, la position de USD est fixée quelle que soit la locale
ou la monnaie considérée.
Par exemple, la plupart des locales utilisent le script latin pour rendre les nombres. Mais certaines
langues, comme l'arabe, utilisent d'autres chiffres. Et un site Web Arabe va utiliser le rendu arabe
pour toutes les monnaies, voyez l'exemple:
Imagnons que nous utisons la monnaie "Dollar". Mais nous voulons rendre notre page avec
des scripts arabes.
Pour plus d'informations sur les scripts disponibles, voyez le manuel de Zend_Localesur les
systèmes des nombres.
Le formattage d'une monnaie peut être changé. Par défaut, la locale est utilisée. Elle indique le
séparateur des milliers, le point de la décimale et la précision.
383
Zend_Currency
array(
'value' => 1000,
'currency' => 'USD'
'format' => 'de',
)
);
Il existe deux manières de préciser le format à utiliser, manuellement ou via une locale.
Utiliser la locale vous permet de bénéficier de certains automatismes. Par exemple la locale 'de'
definit le point '.' comme séparateur des milliers, et la virgule ',' comme séparateur décimal. En
anglais, c'est l'inverse.
Si vous les définissez manuellement, vous devez alors respecter le format décrit dans ce chapitre
de la localisation . Voyez plutôt:
384
Zend_Currency
Comme la précision est de 2, vous obtiendrez des décimales à '00' pour un chiffre rond.
Pour pallier à ce problème de précision, vous pouvez simplement utiliser l'option precision avec la
valeur '0'. La précision prend une valeur entre 0 et 9. Les valeurs des monnaies seront arrondies
lorsqu'elles ne tiennent pas dans la précision demandée.
385
Zend_Currency
);
• add(): Ajoute la valeur de la monnaie à celle actuellement stockée en mémoire dans l'objet
• sub(): Soustrait la valeur de la monnaie à celle actuellement stockée en mémoire dans l'objet
• div(): Divise la valeur de la monnaie à celle actuellement stockée en mémoire dans l'objet.
• mul(): Multiplie la valeur de la monnaie à celle actuellement stockée en mémoire dans l'objet.
Comme vous le voyez ces méthodes permettent n'importe quel calcul avec Zend_Currency.
Voici quelques exemples:
$currency->add(500);
print $currency; // Pourrait retourner '$ 1.500,00'
386
Zend_Currency
if ($currency->isMore($currency_2)) {
print "First is more";
}
$currency->div(5);
print $currency; // Pourrait retourner '$ 200,00'
Dans un tel cas, les monnaies doivent être converties avec un taux. Les taux officiels sont
conservés par les banques ou encore les journaux. Dans le Web, des services de conversion
existent. Zend_Currency permet leur utilisation par fonction de rappel.
switch ($to) {
case 'EUR':
return 0.5;
case 'JPE':
return 0.7;
}
Dans un service réel, vous demanderiez au fournisseur les taux de change, dans notre exemple
nous les codons en dur.
Attachons maintenant notre classe avec Zend_Currency. Ceci se fait de deux manières , soit
en attachant l'objet ou en attachant le nom de sa classe.
387
Zend_Currency
print $currency->add($currency2);
L'exemple ci-dessus retournera '$ 3.000' car 1.000 EUR seront convertis avec un taux de 2 vers
2.000 USD.
• getCurrencyList(): Retourne un tableau listant toutes les monnaies utilisées dans une
région donnée. La locale par défaut est utilisée si aucune information de région n'est fournie.
• getName(): Retourne le nom complet de la monnaie actuelle. Si aucun nom complet n'est
trouvé, l'abbréviation sera retournée.
var_dump($currency->getValue());
// retourne 0
388
Zend_Currency
var_dump($currency->getRegionList());
// retourne un tableau représentant toutes les régions dans lesquelles USD est utilisé
var_dump($currency->getRegionList('EUR'));
// retourne un tableau avec toutes les régions utilisant l'EUR
var_dump($currency->getName());
// pourrait retourner 'US Dollar'
var_dump($currency->getName('EUR'));
// Retourne 'Euro'
Comme vous le voyez, beaucoup de méthodes prennent des paramètres supplémentaires pour
surcharger l'objet actuel et le faire travailler sur une autre monnaie que celle stockée en interne.
389
Zend_Date
1. Introduction
Le composant Zend_Date fournit une API simple de manipulation des dates et des temps. Ses
méthodes acceptent un grand nombre de paramètres formatés, comme des parties de dates, et
complètent bien le support natif des dates de PHP.
Même si rester simple est un objectif, manipuler des dates et des temps localisés, les additionner,
par parties, les convertir ... mène vers une certaine complexité inévitable. Les dates et les
temps sont souvent notés de manière très différente selon la locale. Par exemple, certaines
locales placent le mois en premier, d'autres l'année. Pour plus d'information sur la gestion de la
localisation et de la normalisation, voyez le manuel de Zend_Locale.
Zend_Date supporte aussi les noms abrégés des mois dans beaucoup de
langues.Zend_Locale facilite la localisation et la normalisation des mois localisés et des jours
de semaines, leur conversion vers des timestamps.
Lorsque vous créez des instances de Zend_Date, leur fuseau sera automatiquement mis à celui
par défaut ! Ainsi, le paramètre de fuseau horaire prendre en compte le DST si besoin, vous
n'aurez donc pas besoin de leconfigurer.
Souvenez vous que les fuseaux UTC et GMT n'incluent pas la notion "d'heure d'hiver"(Daylight
Saving Time, DST). Donc même si vous dites à Zend_Date de prendre en compte le DST, il
sera annulé pour les futures instances de Zend_Date utilisant UTC ou GMT.
• API simple
Zend_Date propose une API simple, qui combine les meilleures fonctionnalités depuis 4
langages de programmation. Il est par exemple possible de comparer ou ajouter 2 dates.
• Internationalisé
390
Zend_Date
Toutes les abréviations des noms des mois ou des jours sont traduits dans plus de 130
langues. Les méthodes supportent des entrées et sorties de dates utilisant ces noms localisés.
• Timestamps infinis
Le support complet de ISO_8601 est pris en charge. Même les données partiellement
ISO_8601 seront identifiées correctement. Ces formats sont utiles pour le travail avec des
bases de données. Par exemple MSSQL et MySQLles utilise mais diffèrent légèrement dans
leur format, cependant ils sont supportés par Zend_Date grâce à la constante de spécification
de format Zend_Date::ISO_8601. Si les chaînes des dates sont conformes à "Y/m/d" ou "Y-
m-d H:i:s", selon les formats de date() de PHP, utilisez le support de Zend_Date pour le
format ISO 8601.
N'importe où dans la journée, les temps du sunrise (levé de soleil) et sunset (couché de soleil)
peuvent être affichés.
2. Aspect théorique
Pourquoi n'existe-il que l'unique classe Zend_Date pour gérer les dates et les heures dans
Zend Framework ?
Beaucoup de langages divisent la gestion des heures et des dates de calendrier en deux classes.
Cependant Zend Framework lutte pour une extrême simplicité, et forcer le développeur à gérer
différents objets avec différentes méthodes pour les heures et les dates entraîne un fardeau
dans beaucoup de situations. Puisque les méthodes de Zend_Date supporte le travail avec des
dates ambiguës qui n'incluraient pas toutes les parties (ère, année, mois, jour, heure, minute,
seconde, décalage horaire), les développeurs aiment la flexibilité et la facilité d'utilisation d'une
même classe et des mêmes méthodes afin de réaliser les mêmes actions par exemple addition,
soustraction, comparaison, fusion de parties de dates, etc.). Diviser la gestion de ces fragments
de date dans de multiples classes pourraient entraîner des complications quand on souhaite
réaliser des inter-opérations. Une unique classe réduit la duplication de code pour des opérations
similaires, sans l'obligation d'une hiérarchie d'héritage complexe.
Toutes les dates et heures, même celles ambiguës (par exemple sans année), sont
représentées en interne par des moments absolus dans le temps, stockés en tant que
référence temporelle UNIX exprimant la différence entre le moment désiré et le 1er janvier
391
Zend_Date
1970 à 00:00:00 GMT. Ceci est seulement possible, parce que Zend_Date n'est pas limité aux
références temporelles UNIX ou aux valeurs entières. L'extension BCMath est requise pour
supporter les très grandes dates hors de la plage du Vendredi 13 décembre 1901 à 20:45:54
GMT au Mardi 19 janvier 2038 à 03:14:07 GMT. De plus de petites erreurs mathématiques
peuvent apparaître causées par les limitations inhérentes aux types de données float et aux
arrondis, à moins d'utiliser l'extension BCMath.
Ainsi, une instance d'objet représentant trois heures peut être exprimé en tant que trois heures
après le 1er janvier 1970 à 00:00:00 GMT - c'est-à-dire 0 + 3 * 60 * 60 = 10800.
• Fonctions PHP :
Quand cela est possible, Zend_Date utilise actuellement les fonctions PHP pour améliorer
les performances.
3. Méthodes de base
Les sections qui suivent montrent l'utilisation de base de Zend_Date au travers d'exemples.
Dans ce manuel, une "date" représente toujours une date calendaire et un temps, même lorsque
cela n'est pas mentionné spécifiquement. La partie (date calendaire, ou temps) non spécifiée
prend la valeur par défaut "zéro". Ainsi additionner une date ayant une date calendaire mais pas
de temps, à un temps "12 heures" n'ayant pas de date calendaire, résultera en la combinaison
des deux : la date calendaire connue avec une partie temps de 12 heures, soit midi.
Paramétrer une date sans temps implique un temps par défaut de 00:00:00. Inversement
paramétrer un temps mais sans date calendaire lui verra affectée la valeur de 01.01.1970 plus
le temps. La plupart des ordinateurs entendent la date "nulle" comme étant la première seconde
de l'année 1970. La notion de timestamp est un nombre de secondes depuis cette date dite
"EPOCH".
392
Zend_Date
// utilise __toString()
echo "Date via toString() = ", $date, "\n";
Pour demander une simple égalité, utilisez equals(), qui retourne un booléen.
393
Zend_Date
If your actual timezone within the instance of Zend_Date is set to UTC or GMT the option 'fix_dst'
will not be used because these two timezones do not work with DST. When you change the
timezone for this instance again to a timezone which is not UTC or GMT the previous set 'fix_dst'
option will be used again for date "math".
394
Zend_Date
For example, when adding one month to January 31st, people familiar with SQL will expect
February 28th as the result. On the other side, people familiar with Excel and OpenOffice will
expect March 3rd as the result. The problem only occurs, if the resulting month does not have the
day, which is set in the original date. For Zend Framework developers, the desired behavior is
selectable using the extend_month option to choose either the SQL behaviour, if set to FALSE, or
the spreadsheet behaviour when set to TRUE. The default behaviour for extend_month is FALSE,
providing behavior compatible to SQL. By default, Zend_Date computes month calculations by
truncating dates to the end of the month (if necessary), without wrapping into the next month
when the original date designates a day of the month exceeding the number of days in the
resulting month. Use Zend_Date::setOptions(array('extend_month' => true)) to
make month calculations work like popular spreadsheet programs.
You can speed up Zend_Date by using an Zend_Cache adapter. This speeds up all methods
of Zend_Date when you are using localized data. For example all methods which accept
Zend_Date::DATE and Zend_Date::TIME constants would benefit from this. To set an
Zend_Cache adapter to Zend_Date just use Zend_Date::setOptions(array('cache'
=> $adapter)).
Normally the clocks from servers and computers differ from each other. Zend_Date is able
to handle such problems with the help of Zend_TimeSync. You can set a timeserver with
Zend_Date::setOptions(array('timesync' => $timeserver)) which will set the
offset between the own actual timestamp and the real actual timestamp for all instances of
Zend_Date. Using this option does not change the timestamp of existing instances. So best
usage is to set it within the bootstrap file.
Beware of mixing and matching operations with date parts between date objects for different
timezones, which generally produce undesireable results, unless the manipulations are only
related to the timestamp. Operating on Zend_Date objects having different timezones generally
works, except as just noted, since dates are normalized to UNIX timestamps on instantiation of
Zend_Date.
Most methods expect a constant selecting the desired $part of a date, such as
Zend_Date::HOUR. These constants are valid for all of the functions below. A list of
all available constants is provided in list of all constants. If no $part is specified,
then Zend_Date::TIMESTAMP is assumed. Alternatively, a user-specified format may
be used for $part, using the same underlying mechanism and format codes as
Zend_Locale_Format::getDate(). If a date object is constructed using an obviously invalid
date (e.g. a month number greater than 12), then Zend_Date will throw an exception, unless
no specific date format has been selected -i.e. $part is either NULL or Zend_Date::DATES
(a "loose" format).
395
Zend_Date
If the optional $locale parameter is provided, then the $locale disambiguates the
$date operand by replacing month and weekday names for string $date operands, and
even parsing date strings expressed according to the conventions of that locale (see
Zend_Locale_Format::getDate()). The automatic normalization of localized $date operands of
a string type occurs when $part is one of the Zend_Date::DATE* or Zend_Date::TIME*
constants. The locale identifies which language should be used to parse month names and
weekday names, if the $date is a string containing a date. If there is no $date input parameter,
then the $locale parameter specifies the locale to use for localizing output (e.g. the date format
for a string representation). Note that the $date input parameter might actually have a type name
instead (e.g. $hour for addHour()), although that does not prevent the use of Zend_Date
objects as arguments for that parameter. If no $locale was specified, then the locale of the
current object is used to interpret $date, or select the localized format for output.
Since Zend Framework 1.7.0 Zend_Date does also support the usage of an application wide
locale. You can simply set a Zend_Locale instance to the registry like shown below. With this
notation you can forget about setting the locale manually with each instance when you want to
use the same locale multiple times.
396
Zend_Date
Convenience methods exist for each combination of the basic operations and several common
date parts as shown in the tables below. These convenience methods help us lazy programmers
avoid having to type out the date part constants when using the general methods above.
Conveniently, they are named by combining a prefix (name of a basic operation) with a suffix
(type of date part), such as addYear(). In the list below, all combinations of "Date Parts" and
"Basic Operations" exist. For example, the operation "add" exists for each of these date parts,
including addDay(), addYear(), etc.
These convenience methods have the same equivalent functionality as the basic operation
methods, but expect string and integer $date operands containing only the values representing
the type indicated by the suffix of the convenience method. Thus, the names of these methods
(e.g. "Year" or "Minute") identify the units of the $date operand, when $date is a string or
integer.
397
Zend_Date
The basic operations below can be used instead of the convenience operations for specific date
parts, if the appropriate constant is used for the $part parameter.
398
Zend_Date
Behaviour of get()
Unlike get(), the other get*()
convenience methods only
return instances of Zend_Date
containing a date representing
the selected or computed date
or time.
set() set($date, $part = null, $locale =
null)
399
Zend_Date
Method Explanation
equals() equals($date, $part = null, $locale
= null)
400
Zend_Date
• Number of day as
'day' (Zend_Date::DAY_SHORT)
• Number of month as
'month' (Zend_Date::MONTH_SHORT)
• Minute as
'minute' (Zend_Date::MINUTE_SHORT)
• Second as
'second' (Zend_Date::SECOND_SHORT)
• Abbreviated timezone as
'timezone' (Zend_Date::TIMEZONE)
• Unix timestamp as
'timestamp' (Zend_Date::TIMESTAMP)
• Number of weekday as
'weekday' (Zend_Date::WEEKDAY_DIGIT)
• Day of year as
'dayofyear' (Zend_Date::DAY_OF_YEAR)
401
Zend_Date
Method Explanation
toValue() toValue($part = null)
Limitation of toValue()
This method calls get()
and casts the result to
a PHP integer, which will
give unpredictable results, if
get() returns a numeric string
containing a number too large
for a PHP integer on your
system. Use get() instead.
get() get($part = null, $locale = null)
Method Explanation
getFractionalPrecision() Return the precision of the part seconds
setFractionalPrecision() Set the precision of the part seconds
Method Explanation
getSunrise($location) Return the date's time of sunrise
getSunset($location) Return the date's time of sunset
402
Zend_Date
Method Explanation
getSunInfo($location) Return an array with the date's sun dates
La méthode statique now() a exactement le même effet qu'un passage par le constructeur.
Cependant la méthode statique, elle, ne peut pas prendre de paramètres additionnels.
$date = Zend_Date::now();
Il est possible de demander au serveur de base de données de formater la date dans un certain
format. Mais ceci reste en général assez lourd, et doit être pensé à chaque fois.
Nous avons donc des méthodes très pratiques et rapides dans Zend_Date, pour récupérer les
dates depuis une base de données.
Toutes les bases de données doivent répondre extrêmement rapidement à leurs requêtes.
Pour stocker une date de manière optimale dans une base, il faut utiliser un timestamp. En
interne, tous les SGBDs représentent les dates sous forme de timestamp (non Unix). Ainsi,
leur demander un timestamp sera toujours plus performant que de leur demander une date
dans un format précis.
403
Zend_Date
Exemple 124. Création d'une date depuis une base de données dans un format
En temps normal on utilisera un tableau avec toutes ses clés. Si une clé est absente, la
valeur qu'elle représente sera mise à la valeur zéro par défaut. Par exemple une tableau
ne comportant pas la clé "hour", donnera naissance à une date ayant une partie heures
à la valeur "0".
404
Zend_Date
parts that are expressed differently when in different parts of the world, the object's timezone
will automatically be used to compute the correct value, even though the internal timestamp is
the same for the same moment in time, regardless of the user's physical location in the world.
Regardless of the units involved, output must be expressed either as GMT or UTC or localized to
a locale. The example output below reflects localization to Europe/GMT+1 hour (e.g. Germany,
Austria, France).
405
Zend_Date
406
Zend_Date
407
Zend_Date
Especially note Zend_Date::DATES, since this format specifier has a unique property within
Zend_Date as an input format specifier. When used as an input format for $part, this constant
provides the most flexible acceptance of a variety of similar date formats. Heuristics are used to
automatically extract dates from an input string and then "fix" simple errors in dates (if any), such
as swapping of years, months, and days, when possible.
408
Zend_Date
409
Zend_Date
The accepted format specifiers can be changed from ISO Format to PHP's date format if you are
more comfortable with it. However, not all formats defined in the ISO norm are supported with
PHP's date format specifiers. Use the Zend_Date::setOptions(array('format_type'
=> 'php')) method to switch Zend_Date methods from supporting ISO format specifiers to
PHP date() type specifiers (see Self-Defined OUTPUT Formats Using PHP's date() Format
Specifiers below).
410
Zend_Date
411
Zend_Date
Note that the default ISO format differs from PHP's format which can be irritating
if you have not used in previous. Especially the format specifiers for Year and
Minute are often not used in the intended way.
For year there are two specifiers available which are often mistaken. The Y
specifier for the ISO year and the y specifier for the real year. The difference is
small but significant. Y calculates the ISO year, which is often used for calendar
formats. See for example the 31. December 2007. The real year is 2007, but it is
the first day of the first week in the week 1 of the year 2008. So, if you are using
'dd.MM.yyyy' you will get '31.December.2007' but if you use 'dd.MM.YYYY' you
will get '31.December.2008'. As you see this is no bug but a expected behaviour
depending on the used specifiers.
For minute the difference is not so big. ISO uses the specifier m for the minute,
unlike PHP which uses i. So if you are getting no minute in your format check if
you have used the right specifier.
412
Zend_Date
vice-versa. If you are not already comfortable with it, then use the standard ISO format instead.
Also, if you have legacy code using PHP's date format, then either manually convert it to the
ISO format using Zend_Locale_Format::convertPhpToIsoFormat(), or use setOptions(). The
following examples illustrate the usage of constants from the table below to create self-defined
formats.
The following table shows the list of PHP date format specifiers with their equivalent Zend_Date
constants and CLDR and ISO equivalent format specifiers. In most cases, when the CLDR and
ISO format does not have an equivalent format specifier, the PHP format specifier is not altered by
Zend_Locale_Format::convertPhpToIsoFormat(), and the Zend_Date methods then
recognize these "peculiar" PHP format specifiers, even when in the default "ISO" format mode.
413
Zend_Date
414
Zend_Date
7. Exemples concrets
Dans ce chapitre, nous décrirons plusieurs fonctionnalités disponibles dans Zend_Date. Ceci
sera fait au travers d'exemples concrets.
Le 3ème paramètre est aussi optionnel et peut être utilisé pour spécifier une locale. Celle-ci est
nécessaire afin de normaliser les noms des mois et des jours. Avec le 3ème paramètre fourni,
415
Zend_Date
isDate(); bien sûr, vérifie que la date existe. Zend_Date ne vérifie pas une date elle-même.
Il est possible de créer une date avec "31.February.2000" dans Zend_Date, simplement la date
sera convertie automatiquement par Zend_Date en "03.March.2000". isDate() effectue cette
vérification et retournera FALSE sur "31.February.2000" car cette date n'existe pas.
// Vérification de dates
$date = '01.03.2000';
if (Zend_Date::isDate($date)) {
print "la chaine $date est une date";
} else {
print "la chaine $date n'est PAS une date";
}
Comme presque personne ne connaît la localisation précise d'une ville sur la planète, nous avons
aussi écrit une classe qui donne ces coordonnées, pour plus de 250 grandes villes et capitales.
Ainsi la plupart des gens pourra utiliser des villes proches de chez eux.
A cet effet, Zend_Date_Cities::getCityList peut être utilisée, cette méthode retourne les
noms de toutes les villes prédéfinies dans la classe d'aide.
416
Zend_Date
Il y a 4 horizons définis, qui peuvent être utilisés avec des lieux pour déterminer la date exacte
de levé et couché du soleil. Le paramètre "horizon" est toujours optionnel, quelle que soit la
fonction dans laquelle il est utilisé. S'il n'est pas précisé, la valeur "effective" lui sera attribuée
par défaut.
Tableau 50. Valeurs d'horizons supportées pour les levé et couché de soleil
Évidemment, un endroit personnalisé peut aussi être utilisé pour le calcul. Une "latitude" et
une "longitude" seront alors nécessaires, en plus du paramètre optionnel "horizon".
// Voici une ville personnalisée qui n'est pas dans la liste prédéfinie
$mylocation = array('latitude' => 41.5, 'longitude' => 13.2446);
Dès lors, il faut créer un objet Zend_Date contenant la date dont on veut connaître les
informations de levé et de couché du soleil. 3 méthodes nous seront proposées : getSunset(),
getSunrise() et getSunInfo(). Ces 3 méthodes s'appliquent sur un objet Zend_Date et
retournent un objet Zend_Date.
417
Zend_Date
Un objet Zend_Date encapsule son propre fuseau horaire. Même en changeant le fuseau après
la création de l'objet, celui-ci s'en souviendra. De même, changer le fuseau via PHP n'aura
aucun impact sur l'objet Zend_Date avec lequel un travail est en cours, c'est celui-ci qui va vous
permettre via des méthodes de gérer son fuseau.
getTimezone() retourne le fuseau horaire actuel sur lequel travaille Zend_Date. Souvenez
vous que Zend_Date n'est pas lié aux mécanismes interne de PHP, ainsi le fuseau retourné
peut être différent de celui sur lequel PHP est réglé.setTimezone($zone) change le fuseau
horaire actuel de l'objet Zend_Date. Le fuseau ainsi fournit est toujours vérifié, s'il n'existe pas,
une exception sera levée. Si vous ne spécifiez pas de fuseau à cette méthode, alors c'est le
fuseau interne de PHP qui sera utilisé par défaut, comme c'est le cas lors de la création d'un
objet Zend_Date banal.
Zend_Date utilise toujours le fuseau par défaut (de PHP) lors de la création de l'instance.
Remarquez que changer le fuseau de l'objet a un impact sur la date s'y trouvant. Une date est
toujours exprimée relativement à un fuseau horaire, changer le fuseau dans l'objet ne change
pas la date de l'objet, mais bien sa représentation. Rappelez vous qu'en interne, les dates sont
représentées comme des timestamps GMT. Le fuseau donne une information de décalage par
rapport à GMT, en positif ou négatif.
Coupler le fuseau dans l'objet Zend_Date a un autre effet positif : il est possible de posséder
plusieurs objets de date, avec chacun un fuseau horaire différent.
418
Zend_Date
419
Zend_Db
1. Zend_Db_Adapter
Zend_Db et ses autres sous classes proposent une interface de connexion aux bases de
données avec Zend Framework. Zend_Db_Adapter est la classe de base que vous utilisez
pour vous connecter aux bases de données (SGBDs). Il y a différentes classes d'adaptateur
par SGBD.
Les classes Adapters de Zend_Db créent un pont entre les extensions PHP et une interface
commune. Ceci vous aide à écrire des applications déployables avec de multiples SGBDs,
demandant peu d'efforts.
L'interface de la classe d'adaptateur est semblable à celle de l'extension PHP Data Objects.
Zend_Db propose des classes d'adaptateurs vers les drivers PDO pour les SGBDs suivants :
• IBM DB2 et Informix Dynamic Server (IDS), en utilisant l'extension PHP pdo_ibm.
De plus, Zend_Db fournit aussi des classes se connectant avec les extensions PHP propres aux
SGBDs (hors PDO donc), pour les SGBDs suivants :
Chaque Zend_Db_Adapter utilise une extension PHP. Vous devez donc les
avoir activées pour utiliser les classes en question. Par exemple, si vous voulez
utiliser une classe Zend_Db_Adapter basée sur PDO, vous devrez alors avoir
l'extension PDO d'installée, ainsi que l'extension représentant le driver spécifique
à votre SGBD.
420
Zend_Db
Le premier argument est une chaîne désignant l'adaptateur souhaité. Par exemple,
"Pdo_Mysql" va correspondre à la classe Zend_Db_Adapter_Pdo_Mysql. Le second
paramètre est un tableau d'options. C'est le même que celui que vous auriez passé au
constructeur de la classe directement.
// require_once 'Zend/Db/Adapter/Pdo/Mysql.php';
Si vous créez votre propre classe d'adaptateur qui étend Zend_Db_Adapter_Abstract et que
celle-ci ne respecte pas la syntaxe du préfixe package "Zend_Db_Adapter", utilisez alors la clé
"adapterNamespace" dans le tableau de configuration passé à la méthode factory() afin
de charger votre adaptateur.
421
Zend_Db
Il est alors nécessaire que l'objet de configuration contienne une propriété adapter, qui
représente une chaîne de caractères décrivant l'adaptateur à utiliser. De plus, l'objet peut aussi
contenir une propriété nommée params, avec toutes les sous propriétés requises pour la
configuration de l'adaptateur.
Dans l'exemple qui va suivre, l'objet Zend_Config est crée à partir d'un tableau. Il
eut été possible de le créer à partir de fichiers externes, grâce à Zend_Config_Ini ou
Zend_Config_Xml.
$db = Zend_Db::factory($config->database);
• host : le nom de l'hôte hébergeant le SGBD. Vous pouvez aussi spécifier une adresse IP.
Si le SGBD se situe sur la même machine que l'application PHP, "localhost" ou "127.0.0.1"
devraient alors être utilisés.
• port : Certains SGBDs acceptent que l'on spécifie un port pour d'y connecter. Indiquez le alors
ici.
• options : Ce paramètre est un tableau associatif d'options génériques à toutes les classes
Zend_Db_Adapter.
422
Zend_Db
$options = array(
Zend_Db::CASE_FOLDING => Zend_Db::CASE_UPPER
);
$params = array(
'host' => '127.0.0.1',
'username' => 'webuser',
'password' => 'xxxxxxxx',
'dbname' => 'test',
'options' => $options
);
$options = array(
Zend_Db::AUTO_QUOTE_IDENTIFIERS => false
);
$params = array(
'host' => '127.0.0.1',
'username' => 'webuser',
'password' => 'xxxxxxxx',
'dbname' => 'test',
'options' => $options
);
423
Zend_Db
$pdoParams = array(
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true
);
$params = array(
'host' => '127.0.0.1',
'username' => 'webuser',
'password' => 'xxxxxxxx',
'dbname' => 'test',
'driver_options' => $pdoParams
);
echo $db->getConnection()
->getAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY);
$options = array(
Zend_Db::ALLOW_SERIALIZATION => false
);
$params = array(
'host' => '127.0.0.1',
'username' => 'webuser',
'password' => 'xxxxxxxx',
'dbname' => 'test',
'options' => $options
);
Afin de vérifier si les paramètres de connexion au SGBD sont corrects, surveillez les exceptions
envoyées par la méthode getConnection().
De plus, un adaptateur peut être sérialisé pour être stocké, par exemple, dans une variable
de session. Ceci peut être utile non seulement pour l'adaptateur lui-même, mais aussi
pour les autres objets qui l'agrègent, comme un objet Zend_Db_Select. Par défaut, les
424
Zend_Db
adaptateurs sont autorisés à être sérialisés, si vous ne le voulez pas, vous devez passer
l'option Zend_Db::ALLOW_SERIALIZATION=false, regardez l'exemple ci-dessus. Afin de
respecter le principe de connexions paresseuses, l'adaptateur ne se reconnectera pas
après la désérialisation. Vous devez appeler vous-même getConnection(). Vous pouvez
permettre à l'adaptateur de se reconnecter automatiquement en utilisant l'option d'adaptateur
Zend_Db::AUTO_RECONNECT_ON_UNSERIALIZE=true.
try {
$db = Zend_Db::factory('Pdo_Mysql', $parameters);
$db->getConnection();
} catch (Zend_Db_Adapter_Exception $e) {
// probablement mauvais identifiants,
// ou alors le SGBD n'est pas joignable
} catch (Zend_Exception $e) {
// probablement que factory() n'a pas réussi à charger
// la classe de l'adaptateur demandé
}
• accounts correspond aux informations sur les utilisateurs qui gèrent les bugs.
• products enregistre les produits pour lesquels des bugs vont être relevés.
• bugs est la table qui contient les bugs, à savoir leur état actuel, la personne ayant relevé le
bug, la personne en charge de le corriger, et la personne chargée de vérifier le correctif.
• bugs_products enregistre les relations entre les bugs, et les produits. C'est une relation
plusieurs à plusieurs car un même bug peut faire partie de plusieurs produits, et un produit
peut évidemment posséder plusieurs bugs.
Le pseudo-code SQL suivant représente les tables de notre base de données d'exemple. Ces
tables sont utilisées aussi pour les tests unitaires automatisés de Zend_Db.
425
Zend_Db
Notez aussi que la table bugs contient plusieurs référence (clés étrangères) vers la table
accounts. Chacune de ces clés peut référencer un enregistrement différent de la table
accounts, pour un bug donné.
Le premier paramètre de cette méthode est une chaîne représentant la requête SELECT à
exécuter. Aussi, ce premier paramètre peut être un objet Zend_Db_Select, qui sera alors converti
en une chaîne automatiquement.
426
Zend_Db
Vous pouvez spécifier un mode de récupération de résultats différent, ceci par la méthode
setFetchMode(). Les modes supportés sont identifiés par des constantes :
Notez que si votre résultat comporte plusieurs colonnes avec le même nom, par exemple lors
d'une jointure, il ne peut y avoir qu'un clé avec un nom définit dans le tableau de résultat. Vous
devriez toujours utiliser des alias avec le mode FETCH_ASSOC.
Les noms des clés des tableaux correspondants aux noms des colonnes SQL telles que
retournées par le SGBD, vous pouvez spécifier la casse pour ces noms, grâce à l'option
Zend_Db::CASE_FOLDING. Spécifiez ceci lors de l'instanciation de votre adaptateur. Voyez
Exemple 138, « Passer l'option de gestion de la casse à la fabrique ».
$db->setFetchMode(Zend_Db::FETCH_OBJ);
427
Zend_Db
$db->setFetchMode(Zend_Db::FETCH_OBJ);
$db->setFetchMode(Zend_Db::FETCH_OBJ);
Vous devriez réfléchir votre requête SELECT de manière à ce que la première colonne
sélectionnée, correspondant à la clé du tableau de résultat, soit unique (une clé primaire par
exemple). Cette méthode est indépendante de "fetch mode" éventuellement précédemment
défini.
$db->setFetchMode(Zend_Db::FETCH_OBJ);
428
Zend_Db
$db->setFetchMode(Zend_Db::FETCH_OBJ);
$data = array(
'created_on' => '2007-03-22',
'bug_description' => 'Something wrong',
'bug_status' => 'NEW'
);
$db->insert('bugs', $data);
Les colonnes non citées dans le tableau associatif sont laissées telles quelles. Ainsi, si le SGBD
possède une valeur DEFAULT pour les colonnes concernées, celle-ci sera utilisée, autrement,
NULL sera utilisé.
Par défaut, les valeurs insérées avec cette méthode sont automatiquement échappées. Ceci
pour des raisons de sécurité, vous n'avez donc pas besoin de vous occuper de ce point là.
Si vous avez besoin d'écrire de la syntaxe SQL, comme des mots réservés, des noms de
fonctions SQL, vous voulez que ceux-ci ne soient pas échappés, et ne soient pas traités comme
de vulgaires chaînes de caractères, mais plutôt comme des expressions. Pour ceci, vous devriez
passer ces valeurs dans votre tableau de données, en tant qu'objets de type Zend_Db_Expr
au lieu de chaînes de caractères banales.
429
Zend_Db
$data = array(
'created_on' => new Zend_Db_Expr('CURDATE()'),
'bug_description' => 'Something wrong',
'bug_status' => 'NEW'
);
$db->insert('bugs', $data);
Si votre table a été définie avec une clé auto-incrémentée, alors vous pouvez appeler la méthode
lastInsertId() après une opération d'insertion. Cette méthode retourne la valeur auto-
incrémentée, générée dans le cadre de la connexion au SGBD.
$db->insert('bugs', $data);
Certains SGBD supporte un objet de séquence, qui sert à générer des valeurs uniques qui vont
servir pour les clé primaires. Pour supporter ce procédé, la méthode lastInsertId() accepte
deux paramètres optionnels (chaînes de caractères). Ces paramètres nomment la table et la
colonne en supposant que vous ayez respecté la convention qui définit que la séquence est
nommée en utilisant le nom de la table et des colonnes utilisées, avec le suffixe "_seq". Ces
conventions sont celles de PostgreSQL pour les colonnes de type SERIAL. Par exemple, une
table "bugs" avec une clé primaire "bug_id" utilisera une séquence nommée "bugs_bug_id_seq".
$db->insert('bugs', $data);
Si le nom de votre objet de séquence ne suit pas ces conventions de nommage, utilisez
alors lastSequenceId(). Cette méthode prend un paramètre qui nomme la séquence
explicitement.
$db->insert('bugs', $data);
430
Zend_Db
Pour les SGBDs ne supportant pas les séquences, comme MySQL, Microsoft SQL Server, et
SQLite, les arguments passés à la méthode lastInsertId() sont ignorés. La valeur retournée
est la dernière valeur générée pour la dernière requête INSERT, quelque soit la table concernée
(pour cette connexion). Aussi, pour ces SGBDs, la méthode lastSequenceId() retournera
toujours NULL.
De plus, utiliser une requête du type "MAX(id)+1" pour générer une nouvelle
valeur de clé primaire n'est pas sécurisé non plus, car deux client peuvent se
connecter simultanément et créer des effets indésirables.
Les valeurs dans ce tableau sont traitées comme des chaînes. Voyez Section 1.4.1, « Insérer
des données » pour plus d'informations sur la gestion des expressions SQL dans ce tableau.
Le troisième argument est une chaîne contenant l'expression SQL utilisée comme critère pour
la mise à jour des données dans la table. Les valeurs et les arguments dans ce paramètre ne
sont pas échappés pour vous. Vous devez donc vous assurer de l'éventuel bon échappement
des caractères. Voyez Section 1.5, « Échapper des valeurs ou des identifiants » pour plus
d'informations.
La valeur de retour de cette méthode est le nombre d'enregistrements affectés par l'opération
de mise à jour (UPDATE).
$data = array(
'updated_on' => '2007-03-23',
'bug_status' => 'FIXED'
);
431
Zend_Db
Si vous oubliez le troisième paramètre, alors tous les enregistrements de la table sont mis à jour
avec les valeurs spécifiées dans le tableau de données.
Si vous spécifiez un tableau de chaîne en tant que troisième paramètre, alors ces chaînes sont
jointes entre elles avec une opération AND.
Si vous fournissez un tableau de tableaux en tant que troisième argument, les valeurs seront
automatiquement échappées dans les clés. Elles seront ensuite jointes ensemble, séparées par
des opérateurs AND.
$data = array(
'updated_on' => '2007-03-23',
'bug_status' => 'FIXED'
);
$data = array(
'updated_on' => '2007-03-23',
'bug_status' => 'FIXED'
);
Le second paramètre est une chaîne contenant l'expression SQL utilisée comme critère
pour effacer les enregistrements. Les valeurs de cette expression de sont pas échappées
automatiquement, vous devez donc vous en occuper le cas échéant. Voyez Section 1.5,
« Échapper des valeurs ou des identifiants » pour les méthodes concernant l'échappement.
432
Zend_Db
Si vous ne spécifiez pas le second paramètres, tous les enregistrements de la table seront alors
supprimés.
Si le second paramètre est un tableau de chaînes, alors celles ci seront jointe en une expression
SQL, séparées par l'opérateur AND.
Si vous fournissez un tableau de tableaux en tant que troisième argument, les valeurs seront
automatiquement échappées dans les clés. Elles seront ensuite jointes ensemble, séparées par
des opérateurs AND.
$name = "O'Reilly";
$sql = "SELECT * FROM bugs WHERE reported_by = '$name'";
echo $sql;
// SELECT * FROM bugs WHERE reported_by = 'O'Reilly'
Pire encore est le cas où de telles erreurs SQL peuvent être utilisées délibérément par une
personne afin de manipuler la logique de votre requête. Si une personne peut manipuler un
paramètre de votre requête, par exemple via un paramètre HTTP ou une autre méthode, alors il
peut y avoir une fuite de données, voire même une corruption totale de votre base de données.
Cette technique très préoccupante de violation de la sécurité d'un SGBD, est appelée "injection
SQL" (voyez http://en.wikipedia.org/wiki/SQL_Injection).
La classe Zend_Db Adapter possède des méthodes adaptées pour vous aider à faire face à
de telles vulnérabilités. La solution proposée est l'échappement de tels caractères (comme la
"quote" = ') dans les valeurs PHP avant leur passage dans la chaîne de requête. Ceci vous
protège de l'insertion malveillante ou involontaires, de caractères spéciaux dans les variables
PHP faisant partie d'une requête SQL.
$name = $db->quote("O'Reilly");
echo $name;
// 'O\'Reilly'
echo $sql;
// SELECT * FROM bugs WHERE reported_by = 'O\'Reilly'
Notez que la valeur de retour contient les apostrophes de délimitation autour de la chaîne. Ceci
est différent de certaines fonctions qui se contentent juste d'échapper les caractères spéciaux,
telle que mysql_real_escape_string().
433
Zend_Db
Certaines valeurs en revanche n'ont pas besoin d'être délimitées. Certains SGBDs n'acceptent
pas que les valeurs correspondant à des champs de type entier, soient délimitées. Autrement
dit, l'exemple suivant est erroné dans certaines implémentations de SQL. Nous supposons
intColumn ayant un type SQL INTEGER :
$value = '1234';
$sql = 'SELECT * FROM atable WHERE intColumn = '
. $db->quote($value, 'INTEGER');
Zend_Db_Table fournit les types SQL à quote() automatiquement en fonction des colonnes
utilisées par la table référencée.
Le joker est le même symbole que celui utilisé par beaucoup de SGBDs pour la substitution de
paramètre dans une requête préparée.quoteInto() ne fait qu'émuler ce comportement : la
méthode ne fait que remplacer le joker par la valeur PHP, en lui appliquant la méthode quote.
De vrais paramètres de requêtes préparées conservent une réelle isolation entre la requête et
ses paramètres.
echo $sql;
// SELECT * FROM bugs WHERE reported_by = 'O\'Reilly'
Le troisième paramètre optionnel s'utilise comme avec la méthode quote. Il sert à spécifier un
type SQL, les types numériques ne sont pas délimités.
echo $sql;
// SELECT * FROM bugs WHERE reported_by = 1234
434
Zend_Db
Les valeurs ne sont pas les seuls données qui peuvent être dynamiques dans une requête
SQL,et donc passées par des variables PHP. Les noms des tables, des colonnes, ou tout autre
identifiant SQL spécial de la requête peuvent aussi être dynamiques. En général, les identifiant
spéciaux d'une requête ont une syntaxe identique à celle des variables PHP : pas d'espaces
dans les noms, certains autres caractères interdits, la ponctuation est interdite, etc... Aussi, les
identifiants ne peuvent valoir certaines valeurs de mots réservés : une table ne peut s'appeler
"FROM". Il se peut donc que vous ayez besoin aussi d'échapper des paramètres voués à être
substitués à des identifiant dans la requête SQL, et non plus à des valeurs.
Le langage SQL possède une caractéristique appelée identifiant délimités. Si vous entourez un
identifiant SQL dans un type spécial de délimiteurs, alors vous pouvez écrire des requêtes qui
auraient été invalides autrement. Ainsi, vous pouvez inclure des espaces, de la ponctuation ou
des caractères internationaux dans vos identifiant, et aussi utiliser des mots réservés.
echo $sql
// SELECT * FROM "order"
Les identifiant SQL délimités sont sensibles à la casse. Vous devriez toujours utiliser la casse
telle qu'elle est utilisée dans votre base de données (nom des tables, des colonnes ...).
Dans les cas où le SQL est généré à l'intérieur des classes Zend_Db, alors les identifiant
SQL seront automatiquement échappés. Vous pouvez changer ce comportement avec l'option
Zend_Db::AUTO_QUOTE_IDENTIFIERS.Spécifiez la lors de l'instanciation de l'adaptateur.
Voyez Exemple 139, « Passer l'option d'auto-échappement à la fabrique ».
Vous pouvez manuellement spécifier lorsque vous voulez démarrer une transaction. Vous
contrôler ainsi combien de requêtes doivent y être exécutées, et valider ou annuler ce groupe de
requêtes. Utilisez beginTransaction() pour démarrer une transaction. Toutes les requêtes
suivantes seront alors exécutées dans cette transaction avant que vous ne l'annuliez, ou validiez.
435
Zend_Db
Pour terminer une transaction, utilisez les méthodes commit() ou rollBack(). commit()
validera et appliquera les changements de la transaction au SGBD, ils deviendront alors visibles
dans les autres transactions.
rollBack() fait le contraire : elle annule les changements qu'ont générés les requêtes dans
la transaction. L'annulation n'a aucun effet sur les changements qui ont été opérés par d'autres
transactions parallèles.
try {
// Essaye d'executer une ou plusieurs requêtes :
$db->query(...);
$db->query(...);
$db->query(...);
Les clés de ce tableau représentent les noms des colonnes, les valeurs sont un tableau avec
les clés suivantes :
436
Zend_Db
• AUTO_INCREMENT - MySQL
• SERIAL - PostgreSQL
• SEQUENCE - Oracle
437
Zend_Db
Cependant, il se peut que vous trouviez utile de fermer la connexion manuellement. Vous pouvez
alors utiliser la méthode de l'adaptateur closeConnection() afin de fermer explicitement la
connexion vers le SGBD.
A partir de la version 1.7.2, vous pouvez vérifier si vous êtes actuellement connecté au serveur
SGBD grâce à la méthode isConnected(). Ceci correspond à une ressource de connexion
qui a été initiée et qui n'est pas close. Cette fonction ne permet pas actuellement de tester la
fermeture de la connexion au niveau du SGBD par exemple. Cette fonction est utilisée en interne
pour fermer la connexion. Elle vous permet entre autres de fermer plusieurs fois une connexion
sans erreurs. C'était déjà le cas avant la version 1.7.2 pour les adaptateurs de type PDO mais
pas pour les autres.
$db->closeConnection();
Les connexions aux bases de données possède un état. Dans cet état sont
mémorisés des objets propres au SGBD. Par exemples des verrous, des
variables utilisateur, des tables temporaires, des informations sur les requêtes
récentes, les derniers enregistrements affectés, les dernières valeurs auto-
générées, etc. Avec des connexions persistantes, il se peut que vous accédiez
à des données ne faisant pas partie de votre session de travail avec le SGBD,
ce qui peut s'avérer dangereux.
Actuellement, seuls les adpatateurs Oracle, DB2 et PDO (si spécifiés par PHP)
supportent la persistance avec Zend_Db.
Par exemple, toute requête effectuée par Zend_Db est préparée, et exécutée. Cependant,
certaines caractéristiques des bases de données ne sont pas compatibles avec les requêtes
438
Zend_Db
préparées. Par exemple, des requêtes du type CREATE ou ALTER ne peuvent pas être
préparées sous MySQL. De même, les requêtes préparées ne bénéficient pas du cache de
requêtes, avant MySQL 5.1.17.
La plupart des extensions PHP pour les bases de données proposent une méthode permettant
d'envoyer une requête directe, sans préparation. Par exemple, PDO propose pour ceci la
méthode exec(). Vous pouvez récupérer l'objet de connexion "bas niveau" grâce à la méthode
de l'adaptateur getConnection().
De la même manière, vous pouvez accéder à toutes les propriétés ou méthodes de l'objet "bas
niveau", utilisé par Zend_Db. Attention toutefois en utilisant ce procédé, vous risquez de rendre
votre application dépendante du SGBD qu'elle utilise, en manipulant des méthodes propres à
l'extension utilisée.
Dans de futures versions de Zend_Db, il sera possible d'ajouter des méthodes pour des
fonctionnalités communes aux extensions de bases de données de PHP. Ceci ne rompra pas
la compatibilité.
$version = $db->getServerVersion();
if (!is_null($version)) {
if (version_compare($version, '5.0.0', '>=')) {
// faire quelquechose
} else {
// faire autre chose
}
} else {
// impossible de lire la version du serveur
}
• IBM DB2 supporte les séquences et les clés auto-incrémentées. Les arguments de
lastInsertId() sont donc optionnels. Si vous ne passez pas de paramètres, alors
l'adaptateur retourne la dernière valeur de clé auto- incrémentée. Sinon, il retourne la
439
Zend_Db
1.11.2. MySQLi
• Passez le paramètre 'Mysqli' à la méthode factory().
• MySQL ne supporte pas les séquences, donc lastInsertId() ignore tout paramètre
qu'on lui passe. Elle retourne toujours la valeur de la dernière clé auto-incrémentée.
lastSequenceId(), elle, retourne toujours NULL.
1.11.3. Oracle
• Passez le paramètre 'Oracle' à la méthode factory().
• Oracle ne supporte pas les clé auto-incrémentées, donc vous devriez spécifier un paramètre
de séquence à lastInsertId() ou lastSequenceId().
• L'extension Oracle ne supporte pas les paramètres positionnés (?). Vous devez utiliser des
paramètres nommés (:name).
• Par défaut les champs LOB ("Large Objet Binaire") sont retournés sous la forme d'objets OCI-
Lob. Vous pouvez les récupérer sous forme de chaînes pour toutes les requêtes en utilisant
l'option de driver 'lob_as_string' ou pour une requête en particulier en utilisant la méthode
setLobAsString(boolean) de l'adaptateur ou de l'objet statement.
• Microsoft SQL Server does not support sequences, so lastInsertId() ignores primary key
argument and returns the last value generated for an auto-increment key if a table name is
specified or a last insert query returned id. The lastSequenceId() method returns NULL.
• You can specify driver_options as a key in the options array. The value can be a anything from
here http://msdn.microsoft.com/en-us/library/cc296161(SQL.90).aspx.
440
Zend_Db
• As of Zend Framework 1.9, the minimal supported build of the PHP SQL Server extension
from Microsoft is 1.0.1924.0. and the MSSQL Server Native Client version 9.00.3042.00.
• Vous devez possédez l'extension PDO_IBM en version 1.2.2 minimum. Si ce n'est pas le cas,
vous devrez la mettre à jour via PECL.
• Microsoft SQL Server ne supporte pas les séquences, ainsi lastInsertId() ignore les
paramètres qu'on lui passe et retourne toujours la valeur de la dernière clé auto-incrémentée.
lastSequenceId() retourne toujours NULL.
• Si vous travaillez avec des chaînes Unicode avec un encodage autre que UCS-2 (comme
UTF-8), vous devrez peut-être réaliser une conversion dans votre code d'application ou stocker
les données dans un champs binaire. Reportez vous à la base de connaissance Microsoft
pour plus d'informations.
• MySQL ne supporte pas les séquences, ainsi lastInsertId() ignore les paramètres
qu'on lui passe et retourne toujours la valeur de la dernière clé auto-incrémentée.
lastSequenceId() retourne toujours NULL.
• Oracle ne supporte pas les clé auto-incrémentées, donc vous devriez spécifier un paramètre
de séquence à lastInsertId() ou lastSequenceId().
441
Zend_Db
• SQLite ne supporte pas les séquences, ainsi lastInsertId() ignore les paramètres
qu'on lui passe et retourne toujours la valeur de la dernière clé auto-incrémentée.
lastSequenceId() retourne toujours NULL.
• Pour vous connecter à une base de données SQLite en mémoire, spécifiez le paramètre
'dsnprefix'=>':memory:' dans le tableau d'options passé à l'adaptateur, lors de la
création de l'instance de Pdo_Sqlite Adapter.
• Les anciennes versions du driver SQLite pour PHP ne semblent pas supporter les commandes
PRAGMA nécessaires pour s'assurer que les colonnes ayant un nom court soient utilisées
dans les résultats. Si vous avez des problèmes, tels que vos enregistrements sont retournés
avec une forme "nomtable.nomcolonne" lors de vos jointures, vous devriez alors mettre à jour
votre version de PHP.
1.11.11. Firebird/Interbase
• Cet adaptateur utilise l'extension PHP php_interbase.
• Firebird/interbase ne supporte pas les clé auto-incrémentées, donc vous devez spécifier un
paramètre de séquence à lastInsertId() ou lastSequenceId().
Rappelez vous qu'il est nécessaire d'utiliser le paramètre 'adapterNamespace' avec la valeur
ZendX_Db_Adapter.
Nous recommandons de mettre à jour gds32.dll (ou l'équivalent linux) embarqué avec
PHP, à la même version que celle du serveur. Pour Firebird l'équivalent à gds32.dll est
fbclient.dll.
Par défaut tous les identifiants (nomde tables, de cahmps) sont retournés en majuscules.
442
Zend_Db
2. Zend_Db_Statement
En plus des méthodes telles que fetchAll() et insert() documentée dans Section 1,
« Zend_Db_Adapter », vous pouvez utiliser un objet statement pour l'analyser de manière plus
complète et récupérer vos résultats. Cette section décrit la marche à suivre pour obtenir un
statement et utiliser ses méthodes propres.
Zend_Db_Statement est basé sur l'objet PDOStatement dans l'extension PHP PHP Data Objects
(PDO).
L'objet statement représente un statement SQL qui a été préparé, et exécuté une fois avec les
paramètres de liaison ("bind") spécifiés. S'il s'agissait d'une requête SELECT par exemple, alors
les résultats sont prêts à être récupérés.
Vous pouvez créer un statement avec son constructeur, mais c'est assez peu usuel. Passez
alors l'objet adaptateur en premier argument, et la chaîne représentant la requête en second.
Un fois construit, le statement est préparé automatiquement, mais pas exécuté.
Si vous utilisez les paramètres positionnés, ceux utilisés avec le point d'interrogation (?), passez
simplement les valeurs dans le tableau.
$stmt->execute(array('goofy', 'FIXED'));
Si vous utilisez les paramètres nommés, ceux définis avec un identifiant chaîne de caractère
précédée d'un (:), passez les valeurs liées sous forme de tableau associatif.
443
Zend_Db
Les statements PDO acceptent les paramètres positionnés, ou nommés, mais pas les deux types
en même temps. Certaines classes Zend_Db_Statement pour les extensions non PDO peuvent
ne supporter qu'un seul de ces types.
• Fetch style en premier, permet de spécifier le mode de capture du résultat. C'est la structure
dans laquelle celui-ci vous sera retourné. Voyez Section 1.3.2, « Changer le mode de
récupération (Fetch Mode) » pour une description des valeurs valides et de la forme des
résultats alors renvoyés.
444
Zend_Db
fetchAll() accepte deux paramètres. Le premier est le mode de capture (fetch style), le
deuxième est le numéro de la colonne à retourner, si Zend_Db::FETCH_COLUMN est utilisé.
$rows = $stmt->fetchAll();
echo $rows[0]['bug_description'];
$stmt->setFetchMode(Zend_Db::FETCH_NUM);
$rows = $stmt->fetchAll();
echo $rows[0][0];
$stmt = $db->query($sql);
$bug_status = $stmt->fetchColumn(2);
445
Zend_Db
$stmt = $db->query($sql);
$obj = $stmt->fetchObject();
echo $obj->bug_description;
3. Zend_Db_Profiler
3.1. Introduction
Zend_Db_Profiler peut être activé pour permettre le profilage de requête. Les profils incluent
les requêtes exécutées par l'adaptateur, ainsi que leur temps d'exécution, permettant l'inspection
des requêtes qui ont été exécutées sans avoir besoin de rajouter le code spécifique de débogage
aux classes. L'utilisation avancée permet aussi au développeur de filtrer quelles requêtes il
souhaite profiler.
$params = array(
'host' => '127.0.0.1',
'username' => 'webuser',
'password' => 'xxxxxxxx',
'dbname' => 'test',
'profiler' => true // active le profileur ;
// mettre à false pour désactiver
// (désactivé par défaut)
);
// stoppe le profileur :
$db->getProfiler()->setEnabled(false);
// active le profileur :
$db->getProfiler()->setEnabled(true);
La valeur de l'option profiler est souple. Elle est interprétée différemment suivant son type. La
plupart du temps, vous devriez simplement utiliser une variable booléenne, mais d'autres valeurs
vous permettent de personnaliser le comportement du profileur.
Un argument booléen active le profileur si c'est une valeur TRUE, ou le désactive si FALSE. La
classe de profileur est celle par défaut, par exemple Zend_Db_Profiler.
446
Zend_Db
$params['profiler'] = true;
$db = Zend_Db::factory('PDO_MYSQL', $params);
Une instance d'un objet profileur fait que l'adaptateur utilise cet objet. L'objet doit être de type
Zend_Db_Profiler ou une sous-classe. L'activation du profileur est faite séparément.
$profiler = Mon_Db_Profiler();
$profiler->setEnabled(true);
$params['profiler'] = $profiler;
$db = Zend_Db::factory('PDO_MYSQL', $params);
L'argument peut être un tableau associatif contenant une ou toutes les clés suivantes : "enabled",
"instance", et "class". Les clés "enabled" et "instance" correspondent aux types booléen et
instance décrites ci-dessus. La clé "class" est utilisée pour nommer une classe à prendre en tant
que profileur personnalisé. La classe doit être de type Zend_Db_Profiler ou une sous-classe.
La classe est instanciée sans aucun argument de constructeur. L'option "class" est ignorée
quand l'option "instance" est fournie.
$params['profiler'] = array(
'enabled' => true,
'class' => 'Mon_Db_Profiler'
);
$db = Zend_Db::factory('PDO_MYSQL', $params);
Enfin, l'argument peut être un objet de type Zend_Config contenant des propriétés, qui sont
traitées comme les clés de tableaux décrites ci-dessus. Par exemple, un fichier "config.ini"
peut contenir les données suivantes :
[main]
db.profiler.class = "Mon_Db_Profiler"
db.profiler.enabled = true
$profileur = $db->getProfiler();
447
Zend_Db
Ceci retourne une instance de Zend_Db_Profiler. Avec cette instance, le développeur peut
examiner les requêtes en utilisant un éventail de méthodes :
• getQuery() retourne le SQL de la requête sous forme de texte. Le texte de SQL d'une
requête préparée avec des paramètres est le texte au moment où la requête a été préparée,
donc il contient les emplacements de paramètre, mais pas les valeurs utilisées quand la
déclaration est exécutée.
L'information que Zend_Db_Profiler fourni est utile pour profiler des goulots d'étranglement
dans les applications, ainsi que pour déboguer les requêtes qui viennent d'être exécutées. Par
exemple, pour voir la dernière requête qui vient de s'exécuter :
$query = $profileur->getLastQueryProfile();
echo $query->getQuery();
Si une page se génère lentement, utilisez le profileur pour déterminer le nombre total de requêtes,
et ensuite passer d'une requête à l'autre pour voir laquelle a été la plus longue :
$tempsTotal = $profileur->getTotalElapsedSecs();
$nombreRequetes = $profileur->getTotalNumQueries();
$tempsLePlusLong = 0;
$requeteLaPlusLongue = null;
448
Zend_Db
Comme avec setFilterElapsedSecs(), vous pouvez retirer tous les filtres en passant NULL
comme unique argument.
449
Zend_Db
Éléments requis :
• L'extension Firefox nommée Firebug qui peut être téléchargée à cette adresse https://
addons.mozilla.org/en-US/firefox/addon/1843.
• L'extension Firefox nommée FirePHP qui peut être téléchargée à cette adresse https://
addons.mozilla.org/en-US/firefox/addon/6149.
450
Zend_Db
4. Zend_Db_Select
4.1. Introduction
L'objet Zend_Db_Select représente une instruction de requête SQL de type SELECT. La classe
a des méthodes pour ajouter différentes parties à la requête. Vous pouvez indiquer quelques
parties de la requête en utilisant des structures de données et des méthodes de PHP, et la classe
forme la syntaxe correcte de SQL pour vous. Après avoir construit une requête, vous pouvez
l'exécuter comme si vous l'aviez écrite comme une chaîne de caractères.
• des méthodes orientées objet pour spécifier des requêtes SQL morceau par morceau ;
• l'échappement automatique des identificateurs de méta-données dans la plupart des cas, pour
supporter les identificateurs contenant les mots réservés SQL et les caractères spéciaux ;
451
Zend_Db
• l'échappement des identificateurs et des valeurs, afin de réduire les risques d'attaques par
injection SQL.
L'utilisation de Zend_Db_Select n'est pas obligatoire. Pour de très simple requêtes SELECT ,
il est d'habitude plus simple de spécifier la requête SQL entière comme une chaîne et l'exécuter
en utilisant des méthodes de l'adaptateur comme query() ou fetchAll(). L'utilisation de
Zend_Db_Select est utile si vous devez assembler une requête SELECT par procédure, ou
basé sur une logique conditionnelle dans votre application.
Une autre manière de créer un objet Zend_Db_Select est avec son constructeur, en indiquant
l'adaptateur de base de données comme argument.
Vous pouvez également employer la plupart des méthodes de l'objet Zend_Db_Select avec une
interface fluide et simple. Une interface fluide signifie que chaque méthode renvoie une référence
à l'objet qui a été appelé, ainsi vous pouvez immédiatement appeler une autre méthode.
$select = $db->select()
->from( ...spécifiez une table et des colonnes... )
->where( ...spécifiez des critères de recherche... )
->order( ...spécifiez des critères de tri... );
452
Zend_Db
Les exemples de cette section montrent l'utilisation de l'interface fluide, mais vous pouvez
employer une interface non-fluide dans tous les cas. Il est souvent nécessaire d'employer
l'interface non-fluide, par exemple, si votre application doit exécuter de la logique avant d'ajouter
une clause à une requête.
Indiquez la table pour la requête en utilisant la méthode from(). Vous pouvez indiquer le
nom de table comme une chaîne de caractères. Zend_Db_Select applique l'échappement des
identificateurs autour du nom de table, ainsi vous pouvez employer les caractères spéciaux.
$select = $db->select()
->from( 'produits' );
Vous pouvez également indiquer le nom de corrélation (parfois appelé "l'alias de table") pour
une table. Au lieu d'une chaîne de caractère simple, employez un tableau associatif faisant
correspondre le nom de corrélation au nom de table. Dans d'autres clauses de la requête SQL,
employez ce nom de corrélation. Si votre requête réalise des jointures sur plus d'une table,
Zend_Db_Select produit des noms uniques de corrélation basés sur les noms de table, pour
chaque table pour lesquelles vous n'indiquez pas le nom de corrélation.
$select = $db->select()
->from( array('p' => 'produits') );
$select = $db->select()
->from( 'monschema.produits' );
// ou
$select = $db->select()
->from('produits', '*', 'monschema');
453
Zend_Db
Vous pouvez énumérer les colonnes dans un tableau simple de chaîne de caractère, ou en
tant que tableau associatif faisant correspondre l'alias de la colonne au nom de la colonne. Si
vous avez seulement une colonne à requêter, et vous n'avez pas besoin d'indiquer un alias de
colonne, vous pouvez l'énumérer comme une chaîne simple au lieu d'un tableau.
Si vous passez un tableau comme argument pour les colonnes, aucune colonne pour la table
correspondante ne sera inclus dans le jeu de résultat. Voir un exemple de code sous la section
concernant la méthode join().
$select = $db->select()
->from(array('p' => 'produits'),
array('produit_id', 'produit_nom'));
$select = $db->select()
->from(array('p' => 'produits'),
array('p.produit_id', 'p.produit_nom'));
$select = $db->select()
->from(array('p' => 'produits'),
array('prodno' => 'produit_id', 'produit_nom'));
Vous pouvez aussi créer un objet de type Zend_Db_Expr explicitement, pour éviter qu'une
chaîne soit traitée comme un nom de colonne. Zend_Db_Expr est une classe minimale qui
contient une unique chaîne de caractère. Zend_Db_Select reconnaît les objets de type
454
Zend_Db
$select = $db->select()
->from(array('p' => 'produits'),
array('produit_id', 'LOWER(produit_nom)'));
$select = $db->select()
->from(array('p' => 'produits'),
array('produit_id',
'prix_avec_taxe' => '(p.prix * 1.08)'));
$select = $db->select()
->from(array('p' => 'produits'),
array('produit_id',
'prix_avec_taxe' =>
new Zend_Db_Expr('p.prix * 1.08')));
Dans les cas ci-dessus, Zend_Db_Select ne change pas la chaîne pour appliquer des alias ou
échapper les identificateurs. Si ces changements sont nécessaires pour résoudre l'ambiguïté,
vous devez faire manuellement les changements dans la chaîne de caractères.
Si vos noms de colonne sont des mots-clés de SQL ou contiennent les caractères spéciaux,
vous devriez employer la méthode quoteIdentifier() de l'adaptateur et interpoler le résultat
dans la chaîne de caractères. La méthode quoteIdentifier() utilise l'échappement SQL
pour délimiter les identificateurs, qui indique clairement que c'est un identificateur pour une table
ou une colonne, et non n'importe quelle autre partie de la syntaxe de SQL.
Votre code est plus indépendant du SGBDR si vous utilisez la méthode quoteIdentifier()
au lieu d'échapper littéralement dans votre chaîne, car quelques marques de SGBDR
utilisent des symboles non standards pour échapper les identificateurs. La méthode
quoteIdentifier() est conçue pour utiliser le symbole d'échappement approprié basé
sur le type d'adaptateur. La méthode quoteIdentifier() échappe aussi tout caractère
d'échappement qui apparaissent dans l'identificateur lui-même.
455
Zend_Db
$select = $db->select()
->from(array('p' => 'produits'),
array('origine' => '(p.'
. $db->quoteIdentifier('from')
. ' + 10)'));
Il peut y avoir des cas où vous souhaitez ajouter des colonnes à une table FROM ou
JOIN existante après que ces méthodes aient été appelées. La méthode columns() vous
permet d'ajouter des colonnes spécifiques à n'importe quel moment avant que la requête ne
soit exécutée. Vous pouvez fournir les colonnes en tant qu'une chaîne de caractères, une
Zend_Db_Expr ou un tableau de ces derniers. Le second argument de cette méthode peut être
omis, impliquant que les colonnes sont ajoutées à la table FROM, sinon un alias déjà défini doit
être utilisé.
$select = $db->select()
->from(array('p' => 'produits'), 'produit_id')
->columns('produit_nom');
$select = $db->select()
->from(array('p' => 'produits'), 'p.produit_id')
->columns('produit_nom', 'p');
// Ou alternativement columns('p.produit_nom')
Beaucoup de requêtes utiles impliquent l'utilisation de JOIN pour combiner les lignes issues de
tables multiples. Vous pouvez ajouter des tables à une requête en utilisant la méthode join().
L'utilisation de cette méthode est similaire à la méthode from(), excepté que vous pouvez aussi
spécifier une condition join dans la plupart des cas.
456
Zend_Db
$select = $db->select()
->from(array('p' => 'produits'),
array('produit_id', 'produit_nom'))
->join(array('l' => 'ligne_items'),
'p.produit_id = l.produit_id');
Le deuxième argument de join() est une chaîne qui représente la condition join. C'est une
expression qui déclare les critères par lesquels les lignes d'une table correspondent aux lignes
dans une autre table. Vous pouvez utiliser un nom d'alias dans cette expression.
Aucun échappement n'est appliqué à une expression que vous spécifiez pour
une condition join ; si vous avez des noms de colonnes qui nécessitent d'être
échappées, vous devez utiliser quoteIdentifier() quand vous préparez la
chaîne pour une condition join.
Le troisième argument de join() est un tableau des noms de colonnes, comme c'est utilisé
dans la méthode from(). La valeur par défaut est "*", la méthode supporte les alias, les
expressions, et les objets Zend_Db_Expr de la même manière que le tableau de noms de
colonnes de la méthode from().
Pour ne choisir aucune colonne à partir d'une table, utilisez un tableau vide pour la liste de
colonnes. Cette utilisation fonctionnerait aussi avec la méthode from(), mais typiquement vous
pouvez avoir besoin de colonnes issues de la table primaire dans vos requêtes, tandis que vous
pourriez ne vouloir aucune colonne de la table jointe.
$select = $db->select()
->from(array('p' => 'produits'),
array('produit_id', 'produit_name'))
->join(array('l' => 'ligne_items'),
'p.produit_id = l.produit_id',
array() ); // liste de colonnes vide
Notez le tableau vide (array()) dans l'exemple ci-dessus à la place de la liste de colonnes
de la table jointe.
Le SQL a plusieurs types de jointures. Voyez la liste ci-dessous des méthodes supportant les
différents types de jointures dans Zend_Db_Select.
457
Zend_Db
Ceci est le type de jointure le plus commun. Les lignes de chaque table sont comparées
en utilisant la condition join spécifiée. Le résultat inclut seulement les lignes qui vérifient la
condition join. Le résultat peut être vide si aucune ligne ne satisfait la condition.
Toutes les lignes issues de la table opérande de gauche sont inclues, les lignes
correspondantes de la table de droite sont inclues, et les colonnes de la table opérande de
droite sont remplies de NULL si aucune ligne existante ne correspond à la table de gauche.
Tous les marques de SGBDR supportent ce type de jointure.
La jointure étrangère droite est le complément de la jointure étrangère gauche. Toutes les
lignes issues de la table opérande de droite sont inclues, les lignes correspondantes de la
table de gauche sont inclues, et les colonnes de la table opérande de gauche sont remplies
de NULL si aucune ligne existante ne correspond à la table de droite.
Certaines marques de SGBDR ne supportent pas ce type de jointure, mais en général toute
jointure droite peut être représentée comme une jointure gauche en inversant l'ordre des
tables.
Une jointure étrangère complète est comme la combinaison d'une jointure étrangère gauche
et d'une jointure étrangère droite. Toutes les lignes des deux tables sont inclues, appairées
ensemble dans la même ligne de résultat si elles satisfont la condition de jointure, et sinon
appairées avec des valeurs NULL à la place des colonnes de l'autre table.
Une jointure croisée est un produit cartésien. Chaque ligne de la première table est assortie
avec chaque ligne de la seconde. Ainsi le nombre de lignes du résultat est équivalent au
produit du nombre de lignes de chacune des tables. Vous pouvez filtrer le résultat en utilisant
une clause WHERE ; dans ce cas une jointure croisée est semblable à l'ancienne syntaxe
de jointure SQL-89.
Une jointure naturelle compare chaque(s) colonne(s) qui apparaissent avec le même nom
dans les deux tables. La comparaison est l'égalité pour toute(s) la(es) colonne(s) ; la
comparaison des colonnes utilisant l'inégalité n'est pas une jointure naturelle. Seules les
jointures internes (NdT : INNER) naturelles sont supportées par cette API, même si la syntaxe
SQL permet aussi bien des jointures naturelles étrangères (NdT : OUTER).
En plus de ces méthodes join, vous pouvez simplifier vos requêtes en utilisant les méthodes de
type join*Using. Au lieu de fournir une condition complète à votre jointure, vous fournissez
458
Zend_Db
$select = $db->select()
->from('table1')
->joinUsing('table2', 'colonne1')
->where('column2 = ?', 'foo');
$select = $db->select()
->from('produits',
array('produit_id', 'produit_nom', 'prix'))
->where('prix > 100.00');
Le second argument de la méthode where() est optionnel. C'est une valeur à substituer dans
l'expression. Zend_Db_Select échappe cette valeur et la substitue au caractère point ("?")
d'interrogation dans l'expression.
459
Zend_Db
$prixminimum = 100;
$select = $db->select()
->from('produits',
array('produit_id', 'produit_nom', 'prix'))
->where('prix > ?', $prixminimum);
Vous pouvez fournir un tableau en tant que second paramètre de la méthode where() quand
vous utilisez l'opérateur SQL "IN".
Exemple 196. Exemple d'un paramètre de type tableau pour la méthode where()
$select = $db->select()
->from('produits',
array('produit_id', 'produit_nom', 'prix'))
->where('produit_id IN (?)', $productIds);
Vous pouvez appeler la méthode where() plusieurs fois sur la même objet Zend_Db_Select.
La requête résultante combine les différents termes ensemble en utilisant AND entre eux.
$prixminimum = 100;
$prixmaximum = 500;
$select = $db->select()
->from('produits',
array('produit_id', 'produit_nom', 'prix'))
->where('prix > ?', $prixminimum)
->where('prix < ?', $prixmaximum);
Si vous devez combiner ensemble des termes en utilisant OR, utilisez la méthode orWhere().
Cette méthode est utilisée de la même manière que la méthode where(), excepté que le terme
spécifié est précédé par OR, au lieu de AND.
460
Zend_Db
$prixminimum = 100;
$prixmaximum = 500;
$select = $db->select()
->from('produits',
array('produit_id', 'produit_nom', 'prix'))
->where('prix < ?', $prixminimum)
->orWhere('prix > ?', $prixmaximum);
$prixminimum = 100;
$prixmaximum = 500;
$prod = 'Pomme';
$select = $db->select()
->from('produits',
array('produit_id', 'produit_nom', 'prix'))
->where("prix < $prixminimum OR prix > $prixmaximum")
->where('produit_nom = ?', $prod);
Dans l'exemple ci-dessus, le résultat serait tout à fait différent sans parenthèses, car AND a
une plus grande priorité que OR. Zend_Db_Select applique les parenthèses avec pour effet
de relier de manière plus étroite chaque expression dans les appels successifs de where()
qu'avec AND qui combine les expressions.
Dans la syntaxe SQL, la clause GROUP BY vous permet de réduire le nombre de lignes du
résultat de la requête à une ligne par valeur unique trouvé dans une(des) colonne(s) nommées)
dans la clause GROUP BY.
Dans Zend_Db_Select, vous pouvez spécifier la(es) colonne(s) à utiliser pour calculer les
groupes de lignes en utilisant la méthode group(). L'argument de cette méthode est une
colonne ou un tableau de colonnes à utiliser dans la clause GROUP BY.
461
Zend_Db
$select = $db->select()
->from(array('p' => 'produits'),
array('produit_id'))
->join(array('l' => 'ligne_items'),
'p.produit_id = l.produit_id',
array('ligne_items_par_produit' => 'COUNT(*)'))
->group('p.produit_id');
Comme le tableau de colonnes de la méthode from(), vous pouvez utiliser des noms d'alias
dans le nom de la colonne, et la colonne est échappée comme un identificateur à moins que la
chaîne ne contiennent des parenthèses ou que ce soit un objet de type Zend_Db_Expr.
Dans Zend_Db_Select, vous pouvez spécifier des conditions pour restreindre des groupes
en utilisant la méthode having(). Son utilisation est similaire à celle de la méthode where().
Le premier argument est une chaîne contenant une expression SQL. Le second argument
facultatif est une valeur qui est utilisé pour remplacer le caractère de substitution positionné dans
l'expression SQL. Les expressions passées dans de multiples appels de la méthode having()
sont combinées en utilisant l'opérateur booléen AND, ou l'opérateur OR si vous utilisez la méthode
orHaving().
$select = $db->select()
->from(array('p' => 'produits'),
array('produit_id'))
->join(array('l' => 'ligne_items'),
'p.produit_id = l.produit_id',
array('ligne_items_par_produit' => 'COUNT(*)'))
->group('p.produit_id')
->having('ligne_items_par_produit > 10');
462
Zend_Db
Dans la syntaxe SQL, la clause ORDER BY spécifie une ou plusieurs colonnes ou expressions
suivant lesquelles le résultat d'une requête doit être trié. Si plusieurs colonnes sont listées, les
colonnes secondaires sont utilisées pour résoudre les égalités ; l'ordre du tri est déterminé par
les colonnes secondaires si les colonnes précédentes contiennent des valeurs identiques. Le
tri par défaut est ascendant (du plus petit vers le plus grand). Vous pouvez aussi appliqué un
tri descendant (du plus grand vers le plus petit) pour une colonne en spécifiant le mot-clé DESC
après la colonne.
Dans Zend_Db_Select, vous pouvez utiliser la méthode order() pour spécifier une colonne
ou un tableau de colonnes par lesquelles vous voulez trier. Chaque élément du tableau est une
chaîne nommant une colonne, facultativement suivi les mots-clés ASC ou DESC en séparant avec
un espace.
Comme pour les méthodes from() et group(), les noms de colonnes sont échappées comme
des identificateurs, à moins qu'elles ne contiennent des parenthèses ou ne soient des objets de
type Zend_Db_Expr.
$select = $db->select()
->from(array('p' => 'produits'),
array('produit_id'))
->join(array('l' => 'ligne_items'),
'p.produit_id = l.produit_id',
array('ligne_items_par_produit' => 'COUNT(*)'))
->group('p.produit_id')
->order(array('ligne_items_par_produit DESC',
'produit_id'));
Certaines marques de SGBDR étendent la syntaxe SQL avec une clause LIMIT. Cette clause
réduit le nombre de lignes d'un résultat à un nombre maximum que vous spécifiez. Vous pouvez
de plus indiquer un nombre de lignes à éviter avant de commencer à produire le résultat. Cette
fonctionnalité facilite l'extraction d'un sous-ensemble d'un résultat, par exemple quand vous
affichez des résultats avec un défilement de pages.
Dans Zend_Db_Select, vous pouvez utiliser la méthode limit() pour spécifier le nombre de
lignes ainsi que le nombre de lignes à omettre. Le premier argument de cette méthode est le
nombre de lignes désirées. Le second argument est le nombre de lignes à omettre.
463
Zend_Db
$select = $db->select()
->from(array('p' => 'produits'),
array('produit_id', 'produit_nom'))
->limit(10, 20);
La syntaxe LIMIT n'est pas supporté par toutes les marques de SGBDR.
Quelques SGBDR nécessite une syntaxe différente pour supporter une
fonctionnalité similaire. Chaque classe Zend_Db_Adapter_Abstract inclue
une méthode pour produire le code SQL approprié à ce SGBDR.
$select = $db->select()
->from(array('p' => 'products'),
array('product_id', 'product_name'))
->limitPage(2, 10);
$select = $db->select()
->distinct()
->from(array('p' => 'produits'), 'produit_nom');
464
Zend_Db
$select = $db->select()
->forUpdate()
->from(array('p' => 'produits'));
Vous pouvez construire des requêtes de type union avec Zend_Db_Select en fournissant
un tableau de Zend_Db_Select ou de chaînes de requêtes SQL à la méthode union().
En second paramètre, vous pouvez fournir les constantes Zend_Db_Select::SQL_UNION
ou Zend_Db_Select::SQL_UNION_ALL pour spécifier le type d'union que vous souhaitez
réaliser.
$sql1 = $db->select();
$sql2 = "SELECT ...";
$select = $db->select()
->union(array($sql1, $sql2))
->order("id");
$select = $db->select()
->from('produits');
$stmt = $db->query($select);
$result = $stmt->fetchAll();
Comme alternative à l'emploi de la méthode query()de l'objet adaptateur, vous pouvez utiliser
la méthode query() de l'objet Zend_Db_Select. Les deux méthodes retourne un objet de
type Zend_Db_Statement ou PDOStatement, dépendant du type d'adaptateur.
465
Zend_Db
$select = $db->select()
->from('produits');
$stmt = $select->query();
$result = $stmt->fetchAll();
$select = $db->select()
->from('produits');
$sql = $select->__toString();
echo "$sql\n";
La valeur retournée n'est pas une chaîne de caractère contenant un fragment de syntaxe SQL.
La valeur retournée est une représentation interne, qui est typiquement une structure de type
tableau contenant des valeurs et des expressions. Chaque partie de la requête a une structure
différente.
L'argument unique de la méthode getPart() est une chaîne qui identifie quelle partie de la
requête Select doit être retournée. Par exemple, la chaîne "from" identifie la partie de l'objet
Select qui stocke l'information concernant les tables dans la clause FROM, incluant les tables
jointes.
La classe Zend_Db_Select définit des constantes que vous pouvez utiliser pour les parties de
la requête SQL. Vous pouvez utiliser ces constantes ou des chaînes de caractères littérales.
466
Zend_Db
$select = $db->select()
->from('produits')
->order('produit_id');
// La valeur retournée peut être une structure tableau, pas une chaîne.
// Chaque partie a une structure différente
print_r( $orderData );
La méthode reset() vous permet de vider une partie spécifique de la requête SQL, ou toutes
les parties si vous omettez l'argument.
L'argument unique est facultatif. Vous pouvez spécifier la partie de la requête à effacer, en
utilisant les mêmes chaînes que vous utilisez en tant qu'argument de la méthode getPart().
La partie de la requête que vous spécifiez est initialisée à l'état par défaut.
Si vous omettez le paramètre, reset() initialise toutes les parties de la requête à leurs valeurs
par défaut. Ceci rend l'objet Zend_Db_Select équivalent à un nouvel objet, comme si vous l'aviez
tout juste instancié.
467
Zend_Db
$select = $db->select()
->from(array('p' => 'produits')
->order('produit_nom');
5. Zend_Db_Table
5.1. Introduction
La classe Zend_Db_Table est une interface orientée objet vers les tables d'une base de
données. Elle fournit des méthodes pour la gestion de la plupart des opérations concernant
une table. Bien entendu, vous pouvez étendre la classe de base pour ajouter une logique
personnalisée.
La solution que représente Zend_Db_Table est basée sur le motif de conception Table Data
Gateway. Cette solution inclut aussi une classe implémentant le motif Row Data Gateway.
Exemple 213. Déclarer une classe de Table avec un nom de table spécifique
Si vous ne spécifiez pas le nom de la table, le nom de la classe sera alors utilisé comme nom
de table par défaut.
468
Zend_Db
Exemple 214. Déclarer une classe de Table sans nom de table spécifique
Vous pouvez aussi déclarer le nom de la base de données contenant la table, toujours au moyen
d'une propriété protégée de la classe : $_schema, ou avec le nom de la base précédant le nom
de la table dans la propriété $_name. Si vous choisissez de définir le nom de la base de données
dans la propriété $_name, alors ce choix sera prioritaire sur celui utilisant $_schema.
Exemple 215. Déclarer une classe de Table avec un nom de base de données
// Première alternative :
class Bugs extends Zend_Db_Table_Abstract
{
protected $_schema = 'bug_db';
protected $_name = 'bugs';
}
// Seconde alternative :
class Bugs extends Zend_Db_Table_Abstract
{
protected $_name = 'bug_db.bugs';
}
Les noms de la base de données et de la table peuvent aussi être définis via le constructeur
de la classe de Table. Ils écrasent alors ceux éventuellement définis dans les propriétés de la
classe (avec $_name et $_schema).
469
Zend_Db
// Première alternative :
// Seconde alternative :
Si vous n'indiquez pas de base de données, c'est celle utilisée par l'adaptateur qui sera utilisée.
Toute table doit posséder une clé primaire. Zend_Db_Table ne fonctionne pas avec les tables
sans clé primaire. Vous pouvez les déclarer la(les) colonne servant de clé primaire grâce à la
propriété protégée de la classe $_primary. Celle-ci peut être soit une chaîne, dans le cas d'une
clé sur une colonne, ou un tableau de chaînes pour une clé sur plusieurs colonnes (clé primaire
composée).
Toute classe de table doit, par un moyen ou un autre, connaître la clé primaire de
la table ciblée. Si la clé primaire ne peut être trouvée ( spécifiée dans la classe,
ou découverte par describeTable()), alors la table ne va pas pouvoir être
utilisée avec Zend_Db_Table.
470
Zend_Db
471
Zend_Db
Exemple 220. Exemple de construction d'un objet Table avec l'objet adaptateur
// Plus tard...
Il peut être intéressant de créer son objet adaptateur de base de données en un lieu approprié,
comme le fichier d'amorçage ("bootstrap"), et ensuite de le spécifier comme adaptateur par
défaut pour toutes les tables, à travers toute l'application. Attention toutefois, ce procédé fixe un et
un seul adaptateur, pour toutes les classes de table (héritant de Zend_Db_Table_Abstract).
// Plus tard...
Cette option est très semblable à celle qui consiste à définir un adaptateur par défaut à toutes
les classes. Le registre est en revanche plus flexible, car vous pouvez y stocker plusieurs
adaptateurs, correspondants à plusieurs SGBD différents. Changer de SGBD pour ses classes
de Table est alors aussi simple que de changer de valeur de registre.
472
Zend_Db
$data = array(
'created_on' => '2007-03-22',
'bug_description' => 'Something wrong',
'bug_status' => 'NEW'
);
$table->insert($data);
Par défaut les paramètres sont traités comme des valeurs littérales. Si vous souhaitez utiliser
une expression SQL à la place, manipulez un objet Zend_Db_Expr plutôt.
$data = array(
'created_on' => new Zend_Db_Expr('CURDATE()'),
'bug_description' => 'Something wrong',
'bug_status' => 'NEW'
);
Dans les exemples ci-dessus, il est supposé que la table possède une clé primaire auto-
incrémentée. C'est le comportement par défaut que gère Zend_Db_Table_Abstract, mais il
y a d'autres comportements valides, qui sont détaillés ci-dessous.
MySQL, MSSQL, et SQLite sont des exemples de SGBD supportant les clé primaires auto-
incrémentées.
PostgreSQL a une propriété SERIAL qui définit une séquence automatiquement, basée sur le
nom de la table et d'une colonne, et utilise cette séquence pour générer des valeurs de clés
pour les nouveaux enregistrements. IBM DB2 a une propriété IDENTITY qui fonctionne de
la même manière. Si vous utilisez ces propriétés d'automatisme, considérez votre classe de
473
Zend_Db
Table (Zend_Db_Table) comme si elle avait une clé primaire auto-incrémentée. Déclarez ainsi
$_sequence à TRUE.
Exemple 226. Exemple de déclaration d'une séquence dans une classe de Table
Oracle, PostgreSQL, et IBM DB2 sont des SGBDs qui supportent les séquences.
PostgreSQL et IBM DB2 ont aussi des mécanismes définissant implicitement la séquence et les
colonnes associées. Si vous utilisez un de ces procédés, considérez votre classe de table comme
ayant une clé primaire auto-incrémentée. N'utilisez la chaîne de la séquence dans $_sequence
que si vous voulez explicitement utiliser cette séquence pour générer la valeur suivante de clé.
Tous les SGBDs gère ce cas. Les tables d'intersection dans les relations de
type "plusieurs à plusieurs" sont de bons exemples de clés naturelles, souvent
composées d'autres clés étrangères.
474
Zend_Db
des colonnes concernées, et de leurs valeurs respectives. Le deuxième est une expression SQL
qui sera utiliser comme clause WHERE dans la requête UPDATE.
$data = array(
'updated_on' => '2007-03-23',
'bug_status' => 'FIXED'
);
$table->update($data, $where);
$table->delete($where);
Cette méthode est proxiée vers delete() de l'adaptateur. Si le paramètre est un tableau,
chacun des éléments du tableau sera joint au suivant avec l'opération AND pour former la clause
WHERE.
475
Zend_Db
Si une seule clé est passée en paramètre, la méthode retournera au plus un résultat (car
par définition, une clé primaire assure l'unicité d'un enregistrement). Si vous passez plusieurs
valeurs de clés, alors la méthode pourra retourner plusieurs enregistrements. Cette méthode
pourra aussi retourner zéro enregistrement. Quoiqu'il en soit, l'objet de retour est bien un
Zend_Db_Table_Rowset_Abstract.
Si votre clé primaire est une clé composée de plusieurs colonnes, passez alors les autres valeurs
de colonne comme paramètres à la méthode find(). Il doit y avoir autant de paramètres passés
à la méthode, que de colonnes composant la clé.
Ainsi, pour trouver plusieurs enregistrements en passant plusieurs valeurs de clés primaires
composées, passez autant de tableaux composés, que de colonnes représentant les clés. Les
tableaux doivent donc, comporter le même nombre de valeurs. Celles-ci vont ainsi fonctionner
par tuples : tous les premiers éléments des tableaux seront évalués pour la première recherche,
et chacun représentera une colonne composant la clé primaire. Puis ainsi de suite, jusqu'à la
fin des tableaux.
L'exemple suivant appelle find() pour récupérer deux enregistrements en se basant sur
une clé à deux colonnes. Le premier enregistrement aura une clé primaire (1234, 'ABC'), et
le second une valeur de clé primaire (5678, 'DEF').
476
Zend_Db
// Récupérer un rowset
$rows = $table->fetchAll('bug_status = "NEW"', 'bug_id ASC', 10, 0);
$rows = $table->fetchAll($table->select()->where('bug_status = ?', 'NEW')
->order('bug_id ASC')
->limit(10, 0));
// ou avec liaison :
$rows = $table->fetchAll(
$table->select()
->where('bug_status = :status')
->bind(array(':status'=>'NEW')
->order('bug_id ASC')
->limit(10, 0)
);
// Récupérer un row
$row = $table->fetchRow('bug_status = "NEW"', 'bug_id ASC');
$row = $table->fetchRow($table->select()->where('bug_status = ?', 'NEW')
->order('bug_id ASC'));
// ou avec liaison :
$row = $table->fetchRow(
$table->select()
->where('bug_status = :status')
->bind(array(':status'=>'NEW')
->order('bug_id ASC')
);
• Vous pouvez utiliser l'objet pour ne sélectionner que certaines colonnes de l'enregistrement à
retourner. Ceci est pratique dans le cas où vous n'avez pas besoin spécifiquement de toutes
les colonnes d'une table.
• Vous pouvez spécifier des colonnes avec des évaluations envers des expressions SQL.
Cependant, l'enregistrement résultant sera alors en mode lecture seule (readOnly) et ne pourra
pas être propagé en base de données (save()). Un appel à save() lèvera une exception.
• Vous pouvez utiliser des jointures JOIN vers d'autres tables, mais uniquement pour des
critères de jointure, et non sélectionner des colonnes jointes.
• Vous ne pouvez pas spécifier de colonnes JOINtes comme faisant partie du résultat de la
requête. L'objet row/rowset serait alors corrompu, et contiendrait des données d'une table
étrangère à sa table originale. Une erreur sera renvoyée dans un tel cas.
477
Zend_Db
$select = $table->select();
$select->where('bug_status = ?', 'NEW');
$rows = $table->fetchAll($select);
L'objet Select utilise une interface fluide (fluent interface), permettant le chaînage des
méthodes.
$rows = $table->fetchAll($table->select()
->where('bug_status = ?', 'NEW'));
Vous pouvez demander une requête qui retourne plusieurs enregistrements. La méthode
fetchAll() de votre classe de Table permet ceci. Elle retourne un objet de type
Zend_Db_Table_Rowset_Abstract, même si aucun enregistrement ne correspond à la
requête.
$rows = $table->fetchAll($select);
Vous pouvez aussi définir les clauses SQL ORDER BY ou encore LIMIT (ou autre équivalent
comme OFFSET).
$order = 'bug_id';
$rows = $table->fetchAll($select);
Tous les arguments de requêtes sont optionnels. Vous pouvez écrire une requête sans clause
WHERE ni LIMIT ou encore ORDER.
478
Zend_Db
$select = $table->select();
$select->from($table, array('bug_id', 'bug_description'))
->where('bug_status = ?', 'NEW');
$rows = $table->fetchAll($select);
Il est aussi possible de spécifier des expressions dans une clause FROM, et donc récupérer un
objet row/rowset en lecture seule. Dans l'exemple ci-après, nous retournons un enregistrement
de la table "bugs" qui représente un agrégat du nombre de nouveaux bugs reportés. Regardez
la clause GROUP. L'alias SQL "count" sera accessible dans le row/rowset résultant, comme si
il faisait parti de la table en tant que colonne.
Exemple 237. Récupérer des enregistrements avec des requêtes incluant des
expressions
$select = $table->select();
$select->from($table,
array('COUNT(reported_by) as `count`', 'reported_by'))
->where('bug_status = ?', 'NEW')
->group('reported_by');
$rows = $table->fetchAll($select);
Vous pouvez aussi utiliser une table de jointure comme partie de votre requête. Dans l'exemple
ci-dessous, nous utilisons la table "accounts" comme partie de la recherche, pour tous les bugs
reportés par "Bob".
Exemple 238. Utiliser une table intermédiaire par jointure avec fetchAll()
// Récupération avec la partie from déjà spécifié, important lors des jointures
$select = $table->select(Zend_Db_Table::SELECT_WITH_FROM_PART);
$select->setIntegrityCheck(false)
->where('bug_status = ?', 'NEW')
->join('accounts', 'accounts.account_name = bugs.reported_by')
->where('accounts.account_name = ?', 'Bob');
$rows = $table->fetchAll($select);
479
Zend_Db
L'objet Zend_Db_Table_Select est destiné à sélectionner des données sur une table précise.
Des jointures peuvent être faites, mais il n'est pas possible de sélectionner des colonnes ne
faisant pas partie de la table sous-jacente. Cependant, ceci aurait pu être utile dans certains
cas, et l'objet Zend_Db_Table_Select possède une clause spéciale déverrouillant cette
limitation. Passez la valeur FALSE à sa méthode setIntegrityCheck. Il est alors possible
de sélectionner des colonnes hors table. Attention toutefois, l'objet row/rowset résultant sera
verrouillé. Impossible d'y appeler save(), delete() ou même d'affecter une valeur à certains
de ses champs. Une exception sera systématiquement levée.
$select = $table->select(Zend_Db_Table::SELECT_WITH_FROM_PART)
->setIntegrityCheck(false);
$select->where('bug_status = ?', 'NEW')
->join('accounts',
'accounts.account_name = bugs.reported_by',
'account_name')
->where('accounts.account_name = ?', 'Bob');
$rows = $table->fetchAll($select);
$row = $table->fetchRow($select);
$info = $table->info();
480
Zend_Db
• primary => un tableau contenant la(les) colonnes utilisée(s) pour définir la clé primaire de la
table.
• metadata => un tableau associatif, associant les noms des colonnes de la tables, à
leurs informations intrinsèques. Les données sont les mêmes que celles retournée par
describeTable().
• rowClass => le nom de la classe concrète servant les objets représentants les enregistrements
de la table. Par défaut : Zend_Db_Table_Row.
• rowsetClass => le nom de la classe concrète servant de conteneur d'objets représentants les
enregistrements de la table. Par défaut : Zend_Db_Table_Rowset.
• referenceMap => un tableau associatif. Il représente les références de cette table vers ses
parents éventuelles. Voyez Section 8.2, « Définir ses relations ».
• dependentTables => un tableau de noms de classes de tables qui référencent cette table.
Voyez Section 8.2, « Définir ses relations ».
• insert()
• find()
• info()
Cependant, il peut être dégradant pour les performances du SGBD de lui demander ces
informations à chaque instanciation de chaque objet de chaque table. Ainsi, un système de cache
pour les méta données a été mis en place.
La mise en cache des méta données des tables peut être contrôlée de deux manières :
Dans tous les cas, vous devrez passer soit NULL (et ainsi désactiver le cache des méta
données des tables), soit une instance de Zend_Cache_Core. Il est possible d'utiliser à la fois
setDefaultMetadataCache et le constructeur afin d'avoir un objet de cache par défaut, puis
un spécifique pour certaines classes.
481
Zend_Db
Exemple 242. Utiliser un objet de cache de méta données pour toutes les classes
L'exemple qui suit illustre la manière de passer un objet de cache de méta données général,
pour toutes les classes de table :
$backendOptions = array(
'cache_dir' => 'cacheDir'
);
$cache = Zend_Cache::factory('Core',
'File',
$frontendOptions,
$backendOptions);
482
Zend_Db
L'exemple qui suit illustre la manière de passer un objet de cache de méta données
spécifique, pour une instance précise :
$backendOptions = array(
'cache_dir' => 'cacheDir'
);
$cache = Zend_Cache::factory('Core',
'File',
$frontendOptions,
$backendOptions);
Dans nos exemples, nous utilisons Zend_Cache_Backend_File, mais vous pouvez utiliser le
backend que vous souhaitez, voyez Zend_Cache pour plus d'informations.
Pour cacher les métadonnées une étape plus avant, vous pouvez aussi choisir de coder en dur
ces métadonnées. Dans ce cas particulier, cependant, tout changement au schéma de la table
requerra un changement dans votre code. Ainsi, il est seulement recommandé pour ceux qui
sont dans la phase d'optimisation pour un usage en production.
483
Zend_Db
Une manière simple de récupérer les valeurs appropriées est d'activer le cache des
métadonnées et d'utiliser celles présentes dans votre cache.
// Lors de l'instanciation :
$bugs = new Bugs(array('metadataCacheInClass' => false));
// Ou plus tard :
$bugs->setMetadataCacheInClass(false);
Ce paramètre est activé par défaut, ce qui assure que le tableau $_metadata n'est chargé
qu'une seule fois par instance
Par défaut, les méthodes de la classe de Table retourne des jeux d'enregistrements
comme étant des instances de la classe Zend_Db_Table_Rowset, ces "Rowsets"
contiennent des enregistrements de la table, représentés par des objets instances de
Zend_Db_Table_Row. Vous pouvez spécifier vos propres classes pour row/rowset, mais elles
doivent étendre Zend_Db_Table_Rowset_Abstract ou Zend_Db_Table_Row_Abstract,
respectivement.
Vous pouvez spécifier vos classes row/rowset en utilisant le constructeur de la classe de Table,
via le tableau d'options, aux clés "rowClass" et "rowsetClass". Indiquez les noms des
classes sous forme de chaînes de caractères.
484
Zend_Db
$table->setRowClass('My_Row');
$table->setRowsetClass('My_Rowset');
Pour des informations détaillées concernant les classes Row et Rowset, voyez Section 6,
« Zend_Db_Table_Row » et Section 7, « Zend_Db_Table_Rowset ».
Vous pouvez redéfinir les méthodes insert() et update() afin d'y ajouter votre propre
logique. Assurez vous d'appeler les méthodes parentes une fois votre code écrit.
485
Zend_Db
Certaines personnes peuvent vouloir utiliser un mécanisme d'inflexion pour transformer le nom
de la classe d'une manière bien spécifique, afin de retrouver le nom de la table.
486
Zend_Db
Par exemple, une classe nommée "BugsProducts", peut vouloir refléter une table s'appelant
"bugs_products," sans utiliser la propriété de classe $_name. Dans cette règle d'inflexion, les
mots composant le nom de la classe sont écrits en "CamelCase", et seraient transformés en
mots en minuscules, et séparés par des tirets bas.
Vous pouvez aussi spécifier le nom de la table indépendamment du nom de la classe. Utilisez
pour cela la propriété $_name de la classe de Table.
Si vous voulez utiliser l'inflexion, vous devrez créer une classe (abstraite) étendant
Zend_Db_Table_Abstract, et redéfinissant sa méthode protégée _setupTableName().
Toutes les classes de Table devront alors hériter de cette nouvelle classe abstraite.
C'est à vous d'écrire les fonctions qui vont établir le mécanisme d'inflexion.
6. Zend_Db_Table_Row
6.1. Introduction
Zend_Db_Table_Row est la classe qui donne accès à chacun des résultats issus d'un objet
Zend_Db_Table. Lorsque vous exécutez une requête via une classe de Table, alors les
résultats sont des objets Zend_Db_Table_Row. Vous pouvez aussi utiliser ces objets comme
résultats vides : pour créer des nouveaux résultats à ajouter à la base de données.
487
Zend_Db
Le tableau retourné par toArray() n'est pas une référence. Vous pouvez modifier ses valeurs,
cela n'aura aucune répercussion dans la base de données.
488
Zend_Db
Utiliser l'accesseur pour spécifier une valeur à une colonne d'un résultat Row ne répercute
pas le comportement immédiatement en base de données. Vous devez utiliser explicitement la
méthode save() pour ceci.
Exemple 254. Exemple de création d'un Row vierge pour une table
L'argument optionnel de createRow() est un tableau associatif qui sert à peupler tout de suite
l'objet de valeurs.
Exemple 255. Exemple de remplissage des valeurs d'un nouveau Row vierge
$data = array(
'bug_description' => '...description...',
'bug_status' => 'NEW'
);
489
Zend_Db
Notez qu'il n'est pas nécessaire d'appeler save() pour un effacement. Celui-ci est à effet
immédiat.
490
Zend_Db
Utilisez simplement la fonction PHP unserialize(). L'objet Row originel est alors recréé.
Notez que l'objet retourné fonctionne alors en mode déconnecté. Vous pouvez lire les valeurs
des colonnes, mais pas les modifier ni enregistrer l'objet en base de données (save()).
$rowClone = unserialize($serializedRow);
Un objet sérialisé est une chaîne de caractère, humainement visible. Il est donc
peu sécurisé d'y laisser un mot de passe vers un serveur de base de données.
Le lecteur d'un objet Row sérialisé ne devrait pas pouvoir accéder à la base
de données. De plus, une connexion à une base de données est un type non
sérialisable par PHP (ressource).
Il est bien entendu possible de reconnecter l'objet Row à la base de données, et plus précisément
à la Table dont il fut issu. Utilisez la méthode setTable() et passez lui une instance héritant de
Zend_Db_Table_Abstract. Une fois reconnecté, l'objet Row possède de nouveau un accès
à la base de données, et n'est donc plus en mode lecture seule.
$rowClone = unserialize($serializedRow);
491
Zend_Db
Si vous avez un besoin spécifique d'implémenter une logique spéciale après la création
d'une instance de Row, vous pouvez utiliser sa méthode init(), qui est appelée dans son
constructeur, mais après que les méta données aient été calculées.
La classe des Rows appelle les méthodes protégées _insert(), _update(), et _delete()
avant d'effectuer chacune des opérations respectives INSERT, UPDATE, et DELETE. Il est donc
possible de définir sa propre logique dans votre sous-classe de Row.
Ci-dessous vous trouverez des exemples d'utilisation d'une logique personnalisée dans les
classes de Row :
492
Zend_Db
La logique personnelle peut donc être déportée dans une classe de Row qui ne s'appliquera
qu'à certaines tables, et pas à d'autres. Sinon, la classe de Table utilise le Row par défaut.
Par exemple, vous souhaitez historiser toutes les insertions sur une Table spécifique, mais
uniquement si la configuration du site le permet :
Exemple 264. Exemple d'une classe de Row qui historise les insertions de
plusieurs tables
En passant l'objet Row personnalisé à chacune des Tables concernées, alors vous n'aurez
pas besoin de définir cette logique dans chacune des classes des Tables.
Dans cet exemple, le code qui effectue l'historisation est identique à celui de l'exemple
précédent.
493
Zend_Db
Les classes Zend_Db n'utilisent pas l'inflexion par défaut. Voyez Section 5.12.4, « Utiliser
l'inflexion dans Zend_Db_Table » pour plus de détails sur ce procédé.
Ainsi si vous voulez utiliser l'inflexion, vous devez implémenter vous-même la transformation à
effectuer en redéfinissant la méthode _transformColumn() dans votre classe de Row, et bien
entendu utiliser cette classe de Row pour votre Table.
Ceci vous permet d'utiliser les accesseurs de votre Row de manière transformée. La classe
de votre Row utilisera _transformColumn() pour changer le nom de la colonne appelée,
avant de le faire correspondre à un nom dans la table réelle de la base de données.
7. Zend_Db_Table_Rowset
7.1. Introduction
Lorsque vous effectuez une requête avec une classe de Table en utilisant
find() ou fetchAll() , le résultat retourné est alors un objet de type
Zend_Db_Table_Rowset_Abstract. Un Rowset est un conteneur d'objets descendants de
Zend_Db_Table_Row_Abstract. Vous pouvez itérer à travers ce conteneur et accéder aux
objet Row individuellement, en lecture ou écriture bien entendu.
494
Zend_Db
Un requête légitime peut retourner zéro enregistrement, donc zéro Rows. De ce fait, un
objet Rowset peut contenir zéro objet Row. Comme Zend_Db_Table_Rowset_Abstract
implémente l'interface Countable, vous pouvez utiliser la fonction PHP count() dessus, pour
compter les Rows qu'il contient.
$rowCount = count($rowset);
if ($rowCount > 0) {
echo "$rowCount rows trouvés";
} else {
echo 'Pas de rows pour cette requête';
}
La façon la plus simple d'accéder à un Row depuis l'objet Rowset est d'utiliser la méthode
current(). C'est tout à fait adapté lorsque le Rowset ne contient qu'un résultat (Row).
495
Zend_Db
SeekableIterator vous permet de vus déplacer à une position précise dans l'itérateur.
Utilisez pour ceci la méthode seek(). Elle prend en paramètre un entier représentant le
numéro de la position désirée. N'oubliez pas que le premier enregistrement est stocké à
la position zéro. Si vous spécifiez une position qui n'existe pas, une exception sera levée.
Vous devriez utiliser count() pour vérifier le nombre d'enregistrements Rows présents.
// et utilisation
$row9->assigned_to = 'mmouse';
$row9->save();
496
Zend_Db
Dès que vous avez accès à un objet individuel Row, vous pouvez le piloter comme présenté
dans la section Section 6, « Zend_Db_Table_Row ».
$rowsetArray = $rowset->toArray();
$rowCount = 1;
foreach ($rowsetArray as $rowArray) {
echo "row #$rowCount:\n";
foreach ($rowArray as $column => $value) {
echo "\t$column => $value\n";
}
++$rowCount;
echo "\n";
}
Le tableau retourné par toArray() n'est pas une référence. Le modifier ne modifiera en aucun
cas les données réelles dans la base de données.
Utilisez simplement la fonction PHP serialize() pour créer une chaîne de caractères
représentant votre objet Rowset.
497
Zend_Db
Notez que l'objet retourné fonctionne alors en mode déconnecté. Vous pouvez itérer à
travers, et lire les objets Row qu'il contient, mais vous ne pouvez plus faire intervenir la base
de données, ni changer de valeurs dans les Rows.
$rowsetDisconnected = unserialize($serializedRowset);
Un objet sérialisé est une chaîne de caractère, humainement visible. Il est donc
peut sécurisé d'y laisser un mot de passe vers un serveur de base de données.
Le lecteur d'un objet Rowset sérialisé ne devrait pas pouvoir accéder à la base
de données. De plus, une connexion à une base de données est un type non
sérialisable par PHP (ressource).
Il est bien entendu possible de reconnecter l'objet Rowset à la base de données, et plus
précisément à la Table dont il fut issu. Utilisez la méthode setTable() et passez lui une
instance héritant de Zend_Db_Table_Abstract. Une fois reconnecté, l'objet Rowset possède
de nouveau un accès à la base de données, et n'est donc plus en mode lecture seule.
$rowset = unserialize($serializedRowset);
$row = $rowset->current();
498
Zend_Db
En temps normal, la classe standard Zend_Db_Rowset est suffisante. Cependant, il peut être
judicieux de rajouter de la logique dans son Rowset, pour une table précise. Par exemple, une
nouvelle méthode pourrait effectuer des calculs.
Exemple 276. Exemple d'une classe Rowset personnalisée avec une nouvelle
méthode
8. Relations Zend_Db_Table
8.1. Introduction
Les tables possèdent des relations entre elles, dans une base de données relationnelle. Une
entité d'une table peut être liée à une autre entité d'une autre table, via un procédé appelé
contrainte d'intégrité référentielle
499
Zend_Db
500
Zend_Db
Si vous utilisez Zend_Db_Table pour émuler les cascades UPDATE et DELETE, alors déclarez
$_dependentTables en tant que tableau dans la classe des tables parentes. Listez ainsi le
nom de chaque table dépendante. Utilisez bien le nom des classes, et non les noms physiques
des tables.
Si votre SGBD implémente le mécanisme des cascades, alors vous n'avez pas
besoin de déclarer $_dependentTables. Voyez Section 8.6, « Opérations
d'écritures en cascade » pour plus d'informations.
Déclarez un tableau $_referenceMap dans les classes de chaque table dépendante (qui
"reçoit une clé"). C'est un tableau associatif, dit de "rôles". Un rôle définit quelle table est parente
dans la relation, et quelle est sa colonne de parenté.
Le rôle est utilisé comme index du tableau $_referenceMap. Il est utilisé pour définir la
relation, et pourra faire partie du nom de certaines méthodes, comme nous le verrons plus tard.
Choisissez ainsi un nom de rôle de manière intelligente.
Dans l'exemple du dessus, les rôles dans la classe Bugs sont : "Reporter", "Engineer",
"Verifier" et "Product".
La valeur de chaque rôle dans le tableau $_referenceMap est aussi un tableau associatif. Les
éléments de chaque rôle sont décrits ci-après.
• columns => une chaîne de caractères ou un tableau de chaînes désignant le(s) nom(s) des
clés étrangères dans la table dépendante (la table actuelle donc).
Il est courant qu'il s'agisse d'une seule colonne, mais on peut rencontrer le cas de clés
composées de multiples colonnes.
• refTableClass => désigne la classe de la table parente, liée à cette colonne. Utilisez le nom
de la classe et non le nom de la table physique.
Il est courant qu'une table dépendante n'ait qu'une seule référence d'une même table parente.
Cependant certaines tables peuvent avoir plusieurs références vers une même table parente.
Dans notre base de données d'exemple, c'est le cas avec la table bugs. Elle possède soit
une et une seule colonne référençant la table parente products, mais elle possède trois
références (donc trois colonnes) vers la table parente accounts. Chaque référence doit être
matérialisée par un rôle unique dans le tableau $_referenceMap.
• refColumns => c'est une chaîne de caractères ou un tableau de chaînes nommant la(es)
colonne(s) (clé primaire) de la table parente.
Si vous devez utiliser de multiples colonnes parentes pour une seule clé, alors veillez à bien
les entrer dans 'columns' dans le même ordre que dans 'refColumns'.
Il est optionnel de spécifier la refColumns. La clé primaire est utilisée par défaut comme
colonne parente dans une relation.
501
Zend_Db
• onUpdate => le nom de l'action à exécuter si un enregistrement est mis à jour dans la table
parente. VoyezSection 8.6, « Opérations d'écritures en cascade » pour plus d'informations.
$row->findDependentRowset($table, [$rule]);
Le paramètre $table désigne la table dépendante à utiliser. Ceci peut être une chaîne de
caractères aussi bien qu'un objet de la classe de cette table.
Cet exemple montre comment obtenir un enregistrement actif (objet Row) de la table
Accounts, et comment en récupérer les enfants dépendants de la table Bugs. (les bugs
reportés par ce compte)
$bugsReportedByUser = $user1234->findDependentRowset('Bugs');
Le second paramètre $rule est optionnel. Il s'agit du nom du rôle à utiliser depuis le tableau
$_referenceMap de la classe de la table dépendante. Si vous ne le spécifiez pas, le premier
rôle sera utilisé. Il n'y a dans la majorité des cas qu'un seul rôle.
Dans l'exemple ci dessus, nous ne fournissons pas de nom de rôle, le premier est donc pris en
considération, et il s'agit de "Reporter".
Dans cet exemple nous montrons comment obtenir un enregistrement (Row) depuis la table
Accounts, et comment trouver les Bugs assignés à ce compte (Account). Nous devrons
alors nommer le rôle "Engineer".
$bugsAssignedToUser = $user1234->findDependentRowset('Bugs',
'Engineer');
Vous pouvez rajouter des critères à vos relations, comme l'ordre ou la limite, ceci en utilisant
l'objet select de l'enregistrement parent.
502
Zend_Db
Dans cet exemple nous montrons comment obtenir un enregistrement (Row) depuis la table
Accounts, et comment trouver les Bugs assignés à ce compte (Account), mais limités
seulement à trois enregistrements, et ordonnés par nom. Nous devrons nommer le rôle
"Engineer".
$bugsAssignedToUser = $user1234->findDependentRowset('Bugs',
'Engineer',
$select);
Vous pouvez récupérer les enregistrements dépendants d'une autre manière. En utilisant
les "méthodes magiques". En effet, Zend_Db_Table_Row_Abstract va utiliser la
méthode findDependentRowset('<TableClass>', '<Rule>') si vous appelez sur
l'enregistrement une méthode correspondante à un de ces motifs :
• $row->find<TableClass>()
• $row->find<TableClass>By<Rule>()
Certains frameworks tels que Rails pour Ruby, utilise un mécanisme dit
d'inflexion, qui permet de transformer les noms des identifiants (nom de table,
de rôle...) d'une certaine manière bien spécifique dans les méthodes appelées.
Cela n'est pas le cas de Zend Framework : vous devez, dans vos méthodes
magiques, utiliser l'orthographe exacte des noms des rôles et classes, tels que
vous les définissez.
Cet exemple a le même effet que le précédent. Il utilise simplement les méthodes magiques
pour récupérer les enregistrements dépendants.
503
Zend_Db
$row->findParentRow($table, [$rule]);
La logique veut qu'il ne puisse y avoir qu'un et un seul parent par enregistrement. Ainsi, cette
méthode retourne un objet Row et non un objet Rowset
Le premier paramètre $table désigne la table parente. Ceci peut être une chaîne de caractères,
ou un objet instance de la classe de la table parente.
Cet exemple illustre la récupération d'un enregistrement Bugs (disons par exemple ceux
avec le statut "NEW"), et l'obtention de l'enregistrement parent correspondant à Accounts
(la personne ayant reporté le bug)
$reporter = $bug1->findParentRow('Accounts');
Le second paramètre $rule est optionnel. Il s'agit du nom du rôle à utiliser depuis le tableau
$_referenceMap de la classe de la table dépendante. Si vous ne le spécifiez pas, le premier
rôle sera utilisé. Il n'y a dans la majorité des cas qu'un seul rôle.
Dans l'exemple ci dessus, nous ne fournissons pas de nom de rôle, le premier est donc pris en
considération, et il s'agit de "Reporter".
• $row->findParent<TableClass>([Zend_Db_Table_Select $select])
• $row->findParent<TableClass>By<Rule>([Zend_Db_Table_Select $select])
504
Zend_Db
Cet exemple a le même effet que le précédent. Il utilise simplement les méthodes magiques
pour récupérer l'enregistrement parent.
$row->findManyToManyRowset($table,
$intersectionTable,
[$rule1,
[$rule2,
[Zend_Db_Table_Select $select]]]);
Le premier paramètre $table peut être soit une chaîne soit un objet instance de la classe de
la table de destination dans la relation plusieurs à plusieurs.
Le second paramètre $intersectionTable peut être soit une chaîne soit un objet instance
de la classe de la table d'intersection dans la relation plusieurs à plusieurs.
$productsRowset = $bug1234->findManyToManyRowset('Products',
'BugsProducts');
505
Zend_Db
Les troisième et quatrième paramètres, $rule1 et $rule2, sont optionnels. Ce sont des
chaînes de caractères qui désignent les rôles à utiliser dans le tableau $_referenceMap de
la table d'intersection.
$rule1 nomme le rôle dans la relation entre la table d'origine et la table d'intersection. Dans
notre exemple, il s'agit donc de la relation de Bugs à BugsProducts.
$rule2nomme le rôle dans la relation entre la table d'origine et la table d'intersection. Dans
notre exemple, il s'agit donc de la relation de BugsProducts à Products.
Si vous ne spécifiez pas de rôles, alors le premier rôle trouvé pour la table, dans le tableau
$_referenceMap, sera utilisé. Dans la grande majorité des cas, il n'y a qu'un rôle.
Dans l'exemple ci-dessus, les rôles ne sont pas spécifiés. Ainsi $rule1 prend la valeur
"Reporter" et $rule2 prend la valeur "Product".
Cet exemple montre comment à partir d'un enregistrement de Bugs, récupérer les
enregistrements de Products, représentant les produits comportant ce bug.
$productsRowset = $bug1234->findManyToManyRowset('Products',
'BugsProducts',
'Bug');
Vous pouvez récupérer l'enregistrement de destination d'une autre manière. En utilisant les
"méthodes magiques". En effet, Zend_Db_Table_Row_Abstract va utiliser la méthode
findManyToManyRowset('<TableClass>', '<IntersectionTableClass>',
'<Rule1>', '<Rule2>') si vous appelez sur l'enregistrement une méthode correspondante
à un de ces motifs :
• $row-
>find<TableClass>Via<IntersectionTableClass>([Zend_Db_Table_Select
$select])
• $row-
>find<TableClass>Via<IntersectionTableClass>By<Rule1>([Zend_Db_Table_Select
$select])
• $row-
>find<TableClass>Via<IntersectionTableClass>By<Rule1>And<Rule2>([Zend_Db_Table_Se
$select])
Les noms de la table et des rôles doivent être orthographiés de manière exacte,
tel qu'ils le sont lors de leurs définitions respectives.
506
Zend_Db
Cet exemple illustre la récupération d'enregistrements dans une table de destination, bugs,
depuis un produit, en passant par une table d'intersection, le tout, via des méthodes
magiques.
Il est très important de ne pas déclarer ses règles d'intégrité référentielle à la fois
dans son SGBD et dans les classes Zend_Db_Table.
Vous pouvez déclarer des opérations de cascade sur un UPDATE ou un DELETE, à appliquer sur
les enregistrements dépendants à la table en cours.
$product1234->delete();
// Cascades automatiques vers le table Bugs
// et suppression des enregistrements dépendants.
507
Zend_Db
De la même manière, si vous utilisez un UPDATE pour changer la valeur de la clé primaire d'une
table parente, vous pourriez nécessiter que les clés étrangères des tables dépendantes soient
mises à jour.
En général s'il s'agit d'une séquence, il n'est pas nécessaire de mettre à jour les enregistrements
dépendants. En revanche concernant les clé dites naturelles , il peut s'avérer nécessaire de
propager un changement de valeur.
Afin de déclarer une relation de cascades dans Zend_Db_Table, éditer les rôles dans
$_referenceMap. Ajoutez les clés 'onDelete' et 'onUpdate' et donnez leur la valeur
'cascade' (ou la constante self::CASCADE). Avant qu'un enregistrement ne soit modifié(sa clé
primaire) / supprimé, tous les enregistrements dans les tables dépendantes seront modifiés /
supprimés.
Pas de mise à jour en cascade en revanche pour cette table, si la clé primaire de la table
parente est changée. En effet, l'élément "onUpdate" est mis à self::RESTRICT. Vous
auriez aussi pu tout simplement ne pas spécifier "onUpdate" .
Ceci signifie que si votre SGBD possède un moyen de gérer les cascades, comme l'intégrité
référentielle (et les clés étrangères), alors vous ne devriez pas utiliser les cascades INSERT
via Zend_Db_Table, car elles vont entrer en conflit avec le système d'intégrité référentielle du
SGBD qui lui, est atomique.
Le problème est plus mitigé concernant DELETE. Vous pouvez détruire de manière non atomique
un enregistrement dépendant, avant de détruire son parent.
Cependant, les deux opérations UPDATE et DELETE utilisées de manière non atomique(que),
c'est à dire avec le mécanisme de Zend_Db_Table, peuvent laisser la base de données dans un
état non désiré, ou état intermédiaire. Supposez que vous supprimiez tous les enregistrements
dépendants, pour finir par leur parent unique. A un moment donnée, la base de donnée sera dans
508
Zend_Db
un état tel que le parent sera sans enfants, mais toujours bel et bien présent. Si un autre client
se connecte exactement à ce moment là, il va pouvoir requêter éventuellement le parent, en
croyant que celui-ci n'a plus d'enfant, ce qui normalement n'est pas le cas. Il est alors totalement
impossible pour ce client là de se rendre compte qu'il a effectuer une lecture au beau milieu
d'une plus vaste opération d'effacement.
Les problèmes de changements non-atomique peuvent être anéantis en utilisant les transactions
isolantes, c'est d'ailleurs un de leur rôle clé. Cependant certains SGBDs ne supportent pas
encore les transactions, et autorisent leurs clients à lire des changements incomplets pas validés
en totalité.
Les cascades pour DELETE et UPDATE définies dans vos classes Zend_Db_Table ne sont
utilisées que lors du recours aux méthodes save() ou delete() sur les enregistrements
Row. Si vous utilisez une autre interface pour vos UPDATE ou DELETE, comme par exemple un
outil de requêtes, ou une autre application, les opérations de cascades ne sont bien sûr pas
appliquées. C'est même le cas si vous utilisez les méthodes update() et delete() dans la
classe Zend_Db_Adapter.
Le support pour les cascades d'INSERT n'est pas assuré. Vous devez explicitement insérer les
enregistrements dépendants à un enregistrement parent.
9. Zend_Db_Table_Definition
9.1. Introduction
Zend_Db_Table_Definition est une classe qui peut être utilisée pour décrire les relations et
les options de configuration qui devraient être utilisées lorsque Zend_Db_Table est manipulée
par instantiation concrête.
Voici un exemple d'objet de définition qui va décrire les noms des tables et les relations entre
les objets supports de ces tables. Note: Si 'name' n'est pas précisé, la clé servira alors de nom
à la table, c'est le cas dans notre exemple avec 'genre'.
509
Zend_Db
Comme vous le voyez, les mêmes options que vous utilisez en général en étendant
Zend_Db_Table_Abstract sont présentes dans ce tableau. Cette définition va persister vers
toutes les tables qui seront créees par votre objet, ceci assure une isolation et un bon
fonctionnement.
510
Zend_Db
Dans l'exemple d'après, nous allons placer une des tables sous forme de classe concrête, et
laisser les autres sous forme de définitions. Nous allons voir alors comment les faire intéragir.
511
Zend_Db
512
Zend_Debug
1. Afficher des informations
La méthode statique Zend_Debug::dump() affiche et/ou retourne les informations concernant
une expression. Cette technique simple de débogage est commune, parce que facile à utiliser
de façon ad hoc et n'exigeant aucune initialisation, aucun outils spéciaux, ou environnement de
mise au point.
L'argument $var définit l'expression ou la variable que l'on veut examiner avec
Zend_Debug::dump().
Le booléen $echo indique s'il faut (ou non) afficher la sortie de Zend_Debug::dump(). Si
TRUE, la sortie sera affichée. Quel que soit l'état de $echo, la sortie est toujours retournée.
Il peut être utile de savoir que la méthode Zend_Debug::dump() enveloppe la fonction PHP
var_dump(). Si le flux est reconnu à destination d'un contenu Web, l'affichage de var_dump()
est échappé avec htmlspecialchars() et est enveloppé entre des balises (X)HTML <pre>
et </pre>.
513
Zend_Dojo
1. Introduction
A partir de la version 1.6.0, Zend Framework contient Dojo Toolkitpour permettre le
développement rapide d'applications internet riche (RIA). Les points d'intégration avec Dojo
incluent :
• le support de JSON-RPC ;
dojo.data is often used with XmlHttpRequest to pull dynamic data from the server. The primary
mechanism for this is to extend the QueryReadStore to point at a URL and specify the query
information. The server side then returns data in the following JSON format:
{
identifier: '<name>',
<label: '<label>',>
items: [
{ name: '...', label: '...', someKey: '...' },
...
]
}
514
Zend_Dojo
You can also add a single item at a time, or append items, using addItem() and addItems().
$data->addItems($someMoreItems);
• Associative arrays
You can attach collections of the above items via addItems() or setItems() (overwrites all
previously set items); when doing so, you may pass a single argument:
• Arrays
• Objects implementing the Traversable interface ,which includes the interfaces Iterator
and ArrayAccess.
If you want to specify a field that will act as a label for the item, call setLabel():
$data->setLabel('name');
Finally, you can also load a Zend_Dojo_Data item from a dojo.data JSON array, using the
fromJson() method.
$data->fromJson($json);
515
Zend_Dojo
All items are referenced by the identifier field. Since identifiers must be unique, you can use the
values of this field to pull individual records. There are two ways to do this: with the getItem()
method, or via array notation.
// Using getItem():
$item = $data->getItem('foo');
If you know the identifier, you can use it to retrieve an item, update it, delete it, create it, or test
for it:
516
Zend_Dojo
// Delete an item:
unset($data['foo']);
You can loop over all items as well. Internally, all items are stored as arrays.
Finally, as the class implements __toString(), you can also cast it to JSON simply by echoing
it or casting to string:
Besides the methods necessary for implementing the interfaces listed above, the following
methods are available.
• setItems($items): set multiple items at once, overwriting any items that were previously
set in the object. $items should be an array or a Traversable object.
• addItems($items): add multiple items at once, appending them to any current items. Will
raise an exception if any of the new items have an identifier matching an identifier already in
the collection. $items should be an array or a Traversable object.
• hasItem($id): determine whether an item with the given identifier exists in the collection.
• getItem($id): retrieve an item with the given identifier from the collection; the item returned
will be an associative array. If no item matches, a NULL value is returned.
• removeItem($id): remove an item with the given identifier from the collection.
517
Zend_Dojo
• setIdentifier($identifier): set the name of the field that represents the unique
identifier for each item in the collection.
• setLabel($label): set the name of a field to be used as a display label for an item.
• toArray(): cast the object to an array. At a minimum, the array will contain the keys
'identifier', 'items', and 'label' if a label field has been set in the object.
• dojo(): paramètre l'environnement Dojo de votre page, incluant les valeurs de configuration
dojo, les chemins de modules personnalisés, les appels de chargement des modules requis,
les feuilles de styles des thêmes, l'utilisation ou non d'un CDN, et d'autres encore.
Pour utiliser les aides de vues Dojo, vous devrez informer votre objet de vue de leur
localisation. Vous pouvez faire ceci en appelant addHelperPath() :
$view->addHelperPath('Zend/Dojo/View/Helper/',
'Zend_Dojo_View_Helper');
Zend_Dojo::enableView($view);
518
Zend_Dojo
Next, the view script. In this case, we're going to specify that we will be using a
FilteringSelect -- which will consume a custom store based on QueryReadStore, which we'll
call 'PairedStore' and store in our 'custom' module.
<?php // setup data store for FilteringSelect ?>
<div dojoType="custom.PairedStore" jsId="stateStore"
url="/data/autocomplete/type/state/format/ajax"
requestMethod="get"></div>
In our layout script, we'll then check to see if Dojo is enabled, and, if so, we'll do some more
general configuration and assemble it:
<?php echo $this->doctype() ?>
<html>
<head>
<?php echo $this->headTitle() ?>
<?php echo $this->headMeta() ?>
<?php echo $this->headLink() ?>
<?php echo $this->headStyle() ?>
<?php if ($this->dojo()->isEnabled()){
$this->dojo()->setLocalPath('/js/dojo/dojo.js')
->addStyleSheetModule('dijit.themes.tundra');
echo $this->dojo();
}
?>
<?php echo $this->headScript() ?>
</head>
<body class="tundra">
<?php echo $this->layout()->content ?>
<?php echo $this->inlineScript() ?>
</body>
</html>
At this point, you only need to ensure that your files are in the correct locations and that
you've created the end point action for your FilteringSelect!
519
Zend_Dojo
By default, Zend Framework uses UTF-8 as its default encoding, and, specific to
this case, Zend_View does as well. Character encoding can be set differently on
the view object itself using the setEncoding() method (or the the encoding
instantiation parameter). However, since Zend_View_Interface does not
define accessors for encoding, it's possible that if you are using a custom view
implementation with the Dojo view helper, you will not have a getEncoding()
method, which is what the view helper uses internally for determining the
character set in which to encode.
If you do not want to utilize UTF-8 in such a situation, you will need to implement
a getEncoding() method in your custom view implementation.
Dojo allows both declarative and programmatic usage of many of its features. Declarative usage
uses standard HTML elements with non-standard attributes that are parsed when the page is
loaded. While this is a powerful and simple syntax to utilize, for many developers this can cause
issues with page validation.
Programmatic usage allows the developer to decorate existing elements by pulling them by ID
or CSS selectors and passing them to the appropriate object constructors in Dojo. Because no
non-standard HTML attributes are used, pages continue to validate.
In practice, both use cases allow for graceful degradation when javascript is disabled or the
various Dojo script resources are unreachable. To promote standards and document validation,
Zend Framework uses programmatic usage by default; the various view helpers will generate
javascript and push it to the dojo() view helper for inclusion when rendered.
Developers using this technique may also wish to explore the option of writing their own
programmatic decoration of the page. One benefit would be the ability to specify handlers for
dijit events.
To allow this, as well as the ability to use declarative syntax, there are a number of static methods
available to set this behavior globally.
Zend_Dojo_View_Helper_Dojo::setUseDeclarative();
If you decide instead to use programmatic usage, call the static setUseProgrammatic()
method:
Zend_Dojo_View_Helper_Dojo::setUseProgrammatic();
Finally, if you want to create your own programmatic rules, you should specify programmatic
usage, but pass in the value '-1'; in this situation, no javascript for decorating any dijits used
will be created.
Zend_Dojo_View_Helper_Dojo::setUseProgrammatic(-1);
520
Zend_Dojo
3.1.2. Themes
Dojo allows the creation of themes for its dijits (widgets). You may select one by passing in a
module path:
$view->dojo()->addStylesheetModule('dijit.themes.tundra');
The module path is discovered by using the character '.' as a directory separator and using the
last value in the list as the name of the CSS file in that theme directory to use; in the example
above, Dojo will look for the theme in 'dijit/themes/tundra/tundra.css'.
When using a theme, it is important to remember to pass the theme class to, at the least, a
container surrounding any dijits you are using; the most common use case is to pass it in the
body:
<body class="tundra">
Dojo's answer to this is to provide the ability to create custom builds. Builds do several things:
• Groups required files into layers; a layer lumps all required files into a single JS file. (Hence
the name of this section.)
• "Interns" non-javascript files used by dijits (typically, template files). These are also grouped
in the same JS file as the layer.
• Passes the file through ShrinkSafe, which strips whitespace and comments, as well as
shortens variable names.
Some files can not be layered, but the build process will create a special release directory with
the layer file and all other files. This allows you to have a slimmed-down distribution customized
for your site or application needs.
To use a layer, the dojo() view helper has a addLayer() method for adding paths to required
layers:
$view->dojo()->addLayer('/js/foo/foo.js');
For more information on creating custom builds, please refer to the Dojo build documentation.
521
Zend_Dojo
• removeLayer($path): remove the layer that matches $path from the list of registered
layers (custom builds).
• setCdnBase($url): set the base URL for a CDN; typically, one of the
Zend_Dojo::CDN_BASE_AOL or Zend_Dojo::CDN_BASE_GOOGLE, but it only needs to be
the URL string prior to the version number.
• setCdnVersion($version = null): set which version of Dojo to utilize from the CDN.
• getCdnVersion(): retrieve what version of Dojo from the CDN will be used.
• getCdnDojoPath(): retrieve the last path segment of the CDN url pointing to the dojo.js file.
• useCdn(): tell the container to utilize the CDN; implicitly enables integration.
• setLocalPath($path): tell the container the path to a local Dojo install (should be a path
relative to the server, and contain the dojo.js file itself); implicitly enables integration.
522
Zend_Dojo
• getOnLoadActions(): retrieve all dojo.onLoad actions registered with the container. This
will be an array of arrays.
• __toString(): cast the container to a string; renders all HTML style and script elements.
Zend Framework ships a variety of view helpers that allow you to render and utilize dijits within
your view scripts. There are three basic types:
• Layout Containers: these are designed to be used within your view scripts or consumed by form
decorators for forms, sub forms, and display groups. They wrap the various classes offerred
in dijit.layout. Each dijit layout view helper expects the following arguments:
• $params (optional): dijit-specific parameters. Basically, any non-HTML attribute that can be
used to configure the dijit layout container.
• $attribs (optional): any additional HTML attributes that should be used to render the
container div. If the key 'id' is passed in this array, it will be used for the form element DOM
id, and $id will be used for its name.
If you pass no arguments to a dijit layout view helper, the helper itself will be returned. This
allows you to capture content, which is often an easier way to pass content to the layout
container. Examples of this functionality will be shown later in this section.
523
Zend_Dojo
• Form Dijit: the dijit.form.Form dijit, while not completely necessary for use with dijit form
elements, will ensure that if an attempt is made to submit a form that does not validate against
client-side validations, submission will be halted and validation error messages raised. The
form dijit view helper expects the following arguments:
• $attribs (optional): any additional HTML attributes that should be used to render the
container div.
• $content (optional): the content to wrap in the form. If none is passed, an empty string
will be used.
The argument order varies from the other dijits in order to keep compatibility with the standard
form() view helper.
• Form Elements: these are designed to be consumed with Zend_Form, but can be used
standalone within view scripts as well. Each dijit element view helper expects the following
arguments:
• $params (optional): dijit-specific parameters. Basically, any non-HTML attribute that can be
used to configure a dijit.
• $attribs (optional): any additional HTML attributes that should be used to render the dijit.
If the key 'id' is passed in this array, it will be used for the form element DOM id, and $id
will be used for its name.
Some elements require more arguments; these will be noted with the individual element helper
descriptions.
In order to utilize these view helpers, you need to register the path to the dojo view helpers with
your view object.
$view->addHelperPath('Zend/Dojo/View/Helper', 'Zend_Dojo_View_Helper');
The dijit.layout family of elements are for creating custom, predictable layouts for your site. For
any questions on general usage, read more about them in the Dojo manual.
All dijit layout elements have the signature string ($id = null, $content = '', array $params =
array(), array $attribs = array()). In all caess, if you pass no arguments, the helper object itself
will be returned. This gives you access to the captureStart() and captureEnd() methods,
which allow you to capture content instead of passing it to the layout container.
524
Zend_Dojo
'foo',
$content,
array(
'duration' => 200,
),
array(
'style' => 'width: 200px; height: 300px;',
),
); ?>
525
Zend_Dojo
array(
'orientation' => 'horizontal',
'sizerWidth' => 7,
'activeSizing' => true,
),
array(
'style' => 'width: 400px; height: 500px;',
),
); ?>
The following capture methods are available for all layout containers:
• captureStart($id, array $params = array(), array $attribs = array()): begin capturing content
to include in a container. $params refers to the dijit params to use with the container, while
$attribs refer to any general HTML attributes to use.
526
Zend_Dojo
BorderContainers, particularly when coupled with the ability to capture content, are
especially useful for achieving complex layout effects.
$view->borderContainer()->captureStart('masterLayout',
array('design' => 'headline'));
echo $view->contentPane(
'menuPane',
'This is the menu pane',
array('region' => 'top'),
array('style' => 'background-color: darkblue;')
);
echo $view->contentPane(
'navPane',
'This is the navigation pane',
array('region' => 'left'),
array('style' => 'width: 200px; background-color: lightblue;')
);
echo $view->contentPane(
'mainPane',
'This is the main content pane area',
array('region' => 'center'),
array('style' => 'background-color: white;')
);
echo $view->contentPane(
'statusPane',
'Status area',
array('region' => 'bottom'),
array('style' => 'background-color: lightgray;')
);
echo $view->borderContainer()->captureEnd('masterLayout');
Dojo's form validation and input dijits are in the dijit.form tree. For more information on
general usage of these elements, as well as accepted parameters, please visit the dijit.form
documentation.
The following dijit form elements are available in Zend Framework. Except where noted, all have
the signature string ($id, $value = '', array $params = array(), array $attribs = array()).
527
Zend_Dojo
• an indexed array with two values, a checked value and unchecked value, in that order; or
• ComboBox: dijit.layout.ComboBox. ComboBoxes are a hybrid between a select and a text box
with autocompletion. The key difference is that you may type an option that is not in the list
of available options, and it will still consider it valid input. It accepts an optional fifth argument,
an associative array $options; if provided, ComboBox will be rendered as a select. Note
also that the label values of the $options array will be returned in the form -- not the values
themselves.
Alternately, you may pass information regarding a dojo.data datastore to use with the element.
If provided, the ComboBox will be rendered as a text input, and will pull its options via that
datastore.
• The key 'store', with an array value; the array should contain the keys:
• store: the name of the javascript variable representing the datastore (this could be the
name you would like for it to use).
• params (optional): an associative array of key/value pairs to use to configure the datastore.
The 'url' param is a typical example.
• The keys:
• storeParams: an associative array of key/value pairs with which to configure the datastore.
// As a select element:
echo $view->comboBox(
'foo',
'bar',
array(
'autocomplete' => false,
),
array(),
array(
'foo' => 'Foo',
528
Zend_Dojo
// As a dojo.data-enabled element:
echo $view->comboBox(
'foo',
'bar',
array(
'autocomplete' => false,
'store' => 'stateStore',
'storeType' => 'dojo.data.ItemFileReadStore',
'storeParams' => array('url' => '/js/states.json'),
),
);
echo $view->currencyTextBox(
'foo',
'$25.00',
array('currency' => 'USD'),
array('maxlength' => 20)
);
$view->headMeta()->appendHttpEquiv('Content-Type',
'text/html; charset=utf-8');
This will mean, of course, that you will need to ensure that the headMeta()
placeholder is echoed in your layout script.
echo $view->dateTextBox(
'foo',
'2008-07-11',
array('required' => true)
);
• Editor: dijit.Editor. Provides a WYSIWYG editor via which users may create or edit content.
dijit.Editor is a pluggable, extensible editor with a variety of parameters you can utilize for
customization; see the dijit.Editor documentation for more details.
echo $view->editor('foo');
529
Zend_Dojo
Please see the information above on ComboBox for examples and available options for
defining datastores.
At their most basic, they require the dijit parameters 'minimum', 'maximum', and
'discreteValues'. These define the range of values. Other common options are:
• 'intermediateChanges' can be set to indicate whether or not to fire onChange events while
the handle is being dragged.
• 'clickSelect' can be set to allow clicking a location on the slider to set the value.
• 'pageIncrement' can specify the value by which to increase/decrease when pageUp and
pageDown are used.
• 'showButtons' can be set to allow displaying buttons on either end of the slider for
manipulating the value.
The Zend Framework implementation creates a hidden element to store the value of the slider.
You may optionally desire to show a rule or labels for the slider. To do so, you will assign
one or more of the dijit params 'topDecoration' and/or 'bottomDecoration' (HorizontalSlider)
or 'leftDecoration' and/or 'rightDecoration' (VerticalSlider). Each of these expects the following
options:
• labels (optional): an array of labels to utilize. Use empty strings on either end to provide
labels for inner values only. Required when specifying one of the 'Labels' dijit variants.
• params (optional): dijit params for configuring the Rule dijit in use. Parameters specific to
these dijits include:
530
Zend_Dojo
• container (optional): array of parameters and attributes for the rule container.
• labels (optional): array of parameters and attributes for the labels list container.
• attribs (optional): HTML attributes to use with the rules/labels. This should follow the params
option format and be an associative array with the keys 'container' and 'labels'.
echo $view->horizontalSlider(
'foo',
1,
array(
'minimum' => -10,
'maximum' => 10,
'discreteValues' => 11,
'intermediateChanges' => true,
'showButtons' => true,
'topDecoration' => array(
'container' => 'topContainer'
'dijit' => 'HorizontalRuleLabels',
'labels' => array(
' ',
'20%',
'40%',
'60%',
'80%',
' ',
),
'params' => array(
'container' => array(
'style' => 'height:1.2em; font-size=75%;color:gray;',
),
'labels' => array(
'style' => 'height:1em; font-size=75%;color:gray;',
),
),
),
'bottomDecoration' => array(
'container' => 'bottomContainer'
'labels' => array(
'0%',
'50%',
'100%',
),
'params' => array(
'container' => array(
'style' => 'height:1.2em; font-size=75%;color:gray;',
),
'labels' => array(
'style' => 'height:1em; font-size=75%;color:gray;',
),
),
),
)
);
• NumberSpinner: dijit.form.NumberSpinner. Text box for numeric entry, with buttons for
incrementing and decrementing.
531
Zend_Dojo
Expects either an associative array for the dijit parameter 'constraints', or simply the keys 'min',
'max', and 'places' (these would be the expected entries of the constraints parameter as well).
'places' can be used to indicate how much the number spinner will increment and decrement.
echo $view->numberSpinner(
'foo',
5,
array(
'min' => -10,
'max' => 10,
'places' => 2,
),
array(
'maxlenth' => 3,
)
);
echo $view->numberTextBox(
'foo',
5,
array(
'places' => 4,
'type' => 'percent',
),
array(
'maxlength' => 20,
)
);
echo $view->passwordTextBox(
'foo',
'',
array(
'required' => true,
),
array(
'maxlength' => 20,
)
);
• RadioButton: dijit.form.RadioButton. A set of options from which only one may be selected.
This behaves in every way like a regular radio, but has a look-and-feel consistent with other
dijits.
echo $view->radioButton(
532
Zend_Dojo
'foo',
'bar',
array(),
array(),
array(
'foo' => 'Foo',
'bar' => 'Bar',
'baz' => 'Baz',
)
);
• SimpleTextarea: dijit.form.SimpleTextarea. These act like normal textareas, but are styled
using the current dijit theme. You do not need to specify either the rows or columns attributes;
use ems or percentages for the width and height, instead.
echo $view->simpleTextarea(
'foo',
'Start writing here...',
array(),
array('style' => 'width: 90%; height: 5ems;')
);
• SubmitButton: a dijit.form.Button tied to a submit input element. See the Button view helper
for more details; the key difference is that this button can submit a form.
• Textarea: dijit.form.Textarea. These act like normal textareas, except that instead of having
a set number of rows, they expand as the user types. The width should be specified via a
style setting.
echo $view->textarea(
'foo',
'Start writing here...',
array(),
array('style' => 'width: 300px;')
);
Common dijit parameter flags include 'lowercase' (cast to lowercase), 'uppercase' (cast
to UPPERCASE), 'propercase' (cast to Proper Case), and trim (trim leading and trailing
whitespace); all accept boolean values. Additionally, you may specifiy the parameters 'size'
and 'maxLength'.
echo $view->textBox(
'foo',
'some text',
array(
'trim' => true,
'propercase' => true,
'maxLength' => 20,
),
array(
'size' => 20,
)
533
Zend_Dojo
);
echo $view->timeTextBox(
'foo',
'',
array(
'am.pm' => true,
'visibleIncrement' => 'T00:05:00', // 5-minute increments
'visibleRange' => 'T02:00:00', // show 2 hours of increments
),
array(
'size' => 20,
)
);
• regExp: a regular expression to use to validate the text. Regular expression does not require
boundary markers.
• required: whether or not the element is required. If so, and the element is embedded in a
dijit.form.Form, it will be flagged as invalid and prevent submission.
echo $view->validationTextBox(
'foo',
'',
array(
'required' => true,
'regExp' => '[\w]+',
'invalidMessage' => 'No spaces or non-word characters allowed',
'promptMessage' => 'Single word consisting of alphanumeric ' .
'characters and underscores only',
),
array(
'maxlength' => 20,
)
);
If you delve into Dojo much at all, you'll find yourself writing custom dijits, or using experimental
dijits from Dojox. While Zend Framework cannot support every dijit directly, it does provide some
rudimentary support for arbitrary dijit types via the CustomDijit view helper.
534
Zend_Dojo
The CustomDijit view helper's API is exactly that of any other dijit, with one major difference:
the third "params" argument must contain the attribute "dojotype". The value of this attribute
should be the Dijit class you plan to use.
CustomDijit extends the base DijitContainer view helper, which also allows it to capture
content (using the captureStart()/captureEnd() pair of methods). captureStart() also
expects that you pass the "dojoType" attribute to its "params" argument.
535
Zend_Dojo
If you wanted to capture content instead, simply use the captureStart() method, and
pass the "dojoType" to the "params" argument:
<?php $this->customDijit()->captureStart(
'foo',
array(
'dojoType' => 'dojox.layout.ContentPane',
'title' => 'Custom pane',
'region' => 'center'
)
); ?>
This is the content of the pane
<?php echo $this->customDijit()->captureEnd('foo'); ?>
You can also extend CustomDijit easily to create support for your own custom dijits. As an
example, if you extended dijit.layout.ContentPane to create your own foo.ContentPane
class, you could create the following helper to support it:
class My_View_Helper_FooContentPane
extends Zend_Dojo_View_Helper_CustomDijit
{
protected $_defaultDojoType = 'foo.ContentPane';
As long as your custom dijit follows the same basic API as official dijits, using or extending
CustomDijit should work correctly.
536
Zend_Dojo
Il existe trois options pour utiliser les éléments de formulaires Dojo avec vos formulaires :
• En dernier, et le plus pénible, vous pouvez régler vous même les chemins appropriés vers
les décorateurs et les éléments, régler la classe de DisplayGroup par défaut, et activer dojo
dans l'objet de vue. Puisque Zend_Dojo::enableForm() fait déjà ceci, il n'y a que peu de
raisons d'utiliser cette voie.
"Mais attendez," vous allez me dire ; "j'étends déjà Zend_Form avec ma propre classe de
formulaire personnalisé ! Comment puis-je activer Dojo ?'"
L'utilisation des éléments de formulaires et les décorateurs spécifiques à Dijit est identique à
l'utilisation de tous autres éléments de formulaires ou décorateurs.
537
Zend_Dojo
All dijit decorators look for the dijitParams property of the given element being decorated, and
push them as the $params array to the dijit view helper being used; these are then separated
from any other properties so that no duplication of information occurs.
It is important that each element have a unique ID (as fetched from the element's getId()
method). If duplicates are detected within the dojo() view helper, the decorator will trigger a
notice, but then create a unique ID by appending the return of uniqid() to the identifier.
Standard usage is to simply associate this decorator as the first in your decorator chain, with
no additional options.
$element->setDecorators(array(
'DijitElement',
'Errors',
'Label',
'ContentPane',
));
Since dijit.form.Form does not require any dijit parameters for configuration, the main difference
is that the dijit form view helper require that a DOM ID is passed to ensure that programmatic
dijit creation can work. The decorator ensures this, by passing the form name as the identifier.
• AccordionContainer
• AccordionPane
• BorderContainer
• ContentPane
• SplitContainer
538
Zend_Dojo
• StackContainer
• TabContainer
// Use a ContentPane in your sub form (which can be used with all but
// AccordionContainer):
$subForm->setDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'dl')),
'ContentPane',
));
• setDijitParam($key, $value): set a single dijit parameter. If the dijit parameter already
exists, it will be overwritten.
• getDijitParam($key): retrieve the given dijit parameter. If not available, a NULL value is
returned.
Dijit parameters are stored in the dijitParams public property. Thus, you can dijit-enable an
existing form element simply by setting this property on the element; you simply will not have the
above accessors to facilitate manipulating the parameters.
539
Zend_Dojo
$element->addDecorator('DijitElement')
->addDecorator('Errors')
->addDecorator('HtmlTag', array('tag' => 'dd'))
->addDecorator('Label', array('tag' => 'dt'));
In effect, the DijitElement decorator is used in place of the standard ViewHelper decorator.
Finally, the base Dijit element ensures that the Dojo view helper path is set on the view.
A variant on DijitElement, DijitMulti, provides the functionality of the Multi abstract form element,
allowing the developer to specify 'multiOptions' -- typically select options or radio options.
The following dijit elements are shipped in the standard Zend Framework distribution.
4.2.1. Button
While not deriving from the standard Button element, it does implement the same functionality,
and can be used as a drop-in replacement for it. The following functionality is exposed:
• getLabel() will utilize the element name as the button label if no name is provided.
Additionally, it will translate the name if a translation adapter with a matching translation
message is available.
• isChecked() determines if the value submitted matches the label; if so, it returns TRUE. This
is useful for determining which button was used when a form was submitted.
Additionally, only the decorators DijitElement and DtDdWrapper are utilized for Button
elements.
$form->addElement(
'Button',
'foo',
array(
'label' => 'Button Label',
)
);
4.2.2. CheckBox
While not deriving from the standard Checkbox element, it does implement the same
functionality. This means that the following methods are exposed:
540
Zend_Dojo
$form->addElement(
'CheckBox',
'foo',
array(
'label' => 'A check box',
'checkedValue' => 'foo',
'uncheckedValue' => 'bar',
'checked' => true,
)
);
As noted in the ComboBox dijit view helper documentation, ComboBoxes are a hybrid between
select and text input, allowing for autocompletion and the ability to specify an alternate to the
options provided. FilteringSelects are the same, but do not allow arbitrary input.
ComboBoxes return the label values, and not the option values, which can lead to
a disconnect in expectations. For this reason, ComboBoxes do not auto-register
an InArray validator (though FilteringSelects do).
The ComboBox and FilteringSelect form elements provide accessors and mutators for examining
and setting the select options as well as specifying a dojo.data datastore (if used). They extend
from DijitMulti, which allows you to specify select options via the setMultiOptions() and
setMultiOption() methods. In addition, the following methods are available:
• getStoreInfo(): get all datastore information currently set. Returns an empty array if no
data is currently set.
• getStoreParams(): get any datastore parameters currently set; if none, an empty array is
returned.
• setAutocomplete($flag): indicate whether or not the selected item will be used when the
user leaves the element.
541
Zend_Dojo
By default, if no dojo.data store is registered with the element, this element registers an InArray
validator which validates against the array keys of registered options. You can disable this
behavior by either calling setRegisterInArrayValidator(false), or by passing a FALSE
value to the registerInArrayValidator configuration key.
$form->addElement(
'ComboBox',
'foo',
array(
'label' => 'ComboBox (select)',
'value' => 'blue',
'autocomplete' => false,
'multiOptions' => array(
'red' => 'Rouge',
'blue' => 'Bleu',
'white' => 'Blanc',
'orange' => 'Orange',
'black' => 'Noir',
'green' => 'Vert',
),
)
);
$form->addElement(
'ComboBox',
'foo',
array(
'label' => 'ComboBox (datastore)',
'storeId' => 'stateStore',
'storeType' => 'dojo.data.ItemFileReadStore',
'storeParams' => array(
'url' => '/js/states.txt',
),
'dijitParams' => array(
'searchAttr' => 'name',
),
)
);
4.2.4. CurrencyTextBox
The CurrencyTextBox is primarily for supporting currency input. The currency may be localized,
and can support both fractional and non-fractional values.
• setCurrency($currency): set the currency type to use; should follow the ISO-4217
specification.
542
Zend_Dojo
• setFractional($flag): set whether or not the currency should allow for fractional values.
$form->addElement(
'CurrencyTextBox',
'foo',
array(
'label' => 'Currency:',
'required' => true,
'currency' => 'USD',
'invalidMessage' => 'Invalid amount. ' .
'Include dollar sign, commas, and cents.',
'fractional' => false,
)
);
4.2.5. DateTextBox
DateTextBox provides a calendar drop-down for selecting a date, as well as client-side date
validation and formatting.
Internally, DateTextBox derives from ValidationTextBox and TextBox; all methods available to
those classes are available. In addition, the following methods can be used to set individual
constraints:
• setLocale($locale) and getLocale(): Set and retrieve the locale to use with this
specific element.
543
Zend_Dojo
$form->addElement(
'DateTextBox',
'foo',
array(
'label' => 'Date:',
'required' => true,
'invalidMessage' => 'Invalid date specified.',
'formatLength' => 'long',
)
);
4.2.6. Editor
Editor provides a WYSIWYG editor that can be used to both create and edit rich HTML content.
dijit.Editor is pluggable and may be extended with custom plugins if desired; see the dijit.Editor
documentation for more details.
The Editor form element provides a number of accessors and mutators for manipulating various
dijit parameters, as follows:
• captureEvents are events that connect to the editing area itself. The following accessors and
mutators are available for manipulating capture events:
• addCaptureEvent($event)
• addCaptureEvents(array $events)
• setCaptureEvents(array $events)
• getCaptureEvents()
• hasCaptureEvent($event)
• removeCaptureEvent($event)
• clearCaptureEvents()
• events are standard DOM events, such as onClick, onKeyUp, etc. The following accessors
and mutators are available for manipulating events:
• addEvent($event)
• addEvents(array $events)
• setEvents(array $events)
• getEvents()
• hasEvent($event)
• removeEvent($event)
• clearEvents()
• plugins add functionality to the Editor -- additional tools for the toolbar, additional styles to
allow, etc. The following accessors and mutators are available for manipulating plugins:
544
Zend_Dojo
• addPlugin($plugin)
• addPlugins(array $plugins)
• setPlugins(array $plugins)
• getPlugins()
• hasPlugin($plugin)
• removePlugin($plugin)
• clearPlugins()
• editActionInterval is used to group events for undo operations. By default, this value is 3
seconds. The method setEditActionInterval($interval) may be used to set the
value, while getEditActionInterval() will retrieve it.
• focusOnLoad is used to determine whether this particular editor will receive focus when the
page has loaded. By default, this is FALSE. The method setFocusOnLoad($flag) may be
used to set the value, while getFocusOnLoad() will retrieve it.
• height specifies the height of the editor; by default, this is 300px. The method
setHeight($height) may be used to set the value, while getHeight() will retrieve it.
• inheritWidth is used to determine whether the editor will use the parent container's width
or simply default to 100% width. By default, this is FALSE (i.e., it will fill the width of the
window). The method setInheritWidth($flag) may be used to set the value, while
getInheritWidth() will retrieve it.
• minHeight indicates the minimum height of the editor; by default, this is 1em. The method
setMinHeight($height) may be used to set the value, while getMinHeight() will
retrieve it.
• styleSheets indicate what additional CSS stylesheets should be used to affect the display of the
Editor. By default, none are registered, and it inherits the page styles. The following accessors
and mutators are available for manipulating editor stylesheets:
• addStyleSheet($styleSheet)
• addStyleSheets(array $styleSheets)
• setStyleSheets(array $styleSheets)
• getStyleSheets()
• hasStyleSheet($styleSheet)
• removeStyleSheet($styleSheet)
• clearStyleSheets()
545
Zend_Dojo
That said, there may be times when you want an Editor widget that can gracefully
degrade to a TEXTAREA. In such situations, you can do so by setting the
degrade property to TRUE:
// At instantiation:
$editor = new Zend_Dojo_Form_Element_Editor('foo', array(
'degrade' => true,
));
// Or after instantiation:
$editor->degrade = true;
4.2.7. HorizontalSlider
HorizontalSlider provides a slider UI widget for selecting a numeric value in a range. Internally,
it sets the value of a hidden element which is submitted by the form.
HorizontalSlider derives from the abstract Slider dijit element. Additionally, it has a variety of
methods for setting and configuring slider rules and rule labels.
• setTopDecorationContainer($container) and
setBottomDecorationContainer($container): specify the name to use for the
container element of the rules; e.g. 'topRule', 'topContainer', etc.
546
Zend_Dojo
547
Zend_Dojo
The following will create a horizontal slider selection with integer values ranging from -10 to
10. The top will have labels at the 20%, 40%, 60%, and 80% marks. The bottom will have
rules at 0, 50%, and 100%. Each time the value is changed, the hidden element storing the
value will be updated.
$form->addElement(
'HorizontalSlider',
'horizontal',
array(
'label' => 'HorizontalSlider',
'value' => 5,
'minimum' => -10,
'maximum' => 10,
'discreteValues' => 11,
'intermediateChanges' => true,
'showButtons' => true,
'topDecorationDijit' => 'HorizontalRuleLabels',
'topDecorationContainer' => 'topContainer',
'topDecorationLabels' => array(
' ',
'20%',
'40%',
'60%',
'80%',
' ',
),
'topDecorationParams' => array(
'container' => array(
'style' => 'height:1.2em; font-size=75%;color:gray;',
),
'list' => array(
'style' => 'height:1em; font-size=75%;color:gray;',
),
),
'bottomDecorationDijit' => 'HorizontalRule',
'bottomDecorationContainer' => 'bottomContainer',
'bottomDecorationLabels' => array(
'0%',
'50%',
'100%',
),
'bottomDecorationParams' => array(
'list' => array(
'style' => 'height:1em; font-size=75%;color:gray;',
),
),
)
);
4.2.8. NumberSpinner
A number spinner is a text element for entering numeric values; it also includes elements for
incrementing and decrementing the value by a set amount.
548
Zend_Dojo
• setSmallDelta($delta) and getSmallDelta(): set and retrieve the delta by which the
number should change when a button is pressed once.
• setMin($value) and getMin(): set and retrieve the minimum value possible.
• setMax($value) and getMax(): set and retrieve the maximum value possible.
$form->addElement(
'NumberSpinner',
'foo',
array(
'value' => '7',
'label' => 'NumberSpinner',
'smallDelta' => 5,
'largeDelta' => 25,
'defaultTimeout' => 500,
'timeoutChangeRate' => 100,
'min' => 9,
'max' => 1550,
'places' => 0,
'maxlength' => 20,
)
);
4.2.9. NumberTextBox
A number text box is a text element for entering numeric values; unlike NumberSpinner, numbers
are entered manually. Validations and constraints can be provided to ensure the number stays
in a particular range or format.
Internally, NumberTextBox derives from ValidationTextBox and TextBox; all methods available
to those classes are available. In addition, the following methods can be used to set individual
constraints:
549
Zend_Dojo
• setType($type) and getType(): set and retrieve the numeric format type to use (should
be one of 'decimal', 'percent', or 'currency').
• setPlaces($places) and getPlaces(): set and retrieve the number of decimal places
to support.
• setStrict($flag) and getStrict(): set and retrieve the value of the strict flag, which
indicates how much leniency is allowed in relation to whitespace and non-numeric characters.
$form->addElement(
'NumberTextBox',
'elevation',
array(
'label' => 'NumberTextBox',
'required' => true,
'invalidMessage' => 'Invalid elevation.',
'places' => 0,
'constraints' => array(
'min' => -20000,
'max' => 20000,
),
)
);
4.2.10. PasswordTextBox
PasswordTextBox is simply a ValidationTextBox that is tied to a password input; its sole purpose
is to allow for a dijit-themed text entry for passwords that also provides client-side validation.
Internally, PasswordTextBox derives from ValidationTextBox and TextBox; all methods available
to those classes are available.
$form->addElement(
'PasswordTextBox',
'password',
array(
'label' => 'Password',
'required' => true,
'trim' => true,
'lowercase' => true,
'regExp' => '^[a-z0-9]{6,}$',
'invalidMessage' => 'Invalid password; ' .
'must be at least 6 alphanumeric characters',
)
);
4.2.11. RadioButton
RadioButton wraps standard radio input elements to provide a consistent look and feel with other
dojo dijits.
RadioButton extends from DijitMulti, which allows you to specify select options via the
setMultiOptions() and setMultiOption() methods.
550
Zend_Dojo
By default, this element registers an InArray validator which validates against the
array keys of registered options. You can disable this behavior by either calling
setRegisterInArrayValidator(false), or by passing a FALSE value to the
registerInArrayValidator configuration key.
$form->addElement(
'RadioButton',
'foo',
array(
'label' => 'RadioButton',
'multiOptions' => array(
'foo' => 'Foo',
'bar' => 'Bar',
'baz' => 'Baz',
),
'value' => 'bar',
)
);
4.2.12. SimpleTextarea
SimpleTextarea acts primarily like a standard HTML textarea. However, it does not support either
the rows or cols settings. Instead, the textarea width should be specified using standard CSS
measurements. Unlike Textarea, it will not grow automatically
$form->addElement(
'SimpleTextarea',
'simpletextarea',
array(
'label' => 'SimpleTextarea',
'required' => true,
'style' => 'width: 80em; height: 25em;',
)
);
551
Zend_Dojo
4.2.14. SubmitButton
While there is no Dijit named SubmitButton, we include one here to provide a button dijit capable
of submitting a form without requiring any additional javascript bindings. It works exactly like the
Button dijit.
$form->addElement(
'SubmitButton',
'foo',
array(
'required' => false,
'ignore' => true,
'label' => 'Submit Button!',
)
);
4.2.15. TextBox
TextBox is included primarily to provide a text input with consistent look-and-feel to the other
dijits. However, it also includes some minor filtering and validation capabilities, represented in
the following methods:
• setLowercase($flag) and getLowercase(): set and retrieve the flag indicating whether
or not input should be cast to lowercase.
• setUppercase($flag) and getUppercase(): set and retrieve the flag indicating whether
or not the input should be cast to UPPERCASE.
• setTrim($flag) and getTrim(): set and retrieve the flag indicating whether or not leading
or trailing whitespace should be stripped.
$form->addElement(
'TextBox',
'foo',
array(
'value' => 'some text',
'label' => 'TextBox',
'trim' => true,
'propercase' => true,
)
);
552
Zend_Dojo
4.2.16. Textarea
Textarea acts primarily like a standard HTML textarea. However, it does not support either
the rows or cols settings. Instead, the textarea width should be specified using standard CSS
measurements; rows should be omitted entirely. The textarea will then grow vertically as text
is added to it.
$form->addElement(
'Textarea',
'textarea',
array(
'label' => 'Textarea',
'required' => true,
'style' => 'width: 200px;',
)
);
4.2.17. TimeTextBox
TimeTextBox is a text input that provides a drop-down for selecting a time. The drop-down may
be configured to show a certain window of time, with specified increments.
Internally, TimeTextBox derives from DateTextBox, ValidationTextBox and TextBox; all methods
available to those classes are available. In addition, the following methods can be used to set
individual constraints:
The following will create a TimeTextBox that displays 2 hours at a time, with increments of
10 minutes.
$form->addElement(
'TimeTextBox',
'foo',
array(
'label' => 'TimeTextBox',
'required' => true,
'visibleRange' => 'T04:00:00',
'visibleIncrement' => 'T00:10:00',
'clickableIncrement' => 'T00:10:00',
)
);
553
Zend_Dojo
4.2.18. ValidationTextBox
ValidationTextBox provides the ability to add validations and constraints to a text input. Internally,
it derives from TextBox, and adds the following accessors and mutators for manipulating dijit
parameters:
• setRegExp($regexp) and getRegExp(): set and retrieve the regular expression to use
for validating the element. The regular expression does not need boundaries (unlike PHP's
preg* family of functions).
The following will create a ValidationTextBox that requires a single string consisting solely
of word characters (i.e., no spaces, most punctuation is invalid).
$form->addElement(
'ValidationTextBox',
'foo',
array(
'label' => 'ValidationTextBox',
'required' => true,
'regExp' => '[\w]+',
'invalidMessage' => 'Invalid non-space text.',
)
);
4.2.19. VerticalSlider
VerticalSlider is the sibling of HorizontalSlider, and operates in every way like that element. The
only real difference is that the 'top*' and 'bottom*' methods are replaced by 'left*' and 'right*', and
instead of using HorizontalRule and HorizontalRuleLabels, VerticalRule and VerticalRuleLabels
should be used.
554
Zend_Dojo
The following will create a vertical slider selection with integer values ranging from -10 to
10. The left will have labels at the 20%, 40%, 60%, and 80% marks. The right will have rules
at 0, 50%, and 100%. Each time the value is changed, the hidden element storing the value
will be updated.
$form->addElement(
'VerticalSlider',
'foo',
array(
'label' => 'VerticalSlider',
'value' => 5,
'style' => 'height: 200px; width: 3em;',
'minimum' => -10,
'maximum' => 10,
'discreteValues' => 11,
'intermediateChanges' => true,
'showButtons' => true,
'leftDecorationDijit' => 'VerticalRuleLabels',
'leftDecorationContainer' => 'leftContainer',
'leftDecorationLabels' => array(
' ',
'20%',
'40%',
'60%',
'80%',
' ',
),
'rightDecorationDijit' => 'VerticalRule',
'rightDecorationContainer' => 'rightContainer',
'rightDecorationLabels' => array(
'0%',
'50%',
'100%',
),
)
);
555
'80%',
' ',
),
'topDecorationParams' => array(
Zend_Dojo => array(
'container'
'style' => 'height:1.2em; ' .
'font-size=75%;color:gray;',
),
4.3. Dojo Form Examples 'list' => array(
'style' => 'height:1em; ' .
'font-size=75%;color:gray;',
),
),
'bottomDecorationDijit' => 'HorizontalRule',
'bottomDecorationContainer' => 'bottomContainer',
'bottomDecorationLabels' => array(
'0%',
'50%',
'100%',
),
'bottomDecorationParams' => array(
'list' => array(
'style' => 'height:1em; ' .
'font-size=75%;color:gray;',
),
),
)
)
->addElement(
'VerticalSlider',
'vertical',
array(
'label' => 'VerticalSlider',
'value' => 5,
'style' => 'height: 200px; width: 3em;',
'minimum' => -10,
'maximum' => 10,
'discreteValues' => 11,
'intermediateChanges' => true,
'showButtons' => true,
'leftDecorationDijit' => 'VerticalRuleLabels',
'leftDecorationContainer' => 'leftContainer',
'leftDecorationLabels' => array(
' ',
'20%',
'40%',
'60%',
'80%',
' ',
),
'rightDecorationDijit' => 'VerticalRule',
'rightDecorationContainer' => 'rightContainer',
'rightDecorationLabels' => array(
'0%',
'50%',
'100%',
),
)
);
$this->addSubForm($textForm, 'textboxtab')
->addSubForm($editorForm, 'editortab')
->addSubForm($toggleForm, 'toggletab')
->addSubForm($selectForm, 'selecttab')
->addSubForm($sliderForm, 'slidertab');
}
}
556
Zend_Dojo
// ...
}
}
Of course, if you can do that... you could and should simply alter the class to inherit from
Zend_Dojo_Form, which is a drop-in replacement of Zend_Form that's already Dojo-
enabled...
In order to create a build layer, you would traditionally create a JavaScript file that has
dojo.require statements for each dependency, and optionally some additional code that might
run when the script is loaded. As an example:
557
Zend_Dojo
dojo.provide("custom.main");
dojo.require("dijit.layout.TabContainer");
dojo.require("dijit.layout.ContentPane");
dojo.require("dijit.form.Form");
dojo.require("dijit.form.Button");
dojo.require("dijit.form.TextBox");
Then, in your application's layout, you'd instruct Dojo to load this module:
<html>
<head>
<script type="text/javascript" src="/js/dojo/dojo.js"></script>
<script type="text/javascript">
dojo.registerModulePath("custom", "../custom/");
dojo.require("custom.main");
</script>
$view->dojo()->registerModulePath('custom', '../custom/')
->requireModule('custom.main');
But since Zend_Dojo aggregates your various dojo.require statements, how do you create
your layer script? You could open each page and view the generated dojo.require statements,
and cut and paste them into a layer script file manually.
However, a better solution exists: since Zend_Dojo aggregates this information already,
you can simply pull that information and build your layer file. This is the purpose of
Zend_Dojo_BuildLayer.
As an example, let's say you wanted to create the module layer "custom.main". Assuming you
follow the recommended project directory structure, and that you are storing your JavaScript files
under public/js/, you could do the following:
$layerContents = $build->generateLayerScript();
$filename = APPLICATION_PATH . '/../public/js/custom/main.js';
if (!dir_exists(dirname($filename))) {
mkdir(dirname($filename));
}
file_put_contents($filename, $layerContents);
558
Zend_Dojo
When should you do the above? For it to work correctly, you need to do it after all view scripts
and the layout have been rendered, to ensure that the dojo() helper is fully populated. One
easy way to do so is using a front controller plugin, with a dispatchLoopShutdown() hook:
While the view object may be passed during instantiation, you may also pass it in to an instance
via the setView() method:
559
Zend_Dojo
$build->setView($view);
While the layer name may be passed during instantiation, you may also pass it in to an instance
via the setLayerName() method:
$build->setLayerName("custom.main");
dojo.addOnLoad is a useful utility for specifying actions that should trigger when the DOM has
finished loading. The dojo() view helper can create these statements via its addOnLoad()
and onLoadCapture() methods. In some cases, it makes sense to push these into your layer
file instead of rendering them via your view scripts.
By default, these are not rendered; to enable them, pass the consumeOnLoad configuration key
during instantiation:
$build->setConsumeOnLoad(true);
The dojo() view helper includes methods for capturing arbitrary JavaScript to include in the
<script> tag containing the various dojo.require and dojo.addOnLoad statements. This can
be useful when creating default data stores or globally scoped objects used throughout your
application.
By default, these are not rendered; to enable them, pass the consumeJavascript configuration
key during instantiation:
$build->setConsumeJavascript(true);
The simplest use case is to utilize the generateBuildProfile() method and send the output
to a file:
560
Zend_Dojo
$profile = $build->generateBuildProfile();
$filename = APPLICATION_PATH . '/../misc/scripts/custom.profile.js';
file_put_contents($filename, $profile);
Just like generating layers, you may want to automate this via a dispatchLoopShutdown()
plugin hook; you could even simply modify the one shown for generating layers to read as follows:
As noted, with module layers, you should only create the file once.
As an example, you may want to specify what type of optimizations should be performed, whether
or not to optimize CSS files in the layer, whether or not to copy tests into the build, etc. For a
listing of available options, you should read the Dojo Build documentation and accompanying
documentation.
561
Zend_Dojo
{
action: "release",
optimize: "shrinksafe",
layerOptimize: "shrinksafe",
copyTests: false,
loader: "default",
cssOptimize: "comments"
}
You can pass in whatever key and value pairs you want; the Dojo build script will ignore those
it does not understand.
// A single option:
$build->addProfileOption('version', 'zend-1.3.1');
// Several options:
$build->addProfileOptions(array(
'loader' => 'xdomain',
'optimize' => 'packer',
));
// Or overwrite options:
$build->setProfileOptions(array(
'version' => 'custom-1.3.1',
'loader' => 'shrinksafe',
'optimize' => 'shrinksafe',
));
562
Zend_Dom
1. Introduction
Zend_Dom fournit les outils pour travailler avec les documents et les structures de types DOM.
Actuellement, il existe Zend_Dom_Query, qui offre une interface unifiée pour requêter dans les
documents DOM en utilisant à la fois XPath et les sélecteurs CSS.
2. Zend_Dom_Query
Zend_Dom_Query fournit des mécanismes pour requêter dans les documents XML et (X)HTML
en utilisant soit XPath ou les sélecteurs CSS. Il a été développé pour faciliter les tests fonctionnels
des applications MVC, mais pourrait également être employé pour le développement rapide de
"screen scrapers".
La notation de type sélecteur CSS est fournie comme notation plus simple et plus familière
pour les développeurs Web à utiliser lors de la requête de documents ayant une structure de
type XML. La notation devrait être familière pour n'importe qui ayant écrit des feuilles de styles
CSS ou ayant utiliser des librairies Javascript qui fournissent pour sélectionner des noeuds en
utilisant des sélecteurs CSS ( Prototype's $$()et Dojo's dojo.queryont tous les deux inspirer ce
composant).
• types de l'élément : fourni un type d'élément à rechercher : "div", "a", "span", "h2", etc.
• attributs de style : les classes CSS à rechercher : ".error", "div.error", "label.required", etc. Si
un élément défini plus qu'une classe, la correspondance sera trouvé si la classe est présente
quelque part dans la déclaration de l'attribut.
• attributs arbitraires : tout attribut arbitraire de l'élément à rechercher. Trois types de recherche
sont possibles :
563
Zend_Dom
• Descendants directs : utilise ">" entre les sélecteurs pour représenter une descendance direct.
"div > span" trouvera seulement les éléments "span" qui sont des descendants directs d'un
élément "div". Peut aussi être utilisé avec chacun des sélecteurs ci-dessus.
• Descendants : une chaîne avec des sélecteurs multiples ensemble pour indiquer hiérarchie à
rechercher. "div .foo span #one" trouvera un élément avec un id "one" qui est un descendant
avec un profondeur arbitraire d'un élément "span", qui est lui-même un descendant avec un
profondeur arbitraire d'un élément ayant une classe "foo", qui est un descendant avec un
profondeur arbitraire d'un élément "div". Par exemple, il trouvera le lien vers le mot "One" dans
le code ci-dessous :
<div>
<table>
<tr>
<td class="foo">
<div>
Lorem ipsum <span class="bar">
<a href="/foo/bar" id="one">One</a>
<a href="/foo/baz" id="two">Two</a>
<a href="/foo/bat" id="three">Three</a>
<a href="/foo/bla" id="four">Four</a>
</span>
</div>
</td>
</tr>
</table>
</div>
Une fois que vous avez réalisé votre recherche, vous pouvez ensuite travailler avec l'objet de
résultat pour déterminer les informations sur les noeuds, ainsi que pour les récupérer eux et/ou
leurs contenus directement afin de les examiner et les manipuler. Zend_Dom_Query_Result
implémente Countable and Iterator, et stocke le résultat en interne sous la forme
DOMNodes/DOMElements. En exemple, considérons l'appel suivant sur l'HTML ci-dessus :
Zend_Dom_Query permet aussi de faire directement des recherches de type XPath en utilisant
la méthode queryXpath() ; vous pouvez fournir toute requête XPath valide à cette méthode,
et elle retournera un objet Zend_Dom_Query_Result.
2.2.1. Zend_Dom_Query
Ces méthodes sont disponibles pour Zend_Dom_Query :
564
Zend_Dom
2.2.2. Zend_Dom_Query_Result
Comme mentionné auparavant, Zend_Dom_Query_Result implémente à la fois Iterator
et Countable, et en tant que tel peut être utilisé dans une boucle foreach ainsi qu'avec la
fonction count(). De plus il expose les méthodes suivantes :
• getCssQuery() : retourne le sélecteur CSS utilisé pour produire le résultat (si fourni).
565
Zend_Exception
1. Utiliser les exceptions
Zend_Exception est la classe de base dont dérivent toutes les exceptions levées par les
classes de Zend Framework.
Le code suivant montre comment attraper une exception levée par une classe de Zend
Framework :
try {
Zend_Loader::loadClass('classnonexistante');
} catch (Zend_Exception $e) {
// Appeler Zend_Loader::loadClass() sur une classe non-existante
//entrainera la levée d'une exception dans Zend_Loader
echo "Récupère exception: " . get_class($e) . "\n";
echo "Message: " . $e->getMessage() . "\n";
// puis tout le code nécessaire pour récupérer l'erreur
}
Zend_Exception peut être comme une classe d'exception catch-all dans un bloc catch pour
traquer toues les exceptions levées par les classes de Zend Framework. Ceci peut être utile
quand un programme n'arrive pas à fonctionner en essayant d'attraper un type d'exception
spécifique.
La documentation pour chaque composant de Zend Framework et de ses classes contient les
informations plus spécifiques sur les méthodes qui lèvent des exceptions, les circonstances de
lancement de ces exceptions et quelles types declasses d'exception peuvent être levées.
2. Utilisation classique
Zend_Exception est le classe de base concernant toutes les exceptions envoyées par Zend
Framework. Cette classe étend la classe PHP Exception.
try {
// votre code
} catch (Zend_Exception $e) {
// faire quelque chose
}
try {
// Votre code
} catch (Zend_Db_Exception $e) {
// faire quelque chose
}
566
Zend_Exception
3. Exceptions précédentes
Depuis Zend Framework 1.10, Zend_Exception utilise les exceptions PHP 5.3 concernant
l'exception précédente. Simplement, dans un bloc catch, il est possible d'envoyer une exception
faisant référence à la précédente, ce qui améliore le contexte de débogage. Ce support dans
Zend Framework apporte le support complet de PHP 5.3 concernant les exceptions.
try {
$db->query($sql);
} catch (Zend_Db_Statement_Exception $e) {
if ($e->getPrevious()) {
echo '[' . get_class($e)
. '] a comme exception précédente ['
. get_class($e->getPrevious())
. ']' . PHP_EOL;
} else {
echo '[' . get_class($e)
. '] n'a pas d'exception qui la précède'
. PHP_EOL;
}
echo $e;
// affiche toutes les exception à commencer par la première, puis
// dépile.
}
567
Zend_Feed
1. Introduction
Le composant Zend_Feed offre des services permettant de traiter des flux RSS et Atom. Il
permet d'accéder aux éléments d'un flux, aux attributs d'un flux et aux attributs des entrées
d'un flux, tout cela au moyen d'une syntaxe intuitive. Zend_Feed prend aussi complètement en
charge la modification de la structure des flux et des entrées, avec la même syntaxe intuitive
que précédemment, et il sait transformer le résultat en XML. À l'avenir, la prise en charge des
modifications pourrait aussi inclure la prise en charge du protocole de publication Atom.
Sur le plan de la programmation, Zend_Feed est constitué d'une classe de base Zend_Feed
et de classes de base abstraites Zend_Feed_Abstract et Zend_Feed_Entry_Abstract,
permettant de représenter respectivement des flux et des entrées ; Zend_Feed contient aussi
des implémentations particulières de ces classes abstraites pour les flux et entrées RSS et Atom
ainsi qu'un assistant en coulisses qui assure le bon fonctionnement de la syntaxe intuitive.
Dans l'exemple ci-dessous, nous illustrons une utilisation simple de Zend_Feed : on obtient un
flux RSS et on enregistre les portions du flux qui nous intéressent dans un tableau PHP simple,
qui pourra ensuite être utilisé pour afficher les données, les stocker dans une base de données
etc.
Attention
Beaucoup de flux RSS ont à leur disposition différentes propriétés, pour les
canaux comme pour les éléments. La spécification RSS spécifie beaucoup de
propriétés optionnelles et gardez donc cela à l'esprit lorsque vous écrivez du
code qui manipule des données RSS.
568
Zend_Feed
$flux = Zend_Feed::import('http://flux.example.com/nomDuFlux');
Vous pouvez aussi utiliser Zend_Feed pour aller chercher le contenu d'un flux à partir d'un fichier
ou d'une chaîne PHP :
Dans chacun des exemples ci-dessus, une instance d'une classe étendant
Zend_Feed_Abstract est renvoyée en cas de succès, selon le type du flux. Si un flux
RSS a été obtenu au moyen de l'une des méthodes d'importation décrites ci-dessus, alors un
objet Zend_Feed_Rss sera renvoyé. Par contre, si un flux Atom a été importé, alors un objet
Zend_Feed_Atom est renvoyé. Les méthodes d'importation déclencheront aussi une exception
Zend_Feed_Exception en cas d'échec, par exemple si le flux est illisible ou malformé.
569
Zend_Feed
array(
// obligatoire
'title' => 'titre du flux',
'link' => 'url canonique du flux',
// optionel
'lastUpdate' => 'date de la mise à jour au format timestamp',
'published' => 'date de la publication au format timestamp',
// obligatoire
'charset' => 'charset des données textuelles',
// optionel
'description' => 'description courte du flux',
'author' => 'auteur du flux',
'email' => 'email de l'auteur du flux',
// optionel
'copyright' => 'informations de copyright',
'image' => 'url de l'image',
'generator' => 'générateur du flux',
'language' => 'langue dans la quelle le flux est écrit',
570
Zend_Feed
// obligatoire
'path' => 'chemin du nuage, ex. /RPC2',
'registerProcedure' => 'procédure à appeler, '
. 'ex. myCloud.rssPleaseNotify',
'protocol' => 'protocole à utiliser , ex. soap ou xml-rpc',
),
571
Zend_Feed
// optionel
'block' => 'empêcher l'apparition d'un épisode (yes|no)',
// optionel
'explicit' => 'graphique d'avertissement parental (yes|no|clean)',
'keywords' => 'une liste d'au maximum 12 mot clés'
. 'séparés par des virgules',
'new-feed-url' => 'utiliser pour informer iTunes'
. 'd'un nouvel URL de flux',
)
// optionel
'guid' => 'id de l'article, si aucun alors'
. 'la valeur link est utilisée',
// optionel
'lastUpdate' => 'date de publication au format timestamp',
'comments' => 'page de commentaires de l'item',
'commentRss' => 'l'url du flux des commentaires associés',
// optionel
572
Zend_Feed
// optionel
'type' => 'type mime de la pièce jointe',
'length' => 'length de la pièce jointe en octets'
),
array(
//données de la seconde pièce jointe et ainsi de suite
)
)
),
array(
//données du second item et ainsi de suite
)
)
);
Références :
573
Zend_Feed
$atomFeedFromArray =
Zend_Feed::importBuilder(new Zend_Feed_Builder($array));
Pour décharger le contenu d'une instance Zend_Feed_Abstract, vous pouvez utiliser les
méthodes send() ou saveXml().
$tableauFlux =
Zend_Feed::findFeeds('http://www.exemple.com/news.html');
Si une erreur a lieu lors de l'obtention du flux, une Zend_Feed_Exception sera déclenchée.
Une fois que vous disposez d'un objet "flux RSS", vous pouvez accéder aux propriétés RSS
standard du canal, et ce directement à partir de l'objet :
echo $canal->title();
574
Zend_Feed
Notez la syntaxe utilisée : un appel de fonction. Zend_Feed utilise une convention selon laquelle
les propriétés sont traitées comme des objets XML si elles sont demandées au moyen de la
syntaxe $obj->propriété et comme des chaînes si elles sont demandées au moyen de la
syntaxe $obj->propriété(). Ceci permet d'accéder à la totalité du contenu textuel d'un nœud
particulier tout comme à l'ensemble des enfants de ce nœud.
Si les propriétés du canal possèdent des attributs, ils sont accessibles à l'aide de l'indexage PHP :
echo $canal->category['domain'];
Comme les attributs XML ne peuvent avoir des enfants, la syntaxe $obj-
>propriété['attribut']() n'est pas nécessaire pour accéder aux valeurs des attributs.
La plupart du temps vous voudrez itérer sur le flux et réaliser quelque chose avec ses entrées.
Zend_Feed_Abstract implémente l'interface iterator de PHP, ce qui résume au code
suivant l'affichage des titres de tous les articles du canal :
Si vous n'êtes pas un familier de RSS, voici les éléments standard associés au canal RSS et à
ses éléments pris séparément (les entrées).
Les éléments RSS <item> n'ont pas d'éléments requis particulier. Cependant soit title soit
description doivent être présents.
575
Zend_Feed
• pubDate (date de publication) : la date à laquelle l'élément a été publié, au format RFC 822
Dans votre code vous pouvez toujours tester si un élément est non-vide au moyen du test
suivant :
if ($element->nomPropriete()) {
// ... traitement
}
Pour plus d'informations, la spécification RSS 2.0 officielle est disponible à l'adresse : http://
blogs.law.harvard.edu/tech/rss
Pour lire un flux Atom et afficher le titre (propriété title) et le résumé (propriété summary)
de chaque entrée :
Voici les propriétés liées au flux que vous pourrez trouver dans un flux Atom :
• title (titre) : le titre du flux, la même chose que le titre d'un canal RSS
• link (lien) : les flux peuvent posséder plusieurs liens, qui se distinguent les uns des autres
par un attribut type
Le lien équivalent au lien d'un canal RSS aurait pour type "text/html". Si le lien
désigne une version alternative du contenu présent dans le flux, il possédera un attribut
rel="alternate"
• subtitle (sous-titre) : la description du flux, qui équivaut à la description d'un canal RSS
576
Zend_Feed
• title (titre) : le titre de l'entrée, la même chose que le titre d'un élément RSS
• link (lien) : un lien vers un autre format ou une vue alternative de l'entrée
• content (contenu) : le contenu de l'entrée dans son entier ; vous pouvez l'ignorer si le flux
ne contient que des résumés
• published (publié) : la date à laquelle l'entrée a été publiée, au format RFC 3339
• updated (publié) : la date à laquelle l'entrée a été mise à jour, au format RFC 3339
Pour plus d'informations sur Atom ainsi qu'un grand nombre de ressources, voyez le site http://
www.atomenabled.org/.
Si vous lisez une entrée seule, vous obtiendrez toujours un objet Zend_Feed_Atom mais cette
classe créera automatiquement un flux "anonyme" pour contenir l'entrée.
$entree = $flux->current();
Vous pouvez aussi instancier directement la classe représentant les entrées si vous êtes sûr
que vous accédez à un document contenant une seule balise <entry> :
577
Zend_Feed
echo $entree->saveXML();
Ce code affichera une représentation XML complète (y compris le prologue <?xml ...>)
de la nouvelle entrée, avec les espaces de noms XML nécessaires.
Notez que le code ci-dessus fonctionnera même si l'entrée existante ne possédait pas de
balise author. Vous pouvez utiliser autant de fois que vous le souhaitez l'opérateur d'accès
-> dans une instruction d'affectation ; si nécessaire, les niveaux intermédiaires seront créés
pour vous automatiquement.
Si vous souhaitez utiliser dans votre entrée un espace de noms autre que atom:, rss: ou
osrss:, vous devez enregistrer cet espace de noms auprès de Zend_Feed à l'aide de la
méthode Zend_Feed::registerNamespace(). Lorsque vous modifiez un élément existant,
il gardera toujours son espace de noms d'origine. Lorsque vous ajoutez un élément, il utilisera
l'espace de noms par défaut si vous ne spécifiez pas explicitement un autre espace de noms.
Exemple 337. Créer une entrée Atom dont les éléments appartiennent à un
espace de noms personnalisé
echo $entree->saveXML();
Voici un exemple d'entrée Atom personnalisée qui gère son propre espace de noms monen.
Notez aussi que la classe se charge d'appeler la méthode registerNamespace() pour que
l'utilisateur n'ait au final pas du tout à se soucier des espaces de noms.
578
Zend_Feed
/** Exemple 338. Étendre la classe représentant les entrées Atom pour ajouter la
gestion
* La classed'un espace de noms
personnalisée personnalisé
connaît automatiquement l'URI du flux
* (qui est optionnelle) et elle peut ajouter automatiquement
* des espaces de noms supplémentaires.
*/
class MonEntree extends Zend_Feed_Entry_Atom
{
default:
return parent::__get($var);
}
}
default:
parent::__set($var, $valeur);
}
}
default:
return parent::__call($var, $unused);
}
Puis }pour= utiliser
$entree cette classe, instanciez-la directement et définissez la propriété
new MonEntree();
}
maMiseAJour :
$entree->maMiseAJour = '2005-04-19T15:30';
579
Zend_Feed
9. Zend_Feed_Reader
9.1. Introduction
Zend_Feed_Reader is a component used to consume RSS and Atom feeds of any version,
including RDF/RSS 1.0, RSS 2.0 and Atom 0.3/1.0. The API for retrieving feed data is deliberately
simple since Zend_Feed_Reader is capable of searching any feed of any type for the
information requested through the API. If the typical elements containing this information are not
present, it will adapt and fall back on a variety of alternative elements instead. This ability to
choose from alternatives removes the need for users to create their own abstraction layer on top
of the component to make it useful or have any in-depth knowledge of the underlying standards,
current alternatives, and namespaced extensions.
Internally, Zend_Feed_Reader works almost entirely on the basis of making XPath queries
against the feed XML's Document Object Model. The DOM is not exposed though a
chained property API like Zend_Feed though the underlying DOMDocument, DOMElement and
DOMXPath objects are exposed for external manipulation. This singular approach to parsing is
consistent and the component offers a plugin system to add to the Feed and Entry level API by
writing Extensions on a similar basis.
Performance is assisted in three ways. First of all, Zend_Feed_Reader supports caching using
Zend_Cache to maintain a copy of the original feed XML. This allows you to skip network
requests for a feed URI if the cache is valid. Second, the Feed and Entry level API is backed
by an internal cache (non-persistant) so repeat API calls for the same feed will avoid additional
DOM/XPath use. Thirdly, importing feeds from a URI can take advantage of HTTP Conditional
GET requests which allow servers to issue an empty 304 response when the requested feed has
not changed since the last time you requested it. In the final case, an instance of Zend_Cache
will hold the last received feed along with the ETag and Last-Modified header values sent in the
HTTP response.
In the following example, we import an RDF/RSS 1.0 feed and extract some basic information
that can be saved to a database or elsewhere.
$feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
$data = array(
'title' => $feed->getTitle(),
'link' => $feed->getLink(),
'dateModified' => $feed->getDateModified(),
'description' => $feed->getDescription(),
580
Zend_Feed
The example above demonstrates Zend_Feed_Reader's API, and it also demonstrates some
of its internal operation. In reality, the RDF feed selected does not have any native date or author
elements, however it does utilise the Dublin Core 1.1 module which offers namespaced creator
and date elements. Zend_Feed_Reader falls back on these and similar options if no relevant
native elements exist. If it absolutely cannot find an alternative it will return NULL, indicating
the information could not be found in the feed. You should note that classes implementing
Zend_Feed_Reader_FeedInterface also implement the SPL Iterator and Countable
interfaces.
Feeds can also be imported from strings, files, and even objects of type Zend_Feed_Abstract.
// from a URI
$feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
// from a String
$feed = Zend_Feed_Reader::importString($feedXmlString);
// from a file
$feed = Zend_Feed_Reader::importFile('./feed.xml');
• saveXml() returns an XML string containing only the element representing the current object.
• getDomDocument() returns the DOMDocument object representing the entire feed (even if
called from an Entry object).
• getElement() returns the DOMElement of the current object (i.e. the Feed or current Entry).
581
Zend_Feed
• getXpath() returns the DOMXPath object for the current feed (even if called from an Entry
object) with the namespaces of the current feed type and all loaded Extensions pre-registered.
• getXpathPrefix() returns the query prefix for the current object (i.e. the Feed or current
Entry) which includes the correct XPath query path for that specific Feed or Entry.
Here's an example where a feed might include an RSS Extension not supported by
Zend_Feed_Reader out of the box. Notably, you could write and register an Extension
(covered later) to do this, but that's not always warranted for a quick check. You must register
any new namespaces on the DOMXPath object before use unless they are registered by
Zend_Feed_Reader or an Extension beforehand.
$feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
$xpathPrefix = $feed->getXpathPrefix();
$xpath = $feed->getXpath();
$xpath->registerNamespace('admin', 'http://webns.net/mvcb/');
$reportErrorsTo = $xpath->evaluate('string('
. $xpathPrefix
. '/admin:errorReportsTo)');
$frontendOptions = array(
'lifetime' => 7200,
'automatic_serialization' => true
);
$backendOptions = array('cache_dir' => './tmp/');
$cache = Zend_Cache::factory(
'Core', 'File', $frontendOptions, $backendOptions
);
Zend_Feed_Reader::setCache($cache);
While it's a little off track, you should also consider adding a cache to
Zend_Loader_PluginLoader which is used by Zend_Feed_Reader to load
Extensions.
582
Zend_Feed
Using this method, you can request feeds from URIs and include their last known ETag and
Last-Modified response header values with the request (using the If-None-Match and If-Modified-
Since headers). If the feed on the server remains unchanged, you should receive a 304 response
which tells Zend_Feed_Reader to use the cached version. If a full feed is sent in a response
with a status code of 200, this means the feed has changed and Zend_Feed_Reader will parse
the new version and save it to the cache. It will also cache the new ETag and Last-Modified
header values for future use.
These "conditional" requests are not guaranteed to be supported by the server you request
a URI of, but can be attempted regardless. Most common feed sources like blogs should
however have this supported. To enable conditional requests, you will need to provide a cache
to Zend_Feed_Reader.
$frontendOptions = array(
'lifetime' => 86400,
'automatic_serialization' => true
);
$backendOptions = array('cache_dir' => './tmp/');
$cache = Zend_Cache::factory(
'Core', 'File', $frontendOptions, $backendOptions
);
Zend_Feed_Reader::setCache($cache);
Zend_Feed_Reader::useHttpConditionalGet();
$feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
In the example above, with HTTP Conditional GET requests enabled, the response header
values for ETag and Last-Modified will be cached along with the feed. For the next 24hrs (the
cache lifetime), feeds will only be updated on the cache if a non-304 response is received
containing a valid RSS or Atom XML document.
If you intend on managing request headers from outside Zend_Feed_Reader, you can set the
relevant If-None-Matches and If-Modified-Since request headers via the URI import method.
$lastEtagReceived = '5e6cefe7df5a7e95c8b1ba1a2ccaff3d';
$lastModifiedDateReceived = 'Wed, 08 Jul 2009 13:37:22 GMT';
$feed = Zend_Feed_Reader::import(
$uri, $lastEtagReceived, $lastModifiedDateReceived
);
This method calls any URI and searches for the location of RSS, RDF and Atom feeds assuming
the website's HTML contains the relevant links. It then returns a value object where you can
check for the existence of a RSS, RDF or Atom feed URI.
583
Zend_Feed
to access all the detected links. However, as a simple shortcut, you can just grab the first RSS,
RDF or Atom link using its public properties as in the example below. Otherwise, each element
of the ArrayObject is a simple array with the keys "type" and "uri" where the type is one of
"rdf", "rss" or "atom".
$links = Zend_Feed_Reader::findFeedLinks('http://www.planet-php.net');
if(isset($links->rdf)) {
echo $links->rdf, "\n"; // http://www.planet-php.org/rdf/
}
if(isset($links->rss)) {
echo $links->rss, "\n"; // http://www.planet-php.org/rss/
}
if(isset($links->atom)) {
echo $links->atom, "\n"; // http://www.planet-php.org/atom/
}
Based on these links, you can then import from whichever source you wish in the usual manner.
This quick method only gives you one link for each feed type, but websites may indicate many
links of any type. Perhaps it's a news site with a RSS feed for each news category. You can
iterate over all links using the ArrayObject's iterator.
$links = Zend_Feed_Reader::findFeedLinks('http://www.planet-php.net');
The main purpose here is to allow the presentation of as much data as possible from the
requested elements, while still allowing access to the most relevant data as a simple array. This
also enforces a standard approach to returning such data which previously may have wandered
between arrays and objects.
The new class type acts identically to ArrayObject with the sole addition being a new method
getValues() which returns a simple flat array containing the most relevant information.
To access category labels by themselves in a simple value array, you might commit to something
like:
584
Zend_Feed
$feed = Zend_Feed_Reader::import('http://www.example.com/atom.xml');
$categories = $feed->getCategories();
$labels = array();
foreach ($categories as $cat) {
$labels[] = $cat['label']
}
It's a contrived example, but the point is that the labels are tied up with other information.
However, the container class allows you to access the "most relevant" data as a simple array
using the getValues() method. The concept of "most relevant" is obviously a judgement call.
For categories it means the category labels (not the terms or schemes) while for authors it would
be the authors' names (not their email addresses or URIs). The simple array is flat (just values)
and passed through array_unique() to remove duplication.
$feed = Zend_Feed_Reader::import('http://www.example.com/atom.xml');
$categories = $feed->getCategories();
$labels = $categories->getValues();
The above example shows how to extract only labels and nothing else thus giving simple access
to the category labels without any additional work to extract that data by itself.
While determining common ground between feed types is itself complex, it should
be noted that RSS in particular is a constantly disputed "specification". This has its
roots in the original RSS 2.0 document which contains ambiguities and does not
detail the correct treatment of all elements. As a result, this component rigorously
applies the RSS 2.0.11 Specification published by the RSS Advisory Board and
its accompanying RSS Best Practices Profile. No other interpretation of RSS 2.0
will be supported though exceptions may be allowed where it does not directly
prevent the application of the two documents mentioned above.
Of course, we don't live in an ideal world so there may be times the API just does not cover what
you're looking for. To assist you, Zend_Feed_Reader offers a plugin system which allows you
to write Extensions to expand the core API and cover any additional data you are trying to extract
from feeds. If writing another Extension is too much trouble, you can simply grab the underlying
DOM or XPath objects and do it by hand in your application. Of course, we really do encourage
writing an Extension simply to make it more portable and reusable, and useful Extensions may
be proposed to the Framework for formal addition.
Here's a summary of the Core API for Feeds. You should note it comprises not only the basic
RSS and Atom standards, but also accounts for a number of included Extensions bundled with
585
Zend_Feed
Zend_Feed_Reader. The naming of these Extension sourced methods remain fairly generic
- all Extension methods operate at the same level as the Core API though we do allow you to
retrieve any specific Extension object separately if required.
586
Zend_Feed
Given the variety of feeds in the wild, some of these methods will undoubtedly return NULL
indicating the relevant information couldn't be located. Where possible, Zend_Feed_Reader
will fall back on alternative elements during its search. For example, searching an RSS feed
for a modification date is more complicated than it looks. RSS 2.0 feeds should include a
<lastBuildDate> tag and/or a <pubDate> element. But what if it doesn't, maybe this is an
RSS 1.0 feed? Perhaps it instead has an <atom:updated> element with identical information
(Atom may be used to supplement RSS's syntax)? Failing that, we could simply look at the
entries, pick the most recent, and use its <pubDate> element. Assuming it exists... Many feeds
also use Dublin Core 1.0/1.1 <dc:date> elements for feeds/entries. Or we could find Atom
lurking again.
The point is, Zend_Feed_Reader was designed to know this. When you ask for the modification
date (or anything else), it will run off and search for all these alternatives until it either gives up
and returns NULL, or finds an alternative that should have the right answer.
In addition to the above methods, all Feed objects implement methods for retrieving the DOM
and XPath objects for the current feeds as described earlier. Feed objects also implement the
SPL Iterator and Countable interfaces. The extended API is summarised below.
587
Zend_Feed
588
Zend_Feed
589
Zend_Feed
The extended API for entries is identical to that for feeds with the exception of the Iterator methods
which are not needed here.
There is often confusion over the concepts of modified and created dates. In
Atom, these are two clearly defined concepts (so knock yourself out) but in RSS
they are vague. RSS 2.0 defines a single <pubDate> element which typically
refers to the date this entry was published, i.e. a creation date of sorts. This
is not always the case, and it may change with updates or not. As a result, if
you really want to check whether an entry has changed, don't rely on the results
of getDateModified(). Instead, consider tracking the MD5 hash of three
other elements concatenated, e.g. using getTitle(), getDescription()
and getContent(). If the entry was truly updated, this hash computation will
give a different result than previously saved hashes for the same entry. This
is obviously content oriented, and will not assist in detecting changes to other
relevant elements. Atom feeds should not require such steps.
Further muddying the waters, dates in feeds may follow different standards. Atom
and Dublin Core dates should follow ISO 8601, and RSS dates should follow RFC
822 or RFC 2822 which is also common. Date methods will throw an exception
if Zend_Date cannot load the date string using one of the above standards, or
the PHP recognised possibilities for RSS dates.
The values returned from these methods are not validated. This means users
must perform validation on all retrieved data including the filtering of any
HTML such as from getContent() before it is output from your application.
Remember that most feeds come from external sources, and therefore the default
assumption should be that they cannot be trusted.
590
Zend_Feed
There are two types of Extensions possible, those which retrieve information from
elements which are immediate children of the root element (e.g. <channel> for RSS
or <feed> for Atom) and those who retrieve information from child elements of an
entry (e.g. <item> for RSS or <entry> for Atom). On the filesystem these are
grouped as classes within a namespace based on the extension standard's name. For
example, internally we have Zend_Feed_Reader_Extension_DublinCore_Feed and
Zend_Feed_Reader_Extension_DublinCore_Entry classes which are two Extensions
implementing Dublin Core 1.0/1.1 support.
DublinCore (Feed and Entry) Implements support for Dublin Core Metadata
Element Set 1.0 and 1.1
Content (Entry only) Implements support for Content 1.0
Atom (Feed and Entry) Implements support for Atom 0.3 and Atom 1.0
Slash Implements support for the Slash RSS 1.0
module
WellFormedWeb Implements support for the Well Formed Web
CommentAPI 1.0
Thread Implements support for Atom Threading
Extensions as described in RFC 4685
Podcast Implements support for the Podcast 1.0 DTD
from Apple
The Core Extensions are somewhat special since they are extremely common and multi-faceted.
For example, we have a Core Extension for Atom. Atom is implemented as an Extension (not
591
Zend_Feed
just a base class) because it doubles as a valid RSS module - you can insert Atom elements
into RSS feeds. I've even seen RDF feeds which use a lot of Atom in place of more common
Extensions like Dublin Core.
The additional non-Core Extensions are offered but not registered to Zend_Feed_Reader by
default. If you want to use them, you'll need to tell Zend_Feed_Reader to load them in advance
of importing a feed. Additional non-Core Extensions will be included in future iterations of the
component.
Zend_Feed_Reader::registerExtension('Syndication');
$feed = Zend_Feed_Reader::import('http://rss.slashdot.org/Slashdot/slashdot');
$updatePeriod = $feed->current()->getUpdatePeriod();
In the simple example above, we checked how frequently a feed is being updated using the
getUpdatePeriod() method. Since it's not part of Zend_Feed_Reader's core API, it could
only be a method supported by the newly registered Syndication Extension.
As you can also notice, the new methods from Extensions are accessible from the main API
using PHP's magic methods. As an alternative, you can also directly access any Extension object
for a similar result as seen below.
Zend_Feed_Reader::registerExtension('Syndication');
$feed = Zend_Feed_Reader::import('http://rss.slashdot.org/Slashdot/slashdot');
$syndication = $feed->getExtension('Syndication');
$updatePeriod = $syndication->getUpdatePeriod();
As an example, let's take the case of a purely fictitious corporation named Jungle Books. Jungle
Books have been publishing a lot of reviews on books they sell (from external sources and
customers), which are distributed as an RSS 2.0 feed. Their marketing department realises that
web applications using this feed cannot currently figure out exactly what book is being reviewed.
To make life easier for everyone, they determine that the geek department needs to extend
RSS 2.0 to include a new element per entry supplying the ISBN-10 or ISBN-13 number of the
publication the entry concerns. They define the new <isbn> element quite simply with a standard
name and namespace URI:
592
Zend_Feed
JungleBooks 1.0:
http://example.com/junglebooks/rss/module/1.0/
A snippet of RSS containing this extension in practice could be something similar to:
Implementing this new ISBN element as a simple entry level extension would require the following
class (using your own class namespace outside of Zend).
class My_FeedReader_Extension_JungleBooks_Entry
extends Zend_Feed_Reader_Extension_EntryAbstract
{
public function getIsbn()
{
if (isset($this->_data['isbn'])) {
return $this->_data['isbn'];
}
$isbn = $this->_xpath->evaluate(
'string(' . $this->getXpathPrefix() . '/jungle:isbn)'
);
if (!$isbn) {
$isbn = null;
}
$this->_data['isbn'] = $isbn;
return $this->_data['isbn'];
}
593
Zend_Feed
This extension is easy enough to follow. It creates a new method getIsbn() which runs an
XPath query on the current entry to extract the ISBN number enclosed by the <jungle:isbn>
element. It can optionally store this to the internal non-persistent cache (no need to keep querying
the DOM if it's called again on the same entry). The value is returned to the caller. At the end
we have a protected method (it's abstract so it must exist) which registers the Jungle Books
namespace for their custom RSS module. While we call this an RSS module, there's nothing to
prevent the same element being used in Atom feeds - and all Extensions which use the prefix
provided by getXpathPrefix() are actually neutral and work on RSS or Atom feeds with no
extra code.
Since this Extension is stored outside of Zend Framework, you'll need to register the path prefix
for your Extensions so Zend_Loader_PluginLoader can find them. After that, it's merely a
matter of registering the Extension, if it's not already loaded, and using it in practice.
if(!Zend_Feed_Reader::isRegistered('JungleBooks')) {
Zend_Feed_Reader::addPrefixPath(
'/path/to/My/FeedReader/Extension', 'My_FeedReader_Extension'
);
Zend_Feed_Reader::registerExtension('JungleBooks');
}
$feed = Zend_Feed_Reader::import('http://example.com/junglebooks/rss');
// ISBN for whatever book the first entry in the feed was concerned with
$firstIsbn = $feed->current()->getIsbn();
Writing a feed level Extension is not much different. The example feed from earlier included
an unmentioned <jungle:dayPopular> element which Jungle Books have added to their
standard to include a link to the day's most popular book (in terms of visitor traffic). Here's an
Extension which adds a getDaysPopularBookLink() method to the feel level API.
class My_FeedReader_Extension_JungleBooks_Feed
extends Zend_Feed_Reader_Extension_FeedAbstract
{
public function getDaysPopularBookLink()
{
if (isset($this->_data['dayPopular'])) {
return $this->_data['dayPopular'];
}
$dayPopular = $this->_xpath->evaluate(
'string(' . $this->getXpathPrefix() . '/jungle:dayPopular)'
);
if (!$dayPopular) {
$dayPopular = null;
}
$this->_data['dayPopular'] = $dayPopular;
return $this->_data['dayPopular'];
}
Let's repeat the last example using a custom Extension to show the method being used.
594
Zend_Feed
if(!Zend_Feed_Reader::isRegistered('JungleBooks')) {
Zend_Feed_Reader::addPrefixPath(
'/path/to/My/FeedReader/Extension', 'My_FeedReader_Extension'
);
Zend_Feed_Reader::registerExtension('JungleBooks');
}
$feed = Zend_Feed_Reader::import('http://example.com/junglebooks/rss');
// URI to the information page of the day's most popular book with visitors
$daysPopularBookLink = $feed->getDaysPopularBookLink();
// ISBN for whatever book the first entry in the feed was concerned with
$firstIsbn = $feed->current()->getIsbn();
Going through these examples, you'll note that we don't register feed and entry Extensions
separately. Extensions within the same standard may or may not include both a feed and
entry class, so Zend_Feed_Reader only requires you to register the overall parent name, e.g.
JungleBooks, DublinCore, Slash. Internally, it can check at what level Extensions exist and load
them up if found. In our case, we have a full set of Extensions now: JungleBooks_Feed and
JungleBooks_Entry.
10. Zend_Feed_Writer
10.1. Introduction
Zend_Feed_Writer is the sibling component to Zend_Feed_Reader responsible for
generating feeds for output. It supports the Atom 1.0 specification (RFC 4287) and RSS 2.0 as
specified by the RSS Advisory Board (RSS 2.0.11). It does not deviate from these standards. It
does, however, offer a simple Extension system which allows for any extension/module for either
of these two specifications to be implemented if they are not provided out of the box.
As a result of this design, the rest may even be obvious. Behind the scenes, data set on any
Zend_Feed_Reader object is translated at render time onto a DOMDocument object using the
necessary feed elements. For each supported feed type there is both an Atom 1.0 and RSS 2.0
renderer. Using a DOMDocument rather a templating solution has numerous advantages, the
most obvious being the ability to export the DOMDocument for additional processing and relying
on PHP DOM for correct and valid rendering.
10.2. Architecture
The architecture of Zend_Feed_Writer is very simple. It has two core sets of classes:
containers and renderers.
595
Zend_Feed
contain methods to allow for fast rendering and export of the final feed, and these can be reused
at will.
While there are two data containers, there are four renderers - two matching container renderers
per support feed type. The renderer accept a container, and based on its content attempt to
generate a valid feed. If the renderer is unable to generate a valid feed, perhaps due to the
container missing an obligatory data point, it will report this by throwing an Exception. While
it is possible to ignore Exceptions, this removes the default safeguard of ensuring you have
sufficient data set to render a wholly valid feed.
Due to the system being divided between data containers and renderers, it can make Extensions
somewhat interesting. A typical Extension offering namespaced feed and entry level elements,
must itself reflect the exact same architecture, i.e. offer feed and entry level data containers,
and matching renderers. There is, fortunately, no complex integration work required since all
Extension classes are simply registered and automatically used by the core classes. We'll meet
Extensions in more detail at the end of this section.
/**
* Create the parent feed
*/
$feed = new Zend_Feed_Writer_Feed;
$feed->setTitle('Paddy\'s Blog');
$feed->setLink('http://www.example.com');
$feed->setFeedLink('http://www.example.com/atom', 'atom');
$feed->addAuthor(array(
'name' => 'Paddy',
'email' => 'paddy@example.com',
'uri' => 'http://www.example.com',
));
$feed->setDateModified(time());
$feed->addHub('http://pubsubhubbub.appspot.com/');
/**
* Add one or more entries. Note that entries must
* be manually added once created.
*/
$entry = $feed->createEntry();
$entry->setTitle('All Your Base Are Belong To Us');
$entry->setLink('http://www.example.com/all-your-base-are-belong-to-us');
$entry->addAuthor(array(
'name' => 'Paddy',
'email' => 'paddy@example.com',
'uri' => 'http://www.example.com',
));
$entry->setDateModified(time());
$entry->setDateCreated(time());
$entry->setDescription('Exposing the difficultly of porting games to English.');
$entry->setContent('I am not writing the article. The example is long enough as is ;).');
$feed->addEntry($entry);
/**
* Render the resulting feed to Atom 1.0 and assign to $out.
* You can substitute "atom" with "rss" to generate an RSS 2.0 feed.
596
Zend_Feed
*/
$out = $feed->export('atom');
This is a perfectly valid Atom 1.0 example. It should be noted that omitting an obligatory point
of data, such as a title, will trigger an Exception when rendering as Atom 1.0. This will differ
for RSS 2.0 since a title may be omitted so long as a description is present. This gives rise to
Exceptions that differ between the two standards depending on the renderer in use. By design,
Zend_Feed_Writer will not render an invalid feed for either standard unless the end-user
deliberately elects to ignore all Exceptions.
Zend_Feed_Writer offers this API via its data container classes Zend_Feed_Writer_Feed
and Zend_Feed_Writer_Entry. These classes merely store all feed data in type-agnostic
597
Zend_Feed
manner, meaning you may reuse any data container with any renderer without requiring
additional work. Both classes are also amenable to Extensions, meaning that an Extension
may define its own container classes which are registered to the base container classes as
extensions, and are checked when any method call triggers the base container's __call()
method.
Here's a summary of the Core API for Feeds. You should note it comprises not only the basic
RSS and Atom standards, but also accounts for a number of included Extensions bundled with
Zend_Feed_Writer. The naming of these Extension sourced methods remain fairly generic
- all Extension methods operate at the same level as the Core API though we do allow you to
retrieve any specific Extension object separately if required.
598
Zend_Feed
599
Zend_Feed
600
Zend_Feed
11. Zend_Feed_Pubsubhubbub
Zend_Feed_Pubsubhubbub is an implementation of the PubSubHubbub Core 0.2 Specification
(Working Draft). It offers implementations of a Pubsubhubbub Publisher and Subscriber suited
to Zend Framework and other PHP applications.
The protocol does not exist in isolation. Pubsub systems have been around for a while, such
as the familiar Jabber Publish-Subscribe protocol, XEP-0060, or the less well known rssCloud
(described in 2001). However these have not achieved widespread adoption typically due to
either their complexity, poor timing or lack of suitability for web applications. rssCloud, which was
recently revived as a response to the appearance of Pubsubhubbub, has also seen its usage
601
Zend_Feed
increase significantly though it lacks a formal specification and currently does not support Atom
1.0 feeds.
Perhaps surprisingly given its relative early age, Pubsubhubbub is already in use including in
Google Reader, Feedburner, and there are plugins available for Wordpress blogs.
11.2. Architecture
Zend_Feed_Pubsubhubbub implements two sides of the Pubsubhubbub 0.2 Specification: a
Publisher and a Subscriber. It does not currently implement a Hub Server though this is in
progress for a future Zend Framework release.
A Publisher is responsible for notifying all supported Hubs (many can be supported to add
redundancy to the system) of any updates to its feeds, whether they be Atom or RSS based.
This is achieved by pinging the supported Hub Servers with the URL of the updated feed. In
Pubsubhubbub terminology, any updatable resource capable of being subscribed to is referred
to as a Topic. Once a ping is received, the Hub will request the updated feed, process it for
updated items, and forward all updates to all Subscribers subscribed to that feed.
A Subscriber is any party or application which subscribes to one or more Hubs to receive
updates from a Topic hosted by a Publisher. The Subscriber never directly communicates with the
Publisher since the Hub acts as an intermediary, accepting subscriptions and sending updates
to subscribed Subscribers. The Subscriber therefore communicates only with the Hub, either to
subscribe/unsubscribe to Topics, or when it receives updates from the Hub. This communication
design ("Fat Pings") effectively removes the possibility of a "Thundering Herd" issue. This occurs
in a pubsub system where the Hub merely informs Subscribers that an update is available,
prompting all Subscribers to immediately retrieve the feed from the Publisher giving rise to a traffic
spike. In Pubsubhubbub, the Hub distributes the actual update in a "Fat Ping" so the Publisher
is not subjected to any traffic spike.
11.3. Zend_Feed_Pubsubhubbub_Publisher
In Pubsubhubbub, the Publisher is the party who publishes a live feed and frequently updates it
with new content. This may be a blog, an aggregator, or even a web service with a public feed
based API. In order for these updates to be pushed to Subscribers, the Publisher must notify all
of its supported Hubs that an update has occured using a simple HTTP POST request containing
the URI or the updated Topic (i.e the updated RSS or Atom feed). The Hub will confirm receipt of
the notification, fetch the updated feed, and forward any updates to any Subscribers who have
subscribed to that Hub for updates from the relevant feed.
By design, this means the Publisher has very little to do except send these Hub pings whenever
its feeds change. As a result, the Publisher implementation is extremely simple to use and
requires very little work to setup and use when feeds are updated.
602
Zend_Feed
The following example shows a Publisher notifying a collection of Hubs about updates to a pair of
local RSS and Atom feeds. The class retains a collection of errors which include the Hub URLs,
so the notification can be re-attempted later and/or logged if any notifications happen to fail. Each
resulting error array also includes a "response" key containing the related HTTP response object.
In the event of any errors, it is strongly recommended to attempt the operation for failed Hub
Endpoints at least once more at a future time. This may require the use of either a scheduled
task for this purpose or a job queue though such extra steps are optional.
if (!$publisher->isSuccess()) {
// check for errors
$errors = $publisher->getErrors();
$failedHubs = array()
foreach ($errors as $error) {
$failedHubs[] = $error['hubUrl'];
}
}
If you prefer having more concrete control over the Publisher, the methods
addHubUrls() and addUpdatedTopicUrls() pass each array value to the singular
addHubUrl() and addUpdatedTopicUrl() public methods. There are also matching
removeUpdatedTopicUrl() and removeHubUrl() methods.
You can also skip setting Hub URIs, and notify each in turn using the notifyHub() method
which accepts the URI of a Hub endpoint as its only argument.
There are no other tasks to cover. The Publisher implementation is very simple since most of the
feed processing and distribution is handled by the selected Hubs. It is however important to detect
errors and reschedule notifications as soon as possible (with a reasonable maximum number of
retries) to ensure notifications reach all Subscribers. In many cases as a final alternative, Hubs
may frequently poll your feeds to offer some additional tolerance for failures both in terms of their
own temporary downtime or Publisher errors/downtime.
11.4. Zend_Feed_Pubsubhubbub_Subscriber
In Pubsubhubbub, the Subscriber is the party who wishes to receive updates to any Topic (RSS or
Atom feed). They achieve this by subscribing to one or more of the Hubs advertised by that Topic,
usually as a set of one or more Atom 1.0 links with a rel attribute of "hub". The Hub from that point
forward will send an Atom or RSS feed containing all updates to that Subscriber's Callback URL
when it receives an update notification from the Publisher. In this way, the Subscriber need never
actually visit the original feed (though it's still recommended at some level to ensure updates
are retrieved if ever a Hub goes offline). All subscription requests must contain the URI of the
Topic being subscribed and a Callback URL which the Hub will use to confirm the subscription
and to forward updates.
603
Zend_Feed
The Subsciber therefore has two roles. To create and manage subscriptions, including
subscribing for new Topics with a Hub, unsubscribing (if necessary), and periodically renewing
subscriptions since they may have a limited validity as set by the Hub. This is handled by
Zend_Feed_Pubsubhubbub_Subscriber.
The second role is to accept updates sent by a Hub to the Subscriber's Callback URL, i.e. the
URI the Subscriber has assigned to handle updates. The Callback URL also handles events
where the Hub contacts the Subscriber to confirm all subscriptions and unsubscriptions. This is
handled by using an instance of Zend_Feed_Pubsubhubbub_Subscriber_Callback when
the Callback URL is accessed.
Any subscription (or unsubscription) requires the relevant information before proceeding,
i.e. the URI of the Topic (Atom or RSS feed) to be subscribed to for updates, and the
URI of the endpoint for the Hub which will handle the subscription and forwarding of the
updates. The lifetime of a subscription may be determined by the Hub but most Hubs should
support automatic subscription refreshes by checking with the Subscriber. This is supported
by Zend_Feed_Pubsubhubbub_Subscriber_Callback and requires no other work on your
part. It is still strongly recommended that you use the Hub sourced subscription time to live
(ttl) to schedule the creation of new subscriptions (the process is identical to that for any new
subscription) to refresh it with the Hub. While it should not be necessary per se, it covers cases
where a Hub may not support automatic subscription refreshing and rules out Hub errors for
additional redundancy.
With the relevant information to hand, a subscription can be attempted as demonstrated below:
In order to store subscriptions and offer access to this data for general use, the component
requires a database (a schema is provided later in this section). By default, it is assumed
the table name is "subscription" and it utilises Zend_Db_Table_Abstract in the background
604
Zend_Feed
meaning it will use the default adapter you have set for your application. You may also
pass a specific custom Zend_Db_Table_Abstract instance into the associated model
Zend_Feed_Pubsubhubbub_Model_Subscription. This custom adapter may be as simple
in intent as changing the table name to use or as complex as you deem necessary.
While this Model is offered as a default ready-to-roll solution, you may create your own Model
using any other backend or database layer (e.g. Doctrine) so long as the resulting class
implements the interface Zend_Feed_Pubsubhubbub_Model_SubscriptionInterface.
An example schema (MySQL) for a subscription table accessible by the provided model may
look similar to:
Behind the scenes, the Subscriber above will send a request to the Hub endpoint containing the
following parameters (based on the previous example):
605
Zend_Feed
606
Zend_Feed
While Hubs may require the use of a specific verification mode (both
are supported by Zend_Pubsubhubbub), you may indicate a specific
preference using the setPreferredVerificationMode() method. In
"sync" (synchronous) mode, the Hub attempts to confirm a subscription as
soon as it is received, and before responding to the subscription request. In
"async" (asynchronous) mode, the Hub will return a response to the subscription
request immediately, and its verification request may occur at a later time.
Since Zend_Pubsubhubbub implements the Subscriber verification role as a
separate callback class and requires the use of a backend storage medium, it
actually supports both transparently though in terms of end-user performance,
asynchronous verification is very much preferred to eliminate the impact of a
poorly performing Hub tying up end-user server resources and connections for
too long.
Unsubscribing from a Topic follows the exact same pattern as the previous example, with
the exception that we should call unsubscribeAll() instead. The parameters included are
identical to a subscription request with the exception that "hub.mode" is set to "unsubscribe".
607
Zend_Feed
defaults are not apt either by passing in a new Model supporting the required interface or by
passing a new instance of Zend_Db_Table_Abstract to the default Model's constructor to
change the used table name.
Whenever a subscription or unsubscription request is made, the Hub must verify the request by
forwarding a new verification request to the Callback URL set in the subscription/unsubscription
parameters. To handle these Hub requests, which will include all future communications
containing Topic (feed) updates, the Callback URL should trigger the execution of an instance
of Zend_Pubsubhubbub_Subscriber_Callback to handle the request.
The Callback class should be configured to use the same storage medium as the Subscriber
class. Using it is quite simple since most of its work is performed internally.
/**
* Check if the callback resulting in the receipt of a feed update.
* Otherwise it was either a (un)sub verification request or invalid request.
* Typically we need do nothing other than add feed update handling - the rest
* is handled internally by the class.
*/
if ($callback->hasFeedUpdate()) {
$feedString = $callback->getFeedUpdate();
/**
* Process the feed update asynchronously to avoid a Hub timeout.
*/
}
It is essential that developers recognise that Hubs are only concerned with
sending requests and receiving a response which verifies its receipt. If a feed
update is received, it should never be processed on the spot since this leaves
the Hub waiting for a response. Rather, any processing should be offloaded
to another process or deferred until after a response has been returned to the
Hub. One symptom of a failure to promptly complete Hub requests is that a Hub
may continue to attempt delivery of the update/verification request leading to
duplicated update attempts being processed by the Subscriber. This appears
problematic - but in reality a Hub may apply a timeout of just a few seconds,
and if no response is received within that time it may disconnect (assuming
a delivery failure) and retry later. Note that Hubs are expected to distribute
vast volumes of updates so their resources are stretched - please do process
608
Zend_Feed
Since the query string method is the default in anticipation of a greater level of future support for
the full 0.2 specification, this requires some additional work to implement.
/**
* Check if the callback resulting in the receipt of a feed update.
* Otherwise it was either a (un)sub verification request or invalid request.
* Typically we need do nothing other than add feed update handling - the rest
* is handled internally by the class.
*/
if ($callback->hasFeedUpdate()) {
$feedString = $callback->getFeedUpdate();
/**
* Process the feed update asynchronously to avoid a Hub timeout.
*/
}
}
609
Zend_Feed
Actually adding the route which would map the path-appended key to a parameter for retrieval
from a controller can be accomplished using a Route configuration such as the INI formatted
example below for use with Zend_Application bootstrapping.
610
Zend_File
1. Zend_File_Transfer
Zend_File_Transfer permet aux développeurs de contrôler l'upload de fichiers mais aussi le
téléchargement. Il vous permet d'utiliser les validateurs incorporés pour le traitement de fichier et
même la possibilité de changer les fichiers avec des filtres. Zend_File_Transfer fonctionne
avec des adaptateurs ce qui vous permet d'utiliser la même API pour différents protocoles de
transfert HTTP, FTP, WEBDAV et plus encore.
Limitation
Formulaires
Quand vous utilisez Zend_Form vous devriez lire et suivre les exemples
décrits dans le chapitre Zend_Form et ne pas utiliser manuellement
Zend_File_Transfer. Mais les informations que vous pourrez lire dans le
présent chapitre vous seront malgré tout utile, même si vous ne l'utilisez pas
directement. Toutes les considérations, descriptions et solutions sont les mêmes
quand vous utilisez Zend_File_Transfer au travers de Zend_Form.
611
Zend_File
Notez que vous devriez utiliser Zend_Form_Element_File par simplicité plutôt que de créer
le HTML manuellement.
L'étape suivante est de créer le récepteur de l'upload. Dans notre exemple le récepteur est
"/file/upload". Donc nous créons le contrôleur file avec l'action upload.
$adapter->setDestination('C:\temp');
if (!$adapter->receive()) {
$messages = $adapter->getMessages();
echo implode("\n", $messages);
}
Comme vous le voyez, l'utilisation la plus simple est de définir une destination avec la
méthode setDestination et d'appeler la méthode receive(). S'il apparaît des erreurs
au cours de l'upload, alors vous les récupérerez grâce à une exception qui sera retournée.
Attention
Maintenez à l'esprit qu'il s'agit de l'utilisation la plus simple. Vous ne devez jamais
utiliser cet exemple en environnement de production car il causerait de graves
problèmes de sécurité. Vous devez toujours utiliser des validateurs pour accroître
la sécurité.
• ignoreNoFile : si cette option vaut TRUE, tous les validateurs ignoreront le fait que le fichier
à été uploadé ou non par le formulaire. Cette option vaut par défaut FALSE, ce qui lance une
erreur indiquant que le fichier n'a pas été fourni.
612
Zend_File
• isReceived($files = null) : cette méthode vérifie si le(s) fichier(s) fourni(s) a (ont) bien
été reçu(s). Si aucun fichier n'est fourni, tous les fichiers seront vérifiés.
$upload->receive();
• getFileSize($file = null) : cette méthode retourne la taille réelle d'un fichier transféré
donné.
613
Zend_File
• getMimeType($files = null) : cette méthode retourne le type MIME d'un fichier transféré
donné.
getFileName() accepte le nom d'un élément entant que premier paramètre. Si aucun n'est
fourni, tous les fichiers connus seront retournées sous la forme d'un tableau. Si le fichier est un
"multifile", vous le récupérerez aussi sous la forme d'un tableau. S'il n'y a qu'un seul fichier alors
une chaîne sera retournée.
Par défaut les noms de fichier sont retournés avec leur chemin d'accès complet. Si vous
souhaitez seulement le nom de fichier sans le chemin, vous pouvez paramétrer le second
paramètre $path à FALSE ce qui tronquera le chemin.
Notez que le nom de fichier peut changer quand vous recevez le fichier. Ceci
est du au fait qu'après la réception, tous les filtres sot appliqués. Donc vous ne
devriez appeler getFileName() qu'après avoir reçu les fichiers.
getFileSize() retourne par défaut la taille réelle d'un fichier en notation SI ce qui signifie
que vous récupérerez 2kB au lieu de 2048. Si vous voulez la taille brute, utilisez l'option
useByteString à FALSE.
Note that the filesize which is given by the client is not seen as save input.
Therefor the real size of the file will be detected and returned instead of the filesize
sent by the client.
getHash() accepte le nom de l'algorithme de hachage en tant que premier paramètre. Pour
avoir une liste des algorithmes connus, regardez la méthode hash_algos de PHP. Si vous ne
fournissez aucun algorithme, celui par défaut sera crc32.
614
Zend_File
Valeur retournée
Notez que si un fichier ou un élément de formulaire donné contient plus d'un
fichier, la valeur retournée sera un tableau.
getMimeType() retourne le type MIME d'un fichier. Si plus d'un fichier a été uploadé, elle
retourne un tableau, sinon c'est une chaîne.
$mime = $upload->getMimeType();
Exception possible
Notez que cette méthode utilise l'extension fileinfo si elle est disponible. Si elle
n'est pas trouvé, elle utilise l'extension mimemagic. Quand aucune extension
n'est fournie, une exception est levée.
615
Zend_File
or the uploadprogress extension. Both extensions are detected and used automatically. To
be able to get the progress you need to meet some prerequisites.
First, you need to have either APC or uploadprogress to be enabled. Note that you can disable
this feature of APC within your php.ini.
Second, you need to have the proper hidden fields added in the form which sends the files. When
you use Zend_Form_Element_File this hidden fields are automatically added by Zend_Form.
When the above two points are provided then you are able to get the actual progress of the file
upload by using the getProgress method. Actually there are 2 official ways to handle this.
Exemple 345. Using the progressbar adapter to retrieve the actual state
$upload = null;
while (!$upload['done']) {
$upload = Zend_File_Transfer_Adapter_Http:getProgress($upload);
}
Call getProgress() without settings. It will return you an array with several keys. They differ
according to the used PHP extension. But the following keys are given independently of the
extension:
• id: The ID of this upload. This ID identifies the upload within the extension. It is filled
automatically. You should never change or give this value yourself.
• done: Returns true when the upload is finished and false otherwise.
• message: The actual message. Eighter the progress as text in the form 10kB / 200kB, or
a helpful message in the case of a problem. Problems could be, that there is no upload in
progress, that there was a failure while retrieving the data for the progress, or that the upload
has been canceled.
616
Zend_File
• session: This optional key takes the name of a session namespace which will
be used within Zend_ProgressBar. When this key is not given it defaults to
Zend_File_Transfer_Adapter_Http_ProgressBar.
All other returned keys are provided directly from the extensions and will not be checked.
$upload = Zend_File_Transfer_Adapter_Http::getProgress();
while (!$upload['done']) {
$upload = Zend_File_Transfer_Adapter_Http:getProgress($upload);
print "\nActual progress:".$upload['message'];
// do whatever you need
}
• Crc32 : ce validateur vérifie la valeur de hachage crc32 du contenu d'un fichier. Il est basé
sur le validateur Hash et en simplifiant son utilisation par le support unique du Crc32.
• ExcludeExtension : ce validateur vérifie l'extension des fichiers. Il lancera une erreur quand
un fichier aura une extension non souhaitée. Ainsi vous pouvez empêcher la validation de
certaines extensions.
• ExcludeMimeType : ce validateur vérifie le type MIME des fichiers. Il est aussi capable de
valider un groupe de type MIME et générera une erreur quand le type MIME d'un fichier donné
correspond.
• Exists : ce validateur vérifie l'existence des fichiers. Il lancera une erreur quand un fichier
n'existera pas.
• Extension : ce validateur vérifie l'extension des fichiers. Il lancera une erreur quand un fichier
n'aura pas l'extension définie.
• FilesSize : ce validateur vérifie la taille complète de tous les fichiers à valider. Il conserve en
mémoire la taille de tous les fichiers chargés et lance une erreur quand la somme de tous les
fichiers dépasse la taille définie. Il est aussi possible de définir une taille minimum et maximum.
• ImageSize : ce validateur vérifie la taille des images. Il valide la largeur et la hauteur et permet
de paramétrer à la fois une valeur minimum et maximum.
617
Zend_File
• IsCompressed : ce validateur vérifie si le fichier est compressé. Il est basé sur le validateur
MimeType et valide les archives compressées comme zip ou arc. Vous pouvez aussi limiter
à des types d'archives particuliers.
• IsImage : ce validateur vérifie si un fichier est une image. Il est basé sur le validateur
MimeType et valide les images comme jpg ou gif. Vous pouvez aussi limiter à des types
d'images particuliers.
• Hash : ce validateur vérifie la valeur de hachage md5 du contenu d'un fichier. Il supporte de
multiples algorithmes.
• Md5 : ce validateur vérifie la valeur de hachage md5 du contenu d'un fichier. Il est basé sur le
validateur Hash et en simplifiant son utilisation par le support unique du Md5.
• MimeType : ce validateur vérifie le type MIME des fichiers. Il est aussi capable de valider des
groupes de type MIME et de générer une erreur quand le type MIME d'un fichier donné ne
correspond pas.
• NotExists : ce validateur vérifie l'existence des fichiers. Il lancera une erreur quand un fichier
existera déjà.
• Sha1 : ce validateur vérifie la valeur de hachage sha1 du contenu d'un fichier. Il est basé sur
le validateur Hash et en simplifiant son utilisation par le support unique du Sha1.
• Size : ce validateur permet de valider la taille d'un fichier en particulier. Il est possible de
définir un minimum et un maximum et une erreur sera lancée s'ils sont dépassés.
• Upload : ce validateur est interne, il vérifie si l'upload a produit une erreur. Vous ne devez
pas le paramétrer, il est automatiquement activé par Zend_File_Transfer lui-même. Vous
pouvez donc oublier ce validateur. Il faut juste savoir qu'il existe.
• isValid($files = null) : vérifie le(s) fichier(s) fourni(s) avec tout le jeu de validateurs
paramétrés. $files peut être soit un vrai nom de fichier, soit des noms d'éléments de
formulaire ou des noms de fichiers temporaires.
618
Zend_File
Généralement vous devriez simplement utiliser la méthode addValidators(), qui peut être
appelée plusieurs fois.
Souvent il est plus simple d'appeler plusieurs fois addValidator() : un appel pour chaque
validateur. Ceci améliore aussi la lisibilité et rend votre code plus maintenable. Comme
toutes les méthodes fournissent un interface fluide, vous pouvez enchaîner les appels
comme montré ci-dessous :
619
Zend_File
Notez que même si l'ajout du même validateur plusieurs fois est autorisé, faire
ceci peut entraîner des problèmes si vous utilisez différentes options pour le
même validateur.
Et pour finir vous pouvez tout simplement vérifier le(s) fichier(s) en appelant isValid().
if ($upload->isValid()) {
print "Echec de validation";
}
Notez que isValid() sera automatiquement appelé quand vous recevez les
fichiers et qu'il n'a pas été appelé auparavant.
Quand une validation a échouée, c'est probablement intéressant d'avoir des informations sur
les problèmes rencontrés. A cette fin, vous pouvez utiliser la méthode getMessages() qui
retourne tous les messages de validation sous la forme d'un tableau, getErrors() qui retourne
tous les codes d'erreurs et hasErrors() qui retourne TRUE dès qu'une erreur de validation
est rencontrée.
Attention : quand vous utilisez cette option vous devez donner le nombre
minimum au moment où vous appelez ce validateur la première fois ; sinon
vous aurez une erreur en retour.
Avec cette option vous pouvez définir le nombre de fichiers que vous souhaitez recevoir.
Avec cette option vous pouvez limiter le nombre de fichiers que vous acceptez mais vous
permet aussi de détecter une possible attaque quand plus de fichiers, que votre formulaire
n'en définit, sont fournis.
Vous pouvez initialiser ce validateur avec une chaîne ou un entier, la valeur sera utilisée en tant
que max. Mais vous pouvez aussi utiliser les méthodes setMin() et setMax() pour paramétrer
ces options plus tard et getMin() et getMax() pour les récupérer.
620
Zend_File
• * : vous pouvez paramétrer n'importe quelle clé ou utiliser un tableau numérique. Les valeurs
seront utilisées pour vérifier la valeur de hachage.
Vous pouvez paramétrer de multiples hachages en utilisant différentes clés. Chacun sera
vérifié et seulement si tous échouent, la validation elle-même échouera.
• * : vous pouvez paramétrer n'importe quelle clé ou utiliser un tableau numérique. Les valeurs
seront utilisées en tant qu'extensions à vérifier que le fichier n'utilise pas.
• case : paramètre une validation qui tient compte de la casse. Par défaut, ce n'est pas sensible
à la casse. Notez que cette clé est utilisée pour toutes les extensions.
Ce validateur accepte des extensions multiples soit sous la forme d'une chaîne utilisant
le caractère virgule (",") comme séparateur ou sous la forme d'un tableau. Vous pouvez
aussi utiliser les méthodes setExtension(), addExtension(), et getExtension() pour
paramétrer et récupérer les extensions.
Dans certains cas, il est utile vérifier aussi la casse. A cette fin le constructeur autorise un second
paramètre $case qui, s'il est réglé à TRUE, validera l'extension en vérifiant aussi la casse.
621
Zend_File
• * : vous pouvez paramétrer n'importe quelle clé ou utiliser un tableau numérique. Paramètre
le type MIME à vérifier.
Avec cette option vous pouvez définir le(s) type(s) MIME que vous souhaitez exclure.
Ce validateur accepte des types MIME multiples soit sous la forme d'une chaîne utilisant le
caractère virgule (",") comme séparateur ou sous la forme d'un tableau. Vous pouvez aussi
utiliser les méthodes setMimeType(), addMimeType(), et getMimeType() pour paramétrer
et récupérer les types MIME.
// Refuser le type MIME d'image gif et jpg pour tous les fichiers
$upload->addValidator('ExcludeMimeType', false, array('image/gif', 'image/jpeg');
// Refuser les types MIME du groupe image pour tous les fichiers
$upload->addValidator('ExcludeMimeType', false, 'image');
L'exemple ci-dessus montre qu'il est aussi possible de limiter le type MIME accepté à un groupe
de type MIME. Pour refuser toutes les images utilisez simplement "image" en tant que type MIME.
Ceci peut être appliqué à tous les groupes de type MIME comme "image", "audio", "video", "text"
et plus encore.
622
Zend_File
Notez que refuser un groupe de type MIME refusera tous les membres de ce
groupe même si ce n'est pas votre intention. Par exemple quand vous refusez
"image", vous refusez donc "image/jpeg" ou "image/vasa". Quand vous n'êtes
pas sûr de vouloir refuser tous les types, vous devriez définir individuellement
les types MIME plutôt que le groupe complet.
• * : vous pouvez paramétrer n'importe quelle clé ou utiliser un tableau numérique. Vérifie si le
fichier existe dans le dossier fourni.
Ce validateur accepte des extensions multiples soit sous la forme d'une chaîne utilisant
le caractère virgule (",") comme séparateur ou sous la forme d'un tableau. Vous pouvez
aussi utiliser les méthodes setDirectory(), addDirectory(), et getDirectory() pour
paramétrer et récupérer les extensions.
Notez que ce validateur vérifie si le fichier existe dans tous les dossiers fournis.
La validation échoue si le fichier est manquant dans l'un des dossiers.
• * : vous pouvez paramétrer n'importe quelle clé ou utiliser un tableau numérique. Paramètre
l'extension à vérifier.
• case : paramètre une validation sensible à la casse. Par défaut, la validation n'est pas sensible
à la casse. Notez que cette clé est utilisée pour toutes les extensions.
Ce validateur accepte des extensions multiples soit sous la forme d'une chaîne utilisant
le caractère virgule (",") comme séparateur ou sous la forme d'un tableau. Vous pouvez
aussi utiliser les méthodes setExtension(), addExtension(), et getExtension() pour
paramétrer et récupérer les extensions.
Dans certains cas, il est utile vérifier aussi la casse. A cette fin le constructeur autorise un second
paramètre $case qui, s'il est réglé à TRUE, validera l'extension en vérifiant aussi la casse.
623
Zend_File
Avec cette option vous pouvez définir le poids minimum de tous les fichiers que vous souhaitez
transférer.
Avec cette option vous pouvez limiter le poids total des fichiers qui doivent être transférés,
mais pas la taille individuelle de chaque fichier.
• bytestring : définit si un échec est retourné avec un taille plus facilement lisible pour
l'utilisateur, ou avec une taille de fichier brute.
Avec cette option vous pouvez en fait définir si l'utilisateur récupérera "10864" ou "10MB". La
valeur par défaut est TRUE qui retournera "10MB".
Vous pouvez initialiser seulement avec une chaîne qui sera utilisée en tant que max. Mais vous
pouvez aussi utiliser les méthodes setMin() et setMax() pour paramétrer ces options plus
tard et getMin() et getMax() pour les récupérer.
La taille elle-même est acceptée en notation SI comme sur la plupart des systèmes d'exploitation.
Au lieu de 20000 octets (NdT. : "bytes" en anglais), vous pouvez utiliser 20kB. Toutes les unités
sont converties en utilisant 1024 comme valeur de base. Les unités suivantes sont acceptées :
kB, MB, GB, TB, PB et EB. Comme mentionné précédemment vous devez noter que 1kB équivaut
à 1024 octets.
624
Zend_File
// Limite la taille de tous les fichiers dans une plage de 10kB à 4MB
$upload->addValidator('FilesSize', false, array('min' => '10kB',
'max' => '4MB'));
// Comme ci-dessus, mais retourne la taille de fichier brute plutôt qu'une chaîne
$upload->addValidator('FilesSize', false, array('min' => '10kB',
'max' => '4MB',
'bytestring' => false));
Notez que ce validateur stocke le poids des fichiers vérifiés en interne. Le fichier
qui excédera le poids maximum sera retourné en tant qu'erreur.
Vous pouvez aussi utiliser les méthodes setImageMin() et setImageMax() pour régler les
valeurs minimum et maximum plus tard et getMin() et getMax() pour les récupérer.
625
Zend_File
Notez qu'il n'y a pas de vérification si vous paramétrez un type de fichier qui n'est
pas un type de compression. Ainsi il est donc possible de définir que les fichiers
gif sont acceptés par ce validateur même si ce n'est pas logique.
Notez qu'il n'y a pas de vérification si vous paramétrez un type de fichier qui n'est
pas un type d'image. Ainsi il est donc possible de définir que les fichiers zip sont
acceptés par ce validateur même si ce n'est pas logique.
• * : vous pouvez paramétrer n'importe quelle clé ou utiliser un tableau numérique. Paramètre
la valeur de hachage qui doit être vérifié.
626
Zend_File
Vous pouvez paramétrer de multiples hachages en les fournissant sous la forme d'un tableau.
Chacun sera vérifié et seulement si tous échouent, la validation elle-même échouera.
• * : vous pouvez paramétrer n'importe quelle clé ou utiliser un tableau numérique. Paramètre
la valeur de hachage qui doit être vérifié.
Vous pouvez paramétrer de multiples hachages en les fournissant sous la forme d'un tableau.
Chacun sera vérifié et seulement si tous échouent, la validation elle-même échouera.
• * : vous pouvez paramétrer n'importe quelle clé ou utiliser un tableau numérique. Paramètre
le type MIME à contrôler.
Avec cette option vous pouvez définir le type MIME des fichiers qui seront acceptés.
627
Zend_File
Avec cette option vous pouvez définir le magicfile à utiliser. Quand il n'est pas utilisé ou vide,
la constante MAGIC sera utilisée. Cette option est disponible à partir de la version 1.7.1 de
Zend Framework.
Ce validateur accepte des types MIME multiples soit sous la forme d'une chaîne utilisant le
caractère virgule (",") comme séparateur ou sous la forme d'un tableau. Vous pouvez aussi
utiliser les méthodes setMimeType(), addMimeType(), et getMimeType() pour paramétrer
et récupérer les types MIME.
Vous pouvez aussi paramétrer le magicfile qui sera utilisé par fileinfo avec l'option magicfile.
De plus il existe les méthodes setMagicFile() et getMagicFile() qui permettent de
paramétrer ou récupérer plus tard le paramètre magicfile. Ces méthodes sont disponibles à
partir de la version Zend Framework 1.7.1.
// Limite le type MIME de tous les fichiers aux images jpeg et gif
$upload->adValidator('MimeType', false, array('image/gif', 'image/jpeg');
L'exemple ci-dessus montre qu'il est aussi possible de limiter le type MIME accepté à un groupe
de type MIME. Pour autoriser toutes les images utilisez simplement "image" en tant que type
MIME. Ceci peut être appliqué à tous les groupes de type MIME comme "image", "audio", "video",
"text" et plus encore.
628
Zend_File
• * : vous pouvez paramétrer n'importe quelle clé ou utiliser un tableau numérique. Vérifie si le
fichier n'existe pas dans le dossier fourni.
Ce validateur accepte des extensions multiples soit sous la forme d'une chaîne utilisant
le caractère virgule (",") comme séparateur ou sous la forme d'un tableau. Vous pouvez
aussi utiliser les méthodes setDirectory(), addDirectory(), et getDirectory() pour
paramétrer et récupérer les extensions.
Notez que ce validateur vérifie si le fichier n'existe dans aucun des dossiers
fournis. La validation échoue si le fichier existe dans l'un des dossiers.
• * : vous pouvez paramétrer n'importe quelle clé ou utiliser un tableau numérique. Paramètre
la valeur de hachage qui doit être vérifié.
Vous pouvez paramétrer de multiples hachages en les fournissant sous la forme d'un tableau.
Chacun sera vérifié et seulement si tous échouent, la validation elle-même échouera.
629
Zend_File
• bytestring : définit si un échec est retourné avec un taille plus facilement lisible pour
l'utilisateur, ou avec une taille de fichier brute.
Avec cette option vous pouvez en fait définir si l'utilisateur récupérera "10864" ou "10MB". La
valeur par défaut est TRUE qui retournera "10MB".
Vous pouvez initialiser seulement avec une chaîne qui sera utilisée en tant que max. Mais vous
pouvez aussi utiliser les méthodes setMin() et setMax() pour paramétrer ces options plus
tard et getMin() et getMax() pour les récupérer.
Quand seule une chaîne est fournie, elle est utilisée en tant que max. Mais vous pouvez
aussi utiliser les méthodes setMin() et setMax() pour paramétrer ces options plus tard et
getMin() et getMax() pour les récupérer.
La taille elle-même est acceptée en notation SI comme sur la plupart des systèmes d'exploitation.
Au lieu de 20000 octets, vous pouvez utiliser 20kB. Toutes les unités sont converties en utilisant
1024 comme valeur de base. Les unités suivantes sont acceptées : kB, MB, GB, TB, PB et EB.
Comme mentionné précédemment vous devez noter que 1kB équivaut à 1024 octets.
Si vous initialisez ce validateur avec une chaîne ou un entier, la valeur sera utilisée en tant que
max. Mais vous pouvez aussi utiliser les méthodes setMin() et setMax() pour paramétrer
ces options plus tard et getMin() et getMax() pour les récupérer.
630
Zend_File
• Rename : ce filtre peut renommer les fichiers, changer leur localisation et même forcer
l'écrasement de fichiers existants.
• addFilters(array $filters, $files = null) : ajoute les filtres à la pile des filtres.
Chaque entrée peut être soit une paire type/options, ou un tableau avec la clé "filtre" spécifiant
le filtre (tous les autres options seront considérées comme des options du filtre au moment
de l'instanciation).
631
Zend_File
Généralement vous devriez simplement utiliser la méthode addFilters(), qui peut être
appelée plusieurs fois.
Souvent il est plus simple d'appeler plusieurs fois addFilter() : un appel pour chaque
filtre. Ceci améliore aussi la lisibilité et rend votre code plus maintenable. Comme toutes les
méthodes fournissent un interface fluide, vous pouvez enchaîner les appels comme montré
ci-dessous :
Notez que même si l'ajout du même filtre plusieurs fois est autorisé, faire ceci
peut entraîner des problèmes si vous utilisez différentes options pour le même
filtre.
Ce filtre supporte une option additionnelle qui peut être utilisée pour sauvegarder le fichier
décrypté avec un autre nom de fichier. Spécifiez l'option filename pour changer le nom de
fichier dans lequel le fichier décrypté sera stocké. Si vous supprimez cette option le fichier
décrypté écrasera le fichier chiffré original.
632
Zend_File
Ce filtre supporte une option additionnelle qui peut être utilisée pour sauvegarder le fichier chiffré
avec un autre nom de fichier. Spécifiez l'option filename pour changer le nom de fichier dans
lequel le fichier chiffré sera stocké. Si vous supprimez cette option le fichier chiffré écrasera le
fichier original.
Lors de l'initialisation vous pouvez fournir qui sera utilisée en tant qu'encodage. Ou vous pouvez
utiliser la méthode setEncoding() pour le paramétrer plus tard.
633
Zend_File
Notez que les options du filtre LowerCase sont optionnelles, vous devez fournir
un NULL en second paramètre quand vous souhaitez limiter le filtre à un fichier
unique.
• overwrite : paramètre si l'ancien fichier écrase le nouveau fichier s'il existe déjà. La valeur
par défaut est FALSE.
De plus vous pouvez utiliser la méthode setFile() pour paramétrer des fichiers, ce qui effacera
ceux précédemment paramétrés, addFile() pour ajouter un nouveau fichier à ceux déjà
présent, et getFile() pour récupérer les fichiers actuellement paramétrés. Pour simplifier les
choses, ce filtre accepte de multiples notations et ces méthodes et constructeur comprennent
les mêmes notations.
Vous pouvez utiliser différentes notations. Ci-dessous vous trouverez une table fournissant la
description et le but des notations supportées. Notez que quand vous utilisez l'"Adapter" ou le
"Form Element", vous ne pourrez pas utiliser toutes les notations décrites.
notation description
addFile('C:\uploads') Spécifie un nouveau dossier pour tous les
fichiers quand la chaîne est un dossier. Notez
634
Zend_File
notation description
que vous aurez une exception si le fichier
existe déjà, voir le paramètre d'écrasement.
addFile('C:\uploads\file.ext') Spécifie un nouveau dossier et un nom de
fichier pour tous les fichiers quand la chaîne
n'est pas un dossier. Notez que vous aurez
une exception si le fichier existe déjà, voir le
paramètre d'écrasement.
addFile('C:\uploads\file.ext', 'overwrite' => Spécifie un nouveau dossier et un nom
true) de fichier pour tous les fichiers quand la
chaîne n'est pas un dossier et écrase le
fichier existant si celui-ci existe. Notez
que vous aurez pas de notification en cas
d'écrasement.
addFile(array('source' => 'C:\temp\uploads', Spécifie un nouveau dossier pour tous les
'target' => 'C:\uploads')) fichiers qui sont présent dans l'ancien dossier
quand la chaîne est un dossier. Notez que
vous aurez une exception si le fichier existe
déjà, voir le paramètre d'écrasement.
addFile(array('source' => 'C:\temp\uploads', Spécifie un nouveau dossier pour tous
'target' => 'C:\uploads', 'overwrite' => true)) les fichiers qui sont présent dans l'ancien
dossier quand la chaîne est un dossier. Notez
que vous aurez pas de notification en cas
d'écrasement.
Lors de l'initialisation vous pouvez fournir qui sera utilisée en tant qu'encodage. Ou vous pouvez
utiliser la méthode setEncoding() pour le paramétrer plus tard.
Notez que les options du filtre UpperCase sont optionnelles, vous devez fournir
un NULL en second paramètre quand vous souhaitez limiter le filtre à un fichier
unique.
635
Zend_Filter
1. Introduction
Le composant Zend_Filter fournit un ensemble de filtres de données usuel. Il fournit
également un mécanisme simple de chaînage par lequel plusieurs filtres peuvent être appliqués
à une donnée dans un ordre défini.
Cette définition simple d'un filtre peut être étendue pour inclure des transformations généralisées
aux données soumises. Une transformation communément requise dans les applications Web
consiste à échapper les entités HTML. Ainsi, si un champ de formulaire est automatiquement
transmis et contient des données non vérifiées (provenant par exemple d'un navigateur Web),
ces données doivent être purgées de leurs entités HTML ou bien contenir uniquement des
entités HTML échappées, de manière à se garantir contre des comportements non désirés
et les vulnérabilités de sécurité. Afin d'assurer cette opération, les entités HTML qui sont
présentes dans les données saisies doivent être soit supprimées soit échappées. Naturellement,
l'approche adéquate dépend du contexte. Un filtre de suppression des entités HTML opère
dans le contexte défini plus haut : une opération produisant un sous-ensemble à partir d'une
donnée soumise. Cependant, un filtre échappant les entités HTML transforme la valeur entrée
(par exemple, "&" sera transformé en "&". Permettre de telles choses est important pour les
développeurs Web et "filtrer" dans le contexte d'utilisation de Zend_Filter consiste à réaliser
des transformations sur les données soumises.
L'exemple simple ci-dessous démontre l'utilisation d'un filtre sur les caractères esperluette (&,
"ampersand" en anglais) et guillemet double (") :
636
Zend_Filter
Vous pouvez aussi fournir un tableau de paramètres destinés au constructeur de la classe, s'ils
sont nécessaires pour votre classe de filtre.
echo Zend_Filter::filterStatic('"',
'HtmlEntities',
array('quotestyle' => ENT_QUOTES));
L'utilisation statique peut être pratique pour invoquer un filtre ad hoc, mais si vous avez besoin
d'exécuter un filtre pour des saisies multiples, il est plus efficace de suivre le premier exemple
ci-dessus, créant une instance de l'objet de filtre et appelant sa méthode filter().
2.1. Alnum
Retourne la chaîne $value, en retirant tout sauf les caractères alphabétiques et numériques.
Ce filtre inclue une option permettant d'autoriser ou non les caractères espace.
2.2. Alpha
Retourne la chaîne $value, en retirant tout sauf les caractères alphabétiques. Ce filtre inclue
une option permettant d'autoriser ou non les caractères espace.
2.3. BaseName
En passant une chaîne contenant un chemin vers un fichier, ce filtre retourne le nom de base
du fichier.
2.4. Boolean
Ce filtre transforme toute donnée en valeur BOOLEENNE. Ce peut être utile en travaillant avec
des bases de données ou des formulaires.
637
Zend_Filter
Ceci signifie que sans paramètre additionnel, Zend_Filter_Boolean prend toute valeur
d'entrée et retourne un BOOLEEN comme le cast PHP vers le BOOLEEN.
• false_string: Convertit une chaine contenant le mot "false" vers le booléen FALSE.
• yes: Convertit une chaine localisée contenant le mot "no" vers FALSE.
Pour préciser les optiosn ci-dessus, plusieurs manières sont données : utilisez des chaines, des
constantes, ajoutez les, utilisez des tableaux... Voyez l'exemple:
638
Zend_Filter
Vous pouvez aussi passer une instance de Zend_Config pour préciser les options. Pour
préciser ces options après la création de votre objet, utilisez la méthode setType().
// retourne false
echo $filter->filter('nein');
$filter->setLocale('en');
// retourne true
$filter->filter('yes');
Dans un tel cas, Zend_Filter_Boolean fonctionnera comme décrit dans le tableau ci-
dessous qui montre quelles valeurs retournent TRUE ou FALSE. Toute autre valeur non présente
dans ce tableau sera retournée telle quelle lorsque l'option casting est à FALSE
639
Zend_Filter
// retourne false
echo $filter->filter(0);
// retourne true
echo $filter->filter(1);
// retourne la valeur
echo $filter->filter(2);
2.5. Callback
Ce filtre vous permet d'utiliser votre propre fonction en tant que filtre de Zend_Filter. Nul
besoin de créer un filtre si une fonction ou méthode fait déja le travail.
print $filter->filter('Hello!');
// retourne "!olleH"
C'est très simple de passer une fonction à appliquer comme filtre. Dans le cas de méthodes de
classes, passez un tableau comme callback.
// Notre classe
class MyClass
{
public function Reverse($param);
}
// La définition du filtre
$filter = new Zend_Filter_Callback(array('MyClass', 'Reverse'));
print $filter->filter('Hello!');
Pour récupérer la fonction de filtrage actuelle, utilisez getCallback() et pour en affecter une
nouvelle, utilisez setCallback().
Il est aussi possible de définir des paramètres par défaut qui sont alors passés à la méthode
appelée lorsque le filtre est exécuté.
640
Zend_Filter
Notez que passer une fonction qui ne peut être appelée mènera à une exception.
• Bz2
• Gz
• Lzf
• Rar
• Tar
• Zip
Chaque format de compression possède des caractéristiques propres et ils s'utilisent tous d'une
manière commune. Seules leurs options vont différer ainsi que les types de compressions qu'ils
offrent (algorithmes, fichiers, chaines et dossiers)
Par exemple, si nous voulons compresser une chaine nous devons instancier
Zend_Filter_Compress et indiquer un adaptateur.
Il est aussi possible de passer des options sous forme de tableau ou d'objet Zend_Config. Si
vous souhaitez préciser des options, vous devez alors au minimum indiquer la clé "adapter".
Les clés "options" ou "adapterOptions" peuvent ensuite être utilisées et doivent représenter un
tableau.
641
Zend_Filter
Pour récupérer une chaine compressée, il faut indiquer la chaine originale. La valeur "filtrée"
récupérée sera alors la chaine compressée, tout simplement.
Dans l'exemple ci-dessus, la chaine est compressée puis retournée dans une archive.
642
Zend_Filter
Il est aussi possible de préciser le nom d'un dossier plutôt que d'un fichier. Dans ce cas, tout le
dossier sera compressé, récursivement.
Certains adaptateurs permettent la décompression d'une archive dans un dossier cible, dans ce
cas le paramètre target permet de l'indiquer.
643
Zend_Filter
• Chaines
• Fichiers
• Dossiers
• Blocksize: Précise la taille des blocs. Des valeurs de '0' à '9' sont permises. La valeur par
défaut est '4'.
Toutes les options peuvent être passées à l'instanciation ou en utilisant des méthodes. Par
exemple pour la tailles des blocs, getBlocksize() et setBlocksize(). La méthode
setOptions() est aussi présente, elle accepte un tableau
2.6.5. Adaptateur Gz
L'adaptateur Bz2 peut compresser et décompresser:
• Chaines
• Fichiers
• Dossiers
• Level: Niveau de compression. Des valeurs de '0' à '9' sont utilisables, par défaut : '9'.
• Mode: Il existe deux modes supportés : 'compress' et 'deflate'. La valeur par défaut est
'compress'.
Toutes les options peuvent être passées en constructeur ou en utilisant des méthodes. Par
exemple, pour l'option 'Level', getLevel() et setLevel(). La méthode setOptions() est
aussi présente et accepte un tableau.
• Chaines
644
Zend_Filter
• Fichiers
• Dossiers
Toutes les options peuvent être passées au constructeurs ou via des méthodes. Par exemple,
pour l'option 'Target', getTarget() et setTarget().La méthode setOptions() est aussi
disponible et accepte un tableau.
• Fichiers
• Dossiers
645
Zend_Filter
• Mode: Mode de compression. Les modes supportés sont 'null' qui signifie pas de compression,
'Gz' qui utilisera l'extension PHP Zlib et 'Bz2' qui utilisera l'extension PHPBz2. La valeur par
défaut est 'null'.
Toutes les options peuvent être passées au constructeurs ou via des méthodes. Par exemple,
pour l'option 'Target', getTarget() et setTarget().La méthode setOptions() est aussi
disponible et accepte un tableau.
La compression des dossiers avec Tar utilise le chemin complet comme nom de
fichier.
• Chaines
• Fichiers
• Dossiers
Toutes les options peuvent être passées au constructeurs ou via des méthodes. Par exemple,
pour l'option 'Target', getTarget() et setTarget().La méthode setOptions() est aussi
disponible et accepte un tableau.
2.7. Decrypt
Ce filtre va décrypter toute chaine grâce aux paramètres utilisés. Des adaptateurs sont utilisés.
Actuellement des aptateurs existent pour les extensions Mcrypt et OpenSSL de php.
Pour plus de détails sur l'encryptage de contenu, voyez le filtre Encrypt. La documentation
de celui-ci couvre les bases en matière de cryptage, nous n'aborderons ici que les méthodes
utilisées pour le décryptage.
646
Zend_Filter
Si vous n'aviez pas passé de vecteur spécifique à l'encryptage, alors vous devriez récupérer
le vecteur utilisé grâce à la méthode getVector(). Sans ce vecteur, aucun décryptage de la
données originale n'est possible.
$decrypted = $filter->filter('texte_encodé_non_lisible');
print $decrypted;
Si l'extension mcrypt n'est pas présente dans votre environement, une exception
sera levée.
• private: Votre clé privée. Ce peut être un nom de fichier ou juste le contenu de ce fichier : la clé.
Optionnellement il peut être nécessaire de passer la passphrase pour décrypter les clés elles-
mêmes. Utilisez alors setPassphrase().
647
Zend_Filter
$decrypted = $filter->filter('texte_encodé_illisible');
print $decrypted;
2.8. Digits
Retourne la chaîne $value, en retirant tout sauf les caractères numériques.
2.9. Dir
Retourne la partie correspondant au nom de dossier dans le chemin spécifié.
2.10. Encrypt
Ce filtre va crypter toute chaine avec les paramètres spécifiés. Des adaptateurs sont utilisés.
Actuellement, il existe des adaptateurs pour les extensions PHP Mcrypt et OpenSSL.
Comme ces deux méthodes d'encryptage sont très différentes, l'utilisation de leurs adaptateurs
l'est aussi.
// Utiliser Mcrypt
$filter1 = new Zend_Filter_Encrypt(array('adapter' => 'mcrypt'));
// Utiliser OpenSSL
$filter2 = new Zend_Filter_Encrypt(array('adapter' => 'openssl'));
// Utiliser Mcrypt
$filter = new Zend_Filter_Encrypt();
$filter->setAdapter('openssl');
648
Zend_Filter
• key: La clé d'encryptage. Cette même clé sera nécessaire pour le décryptage.
• algorithm: L'algorithme à utiliser pour le cryptage. Voyez PHP's mcrypt ciphers. Si non précisé,
blowfish sera utilisé par défaut.
• mode: Le mode de cryptage à utiliser. Un des modes mcrypt doit être utilisé. Par défaut, cbc
est utilisé.
• mode_directory: Le dossier dans lequel se trouve le mode. Si non précisé, le dossier spécifié
par l'extension mcrypt est alors utilisé.
• vector: Le vecteur d'initialisation à utiliser. Un vecteur aléatoire est utilisé si non précisé.
• salt: Si la clé doit être utilisé comme grain de sel. Dans ce cas la clé utilisée pour le cryptage
sera elle même cryptée. Par défaut false : ce n'est pas le cas.
Si vous passez une chaine à la place d'un tableau pour la clé, celle-ci sera utilisée.
Une exception sera levée si l'extension PHP mcrypt n'est pas disponible.
Notez aussi que tous vos paramètres utilisés à la création de l'instance ou avec
setEncryption() vont être vérifiés. Si mcrypt détecte un problème, une exception
sera levée.
getVector() et setVector() sont aussi disponibles si besoin. Une chaine passée sera mise
à la taille du vecteur pour être utilisée avec l'algorithme en cours.
Notez que si vous n'utilisez pas un vecteur spécifique, alors vous devrez le
récupérer et le stocker. En effet, celui-ci est indispensable pour décoder la valeur
dans le futur.
649
Zend_Filter
$encrypted = $filter->filter('text_to_be_encoded');
print $encrypted;
• public: La clé publique de l'utilisateur auquel vous voulez proposer du contenu crypté.
Plusieurs clés peuvent être spécifiées via un tableau. Il est possible de préciser le contenu de
la clé, ou alors un chemin vers une clé.
• private: Votre clé privée utilisée pour crypter le contenu. La encore la clé peut être précisée
sous forme textuelle, ou alors un chemin vers un fichier contenant la clé.
// Utiliser openssl
$filter = new Zend_Filter_Encrypt(array(
'adapter' => 'openssl',
'private' => '/path/to/mykey/private.pem'
));
Si vous souhaitez encoder aussi les clés, passez alors une passphrase via setPassphrase().
Attention, la passphrase sera nécessaire pour décoder les clés.
Pour décrypter le document, la passphrase (si utilisée) et les clés enveloppe sont nécessaires.
Ceci signifie que vous devez appeler la méthode getEnvelopeKey() après le cryptage pour
récupérer l'enveloppe.
650
Zend_Filter
$encrypted = $filter->filter('text_to_be_encoded');
$envelope = $filter->getEnvelopeKey();
print $encrypted;
2.11. HtmlEntities
Retourne la chaîne $value, en convertissant les caractères en leurs entités HTML
correspondantes quand elles existent.
2.12. Int
Retourne la valeur (int) $value.
2.13. LocalizedToNormalized
Ce filtre va transformer toute entrée localisée en sa représentation normalisée. La transformation
est effectuée grâce à Zend_Locale en interne.
Ceci permet à l'utilisateur de saisir des information dans sa propre locale, et du coté serveur de
stocker les informations normalisées relatives.
• entiers: Nombres entiers localisés. Ils seront normalisés dans la notation anglaise
internationnale.
• float: Nombres flottants. Ils seront normalisés dans la notation anglaise internationnale.
• nombres: Nombres réels. Ils seront normalisés dans la notation anglaise internationnale.
Tout autre type d'entrée sera retourné tel quel, sans tranformation, par ce filtre.
651
Zend_Filter
Notez bien que toute sortie normalisée de ce filtre est de type chaine de
caractères.
Voici un exemple:
// Initialise le filtre
$filter = new Zend_Filter_LocalizedToNormalized();
$filter->filter('123.456,78');
// retourne '123456.78'
Imaginons que nous utilisoons la locale 'de' de manière globale sur toute l'application.
Zend_Filter_LocalizedToNormalized va utiliser cette locale là pour calculer sa sortie.
Il est possible de contrôler la normalisation des nombres. Toute options accépté par
Zend_Locale_Format peut alors être utilisée. Les plus courantes sont:
• date_format
• locale
• precision
// Numeric Filter
$filter = new Zend_Filter_LocalizedToNormalized(array('precision' => 2));
$filter->filter('123.456');
// retourne '123456.00'
$filter->filter('123.456,78901');
// retourne '123456.79'
// Initialise le filtre
$filter = new Zend_Filter_LocalizedToNormalized();
$filter->filter('12.April.2009');
// retourne array('day' => '12', 'month' => '04', 'year' => '2009')
Imaginons une fois de plus une locale globale 'de'. L'entrée est donc automatiquement reconnue
comme date et vous aurez un tableau en sortie.
Vous pouvez contrôler la transformation du filtre grâce aux paramètres date_format et locale.
652
Zend_Filter
// Date Filter
$filter = new Zend_Filter_LocalizedToNormalized(
array('date_format' => 'ss:mm:HH')
);
$filter->filter('11:22:33');
// retourne array('hour' => '33', 'minute' => '22', 'second' => '11')
2.14. NormalizedToLocalized
Ce filtre est l'inverse de Zend_Filter_LocalizedToNormalized et convertira toute entrée
normalisée en entrée localisée. Il utilise Zend_Locale pour celà.
Ceci permet de représenter une valeur normalisée dans la locale de l'utilisateur, qu'il reconnaitra
donc sans problème.
Notez bien que la localisation n'est pas de la traduction, ce filtre ne sait pas
traduire des chaines d'une langue à l'autre (comme des noms de jours ou de
mois).
Tout autre type d'entrée sera retourné tel quel, sans transformation.
// Initialise le filtre
$filter = new Zend_Filter_NormalizedToLocalized();
$filter->filter(123456.78);
// retourne '123.456,78'
Imaginons que vous avez affecté une locale 'de' comme locale de l'application.
Zend_Filter_NormalizedToLocalized va utiliser cette locale pour détecter le type de
sortie à produire, ceci sous forme de chaine de caractères.
Il est aussi possible de contrôler le look de vos nombres localisés. Pour cela vous pouvez préciser
toute option que Zend_Locale_Format reconnait. Les plus courantes sont:
• date_format
653
Zend_Filter
• locale
• precision
Pour plus de détails sur ces options, voyez le chapitre sur Zend_Locale .
// Numeric Filter
$filter = new Zend_Filter_NormalizedToLocalized(array('precision' => 2));
$filter->filter(123456);
// retourne '123.456,00'
$filter->filter(123456.78901);
// retourne '123.456,79'
// Initialise le filtre
$filter = new Zend_Filter_NormalizedToLocalized();
$filter->filter(array('day' => '12', 'month' => '04', 'year' => '2009');
// retoures '12.04.2009'
Imaginons que vous ayiez spécifié la locale 'de' au niveau de l'application, celle-ci est alors
automatiquement détectée et utilisée pour localiser la date.
Bien sûr, vous pouvez contrôler le format d'affichage de vos dates, grâce aux paramètres
date_format et locale.
// Date Filter
$filter = new Zend_Filter_LocalizedToNormalized(
array('date_format' => 'ss:mm:HH')
);
2.15. Null
Ce filtre retournera la valeur NULL si des critères précis sont rencontrés. C'est souvent
nécessaire lorsqu'on travaille avec des bases de données et que l'on souhaite une valeur NULL
plutôt qu'un booléen ou tout autre type.
654
Zend_Filter
Ceci signifie qu'en l'absence d'une configuration spéciale, Zend_Filter_Null accepte tout
type en entrée et retourne NULL dans les mêmes cas que empty().
Toute autre valeur sera retournée telle quelle, sans aucune modification.
• tout: Convertit tous les types cités en NULL. (comportement par défaut).
Il existe plusieurs manières de spécifier les types à filtrer, des constantes, des types ajoutés à
la suite, des chaines de caractères, un tableau... Voyez les exemples suivants:
Un objet Zend_Config peut aussi être utilisé pour préciser les types. La méthode setType()
existe de même.
2.16. PregReplace
Zend_Filter_PregReplace performs a search using regular expressions and replaces all
found elements.
The option match has to be given to set the pattern which will be searched for. It can be a string
for a single pattern, or an array of strings for multiple pattern.
655
Zend_Filter
To set the pattern which will be used as replacement the option replace has to be used. It can
be a string for a single pattern, or an array of strings for multiple pattern.
$filter->filter($input);
// returns 'Hy john!'
$filter->filter($input);
// returns 'Bye john!'
For a more complex usage take a look into PHP's PCRE Pattern Chapter.
2.17. RealPath
Ce filtre va résoudre un lien ou un chemin en chemin absolu canonique. Toutes références à
'/./', '/../' et tout ajout supplémentaire de '/' sera résolu ou supprimé. Aucun caractère
de lien symbolique ne sera présent dans le résultat ('/./' ou '/../')
Zend_Filter_RealPath retourne FALSE en cas d'echec par exemple si le fichier n'existe pas.
Sur les systems BSD, Zend_Filter_RealPath n'échoue pas si seule la dernière partie du
chemin n'existe pas, les autres systèmes retourneront FALSE.
// retourne '/www/mypath'
Il peut être nécessaire quelques fois de vouloir utiliser ce filtre sur des chemins inexistants. Par
exemple récupérer le realpath d'un chemin à créer. Dans ce cas vous pouvez passer FALSE au
constructeur, ou utiliser setExists().
2.18. StringToLower
Ce filtre convertit toute entrée vers des caractères minuscules.
656
Zend_Filter
print $filter->filter('SAMPLE');
// retourne "sample"
Par défaut, seul le jeu de caractères de la locale en cours sera utilisé. Les caractères provenant
d'autres jeux seront ignorés. Cela reste possible de les passer en minuscules si l'extension
mbstring est présente dans votre environnement PHP. Indiquez l'encodage voulu à la création
du filtre StringToLower ou utilisez sa méthode setEncoding().
// utiliser UTF-8
$filter = new Zend_Filter_StringToLower('UTF-8');
// ou passer un tableau
$filter = new Zend_Filter_StringToLower(array('encoding' => 'UTF-8'));
Attention une exception sera levée si vous précisez un encodage alors que
l'extension mbstring est absente.
Une exception sera de même levée si l'encodage que vous précisez n'est pas
pris en compte par mbstring.
2.19. StringToUpper
Ce filtre convertit toute entrée vers une casse majuscule.
print $filter->filter('Sample');
// retourne "SAMPLE"
Tout comme le filtre StringToLower, seul le jeu de caractères de la locale en cours sera utilisé.
Son fonctionnement est le même que celui de StringToLower.
// ou encore
$filter->setEncoding('ISO-8859-1');
2.20. StringTrim
Retourne la chaîne $value en supprimant les caractères vides en début et fin de chaîne.
2.21. Int
Retourne la valeur $value en enlevant les caractères représentant une nouvelle ligne.
2.22. StripTags
Ce filtre retourne une chaîne, où toutes les balises HTML et PHP sont supprimées, exceptées
celles qui sont explicitement autorisées. En plus de pouvoir spécifier quelles balises sont
657
Zend_Filter
autorisées, les développeurs peuvent spécifier quels attributs sont autorisés soit pour toutes les
balises autorisées soit pour des balises spécifiques seulement.
3. Chaînes de filtrage
Souvent, de multiples filtres doivent être appliqués à une valeur dans un ordre particulier. Par
exemple, un formulaire d'authentification accepte un identifiant qui doit être en minuscule et
composé uniquement de caractères alphabétiques. Zend_Filter fournit un moyen simple
permettant de chaîner des filtres. Le code suivant illustre comment chaîner deux filtres pour
l'identifiant soumis :
// Filtrage de l'identifiant
$identifiant = $filtreChaine->filter($_POST['identifiant']);
Les filtres sont exécutés dans leur ordre d'ajout à Zend_Filter. Dans l'exemple ci-dessus,
l'identifiant se voit d'abord retirer tout caractère non-alphabétique, les caractère majuscules
éventuels sont ensuite convertis en minuscules.
Tout objet implémentant Zend_Filter_Interface peut être utilisé comme chaîne de filtrage.
4. Écriture de filtres
Zend_Filter fournit un ensemble de filtres usuels. Cependant, les développeurs auront
souvent besoin d'écrire des filtres personnalisés pour leurs besoins propres. L'écriture de filtre
personnalisés est rendue plus facile via l'implémentation de Zend_Filter_Interface.
Zend_Filter_Interface définit une méthode unique, filter(), qui peut être implémentée
dans les classes créées. Un objet qui implémente cette interface peut être ajouté à une chaîne
de filtrage via Zend_Filter::addFilter().
658
Zend_Filter
return $valeurFiltree;
}
}
Pour ajouter une instance du filtre défini ci-dessus à une chaîne de filtrage :
5. Zend_Filter_Input
Zend_Filter_Input propose une manière générique de déclarer des filtres et des validateurs,
de les appliquer comme un ensemble, à une collection de données, et enfin de récupérer
ces données validées et filtrées. Les valeurs sont retournées échappées par défaut, pour une
meilleure sécurité relative au HTML.
Considérez cette classe comme une boite noire dans laquelle va passer une variable de
collection, typiquement un tableau PHP représentant des données externes. Les données
arrivent dans l'application depuis une source externe, donc potentiellement dangereuse, comme
des variables de requête HTTP, d'un service Web, d'un fichier, ou autre. L'application demande
alors à la boite noire l'accès à une ou plusieurs données, en spécifiant sous quelle forme elle
s'attend à voir la donnée. La boite inspecte alors la donnée pour la valider, et ne la laisse sortir
que si celle-ci respecte les règles que l'application demande. Grâce à une simple classe et un
mécanisme facile, ceci encourage les développeurs à prendre des bonnes pratiques au regard
de la sécurité des applications.
• Les filtres transforment les entrées en supprimant ou changeant des caractères dans leurs
valeurs. Le but est de "normaliser" les valeurs jusqu'à ce qu'elles correspondent aux attentes
exigées. Par exemple si une chaine d'entiers (numériques) est attendue, et que la donnée
d'entrée est "abc123", alors en sortie du filtre la valeur "123" sera proposée.
• Les validateurs vérifient la validité d'une donnée, sans la transformer. Si la validation échoue,
le validateur renseignera sur les problèmes rencontrés.
• Les échappeurs transforment une valeur en supprimant certains caractères qui peuvent
avoir une signification spéciale dans un contexte donné. Par exemple, les caractères '<' et
'>' délimitent les balises HTML, ainsi si une donnée contenant ces caractères est affichée
directement dans un navigateur, la sortie peut être corrompue et mener à des problèmes de
sécurité. Échapper les caractères est le fait de leur enlever toute signification spéciale, ils
seront traités comme des caractères tout à fait normaux.
659
Zend_Filter
L'exemple qui suit indique que le champ "month" est filtré par un Zend_Filter_Digits, et
le champ "account" est filtré par un Zend_Filter_StringTrim. Puis, une règle de validation
s'appliquera au champ "account", celui-ci sera validé s'il ne contient que des caractères
alphabétiques (lettres).
$filters = array(
'month' => 'Digits',
'account' => 'StringTrim'
);
$validators = array(
'account' => 'Alpha'
);
Chaque clé du tableau $filters représente une donnée à laquelle sera appliqué le filtre
correspondant en valeur de tableau.
$validators = array(
'month' => 'Digits',
);
$validators = array(
'month' => $digits
);
• Un tableau, pour déclarer une chaine de filtres ou validateurs. Les éléments de ce tableau
peuvent être des chaînes représentant des noms de classe, ou des objets directement. Aussi,
vous pouvez utiliser comme valeur un tableau contenant le nom du filtre ou validateur, et
d'éventuels arguments à passer à son constructeur.
$validators = array(
'month' => array(
'Digits', // chaine
new Zend_Validate_Int(), // objet
array('Between', 1, 12) // chaine + arguments pour le constructeur
)
);
660
Zend_Filter
Un joker "* " peut être utilisé dans le tableau des filtres ou des validateurs. Ceci aura pour effet
d'appliquer le validateur ou le filtre à toutes les entrées du tableau traité. Notez que l'ordre des
filtres / validateurs est important dans le tableau, car il seront appliqués dans l'ordre dans lequel
ils ont été déclarés.
$filters = array(
'*' => 'StringTrim',
'month' => 'Digits'
);
Les données d'entrée peuvent être placées dans le troisième paramètre du constructeur.
Ces données possèdent en clé leur nom, et en valeur leur valeur. Typiquement, les
tableaux superglobaux $_GET et $_POST possèdent la structure idéale pour passer dans
Zend_Filter_Input.
$data = $_GET;
$input = new Zend_Filter_Input($filters, $validators, $data);
Aussi, la méthode setData() accepte les données de la même manière que le troisième
argument du constructeur.
La méthode setData() réaffecte une nouveau tableau de valeurs d'entrée dans l'objet
Zend_Filter_Input, en écrasant toute autre source s'y trouvant. Ceci est pratique afin de
réutiliser des règles communes de filtres / validateurs, sur différentes sources.
if ($input->isValid()) {
661
Zend_Filter
echo "OK\n";
}
Cette méthode accepte aussi un paramètre facultatif nommant un champ particulier dans la
donnée d'entrée. Ceci permet une vérification individuelle.
if ($input->isValid('month')) {
echo "Le champ 'month' est OK\n";
}
• Les champs absents sont ceux qui ne sont pas présents dans la donnée d'entrée, alors que la
méta commande 'presence'=>'required' était présente (voyez la section sur les méta
commandes).
• Les champs inconnus sont ceux présents dans la donnée d'entrée alors que aucun validateur
ni filtre ne lui avait attribué de règle.
if ($input->hasInvalid() || $input->hasMissing()) {
$messages = $input->getMessages();
}
if ($input->hasInvalid()) {
$invalidFields = $input->getInvalid();
}
if ($input->hasMissing()) {
$missingFields = $input->getMissing();
}
if ($input->hasUnknown()) {
$unknownFields = $input->getUnknown();
}
Les valeurs retournées par getMessages() sont sous la forme d'un tableau dont la clé est la
règle concernée et la valeur un tableau d'erreurs la concernant. Le tableau d'erreurs comporte en
clé le nom de la règle déclarée qui peut être différent des noms de champs vérifiés par la règle.
La méthode getErrors() retourne un tableau associatif dont les clés sont des noms de règles
et les valeurs associées des tableaux identifiants les erreurs. Les identifiants d'erreurs sont des
chaînes constantes et figées, qui permettent d'identifier la raison de l'échec de validation, tandis
que les messages associés sont eux-mêmes personnalisables. Voir Section 1.2, « Utilisation
basique des validateurs » pour plus d'information.
662
Zend_Filter
$options = array(
'missingMessage' => "Field '%field%' is required"
);
// alternative method:
And you can also add a translator which gives you the ability to provide multiple languages for
the messages which are returned by Zend_Filter_Input.
When you are using an application wide translator, then it will also be used by
Zend_Filter_Input. In this case you will not have to set the translator manually.
Le résultat de la méthode getUnknown() est un tableau associatif dont les clés sont des noms
de champs et les valeurs sont les valeurs de champs correspondants. Les noms de champs
sont dans ce cas les clés du tableau au lieu des noms de règles, car tout champs n'ayant pas
de règles définies est considéré comme un champs inconnu.
663
Zend_Filter
But be warned that using this notation introduces a security leak which could be
used for cross-site scripting attacks. Therefor you should always set individual
validators for each field.
Il est possible de définir un autre filtre comme filtre par défaut pour récupération des champs.
Ceci se fait via le constructeur :
Les filtres d'échappement comme vus juste précédemment, doivent être spécifiés de cette
manière là. S'ils avaient été spécifiés comme filtres dans le tableau de Zend_Filter_Input,
ils auraient pu faire échouer les validateurs, car les filtres sont exécutés AVANT les validateurs.
Aussi, il n'aurait plus été possible de proposer la donnée de sortie de manière échappée et non
échappée. Ainsi, déclarer un filtre d'échappement des données devrait toujours être effectué en
utilisant la méthode setDefaultEscapeFilter(), et non pas le tableau $filters.
Comme il n'y a qu'une seule méthode getEscaped(), il ne peut y avoir qu'un seul filtre utilisé
pour l'échappement. Il est cependant possible d'utiliser une chaine de filtre, ou encore de dériver
la classe Zend_Filter_Input en créant d'autres méthodes de récupération de données, plus
spécifiques.
5.4. Utiliser des méta commandes pour contrôler les règles des
filtres et validateurs
En plus de déclarer un mapping entre des champs d'un tableau, et des validateurs et des
filtres, il est possible d'utiliser des méta commandes pour contrôler le comportement de
Zend_Filter_Input. Les méta commandes se présentent sous la forme de chaînes dans le tableau
des filtres ou des validateurs.
$filters = array(
'month' => array(
'Digits', // nom du filtre à l'index [0]
'fields' => 'mo' // nom du champ à l'index ['fields']
)
);
664
Zend_Filter
Dans l'exemple ci dessus, la règle applique le filtre "digits" au champ d'entrée nommé "mo".
La chaine "month" devient alors un simple mnémonique pour cette règle, elle n'est pas utilisée
comme nom de champ si celui-ci est renseigné avec la méta commande "fields", mais elle est
utilisée comme nom de règle.
La valeur par défaut de la méta commande "fields" est l'index de la règle courante. Dans
l'exemple ci dessus, si la méta commande "fields" est omise, la règle s'appliquerait au champ
"month".
Un autre usage de la méta commande "fields" est pour préciser les champs aux filtres ou
validateurs qui en attendent plusieurs en entrée. Si la méta commande "fields" est un tableau,
alors le filtre/validateur correspondant aura comme argument la valeur des champs. Pensez au
cas où l'on demande à l'utilisateur de saisir 2 fois son mot de passe. Imaginons un validateur qui
prend en paramètre un tableau de champs et retourne TRUE si les champs sont égaux.
$validators = array(
'password' => array(
'StringEquals',
'fields' => array('password1', 'password2')
)
);
// Invoque la classe Zend_Validate_StringEquals,
// en lui passant un tableau contenant les valeurs
// des champs 'password1' and 'password2'.
Si la validation échoue, alors le nom de la règle ('password') est utilisé dans le retour de
getInvalid(), et non pas les noms des champs spécifiés dans "fields".
$validators = array(
'month' => array(
'digits',
'presence' => 'required'
)
);
$validators = array(
665
Zend_Filter
Si vous utilisez pour une règle la méta commande FIELDS afin de définir un tableau de champs,
vous pouvez définir un tableau pour la méta commande DEFAULT_VALUE. Les valeurs par défaut
seront alors les clés correspondantes à chaque champ manquant. Si FIELDS définit de multiples
champs mais que DEFAULT_VALUE est un scalaire, alors cette valeur scalaire sera utilisée pour
tous les champs manquants.
Si la chaine vide doit pouvoir être considérée comme valide, utilisez la méta commande
"allowEmpty" avec la valeur TRUE.
$validators = array(
'address2' => array(
'Alnum',
'allowEmpty' => true
)
);
Dans la cas peut commun ou vous déclarez une règle de validation avec aucun validateurs,
mais que la méta commande "allowEmpty" est mise à FALSE (le champ est considéré
invalide s'il est vide), Zend_Filter_Input retourne un message d'erreur par défaut que vous
pouvez récupérer avec la méthode getMessages(). Ce message se change grâce à l'option
"notEmptyMessage" spécifiée en constructeur de Zend_Filter_Input ou via la méthode
setOptions().
$options = array(
'notEmptyMessage' =>
"Une valeur non vide est requise pour le champ '%field%'"
);
// Autre méthode :
666
Zend_Filter
$validators = array(
'month' => array(
'Digits',
new Zend_Validate_Between(1,12),
new Zend_Validate_GreaterThan(0),
'breakChainOnFailure' => true
)
);
$input = new Zend_Filter_Input(null, $validators);
La classe Zend_Validate est plus flexible lors du bris de la chaine d'exécution, par rapport
à Zend_Filter_Input. Avec Zend_Validate, vous pouvez mettre l'option pour casser
la chaine indépendamment pour chaque validateur. Avec Zend_Filter_Input, la méta
commande "breakChainOnFailure" s'applique à tous les validateurs dans la règle. Pour un usage
plus flexible, créez votre propre chaine de validation comme ceci :
Voici un exemple simple qui enregistre un message d'erreur pour une validateur de chiffres.
667
Zend_Filter
$validators = array(
'month' => array(
'digits',
'messages' => 'Un mois doit être un chiffre'
)
);
Si vous possédez plusieurs validateurs dont vous voulez personnaliser les messages d'erreur,
utilisez alors un tableau comme valeur de la méta commande 'messages'.
$validators = array(
'month' => array(
'digits',
new Zend_Validate_Between(1, 12),
'messages' => array(
// utilise le message par défaut du vaidateur [0]
// Affecte un nouveau message pour le validateur [1]
1 => 'Une valeur de mois doit être comprise entre 1 et 12'
)
)
);
Si un des validateurs a plusieurs messages d'erreur, ils sont identifiés par une clé. Il existe
différente clé dans chaque classe de validateur, ceux-ci servent d'identifiants pour les messages
d'erreur. Chaque classe validateur définit aussi des constante pour les clés des messages
d'erreur. Cette constante peut être utilisée dans la méta commande 'messages' en lui passant
un tableau associatif plutôt qu'une chaine.
$validators = array(
'month' => array(
'digits', new Zend_Validate_Between(1, 12),
'messages' => array(
'Un mois ne peut contenir que des chiffres',
array(
Zend_Validate_Between::NOT_BETWEEN =>
'La valeur %value% du mois doit être comprise'
. ' entre %min% et %max%',
Zend_Validate_Between::NOT_BETWEEN_STRICT =>
'La valeur %value% du mois doit être comprise'
. ' strictement entre %min% et %max%'
)
)
)
);
Vous devriez vous référer à la documentation de chaque validateur afin de savoir s'il retourne
plusieurs messages d'erreur, les clés de ces messages et les jetons utilisables dans les modèles
de message.
Si vous n'avez qu'un seul validateur dans vos règles de validation ou que tous les validateurs ont
le même message de paramétrer, alors ils peuvent être référencés la construction additionnelle
de type tableau :
668
Zend_Filter
$validators = array(
'month' => array(
new Zend_Validate_Between(1, 12),
'messages' => array(
Zend_Validate_Between::NOT_BETWEEN =>
'La valeur %value% du mois doit être comprise'
. ' entre %min% et %max%',
Zend_Validate_Between::NOT_BETWEEN_STRICT =>
'La valeur %value% du mois doit être comprise'
. ' strictement entre %min% et %max%'
)
)
);
5.4.7. Utiliser des options pour définir des méta commandes pour toutes
les règles
Les valeurs par défaut des méta commandes "allowEmpty", "breakChainOnFailure", et
"presence" peuvent être dictées pour toutes les règles en utilisant l'argument $options du
constructeur de Zend_Filter_Input.
Les méta commandes "fields", "messages", et "default" ne bénéficient pas d'un tel raccourci.
Si vous écrivez vos propres filtres (ou validateurs), la classe peut exister dans un autre
espace de nom que Zend_Filter ou Zend_Validate. Il est alors possible de dire à
Zend_Filter_Input de chercher dans ces espaces là. Ceci se fait via son constructeur :
$input->addValidatorPrefixPath('Autre_Namespace', 'Autre/Namespace');
$input->addFilterPrefixPath('Foo_Namespace', 'Foo/Namespace');
669
Zend_Filter
// 1. My_Namespace_Validate
// 2. Autre_Namespace
// 3. Zend_Validate
6. Zend_Filter_Inflector
Zend_Filter_Inflector est un outil de conversion de règles (sous forme de chaîne de
caractères), vers une cible. Ce procédé est appelé inflexion.
$string = 'motsEnNotationCamel';
$filtered = $inflector->filter(array('page' => $string));
// pages/mots-en-notation-camel.html
$string = 'ceci_n_est_pas_en_notation_camel';
$filtered = $inflector->filter(array('page' => $string));
// pages/ceci_n_est_pas_en_notation_camel.html
670
Zend_Filter
6.1. Opération
Un inflecteur a besoin d'une cible et d'une ou plusieurs règles. Une cible est une chaîne dans
laquelle des jokers pour les variables à remplacer sont présents. Ils sont préfixés de doubles-
points, par exemple :script.
L'appel à filter(), nécessite un tableau de clés/valeurs pour les jokers présents dans la cible.
Chaque variable dans la cible peut avoir zéro ou plusieurs règles associées. Les règles peuvent
être statiques ou faire référence à une classe Zend_Filter. Les règles statiques sont des
remplacements purs et simples. Sinon, la classe qui correspond à la règle sera utilisée pour
analyser le texte. Ces classes sont spécifiées par leur nom (du filtre), non préfixé.
Par exemple, vous pouvez utiliser n'importe quelle instance de Zend_Filter. Cependant, plutôt
que d'y faire référence via "Zend_Filter_Alpha" ou "Zend_Filter_StringToLower", vous
spécifierez seulement "Alpha" ou encore "StringToLower".
Pour spécifier d'autres chemins, Zend_Filter_Inflector possède une méthode qui proxie
vers le plugin loader, addFilterPrefixPath() :
$inflector->addFilterPrefixPath('Mes_Filtres', 'Mes/Filtres/');
Il est possible également de récupérer le plugin loader, et d'intervenir sur son instance de manière
directe :
$loader = $inflector->getPluginLoader();
$loader->addPrefixPath('Mes_Filtres', 'Mes/Filtres/');
Pour plus d'informations sur la modification des chemins vers les filtres voyez la documentation
de PluginLoader.
// Via le constructeur :
$inflector = new Zend_Filter_Inflector('#foo/#bar.#sfx', null, '#');
// Via l'accesseur :
$inflector->setTargetReplacementIdentifier('#');
671
Zend_Filter
En général, concernant la cible, on la passe en constructeur. C'est le cas classique. Il peut être en
revanche nécessaire de pouvoir passer une cible après la construction de l'objet. (Par exemple
modifier l'inflecteur des composants Zend intégrés tels que ViewRenderer ou Zend_Layout).
setTarget() peut vous y aider :
$inflector = $layout->getInflector();
$inflector->setTarget('layouts/:script.phtml');
De plus, vous pouvez agréger la cible dans un membre de votre classe, si cela vous permet
d'éviter trop d'appels de méthodes. setTargetReference() permet ceci :
class Foo
{
/**
* @var string Inflector target
*/
protected $_target = 'foo/:bar/:baz.:suffix';
/**
* Constructor
* @return void
*/
public function __construct()
{
$this->_inflector = new Zend_Filter_Inflector();
$this->_inflector->setTargetReference($this->_target);
}
/**
* Set target; updates target in inflector
*
* @param string $target
* @return Foo
*/
public function setTarget($target)
{
$this->_target = $target;
return $this;
}
}
Notez bien que quelle que soit la méthode que vous utilisez pour spécifier vos
règles dans l'inflecteur, leur ordre est très important. Vous devez ajouter de la
règle la plus spécifique, à la plus générique. Par exemple, 2 règles nommées
"moduleDir" et "module", la règle "moduleDir" devrait être ajoutée avant la règle
"module", car cette dernière est contenue dans "moduleDir".
672
Zend_Filter
Bien sur il est possible d'agréger la règle dans une propriété de classe, ceci permettra
d'éviter l'appel de méthodes. Ce cas se produit typiquement lorsque l'inflecteur est embarqué
(encapsulé) dans une classe. Vous pouvez à ce moment là interdire la récupération de l'inflecteur
depuis l'extérieur de la classe, par exemple. La méthode setStaticRuleReference() vous
y aidera :
class Foo
{
/**
* @var string Suffix
*/
protected $_suffix = 'phtml';
/**
* Constructor
* @return void
*/
public function __construct()
{
$this->_inflector =
new Zend_Filter_Inflector(':script.:suffix');
$this->_inflector
->setStaticRuleReference('suffix', $this->_suffix);
}
/**
* Set suffix; updates suffix static rule in inflector
*
* @param string $suffix
* @return Foo
*/
public function setSuffix($suffix)
{
$this->_suffix = $suffix;
return $this;
}
}
Les règles des filtres sont ajoutées avec setFilterRule(). Cette méthode écrase toute règle
déjà définie.addFilterRule() au contraire, n'écrase pas mais gère une pile de filtres pour
une variable. Les noms des filtres passés à ces 2 méthodes sont de la forme :
• String : une chaîne de caractères représentant le nom de la classe du filtre, ou alors le nom de
la classe moins le préfixe utilisé par le plugin loader. (le préfixe par défaut étant "Zend_Filter").
673
Zend_Filter
Chacune de ces 2 méthodes prend en paramètre un tableau de variable/règle. La règle peut être
n'importe quel type accepté (string, objet filtre ou array). Les noms des variables proposent une
syntaxe spéciale pour différencier les règles statiques des filtres :
// règles statiques :
'suffix' => 'phtml'
));
• setPluginLoader() peut être utile si vous avez configuré votre propre chargeur de
plugins (plugin loader) et que vous voulez l'utiliser avec Zend_Filter_Inflector ;
getPluginLoader() retourne cette valeur.
674
Zend_Filter
• getRules($spec = null) récupère toutes les règles, ou les règles d'une certaine variable.
• getRule($spec, $index) récupère une règle précise, même dans une chaîne de
filtre.$index doit être précisé.
• throwTargetExceptionsOn est un booléen. Ceci spécifie qu'une exception doit être lancée
si une variable est toujours présente dans la cible après le passage de l'inflecteur.
// Par le constructeur :
$config = new Zend_Config($options);
$inflector = new Zend_Filter_Inflector($config);
// Ou via setConfig() :
$inflector = new Zend_Filter_Inflector();
$inflector->setConfig($config);
675
Zend_Form
1. Zend_Form
Zend_Form simplifie la création et la gestion des formulaires dans vos applications Web. Il
accomplit les buts suivants :
Il utilise fortement d'autres composants de Zend Framework pour accomplir ses buts,
incluant Zend_Config, Zend_Validate, Zend_Filter, Zend_Loader_PluginLoader, et
optionnellement Zend_View.
Pour des usages avancés, vous voudriez probablement dériver Zend_Form, mais pour les
formulaires simples, vous pouvez créez un formulaire depuis une instance de Zend_Form.
Vous pouvez spécifier (c'est une bonne idée) l'action et la méthode d'envoi du formulaire grâce
à setAction() et setMethod() :
$form->setAction('/resource/process')
->setMethod('post');
Le code ci-dessus indique au formulaire d'être envoyé vers l'URL "/resource/process" avec la
méthode HTTP POST. Ceci va impacter le rendu du formulaire (la balise <form>).
Il est possible d'assigner les attributs HTML supplémentaires à la balise <form> via la méthode
setAttrib() ou encore setAttribs(). Par exemple, indiquons un attribut "id" au formulaire :
$form->setAttrib('id', 'login');
676
Zend_Form
• button
• hidden
• image
• password
• radio
• reset
• submit
• text
• textarea
Vous avez 2 manières de procéder pour ajouter les éléments au formulaire : instanciez vous
même les objets des éléments, ou passer le type d'élément à Zend_Form, qui va alors créer
le bon objet pour vous.
Quelques exemples :
Par défaut, ces éléments n'ont ni validateurs, ni filtres. Vous devrez donc ajoutez des validateurs
et/ou des filtres, manuellement. Ceci est possible soit (a) avant de passer l'élément au formulaire,
(b) via les options de configuration passés lors de la création de l'élément, ou (c) en récupérant
l'objet déjà enregistré, depuis le formulaire, et en le configurant ensuite.
Voyons comment passer un validateur à un élément dont nous créons l'objet. On peut passer
soit l'objet Zend_Validate_*, soit une chaîne le décrivant :
En utilisant la technique de passage par le nom, vous pouvez ajouter un tableau d'options à
passer au constructeur de l'objet validateur. Ceci se fait en troisième paramètre :
677
Zend_Form
(Le second paramètre permet d'indiquer au validateur s'il doit briser la chaîne de validation ou
non. Par défaut, FALSE : ce n'est donc pas le cas.)
Vous pouvez avoir besoin de spécifier qu'un élément est requis. Ceci peut être fait en utilisant
un accesseur ou en passant une option à la création de l'élément. Voici un exemple :
Lorsqu'un élément est requis, un validateur "NotEmpty" lui est ajouté, sur le dessus de sa pile
de validateurs.
La gestion des filtres est très semblable à celle des validateurs. Voyons comment ajouter un
filtre qui retourne la donnée en minuscules :
$username->addFilter('StringtoLower');
$username->addValidator('alnum')
->addValidator('regex', false, array('/^[a-z]/'))
->setRequired(true)
->addFilter('StringToLower');
Aussi simple que cela puisse paraître, cela peut très vite devenir fastidieux de répéter ces
opérations sur tous les éléments du formulaire. Reprenons le cas (b) d'au dessus : lorsque l'on
crée un élément, Zend_Form::addElement() agit comme une fabrique et on peut lui passer
des options de configuration. Par exemple, des validateurs ou des filtres. Essayons ceci :
Si vous vous apercevez que vous créez des éléments basés sur les mêmes
options, étendre Zend_Form_Element peut devenir une bonne option. Votre
nouvelle classe configurera directement vos objets.
678
Zend_Form
// Supposant que setView() avec passage d'un objet Zend_View a été appelée avant :
echo $form;
Par défaut, Zend_Form et les Zend_Form_Element vont essayer de récupérer l'objet de vue
depuis l'aide d'action ViewRenderer, ce qui signifie que vous n'aurez pas besoin de spécifier
un objet de vue manuellement si vous utilisez le système MVC de Zend Framework. Pour rendre
un formulaire dans une vue MVC, un simple echo suffit :
Techniquement, Zend_Form utilise des "décorateurs" pour effectuer le rendu visuel. Ces
décorateurs peuvent remplacer le contenu, ou le placer avant ou après. Ils peuvent aussi
introspecter l'élément qui leur est passé. Ainsi, vous pouvez chaîner plusieurs décorateurs pour
utiliser des effets visuels. Par défaut, Zend_Form_Element combine quatre décorateurs pour
s'afficher :
$element->addDecorators(array(
'ViewHelper',
'Errors',
array('HtmlTag', array('tag' => 'dd')),
array('Label', array('tag' => 'dt')),
));
(Où <HELPERNAME> est le nom de l'aide de vue à utiliser, qui varie selon l'élément à rendre.)
Vous pouvez changer les décorateurs utilisés par un élément si vous voulez avoir un visuel
différent ; voyez la section sur les décorateurs pour plus d'informations.
Le formulaire boucle sur ses éléments et entoure leur rendu d'une balise HTML <form>.
Cette balise prend en compte la méthode, l'action, et les éventuels attributs passés via
setAttribs().
Les éléments sont bouclés dans l'ordre dans lequel ils sont ajoutés, ou, si votre élément possède
un attribut "order", celui-ci sera alors utilisé pour gérer sa place dans la pile des éléments :
$element->setOrder(10);
679
Zend_Form
D'où proviennent les données ? Vous pouvez utiliser $_POST ou $_GET, ou n'importe quelle
source de données (service Web par exemple) :
if ($form->isValid($_POST)) {
// succès!
} else {
// echec!
}
Avec des requêtes AJAX, il arrive que l'on ait besoin de ne valider qu'un élément, ou un groupe
d'élément. isValidPartial() validera un formulaire partiel. Contrairement à isValid(), si
une valeur est absente, les autres validateurs ne seront pas interrogés :
if ($form->isValidPartial($_POST)) {
// Tous les éléments présents dans $_POST ont passé la validation
} else {
// un ou plusieurs éléments présent dans $_POST ont échoué
}
La méthode processAjax() peut aussi être utilisée pour valider partiellement un formulaire.
Contrairement à isValidPartial(), cette méthode retournera les messages d'erreur de
validation au format JSON.
En supposant que les validateurs aient passé, vous pouvez dès lors récupérer les valeurs filtrées
depuis les éléments :
$values = $form->getValues();
$unfiltered = $form->getUnfilteredValues();
Si d'un autre côté, vous ne souhaitez que les valeurs filtrées valides d'un formulaire partiellement
valide, vous pouvez appeler :
$values = $form->getValidValues($_POST);
if (!$form->isValid($_POST)) {
echo $form;
680
Zend_Form
Si vous voulez inspecter les erreurs, 2 méthodes s'offrent à vous. getErrors() retourne un
tableau associatif avec en clé le nom de l'élément et en valeur un tableau de codes d'erreurs.
getMessages() retourne un tableau associatif avec en clé le nom de l'élément, et en valeur
un tableau de messages d'erreurs (code=>message). Tout élément ne comportant pas d'erreur
ne sera pas inclus dans le tableau.
• un nom
• un mot de passe
• un bouton d'envoi
// élément nom :
$username = $form->createElement('text', 'username');
$username->addValidator('alnum')
->addValidator('regex', false, array('/^[a-z]+/'))
->addValidator('stringLength', false, array(6, 20))
->setRequired(true)
->addFilter('StringToLower');
681
Zend_Form
{
// Créer le formulaire comme décrit ci-dessus
return $form;
}
$values = $form->getValues();
// les valeurs sont récupérées
}
}
<h2>Identifiez vous:</h2>
<?php echo $this->form ?>
Comme vous le voyez sur le code du contrôleur, il reste du travail à faire une fois le formulaire
validé. Par exemple, utiliser Zend_Auth pour déclencher un processus d'identification.
[development]
; informations générales du formulaire
user.login.action = "/user/login"
user.login.method = "post"
; element username
user.login.elements.username.type = "text"
user.login.elements.username.options.validators.alnum.validator = "alnum"
user.login.elements.username.options.validators.regex.validator = "regex"
user.login.elements.username.options.validators.regex.options.pattern = "/^[a-z]/i"
682
Zend_Form
user.login.elements.username.options.validators.strlen.validator = "StringLength"
user.login.elements.username.options.validators.strlen.options.min = "6"
user.login.elements.username.options.validators.strlen.options.max = "20"
user.login.elements.username.options.required = true
user.login.elements.username.options.filters.lower.filter = "StringToLower"
; element password
user.login.elements.password.type = "password"
user.login.elements.password.options.validators.strlen.validator = "StringLength"
user.login.elements.password.options.validators.strlen.options.min = "6"
user.login.elements.password.options.required = true
; element submit
user.login.elements.submit.type = "submit"
2.8. Conclusion
Vous êtes maintenant capable de libérer la puissance de Zend_Form. Continuez de lire les
chapitres suivants pour utiliser ce composant en profondeur !
• filtering (how is the element escaped or normalized prior to validation and/or for output?)
The base class, Zend_Form_Element, has reasonable defaults for many cases, but it is best
to extend the class for commonly used special purpose elements. Additionally, Zend Framework
ships with a number of standard XHTML elements; you can read about them in the Standard
Elements chapter.
The following loader types are used with the various plugin loader methods: 'validate', 'filter', and
'decorator'. The type names are case insensitive.
683
Zend_Form
• addPrefixPaths(array $spec): allows you to add many paths at once to one or more
plugin loaders. It expects each array item to be an array with the keys 'path', 'prefix', and 'type'.
Custom validators, filters, and decorators are an easy way to share functionality between forms
and to encapsulate custom functionality.
684
Zend_Form
switch ($placement) {
case 'APPEND':
return $content . $separator . $label;
case 'PREPEND':
default:
return $label . $separator . $content;
}
}
}
Now we can tell the element to use this plugin path when looking for decorators:
Alternately, we can do that at the form level to ensure all decorators use this path:
After it added as in the example above, the 'My/Decorator/' path will be searched first to see
if the decorator exists there when you add a decorator. As a result, 'My_Decorator_Label'
will now be used when the 'Label' decorator is requested.
685
Zend_Form
3.2. Filters
It's often useful and/or necessary to perform some normalization on input prior to validation.
For example, you may want to strip out all HTML, but run your validations on what remains
to ensure the submission is valid. Or you may want to trim empty space surrounding input so
that a StringLength validator will use the correct length of the input without counting leading
or trailing whitespace characters. These operations may be performed using Zend_Filter.
Zend_Form_Element has support for filter chains, allowing you to specify multiple, sequential
filters. Filtering happens both during validation and when you retrieve the element value via
getValue():
$filtered = $element->getValue();
• providing a filter name – either a short name or fully qualified class name
Short names are typically the filter name minus the prefix. In the default case, this will mean
minus the 'Zend_Filter_' prefix. The first letter can be upper-cased or lower-cased.
(Recall that the third argument indicates which plugin loader on which to perform
the action.)
If at any time you need the unfiltered value, use the getUnfilteredValue() method:
$unfiltered = $element->getUnfilteredValue();
• addFilters(array $filters)
686
Zend_Form
3.3. Validators
If you subscribe to the security mantra of "filter input, escape output," you'll should use validator
to filter input submitted with your form. In Zend_Form, each element includes its own validator
chain, consisting of Zend_Validate_* validators.
• providing a validator name – either a short name or fully qualified class name
Short names are typically the validator name minus the prefix. In the default case, this will mean
minus the 'Zend_Validate_' prefix. As is the case with filters, the first letter can be upper-cased
or lower-cased.
(Recall that the third argument indicates which plugin loader on which to perform
the action.)
If failing a particular validation should prevent later validators from firing, pass boolean TRUE as
the second parameter:
$element->addValidator('alnum', true);
If you are using a string name to add a validator, and the validator class accepts arguments to
the constructor, you may pass these to the third parameter of addValidator() as an array:
687
Zend_Form
Arguments passed in this way should be in the order in which they are defined in the constructor.
The above example will instantiate the Zend_Validate_StringLenth class with its $min and
$max parameters:
You can also set many validators at once, using addValidators(). The basic usage is to
pass an array of arrays, with each array containing 1 to 3 values, matching the constructor of
addValidator():
$element->addValidators(array(
array('NotEmpty', true),
array('alnum'),
array('stringLength', false, array(6, 20)),
));
If you want to be more verbose or explicit, you can use the array keys 'validator',
'breakChainOnFailure', and 'options':
$element->addValidators(array(
array(
'validator' => 'NotEmpty',
'breakChainOnFailure' => true),
array('validator' => 'alnum'),
array(
'validator' => 'stringLength',
'options' => array(6, 20)),
));
This usage is good for illustrating how you could then configure validators in a config file:
element.validators.notempty.validator = "NotEmpty"
element.validators.notempty.breakChainOnFailure = true
element.validators.alnum.validator = "Alnum"
element.validators.strlen.validator = "StringLength"
element.validators.strlen.options.min = 6
element.validators.strlen.options.max = 20
Notice that every item has a key, whether or not it needs one; this is a limitation of using
configuration files -- but it also helps make explicit what the arguments are for. Just remember
that any validator options must be specified in order.
688
Zend_Form
if ($element->isValid($value)) {
// valid
} else {
// invalid
}
Validation Context
if (is_array($context)) {
if (isset($context['password_confirm'])
&& ($value == $context['password_confirm']))
{
return true;
}
} elseif (is_string($context) && ($value == $context)) {
return true;
}
$this->_error(self::NOT_MATCH);
return false;
}
}
Validators are processed in order. Each validator is processed, unless a validator created with
a TRUE breakChainOnFailure value fails its validation. Be sure to specify your validators in
a reasonable order.
689
Zend_Form
After a failed validation, you can retrieve the error codes and messages from the validator chain:
$errors = $element->getErrors();
$messages = $element->getMessages();
(Note: error messages returned are an associative array of error code / error message pairs.)
• By default, when an element is required, a flag, 'allowEmpty', is also TRUE. This means that if
a value evaluating to empty is passed to isValid(), the validators will be skipped. You can
toggle this flag using the accessor setAllowEmpty($flag); when the flag is FALSE and a
value is passed, the validators will still run.
• By default, if an element is required but does not contain a 'NotEmpty' validator, isValid()
will add one to the top of the stack, with the breakChainOnFailure flag set. This behavior
lends required flag semantic meaning: if no value is passed, we immediately invalidate the
submission and notify the user, and prevent other validators from running on what we already
know is invalid data.
If you do not want this behavior, you can turn it off by passing a FALSE value to
setAutoInsertNotEmptyValidator($flag); this will prevent isValid() from placing
the 'NotEmpty' validator in the validator chain.
This means when an element contains an integer 0 or an string '0' then the
element will be seen as not empty. If you want to have a different behaviour you
must create your own instance of Zend_Validate_NotEmpty. There you can
define the behaviour of this validator. See Zend_Validate_NotEmpty for details.
• setRequired($flag) and isRequired() allow you to set and retrieve the status of the
'required' flag. When set to boolean TRUE, this flag requires that the element be in the data
processed by Zend_Form.
690
Zend_Form
• addValidators(array $validators)
• getErrorMessages(): retrieve the list of custom error messages that have been defined.
• clearErrorMessages(): remove all custom error messages that have been defined.
• hasErrors(): determine whether the element has either failed validation or been marked
as invalid.
• addError($message): add a message to the custom error messages stack and flag the
element as invalid.
• setErrors(array $messages): overwrite the custom error messages stack with the
provided messages and flag the element as invalid.
All errors set in this fashion may be translated. Additionally, you may insert the placeholder
"%value%" to represent the element value; this current element value will be substituted when
the error messages are retrieved.
691
Zend_Form
3.4. Decorators
One particular pain point for many web developers is the creation of the XHTML forms
themselves. For each element, the developer needs to create markup for the element itself
(typically a label) and special markup for displaying validation error messages. The more
elements on the page, the less trivial this task becomes.
Zend_Form_Element tries to solve this issue through the use of "decorators". Decorators are
simply classes that have access to the element and a method for rendering content. For more
information on how decorators work, please see the section on Zend_Form_Decorator.
• ViewHelper: specifies a view helper to use to render the element. The 'helper' element attribute
can be used to specify which view helper to use. By default, Zend_Form_Element specifies
the 'formText' view helper, but individual subclasses specify different helpers.
This option may be mixed with any other options you pass, both as array options
or in a Zend_Config object.
Since the order in which decorators are registered matters- the first decorator registered is
executed first- you will need to make sure you register your decorators in an appropriate order,
or ensure that you set the placement options in a sane fashion. To give an example, here is the
code that registers the default decorators:
$this->addDecorators(array(
array('ViewHelper'),
array('Errors'),
array('Description', array('tag' => 'p', 'class' => 'description')),
array('HtmlTag', array('tag' => 'dd')),
array('Label', array('tag' => 'dt')),
));
The initial content is created by the 'ViewHelper' decorator, which creates the form element itself.
Next, the 'Errors' decorator fetches error messages from the element, and, if any are present,
692
Zend_Form
passes them to the 'FormErrors' view helper to render. If a description is present, the 'Description'
decorator will append a paragraph of class 'description' containing the descriptive text to the
aggregated content. The next decorator, 'HtmlTag', wraps the element, errors, and description
in an HTML <dd> tag. Finally, the last decorator, 'label', retrieves the element's label and passes
it to the 'FormLabel' view helper, wrapping it in an HTML <dt> tag; the value is prepended to the
content by default. The resulting output looks basically like this:
To get around this, you can use aliases. Instead of passing a decorator or
decorator name as the first argument to addDecorator(), pass an array with
a single element, with the alias pointing to the decorator object or name:
// Alias to 'FooBar':
$element->addDecorator(array('FooBar' => 'HtmlTag'),
array('tag' => 'div'));
693
Zend_Form
• addDecorators(array $decorators)
• name: the element name. Uses the setName() and getName() accessors.
• label: the element label. Uses the setLabel() and getLabel() accessors.
• order: the index at which an element should appear in the form. Uses the setOrder() and
getOrder() accessors.
• value: the current element value. Uses the setValue() and getValue() accessors.
• required: flag indicating whether or not the element is required when performing form
validation. Uses the setRequired() and getRequired() accessors. This flag is FALSE
by default.
• allowEmpty: flag indicating whether or not a non-required (optional) element should attempt
to validate empty values. If it is set to TRUE and the required flag is FALSE, empty values are
not passed to the validator chain and are presumed TRUE. Uses the setAllowEmpty() and
getAllowEmpty() accessors. This flag is TRUE by default.
694
Zend_Form
Form elements may require additional metadata. For XHTML form elements, for instance, you
may want to specify attributes such as the class or id. To facilitate this are a set of accessors:
Most of the time, however, you can simply access them as object properties, as
Zend_Form_Element utilizes overloading to facilitate access to them:
By default, all attributes are passed to the view helper used by the element during rendering,
and rendered as HTML attributes of the element tag.
• Configuration:
• setOptions(array $options)
• setConfig(Zend_Config $config)
• I18n:
• getTranslator()
• setDisableTranslator($flag)
• translatorIsDisabled()
• Properties:
• setName($name)
• getName()
• setValue($value)
• getValue()
• getUnfilteredValue()
695
Zend_Form
• setLabel($label)
• getLabel()
• setDescription($description)
• getDescription()
• setOrder($order)
• getOrder()
• setRequired($flag)
• getRequired()
• setAllowEmpty($flag)
• getAllowEmpty()
• setAutoInsertNotEmptyValidator($flag)
• autoInsertNotEmptyValidator()
• setIgnore($flag)
• getIgnore()
• getType()
• setAttrib($name, $value)
• setAttribs(array $attribs)
• getAttrib($name)
• getAttribs()
• getPluginLoader($type)
• addPrefixPaths(array $spec)
• Validation:
• addValidators(array $validators)
• setValidators(array $validators)
• getValidator($name)
696
Zend_Form
• getValidators()
• removeValidator($name)
• clearValidators()
• getErrors()
• getMessages()
• Filters:
• addFilters(array $filters)
• setFilters(array $filters)
• getFilter($name)
• getFilters()
• removeFilter($name)
• clearFilters()
• Rendering:
• getView()
• addDecorators(array $decorators)
• setDecorators(array $decorators)
• getDecorator($name)
• getDecorators()
• removeDecorator($name)
• clearDecorators()
3.8. Configuration
Zend_Form_Element's constructor accepts either an array of options or a Zend_Config object
containing options, and it can also be configured using either setOptions() or setConfig().
Generally speaking, keys are named as follows:
• If 'set' + key refers to a Zend_Form_Element method, then the value provided will be passed
to that method.
697
Zend_Form
• setConfig
• setOptions
• setPluginLoader
• setTranslator
• setView
As an example, here is a config file that passes configuration for every type of configurable data:
[element]
name = "foo"
value = "foobar"
label = "Foo:"
order = 10
required = true
allowEmpty = false
autoInsertNotEmptyValidator = true
description = "Foo elements are for examples"
ignore = false
attribs.id = "foo"
attribs.class = "element"
; sets 'onclick' attribute
onclick = "autoComplete(this, '/form/autocomplete/element')"
prefixPaths.decorator.prefix = "My_Decorator"
prefixPaths.decorator.path = "My/Decorator/"
disableTranslator = 0
validators.required.validator = "NotEmpty"
validators.required.breakChainOnFailure = true
validators.alpha.validator = "alpha"
validators.regex.validator = "regex"
validators.regex.options.pattern = "/^[A-F].*/$"
filters.ucase.filter = "StringToUpper"
decorators.element.decorator = "ViewHelper"
decorators.element.options.helper = "FormText"
decorators.label.decorator = "Label"
698
Zend_Form
There are two methods typically used to extend an element: init(), which can be used to add
custom initialization logic to your element, and loadDefaultDecorators(), which can be
used to set a list of default decorators used by your element.
As an example, let's say that all text elements in a form you are creating need to be filtered with
StringTrim, validated with a common regular expression, and that you want to use a custom
decorator you've created for displaying them, 'My_Decorator_TextItem'. In addition, you have a
number of standard attributes, including 'size', 'maxLength', and 'class' you wish to specify. You
could define an element to accomplish this as follows:
You could then inform your form object about the prefix path for such elements, and start creating
elements:
The 'foo' element will now be of type My_Element_Text, and exhibit the behaviour you've
outlined.
Another method you may want to override when extending Zend_Form_Element is the
loadDefaultDecorators() method. This method conditionally loads a set of default
decorators for your element; you may wish to substitute your own decorators in your extending
class:
There are many ways to customize elements. Read the API documentation of
Zend_Form_Element to learn about all of the available methods.
699
Zend_Form
• Value aggregation, including populating items and retrieving both filtered and unfiltered values
from all items
• Iteration over all items, in the order in which they are entered or based on the order hints
retrieved from each item
• Rendering of the entire form, either via a single decorator that performs custom rendering or
by iterating over each item in the form
While forms created with Zend_Form may be complex, probably the best use case is for simple
forms; its best use is for Rapid Application Development (RAD) and prototyping.
You can optionally pass in a instance of Zend_Config or an array, which will be used to set
object state and potentially create new elements:
Zend_Form is iterable, and will iterate over elements, display groups, and subforms, using the
order they were registered and any order index each may have. This is useful in cases where
you wish to render the elements manually in the appropriate order.
Zend_Form's magic lies in its ability to serve as a factory for elements and display groups, as
well as the ability to render itself through decorators.
The following loader types are used with the various plugin loader methods: 'element' and
'decorator'. The type names are case insensitive.
• setPluginLoader($loader, $type): $loader is the plugin loader object itself, while type
is one of the types specified above. This sets the plugin loader for the given type to the newly
specified loader object.
700
Zend_Form
"Element/" and "Decorator/". If you have all your extra form element classes under a common
hierarchy, this is a convenience method for setting the base prefix for them.
• addPrefixPaths(array $spec): allows you to add many paths at once to one or more
plugin loaders. It expects each array item to be an array with the keys 'path', 'prefix', and 'type'.
Additionally, you can specify prefix paths for all elements and display groups created through a
Zend_Form instance using the following methods:
Custom elements and decorators are an easy way to share functionality between forms and
encapsulate custom functionality. See the Custom Label example in the elements documentation
for an example of how custom elements can be used as replacements for standard classes.
4.2. Elements
Zend_Form provides several accessors for adding and removing form elements from a form.
These can take element object instances or serve as factories for instantiating the element
objects themselves.
The most basic method for adding an element is addElement(). This method can take either
an object of type Zend_Form_Element (or of a class extending Zend_Form_Element), or
arguments for building a new element -- including the element type, name, and any configuration
options.
Some examples:
// Using a factory
//
// Creates an element of type Zend_Form_Element_Text with the
// name of 'foo':
$form->addElement('text', 'foo');
701
Zend_Form
Once an element has been added to the form, you can retrieve it by name. This can be done
either by using the getElement() method or by using overloading to access the element as
an object property:
// getElement():
$foo = $form->getElement('foo');
// As object property:
$foo = $form->foo;
Occasionally, you may want to create an element without attaching it to the form (for instance, if
you wish to make use of the various plugin paths registered with the form, but wish to later attach
the object to a sub form). The createElement() method allows you to do so:
Sometimes you'll want to populate the form with specified values prior to rendering. This can be
done with either the setDefaults() or populate() methods:
$form->setDefaults($data);
$form->populate($data);
On the flip side, you may want to clear a form after populating or validating it; this can be done
using the reset() method:
$form->reset();
702
Zend_Form
You can set prefix paths for all elements by type, or using a global prefix. Some examples:
You can set decorators for all elements. setElementDecorators() accepts an array of
decorators, just like setDecorators(), and will overwrite any previously set decorators in
each element. In this example, we set the decorators to simply a ViewHelper and a Label:
$form->setElementDecorators(array(
'ViewHelper',
'Label'
));
703
Zend_Form
You can also set decorators for a subset of elements, either by inclusion or exclusion. The
second argument to setElementDecorators() may be an array of element names; by
default, specifying such an array will set the specified decorators on those elements only.
You may also pass a third argument, a flag indicating whether this list of elements is for
inclusion or exclusion purposes. If the flag is FALSE, it will decorate all elements except
those in the passed list. As with standard usage of the method, any decorators passed will
overwrite any previously set decorators in each element.
In the following snippet, we indicate that we want only the ViewHelper and Label decorators
for the 'foo' and 'bar' elements:
$form->setElementDecorators(
array(
'ViewHelper',
'Label'
),
array(
'foo',
'bar'
)
);
On the flip side, with this snippet, we'll now indicate that we want to use only the ViewHelper
and Label decorators for every element except the 'foo' and 'bar' elements:
$form->setElementDecorators(
array(
'ViewHelper',
'Label'
),
array(
'foo',
'bar'
),
false
);
You can use the inclusion/exclusion array to overcome this issue as noted in the
previous example.
So, use this method wisely, and realize that you may need to exclude some
elements or manually change some elements' decorators to prevent unwanted
output.
704
Zend_Form
In some cases, you may want to apply the same filter to all elements; a common case is
to trim() all values:
$form->setElementFilters(array('StringTrim'));
• addElements(array $elements)
• setElements(array $elements)
• getElement($name)
• getElements()
• removeElement($name)
• clearElements()
• setDefaults(array $defaults)
• setDefault($name, $value)
• getValue($name)
• getValues()
• getUnfilteredValue($name)
• getUnfilteredValues()
• setElementFilters(array $filters)
• setElementDecorators(array $decorators)
• addElementPrefixPaths(array $spec)
The base class for display groups is Zend_Form_DisplayGroup. While it can be instantiated
directly, it is usually best to use Zend_Form's addDisplayGroup() method to do so. This
method takes an array of elements as its first argument, and a name for the display group as
705
Zend_Form
its second argument. You may optionally pass in an array of options or a Zend_Config object
as the third argument.
Assuming that the elements 'username' and 'password' are already set in the form, the following
code would group these elements in a 'login' display group:
You can access display groups using the getDisplayGroup() method, or via overloading
using the display group's name:
// Using getDisplayGroup():
$login = $form->getDisplayGroup('login');
// Using overloading:
$login = $form->login;
$form->addDisplayGroup(
array('foo', 'bar'),
'foobar',
array('disableLoadDefaultDecorators' => true)
);
This option may be mixed with any other options you pass, both as array options
or in a Zend_Config object.
Exemple 386. Setting Decorator Prefix Path for All Display Groups
By default, display groups inherit whichever decorator paths the form uses; however, if they
should look in alternate locations, you can use the addDisplayGroupPrefixPath()
method.
$form->addDisplayGroupPrefixPath('My_Foo_Decorator', 'My/Foo/Decorator');
You can set decorators for all display groups. setDisplayGroupDecorators() accepts
an array of decorators, just like setDecorators(), and will overwrite any previously set
decorators in each display group. In this example, we set the decorators to simply a fieldset
(the FormElements decorator is necessary to ensure that the elements are iterated):
$form->setDisplayGroupDecorators(array(
'FormElements',
'Fieldset'
));
706
Zend_Form
If the class has not yet been loaded, Zend_Form will attempt to do so using Zend_Loader.
You can also specify a default display group class to use with the form such that all display
groups created with the form object will use that class:
• addDisplayGroups(array $groups)
• setDisplayGroups(array $groups)
• getDisplayGroup($name)
• getDisplayGroups()
• removeDisplayGroup($name)
• clearDisplayGroups()
• setDisplayGroupDecorators(array $decorators)
• addDisplayGroupPrefixPath($prefix, $path)
• setDefaultDisplayGroupClass($class)
• getDefaultDisplayGroupClass($class)
• Configuration:
• setOptions(array $options)
• setConfig(Zend_Config $config)
707
Zend_Form
• Metadata:
• setAttrib($key, $value)
• addAttribs(array $attribs)
• setAttribs(array $attribs)
• getAttrib($key)
• getAttribs()
• removeAttrib($key)
• clearAttribs()
• setName($name)
• getName()
• setDescription($value)
• getDescription()
• setLegend($legend)
• getLegend()
• setOrder($order)
• getOrder()
• Elements:
• addElements(array $elements)
• setElements(array $elements)
• getElement($name)
• getElements()
• removeElement($name)
• clearElements()
• Plugin loaders:
• setPluginLoader(Zend_Loader_PluginLoader $loader)
• getPluginLoader()
• addPrefixPath($prefix, $path)
• addPrefixPaths(array $spec)
708
Zend_Form
• Decorators:
• addDecorators(array $decorators)
• setDecorators(array $decorators)
• getDecorator($name)
• getDecorators()
• removeDecorator($name)
• clearDecorators()
• Rendering:
• getView()
• I18n:
• getTranslator()
• setDisableTranslator($flag)
• translatorIsDisabled()
• Creating logical element groups. Since sub forms are simply forms, you can validate subforms
as individual entities.
• Creating multi-page forms. Since sub forms are simply forms, you can display a separate sub
form per page, building up multi-page forms where each form has its own validation logic. Only
once all sub forms validate would the form be considered complete.
• Display groupings. Like display groups, sub forms, when rendered as part of a larger form,
can be used to group elements. Be aware, however, that the master form object will have no
awareness of the elements in sub forms.
A sub form may be a Zend_Form object, or, more typically, a Zend_Form_SubForm object. The
latter contains decorators suitable for inclusion in a larger form (i.e., it does not render additional
HTML form tags, but does group elements). To attach a sub form, simply add it to the form and
give it a name:
$form->addSubForm($subForm, 'subform');
You can retrieve a sub form using either getSubForm($name) or overloading using the sub
form name:
709
Zend_Form
// Using getSubForm():
$subForm = $form->getSubForm('subform');
// Using overloading:
$subForm = $form->subform;
Sub forms are included in form iteration, although the elements they contain are not.
$form->setSubFormDecorators(array(
'FormElements',
'Fieldset'
));
• addSubForms(array $subForms)
• setSubForms(array $subForms)
• getSubForm($name)
• getSubForms()
• removeSubForm($name)
• clearSubForms()
• setSubFormDecorators(array $decorators)
You can set and retrieve a form's name using the name accessors:
710
Zend_Form
To set the action (url to which the form submits) and method (method by which it should submit,
e.g., 'POST' or 'GET'), use the action and method accessors:
You may also specify the form encoding type specifically using the enctype
accessors. Zend_Form defines two constants, Zend_Form::ENCTYPE_URLENCODED and
Zend_Form::ENCTYPE_MULTIPART, corresponding to the values 'application/x-www-form-
urlencoded' and 'multipart/form-data', respectively; however, you can set this to any arbitrary
encoding type.
The method, action, and enctype are only used internally for rendering, and not
for any sort of validation.
$numItems = count($form);
Setting arbitrary metadata is done through the attribs accessors. Since overloading in
Zend_Form is used to access elements, display groups, and sub forms, this is the only method
for accessing metadata.
// Setting attributes:
$form->setAttrib('class', 'zend-form')
->addAttribs(array(
'id' => 'registration',
'onSubmit' => 'validate(this)',
));
// Retrieving attributes:
$class = $form->getAttrib('class');
$attribs = $form->getAttribs();
// Remove an attribute:
$form->removeAttrib('onSubmit');
4.6. Decorators
Creating the markup for a form is often a time-consuming task, particularly if you plan on re-using
the same markup to show things such as validation errors, submitted values, etc. Zend_Form's
answer to this issue is decorators.
Decorators for Zend_Form objects can be used to render a form. The FormElements decorator
will iterate through all items in a form -- elements, display groups, and sub forms -- and render
711
Zend_Form
them, returning the result. Additional decorators may then be used to wrap this content, or append
or prepend it.
The default decorators for Zend_Form are FormElements, HtmlTag (wraps in a definition list),
and Form; the equivalent code for creating them is as follows:
$form->setDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'dl')),
'Form'
));
Any attributes set on the form object will be used as HTML attributes of the <form> tag.
This option may be mixed with any other options you pass, both as array options
or in a Zend_Config object.
To get around this, you can use aliases. Instead of passing a decorator or
decorator name as the first argument to addDecorator(), pass an array with
a single element, with the alias pointing to the decorator object or name:
// Alias to 'FooBar':
$form->addDecorator(array('FooBar' => 'HtmlTag'), array('tag' => 'div'));
712
Zend_Form
You may create your own decorators for generating the form. One common use case is if you
know the exact HTML you wish to use; your decorator could create the exact HTML and simply
return it, potentially using the decorators from individual elements or display groups.
• addDecorators(array $decorators)
• setDecorators(array $decorators)
• getDecorator($name)
• getDecorators()
• removeDecorator($name)
• clearDecorators()
Zend_Form also uses overloading to allow rendering specific decorators. __call() will
intercept methods that lead with the text 'render' and use the remainder of the method name to
lookup a decorator; if found, it will then render that single decorator. Any arguments passed to the
method call will be used as content to pass to the decorator's render() method. As an example:
4.7. Validation
A primary use case for forms is validating submitted data. Zend_Form allows you to validate
an entire form, a partial form, or responses for XmlHttpRequests (AJAX). If the submitted data
is not valid, it has methods for retrieving the various error codes and messages for elements
and sub forms.
if (!$form->isValid($_POST)) {
// failed validation
}
isValid() will validate every required element, and any unrequired element contained in the
submitted data.
713
Zend_Form
Sometimes you may need to validate only a subset of the data; for this, use
isValidPartial($data):
if (!$form->isValidPartial($data)) {
// failed validation
}
isValidPartial() only attempts to validate those items in the data for which there are
matching elements; if an element is not represented in the data, it is skipped.
When validating elements or groups of elements for an AJAX request, you will typically be
validating a subset of the form, and want the response back in JSON. processAjax() does
precisely that:
$json = $form->processAjax($data);
You can then simply send the JSON response to the client. If the form is valid, this will be a
boolean TRUE response. If not, it will be a javascript object containing key/message pairs, where
each 'message' is an array of validation error messages.
For forms that fail validation, you can retrieve both error codes and error messages, using
getErrors() and getMessages(), respectively:
$codes = $form->getErrors();
$messages = $form->getMessages();
You can retrieve codes and error messages for individual elements by simply passing the element
name to each:
$codes = $form->getErrors('username');
$messages = $form->getMessages('username');
714
Zend_Form
• getErrorMessages(): retrieve the list of custom error messages that have been defined.
• clearErrorMessages(): remove all custom error messages that have been defined.
• addError($message): add a message to the custom error messages stack and flag the
form as invalid.
• setErrors(array $messages): overwrite the custom error messages stack with the
provided messages and flag the form as invalid.
You can retrieve all the valid values that match the submitted data by calling:
$validValues = $form->getValidValues($_POST);
4.8. Methods
The following is a full list of methods available to Zend_Form, grouped by type:
• setOptions(array $options)
• setConfig(Zend_Config $config)
• getPluginLoader($type = null)
• addPrefixPaths(array $spec)
• addElementPrefixPaths(array $spec)
• addDisplayGroupPrefixPath($prefix, $path)
715
Zend_Form
• Metadata:
• setAttrib($key, $value)
• addAttribs(array $attribs)
• setAttribs(array $attribs)
• getAttrib($key)
• getAttribs()
• removeAttrib($key)
• clearAttribs()
• setAction($action)
• getAction()
• setMethod($method)
• getMethod()
• setName($name)
• getName()
• Elements:
• addElements(array $elements)
• setElements(array $elements)
• getElement($name)
• getElements()
• removeElement($name)
• clearElements()
• setDefaults(array $defaults)
• setDefault($name, $value)
• getValue($name)
• getValues()
• getUnfilteredValue($name)
• getUnfilteredValues()
• setElementFilters(array $filters)
• setElementDecorators(array $decorators)
716
Zend_Form
• Sub forms:
• addSubForms(array $subForms)
• setSubForms(array $subForms)
• getSubForm($name)
• getSubForms()
• removeSubForm($name)
• clearSubForms()
• setSubFormDecorators(array $decorators)
• Display groups:
• addDisplayGroups(array $groups)
• setDisplayGroups(array $groups)
• getDisplayGroup($name)
• getDisplayGroups()
• removeDisplayGroup($name)
• clearDisplayGroups()
• setDisplayGroupDecorators(array $decorators)
• Validation
• populate(array $values)
• isValid(array $data)
• isValidPartial(array $data)
• processAjax(array $data)
• persistData()
• getErrors($name = null)
• getMessages($name = null)
• Rendering:
• getView()
717
Zend_Form
• addDecorators(array $decorators)
• setDecorators(array $decorators)
• getDecorator($name)
• getDecorators()
• removeDecorator($name)
• clearDecorators()
• I18n:
• getTranslator()
• setDisableTranslator($flag)
• translatorIsDisabled()
4.9. Configuration
Zend_Form is fully configurable via setOptions() and setConfig() (or by passing options
or a Zend_Config object to the constructor). Using these methods, you can specify form
elements, display groups, decorators, and metadata.
As a general rule, if 'set' + the option key refers to a Zend_Form method, then the value provided
will be passed to that method. If the accessor does not exist, the key is assumed to reference
an attribute, and will be passed to setAttrib().
• setConfig
• setDefault
• setOptions
• setPluginLoader
• setSubForms
• setTranslator
718
Zend_Form
• setView
As an example, here is a config file that passes configuration for every type of configurable data:
[element]
name = "registration"
action = "/user/register"
method = "post"
attribs.class = "zend_form"
attribs.onclick = "validate(this)"
disableTranslator = 0
prefixPath.element.prefix = "My_Element"
prefixPath.element.path = "My/Element/"
elementPrefixPath.validate.prefix = "My_Validate"
elementPrefixPath.validate.path = "My/Validate/"
displayGroupPrefixPath.prefix = "My_Group"
displayGroupPrefixPath.path = "My/Group/"
elements.username.type = "text"
elements.username.options.label = "Username"
elements.username.options.validators.alpha.validator = "Alpha"
elements.username.options.filters.lcase = "StringToLower"
; more elements, of course...
elementFilters.trim = "StringTrim"
;elementDecorators.trim = "StringTrim"
displayGroups.login.elements.username = "username"
displayGroups.login.elements.password = "password"
displayGroupDecorators.elements.decorator = "FormElements"
displayGroupDecorators.fieldset.decorator = "Fieldset"
decorators.elements.decorator = "FormElements"
decorators.fieldset.decorator = "FieldSet"
decorators.fieldset.decorator.options.class = "zend_form"
decorators.form.decorator = "Form"
The above could easily be abstracted to an XML or PHP array-based configuration file.
• You can unit test your form easily to ensure validations and rendering perform as expected.
• Re-use of form objects, and greater portability (no need to track config files).
The most typical use case would be to use the init() method to setup specific form elements
and configuration:
719
Zend_Form
$this->addElements(array(
$username,
$password,
$submit
));
$this->setDecorators(array(
'FormElements',
'Fieldset',
'Form'
));
}
}
and all functionality is already setup and ready; no config files needed. (Note that this example
is greatly simplified, as it contains no validators or filters for the elements.)
Another common reason for extension is to define a set of default decorators. You can do this
by overriding the loadDefaultDecorators() method:
720
Zend_Form
'FormElements',
'Fieldset',
'Form'
));
}
}
Un nombre arbitraire de décorateurs peut être attaché à chaque objet du formulaire (élément,
groupe d'affichage, sous-formulaires ou encore l'objet formulaire lui-même) ; cependant seul un
décorateur par type peut être attaché. Les décorateurs sont appelés dans l'ordre dans lequel ils
ont été enregistrés. En fonction du décorateur en question, celui-ci peut remplacer le contenu
qui lui est passé, ou alors le faire précédé ou suivre.
Avant le rendu d'un décorateur, au travers de sa méthode render(), l'objet sur lequel il agit lui
est passé en argument, grâce à setElement(), et ainsi le décorateur peut piloter l'élément sur
lequel il agit. Ceci permet de créer des décorateurs qui n'agissent que sur un petit paramètre de
l'élément auquel ils sont rattachés, comme le label, les messages d'erreur, etc... En chaînant des
décorateurs qui rendent chacun individuellement un petit morceau d'un élément, vous pouvez
créer une mise en forme complexe représentant l'objet (élément) dans son intégralité.
5.1. Configuration
Pour configurer un décorateur, passez un tableau d'options ou un objet Zend_Config à son
constructeur. Aussi, un tableau peut être passé à setOptions(), ou un objet Zend_Config
à setConfig().
Options de base:
L'interface des décorateurs spécifie les méthodes pour agir sur les options. Les voici :
721
Zend_Form
Les décorateurs sont destinés à agir avec tous les objets du formulaire, Zend_Form,
Zend_Form_Element, Zend_Form_DisplayGroup, et toute classe en dérivant. La méthode
setElement() vous permet de passer l'objet au décorateur sur lequel il travaille.
getElement() vous permet de récupérer cet objet depuis le décorateur.
Chaque méthode render() des décorateurs accepte en paramètre une chaîne $content.
Lorsque le premier décorateur est appelé, cette chaîne est en toute logique vide, alors que tous
les appels successifs travailleront sur le contenu précédent. Selon le type de décorateur et ses
options, la chaîne sera alors remplacée, précédée ou suivie du nouveau contenu décoré. Dans
ces deux derniers cas, un séparateur optionnel peut être utilisé.
interface Zend_Form_Decorator_Interface
{
public function __construct($options = null);
public function setElement($element);
public function getElement();
public function setOptions(array $options);
public function setConfig(Zend_Config $config);
public function setOption($key, $value);
public function getOption($key);
public function getOptions();
public function removeOption($key);
public function clearOptions();
public function render($content);
}
Par exemple, imaginons que vous ne souhaitiez pas vous encombrer avec un nombre important
de décorateurs, et que vous vouliez afficher les principale caractéristiques d'un élément grâce
à un seul décorateur (label, élément, messages d'erreur et description), le tout dans une div.
Voici comment vous pourriez procéder :
722
Zend_Form
$separator = $this->getSeparator();
$placement = $this->getPlacement();
723
Zend_Form
$label = $this->buildLabel();
$input = $this->buildInput();
$errors = $this->buildErrors();
$desc = $this->buildDescription();
switch ($placement) {
case (self::PREPEND):
return $output . $separator . $content;
case (self::APPEND):
default:
return $content . $separator . $output;
}
}
}
Vous pouvez maintenant placer ce décorateur dans les chemins des décorateurs :
// pour un élément:
$element->addPrefixPath('My_Decorator',
'My/Decorator/',
'decorator');
Dès à présent, vous pouvez indiquer que vous voulez utiliser le décorateur 'Composite', (c'est
son nom de classe sans le préfixe) et l'attacher à un élément :
Cet exemple vous montre comment rendre un contenu HTML complexe à partir de propriétés
d'un élément, en une seule passe. Il existe des décorateurs qui ne s'occupent que d'une propriété
de l'élément auquel ils sont rattachés, 'Errors' et 'Label' en sont d'excellents exemples qui
permettent un placement fin.
Par exemple, si vous souhaitez simplement informer l'utilisateur d'une erreur, mais sans lui
montrer les messages d'erreurs, vous pouvez créer votre propre décorateur 'Errors' :
class My_Decorator_Errors
{
public function render($content = '')
{
$output = '<div class="errors">La valeur est invalide, rééssayez</div>';
$placement = $this->getPlacement();
$separator = $this->getSeparator();
switch ($placement) {
724
Zend_Form
case 'PREPEND':
return $output . $separator . $content;
case 'APPEND':
default:
return $content . $separator . $output;
}
}
}
Dans cet exemple particulier, comme le segment final du nom de la classe, 'Errors', respecte
la syntaxe de Zend_Form_Decorator_Errors, il sera alors utilisé à la place du décorateur
par défaut -- ceci signifie que vous n'avez pas besoin de l'injecter dans un élément particulier.
En nommant vos fins de classes de décorateurs comme celles des décorateurs standards, vous
pouvez changer la décoration sans agir sur les éléments en question.
Ceci peut être particulièrement utile lors du rendu d'un formulaire avec le décorateur ViewScript ;
là où chaque élément utilise ses décorateurs pour rendre du contenu, mais de manière très fine.
6.1. Zend_Form_Element_Button
Used for creating HTML button elements, Zend_Form_Element_Button extends
Zend_Form_Element_Submit, specifying some custom functionality. It specifies the 'formButton'
view helper for decoration.
Like the submit element, it uses the element's label as the element value for display purposes; in
other words, to set the text of the button, set the value of the element. The label will be translated
if a translation adapter is present.
725
Zend_Form
Because the label is used as part of the element, the button element uses only the ViewHelper
and DtDdWrapper decorators.
After populating or validating a form, you can check if the given button was clicked using the
isChecked() method.
6.2. Zend_Form_Element_Captcha
CAPTCHAs are used to prevent automated submission of forms by bots and other automated
processes.
The Captcha form element allows you to specify which Zend_Captcha adapter you wish to utilize
as a form CAPTCHA. It then sets this adapter as a validator to the object, and uses a Captcha
decorator for rendering (which proxies to the CAPTCHA adapter).
Adapters may be any adapters in Zend_Captcha, as well as any custom adapters you may have
defined elsewhere. To allow this, you may pass an additional plugin loader type key, 'CAPTCHA'
or 'captcha', when specifying a plugin loader prefix path:
Captcha's may then be registered using the setCaptcha() method, which can take either a
concrete CAPTCHA instance, or the short name of a CAPTCHA adapter:
// Concrete instance:
$element->setCaptcha(new Zend_Captcha_Figlet());
// Using shortnames:
$element->setCaptcha('Dumb');
If you wish to load your element via configuration, specify either the key 'captcha' with an array
containing the key 'captcha', or both the keys 'captcha' and 'captchaOptions':
The decorator used is determined by querying the captcha adapter. By default, the Captcha
decorator is used, but an adapter may specify a different one via its getDecorator() method.
726
Zend_Form
As noted, the captcha adapter itself acts as a validator for the element. Additionally, the NotEmpty
validator is not used, and the element is marked as required. In most cases, you should need to
do nothing else to have a captcha present in your form.
6.3. Zend_Form_Element_Checkbox
HTML checkboxes allow you return a specific value, but basically operate as booleans. When
checked, the checkbox's value is submitted. When the checkbox is not checked, nothing is
submitted. Internally, Zend_Form_Element_Checkbox enforces this state.
By default, the checked value is '1', and the unchecked value '0'. You can specify the values
to use using the setCheckedValue() and setUncheckedValue() accessors, respectively.
Internally, any time you set the value, if the provided value matches the checked value, then it
is set, but any other value causes the unchecked value to be set.
Additionally, setting the value sets the checked property of the checkbox. You can query this
using isChecked() or simply accessing the property. Using the setChecked($flag) method
will both set the state of the flag as well as set the appropriate checked or unchecked value in
the element. Please use this method when setting the checked state of a checkbox element to
ensure the value is set properly.
6.4. Zend_Form_Element_File
The File form element provides a mechanism for supplying file upload fields to your form. It
utilizes Zend_File_Transfer internally to provide this functionality, and the FormFile view helper
as also the File decorator to display the form element.
By default, it uses the Http transfer adapter, which introspects the $_FILES array and allows
you to attach validators and filters. Validators and filters attached to the form element are in turn
attached to the transfer adapter.
727
Zend_Form
The above explanation of using the File form element may seem arcane, but actual usage
is relatively trivial:
You also need to ensure that the correct encoding type is provided to the form; you should
use 'multipart/form-data'. You can do this by setting the 'enctype' attribute on the form:
$form->setAttrib('enctype', 'multipart/form-data');
After the form is validated successfully, you must receive the file to store it in the final
destination using receive(). Additionally you can determinate the final location using
getFileName():
if (!$form->isValid()) {
print "Uh oh... validation error";
}
if (!$form->foo->receive()) {
print "Error receiving the file";
}
$location = $form->foo->getFileName();
File values
Within HTTP a file element has no value. For this reason and because of security
concerns getValue() returns only the uploaded filename and not the complete
path. If you need the file path, call getFileName(), which returns both the path
and the name of the file.
Per default the file will automatically be received when you call getValues() on the form. The
reason behind this behaviour is, that the file itself is the value of the file element.
$form->getValues();
Therefor another call of receive() after calling getValues() will not have an
effect. Also creating a instance of Zend_File_Transfer will not have an effect
as there no file anymore to receive.
728
Zend_Form
Still, sometimes you may want to call getValues() without receiving the file. You can archive
this by calling setValueDisabled(true). To get the actual value of this flag you can call
isValueDisabled().
Now the file will not be received when you call getValues(). So you must call receive()
on the file element, or an instance of Zend_File_Transfer yourself.
$values = $form->getValues();
if ($form->isValid($form->getPost())) {
if (!$form->foo->receive()) {
print "Upload error";
}
}
There are several states of the uploaded file which can be checked with the following methods:
• isFiltered(): Checks if the filters have already been applied to the file element or not.
// The foo file element is optional but when it's given go into here
if ($form->foo->isUploaded()) {
// foo file given... do something
}
729
Zend_Form
Creating a multifile element is the same as setting a single element. Just call
setMultiFile() after the element is created:
You now have 3 identical file upload elements with the same settings. To get the set multifile
number simply call getMultiFile().
If there are 2 file elements with the same name, the second element is not be
displayed or submitted.
Additionally, file elements are not rendered within the sub-form. So when you
add a file element into a subform, then the element will be rendered within the
main form.
To limit the size of the file uploaded, you can specify the maximum file size by
setting the MAX_FILE_SIZE option on the form. When you set this value by using the
setMaxFileSize($size) method, it will be rendered with the file element.
Note, that this is also the case when you use multiple forms.
6.5. Zend_Form_Element_Hidden
Hidden elements inject data that should be submitted, but that should not manipulated by the
user . Zend_Form_Element_Hidden accomplishes this with the 'formHidden' view helper.
730
Zend_Form
6.6. Zend_Form_Element_Hash
This element provides protection from CSRF attacks on forms, ensuring the data is submitted
by the user session that generated the form and not by a rogue script. Protection is achieved by
adding a hash element to a form and verifying it when the form is submitted.
The name of the hash element should be unique. We recommend using the salt option for the
element- two hashes with same names and different salts would not collide:
You can set the salt later using the setSalt($salt) method.
Internally, the element stores a unique identifier using Zend_Session_Namespace, and checks
for it at submission (checking that the TTL has not expired). The 'Identical' validator is then used
to ensure the submitted hash matches the stored hash.
The 'formHidden' view helper is used to render the element in the form.
6.7. Zend_Form_Element_Image
Images can be used as form elements, and you can use these images as graphical elements
on form buttons.
Image elements use the Image Decorator for rendering, in addition to the standard Errors,
HtmlTag, and Label decorators. You can optionally specify a tag to the Image decorator that will
then wrap the image element.
6.8. Zend_Form_Element_MultiCheckbox
Often you have a set of related checkboxes, and you wish to group the results. This is much like a
Multiselect, but instead of them being in a dropdown list, you need to show checkbox/value pairs.
By default, this element registers an InArray validator which validates against the
array keys of registered options. You can disable this behavior by either calling
setRegisterInArrayValidator(false), or by passing a FALSE value to the
registerInArrayValidator configuration key.
You may manipulate the various checkbox options using the following methods:
• addMultiOption($option, $value)
• addMultiOptions(array $options)
731
Zend_Form
• getMultiOption($option)
• getMultiOptions()
• removeMultiOption($option)
• clearMultiOptions()
To mark checked items, you need to pass an array of values to setValue(). The following will
check the values "bar" and "bat":
$element->setValue(array('bar', 'bat'));
Note that even when setting a single value, you must pass an array.
6.9. Zend_Form_Element_Multiselect
XHTML select elements allow a 'multiple' attribute, indicating multiple options may be selected
for submission, instead of the usual one. Zend_Form_Element_Multiselect extends
Zend_Form_Element_Select, and sets the multiple attribute to 'multiple'. Like other classes
that inherit from the base Zend_Form_Element_Multi class, you can manipulate the options
for the select using:
• addMultiOption($option, $value)
• addMultiOptions(array $options)
• getMultiOption($option)
• getMultiOptions()
• removeMultiOption($option)
• clearMultiOptions()
If a translation adapter is registered with the form and/or element, option values will be translated
for display purposes.
By default, this element registers an InArray validator which validates against the
array keys of registered options. You can disable this behavior by either calling
setRegisterInArrayValidator(false), or by passing a FALSE value to the
registerInArrayValidator configuration key.
6.10. Zend_Form_Element_Password
Password elements are basically normal text elements -- except that you typically do not want
the submitted password displayed in error messages or the element itself when the form is re-
displayed.
732
Zend_Form
6.11. Zend_Form_Element_Radio
Radio elements allow you to specify several options, of which you need a single value returned.
Zend_Form_Element_Radio extends the base Zend_Form_Element_Multi class, allowing
you to specify a number of options, and then uses the formRadio view helper to display these.
By default, this element registers an InArray validator which validates against the
array keys of registered options. You can disable this behavior by either calling
setRegisterInArrayValidator(false), or by passing a FALSE value to the
registerInArrayValidator configuration key.
Like all elements extending the Multi element base class, the following methods may be used
to manipulate the radio options displayed:
• addMultiOption($option, $value)
• addMultiOptions(array $options)
• getMultiOption($option)
• getMultiOptions()
• removeMultiOption($option)
• clearMultiOptions()
6.12. Zend_Form_Element_Reset
Reset buttons are typically used to clear a form, and are not part of submitted data. However, as
they serve a purpose in the display, they are included in the standard elements.
6.13. Zend_Form_Element_Select
Select boxes are a common way of limiting to specific choices for a given form datum.
Zend_Form_Element_Select allows you to generate these quickly and easily.
By default, this element registers an InArray validator which validates against the
array keys of registered options. You can disable this behavior by either calling
setRegisterInArrayValidator(false), or by passing a FALSE value to the
registerInArrayValidator configuration key.
As it extends the base Multi element, the following methods may be used to manipulate the
select options:
• addMultiOption($option, $value)
733
Zend_Form
• addMultiOptions(array $options)
• getMultiOption($option)
• getMultiOptions()
• removeMultiOption($option)
• clearMultiOptions()
6.14. Zend_Form_Element_Submit
Submit buttons are used to submit a form. You may use multiple submit buttons; you can
use the button used to submit the form to decide what action to take with the data submitted.
Zend_Form_Element_Submit makes this decisioning easy, by adding a isChecked()
method; as only one button element will be submitted by the form, after populating or validating
the form, you can call this method on each submit button to determine which one was used.
Zend_Form_Element_Submit uses the label as the "value" of the submit button, translating it
if a translation adapter is present. isChecked() checks the submitted value against the label
in order to determine if the button was used.
The ViewHelper and DtDdWrapper decorators to render the element. No label decorator is used,
as the button label is used when rendering the element; also, typically, you will not associate
errors with a submit element.
6.15. Zend_Form_Element_Text
By far the most prevalent type of form element is the text element, allowing for limited text entry;
it's an ideal element for most data entry. Zend_Form_Element_Text simply uses the 'formText'
view helper to display the element.
6.16. Zend_Form_Element_Textarea
Textareas are used when large quantities of text are expected, and place no limits on the
amount of text submitted (other than maximum size limits as dictated by your server or PHP).
Zend_Form_Element_Textarea uses the 'textArea' view helper to display such elements,
placing the value as the content of the element.
7.1. Zend_Form_Decorator_Callback
Le décorateur Callback peut exécuter une fonction de rappel pour rendre du contenu. Les
fonctions doivent être spécifiées grâce à l'option 'callback' passée à la configuration du
décorateur, et peut être n'importe quelle fonction PHP valide. Les fonctions peuvent accepter
3 arguments , $content ( le contenu original passé au décorateur), $element (l'objet étant
décoré), et un tableau d'options $options. Voici un exemple :
734
Zend_Form
class Util
{
public static function label($content, $element, array $options)
{
return '<span class="label">' . $element->getLabel() . "</span>";
}
}
Cette fonction de rappel devrait être spécifiée avec array('Util', 'label'), et générera du
(mauvais) code HTML pour le label. Le décorateur Callback remplacera, fera suivre ou précéder
le contenu original avec la valeur qu'il retourne.
Le décorateur Callback accepte qu'on lui passe une valeur nulle pour l'option de placement, ce
qui remplacera le contenu original par le contenu décoré. 'prepend' et 'append' restent cependant
acceptés.
7.2. Zend_Form_Decorator_Captcha
Le décorateur Captcha est à utiliser avec l'élément de formulaire CAPTCHA. Il utilise la méthode
render() de l'adaptateur de CAPTCHA pour générer son contenu.
Une variante du décorateur Captcha, 'Captcha_Word', est aussi utilisée quelques fois et créer 2
éléments, un id et un input. L'id indique l'identifiant de session à comparer et l'input est pour la
saisie du CAPTCHA. Ces 2 éléments sont validés comme un seul.
7.3. Zend_Form_Decorator_Description
Le décorateur Description peut être utilisé pour afficher la description affectée à un Zend_Form,
Zend_Form_Element, ou Zend_Form_DisplayGroup; il cherche cette description en
utilisant getDescription() sur l'objet en question.
Par défaut, si aucune description n'est présente, rien ne sera généré. Dans le cas contraire ,
la description est entourée d'un tag HTML p par défaut, même si vous pouvez passer le tag
que vous voulez en utilisant l'option tag à la création du décorateur, ou en utilisant sa méthode
setTag(). Vous pouvez aussi spécifier une classe pour le tag en renseignant l'option class
ou en appelant setClass().
La description est échappée en utilisant le mécanisme de l'objet de vue par défaut. Vous pouvez
désactiver cette fonctionnalité en passant FALSE à l'option 'escape' du décorateur ou via sa
méthode setEscape().
7.4. Zend_Form_Decorator_DtDdWrapper
Les décorateurs par défaut utilisent des listes de définition (<dl>) pour rendre les éléments de
formulaire. Comme les éléments d'un formulaire peuvent apparaître dans n'importe quel ordre,
les groupe d'affichage et les sous-formulaires peuvent être encapsulés dedans. Afin de garder
ces types d'éléments dans la liste de définition, DtDdWrapper crée une nouvelle définition vide
(<dt>) et encapsule don contenu dans une nouvelle donnée de définition (<dd>). L'affichage
ressemble alors à ceci :
<dt></dt>
<dd><fieldset id="subform">
<legend>User Information</legend>
...
</fieldset></dd>
735
Zend_Form
Ce décorateur remplace le contenu qu'on lui fournit par ce même contenu entouré de <dd>.
7.5. Zend_Form_Decorator_Errors
Les erreurs des éléments possèdent leur propre décorateur : 'Errors'. Celui-ci utilise l'aide de
vue FormErrors, qui rend les messages d'erreur dans une liste non ordonnée (<ul>) qui reçoit
la classe d'affichage "errors".
7.6. Zend_Form_Decorator_Fieldset
Les groupes d'affichage et les sous-formulaires rendent leur contenu dans des balises
HTML fieldsets par défaut. Le décorateur Fieldset vérifie une option 'legend' ou la méthode
getLegend() dans l'élément, et l'utilise comme élément de légende si non vide. Tout contenu
qu'on lui passe est entouré d'une balise HTML "fieldset", et remplace donc le contenu précédent.
Tout attribut passé à l'élément décoré sera rendu comme attribut de la balise HTML fieldset.
7.7. Zend_Form_Decorator_File
Les éléments de type "File" (upload de fichiers) ont une notation spéciale lorsque vous
utilisez de multiples éléments file ou des sous-formulaires. Le décorateur File est utilisé par
Zend_Form_Element_File et autorise plusieurs éléments avec un seul appel de méthode. Il
est utilisé automatiquement et gère alors le nom de chaque élément.
7.8. Zend_Form_Decorator_Form
Les objets Zend_Form ont en général besoin de rendre une balise HTML "form". Le décorateur
Form utilise l'aide de vue du même nom dans ce but. Il encapsule le contenu qu'on lui passe
dans une balise HTML 'form' et remplace donc ce contenu par le nouveau entouré. Les action,
méthode et attributs de l'objet Zend_Form sont bien entendus utilisés dans la balise.
7.9. Zend_Form_Decorator_FormElements
Les formulaires, groupes d'affichage et sous-formulaires sont des collections d'éléments. Afin
de rendre ces éléments, ils utilisent le décorateur FormElements, qui itère sur tous les éléments
et appelle leur méthode render() en les joignant avec le séparateur. Il peut faire suivre ou
précéder son contenu.
7.10. Zend_Form_Decorator_FormErrors
Certains développeurs ou designers préfèrent regrouper tous les messages d'erreur en haut du
formulaire. Le décorateur FormErrors a été conçu dans ce but.
<ul class="form-errors>
<li><b>[element label or name]</b><ul>
<li>[error message]</li>
<li>[error message]</li>
</ul>
</li>
<li><ul>
<li><b>[subform element label or name</b><ul>
<li>[error message]</li>
736
Zend_Form
<li>[error message]</li>
</ul>
</li>
</ul></li>
</ul>
Vous pouvez lui passer un tas d'options afin de la configurer plus finement :
• ignoreSubForms : ignore ou pas la récursion dans les sous-formulaires. Par défaut : FALSE
(autorise la récursion).
• markupElementLabelEnd : balise à ajouter après le label des éléments. Par défaut: '</b>'
• markupElementLabelStart : balise à ajouter avant le label des éléments. Par défaut: '<b>'
• markupListEnd : balise à ajouter après la liste des messages d'erreur. Par défaut: '</ul>'.
• markupListItemEnd : balise à ajouter après chaque message d'erreur. Par défaut: '</li>'
• markupListItemStart : balise à ajouter avant chaque message d'erreur. Par défaut: '<li>'
• markupListStart : balise à ajouter autour de la liste des messages d'erreur. Par défaut:
'<ul class="form-errors">'
7.11. Zend_Form_Decorator_HtmlTag
Le décorateur HtmlTag vous permet d'utiliser une balise HTML pour décorer votre contenu. La
balise utilisée est passée comme option 'tag' et toute autre option sera utilisée comme attribut
HTML à cette balise. Par défaut, le contenu généré remplace le contenu reçu par le décorateur.
Vous pouvez tout de même préciser un placement 'append' ou 'prepend'.
7.12. Zend_Form_Decorator_Image
Le décorateur Image vous permet de créer un tag HTML d'image (<input
type="image" ... />), et optionnellement le rendre à l'intérieur d'une autre balise HTML.
Par défaut, le décorateur utilise la propriété 'src' , qui peut être renseignée grâce à la méthode
setImage() (avec la source de l'image). Aussi, le label de l'élément va être utilisé comme
propriété 'alt' de la balise et le propriété imageValue (manipulée grâce à setImageValue()
et getImageValue()) sera utilisée comme valeur.
Pour spécifier une balise HTML à utiliser avec l'élément, passez l'option 'tag' au décorateur, ou
utilisez sa méthode setTag().
7.13. Zend_Form_Decorator_Label
Les éléments de formulaire possèdent en général un label, et le décorateur du même nom
permet de le rendre. Il utilise l'aide de vue FormLabel en récupérant le label de l'élément avec
getLabel(). Si aucun label n'est présent, rien n'est rendu. Par défaut, les label sont traduits
lorsqu'un objet de traduction existe.
Vous pouvez aussi spécifier optionnellement une option 'tag'. Si celle-ci est précisée, alors le
label sera encapsulé dans la balise HTML en question. Si la balise est présente mais qu'il n'y a
pas de label, alors la balise est rendu seule. Vous pouvez utiliser aussi une classe CSS grâce
à l'option 'class' ou la méthode setClass().
737
Zend_Form
Aussi, vous pouvez utiliser les préfixes ou des suffixes à afficher pour l'élément, selon si celui-ci
est requis ou pas. Par exemple on peut imaginer que tout label est suivi du caractère ":". Aussi,
tout élément requis à la saisie pourrait comporter une étoile "*". Des méthodes existent pour
effectuer cela :
Par défaut, le décorateur Label préfixe son rendu vis à vis du contenu qu'on lui passe à décorer.
L'option 'placement' reste disponible avec comme autre valeur possible 'append'
7.14. Zend_Form_Decorator_PrepareElements
Les formulaires, les groupes d'affichage et les sous-formulaires sont des collections d'éléments.
Lors de l'utilisation du décorateur ViewScript dans vos formulaires, il peut être utile de
récursivement passer l'objet de vue, le traducteur et tous les noms réels (notation tableau
des sous-formulaires) aux éléments. Cette tache peut être effectuée grâce au décorateur
'PrepareElements'. En général, vous le marquerez en tant que premier décorateur de la pile.
$form->setDecorators(array(
'PrepareElements',
array('ViewScript', array('viewScript' => 'form.phtml')),
));
7.15. Zend_Form_Decorator_ViewHelper
Beaucoup d'éléments utilisent les aides de Zend_View pour leur propre rendu, et ceci est
effectué grâce au décorateur ViewHelper. Avec lui, vous pouvez spécifier une option 'helper'
pour lui indiquer explicitement l'aide de vue à utiliser. Si aucune ne lui est fournie, il utilise le
nom de la classe de l'élément (moins le chemin : la dernière partie du nom de la classe) afin de
déterminer l'aide de vue à utiliser. Par exemple, 'Zend_Form_Element_Text' cherchera l'aide de
vue 'form' + 'Text' soit 'formText'.
Tout attributs ajoutés à l'élément sera passé à l'aide vue comme attribut de l'élément HTML
résultant.
Par défaut, ce décorateur fait suivre son contenu au contenu qu'on lui passe.
7.16. Zend_Form_Decorator_ViewScript
Quelques fois, vous pouvez avoir besoin d'un script de vue pour afficher vos éléments. Ceci
vous permettra un placement très fin et détaillé, ou alors de changer la vue utilisée en fonction
du module MVC dans lequel vous vous situez, par exemple.
Le décorateur ViewScript nécessite une option 'viewScript'. Celle-ci peut aussi être passée à
l'élément lui-même, via sa propriété 'viewScript'. Le décorateur rend alors ce script de vue comme
738
Zend_Form
un script partiel (ce qui signifie que chaque appel à lui possède son propre espace de variables).
Plusieurs variables sont alors peuplées dans le script de vue :
• Aussi, toute variable passée au décorateur via setOptions() et qui n'est pas utilisée en
interne (qui n'est pas 'placement', 'separator', etc.) est alors passée comme variable de vue.
Voici un exemple :
Le contenu n'est pas remplacé par défaut, vous pouvez le demander en spécifiant
l'option 'placement' du décorateur. Il existe plusieurs manières de faire :
// A la création du décorateur:
$element->addDecorator('ViewScript', array('placement' => false));
739
Zend_Form
L'utilisation du décorateur ViewScript est recommandée dans les cas où vous souhaitez avoir
un placement HTML très détaillé et très fin de vos éléments.
Par défaut, aucune internationalisation (i18n) n'est effectuée. Pour l'activer dans Zend_Form,
vous devrez instancier un objet Zend_Translate avec un adaptateur et l'attacher à
Zend_Form et/ou Zend_Validate. Voyez la documentation de Zend_Translate pour plus
d'informations sur la création de son objet et de ses adaptateurs.
• Le plus simple : ajoutez l'objet d'i18n dans le registre. Tout composant utilisant l'i18n dans
Zend Framework a la capacité de découvrir de lui-même un objet de traduction si celui-ci est
enregistré dans le registre à la clé "Zend_Translate" :
• Si tout ce qui vous importe est la traduction des messages d'erreurs, vous pouvez ajouter
l'objet de traduction à Zend_Validate_Abstract :
740
Zend_Form
• Messages d'erreur des validateurs : les messages d'erreurs des validateurs peuvent être
traduits. Pour cela, utilisez les identifiants des messages des validateurs (constantes de vos
validateurs Zend_Validate. Pour plus d'informations sur ces clés, voyez la documentation
de Zend_Validate.
Aussi, depuis la version 1.6.0, vous pouvez fournir des chaînes de traduction en utilisant les
messages d'erreur actuels comme identifiants. C'est le comportement recommandé pour 1.6.0
ou supérieures, nous allons déprécier l'utilisation des clés (constantes de classe) dans les
prochaines versions.
• Labels : les labels des éléments seront traduits si un objet de traduction et une chaîne de
traduction existent.
• Légende des Fieldset : les groupes d'éléments et les sous-formulaires sont rendus dans des
"fieldsets" par défaut. Le décorateur FieldSet essaye de traduire la légende via l'objet de
traduction.
• Description des formulaires et éléments de formulaire : tous les types relatifs au formulaire
(éléments, formulaires, groupes d'éléments ou sous-formulaires) permettent de spécifier une
description optionnelle. Le décorateur Description essaye de traduire la description.
• Labels de Submit et Button : les boutons (éléments Submit, Button et Reset) vont traduire le
label affiché à l'utilisateur.
741
Zend_Form
<form>
<fieldset>
<legend>Shipping Address</legend>
<dl>
<dt><label for="recipient">Ship to:</label></dt>
<dd><input name="recipient" type="text" value="" /></dd>
<dt><label for="address">Address:</label></dt>
<dd><input name="address" type="text" value="" /></dd>
<dt><label for="municipality">City:</label></dt>
<dd><input name="municipality" type="text" value="" /></dd>
<dt><label for="province">State:</label></dt>
<dd><input name="province" type="text" value="" /></dd>
<fieldset>
<legend>Billing Address</legend>
<dl>
<dt><label for="payer">Bill To:</label></dt>
<dd><input name="payer" type="text" value="" /></dd>
<dt><label for="address">Address:</label></dt>
<dd><input name="address" type="text" value="" /></dd>
<dt><label for="municipality">City:</label></dt>
<dd><input name="municipality" type="text" value="" /></dd>
<dt><label for="province">State:</label></dt>
<dd><input name="province" type="text" value="" /></dd>
<dl>
<dt><label for="terms">I agree to the Terms of Service</label></dt>
<dd><input name="terms" type="checkbox" value="" /></dd>
<dt></dt>
<dd><input name="save" type="submit" value="Save" /></dd>
</dl>
</form>
In this example, the billing and shipping address contain some identical fields, which means one
would overwrite the other. We can solve this solution using array notation:
<form>
<fieldset>
<legend>Shipping Address</legend>
<dl>
<dt><label for="shipping-recipient">Ship to:</label></dt>
<dd><input name="shipping[recipient]" id="shipping-recipient"
742
Zend_Form
<dt><label for="shipping-address">Address:</label></dt>
<dd><input name="shipping[address]" id="shipping-address"
type="text" value="" /></dd>
<dt><label for="shipping-municipality">City:</label></dt>
<dd><input name="shipping[municipality]" id="shipping-municipality"
type="text" value="" /></dd>
<dt><label for="shipping-province">State:</label></dt>
<dd><input name="shipping[province]" id="shipping-province"
type="text" value="" /></dd>
<fieldset>
<legend>Billing Address</legend>
<dl>
<dt><label for="billing-payer">Bill To:</label></dt>
<dd><input name="billing[payer]" id="billing-payer"
type="text" value="" /></dd>
<dt><label for="billing-address">Address:</label></dt>
<dd><input name="billing[address]" id="billing-address"
type="text" value="" /></dd>
<dt><label for="billing-municipality">City:</label></dt>
<dd><input name="billing[municipality]" id="billing-municipality"
type="text" value="" /></dd>
<dt><label for="billing-province">State:</label></dt>
<dd><input name="billing[province]" id="billing-province"
type="text" value="" /></dd>
<dl>
<dt><label for="terms">I agree to the Terms of Service</label></dt>
<dd><input name="terms" type="checkbox" value="" /></dd>
<dt></dt>
<dd><input name="save" type="submit" value="Save" /></dd>
</dl>
</form>
In the above sample, we now get separate addresses. In the submitted form, we'll now have
three elements, the 'save' element for the submit, and then two arrays, 'shipping' and 'billing',
each with keys for their various elements.
743
Zend_Form
Zend_Form attempts to automate this process with its sub forms. By default, sub forms render
using the array notation as shown in the previous HTML form listing, complete with ids. The array
name is based on the sub form name, with the keys based on the elements contained in the sub
form. Sub forms may be nested arbitrarily deep, and this will create nested arrays to reflect the
structure. Additionally, the various validation routines in Zend_Form honor the array structure,
ensuring that your form validates correctly, no matter how arbitrarily deep you nest your sub
forms. You need do nothing to benefit from this; this behaviour is enabled by default.
Additionally, there are facilities that allow you to turn on array notation conditionally, as well as
specify the specific array to which an element or collection belongs:
• Zend_Form::setIsArray($flag): By setting the flag TRUE, you can indicate that an entire
form should be treated as an array. By default, the form's name will be used as the name of
the array, unless setElementsBelongTo() has been called. If the form has no specified
name, or if setElementsBelongTo() has not been set, this flag will be ignored (as there is
no array name to which the elements may belong).
You may determine if a form is being treated as an array using the isArray() accessor.
Additionally, on the element level, you can specify individual elements may belong to particular
arrays using Zend_Form_Element::setBelongsTo() method. To discover what this value is
-- whether set explicitly or implicitly via the form -- you may use the getBelongsTo() accessor.
The key to creating a multi-page form is to utilize sub forms, but to display only one such sub form
per page. This allows you to submit a single sub form at a time and validate it, but not process
the form until all sub forms are complete.
744
)),
• Google Spreadsheets provides an online collaborative spreadsheets tool which can be used
as a simple data store for your applications.
• Google Documents List provides an online list of all spreadsheets, word processing
documents, and presentations stored in a Google account.
• Google Provisioning provides the ability to create, retrieve, update, and delete user accounts,
nicknames, and email lists on a Google Apps hosted domain.
• Google Base provides the ability to retrieve, post, update, and delete items in Google Base.
• YouTube provides the ability to search and retrieve videos, comments, favorites, subscriptions,
user profiles and more.
• Google CodeSearch allows you to search public source code from many projects.
Unsupported services
Zend_Gdata does not provide an interface to any other Google service, such
as Search, Gmail, Translation, or Maps. Only services that support the Google
Data API are supported.
• Service classes - inheriting from Zend_Gdata_App. These also include other classes such
as Zend_Gdata, Zend_Gdata_Spreadsheets, etc. These classes enable interacting with
APP or GData services and provide the ability to retrieve feeds, retrieve entries, post entries,
update entries and delete entries.
746
Zend_Gdata
• Entry classes - inheriting from Zend_Gdata_App_Entry. These also include other classes
such as Zend_Gdata_Entry, and Zend_Gdata_Spreadsheets_ListEntry. These
classes represent entries retrieved from services or used for constructing data to send to
services. In addition to being able to set the properties of an entry (such as the spreadsheet
cell value), you can use an entry object to send update or delete requests to a service. For
example, you can call $entry->save() to save changes made to an entry back to service
from which the entry initiated, or $entry->delete() to delete an entry from the server.
We've implemented a magic factory method in all service classes (such as Zend_Gdata_App,
Zend_Gdata, Zend_Gdata_Spreadsheets) that should make constructing new instances
of data model, query and other classes much easier. This magic factory is implemented by
747
Zend_Gdata
using the magic __call method to intercept all attempts to call $service->newXXX(arg1,
arg2, ...). Based off the value of XXX, a search is performed in all registered 'packages' for
the desired class. Here's some examples:
// creates a Zend_Gdata_App_Spreadsheets_CellEntry
$entry = $ss->newCellEntry();
// creates a Zend_Gdata_App_Spreadsheets_Extension_Cell
$cell = $ss->newCell();
$cell->setText('My cell value');
$cell->setRow('1');
$cell->setColumn('3');
$entry->cell = $cell;
Each service class in the inheritance tree is responsible for registering the appropriate
'packages' (directories) which are to be searched when calling the magic factory method.
Most other types of queries against Google Data services do not require authentication.
1.5. Dependencies
Zend_Gdata makes use of Zend_Http_Client to send requests to google.com and fetch
results. The response to most Google Data requests is returned as a subclass of the
Zend_Gdata_App_Feed or Zend_Gdata_App_Entry classes.
Zend_Gdata assumes your PHP application is running on a host that has a direct connection
to the Internet. The Zend_Gdata client operates by contacting Google Data servers.
Beginning with Zend Framework 1.7, support has been added for protocol versioning. This allows
the client and server to support new features while maintaining backwards compatibility. While
748
Zend_Gdata
most services will manage this for you, if you create a Zend_Gdata instance directly (as opposed
to one of its subclasses), you may need to specify the desired protocol version to access certain
server functionality.
• The q parameter specifies a full-text query. The value of the parameter is a string.
• The alt parameter specifies the feed type. The value of the parameter can be atom, rss,
json, or json-in-script. If you don't specify this parameter, the default feed type is atom.
NOTE: Only the output of the atom feed format can be processed using Zend_Gdata. The
Zend_Http_Client could be used to retrieve feeds in other formats, using query URLs
generated by the Zend_Gdata_Query class and its subclasses.
• The maxResults parameter limits the number of entries in the feed. The value of the
parameter is an integer. The number of entries returned in the feed will not exceed this value.
• The startIndex parameter specifies the ordinal number of the first entry returned in the feed.
Entries before this number are skipped.
• The updatedMin and updatedMax parameters specify bounds on the entry date. If you
specify a value for updatedMin, no entries that were updated earlier than the date you specify
are included in the feed. Likewise no entries updated after the date specified by updatedMax
are included.
You can use numeric timestamps, or a variety of date/time string representations as the value
for these parameters.
749
Zend_Gdata
The Zend_Gdata class also implements "magic" getter and setter methods, so you can use the
name of the parameter as a virtual member of the class.
You can clear all parameters with the resetParameters() function. This is useful to do if you
reuse a Zend_Gdata object for multiple queries.
See later sections for special functions in each helper class for Google Data services. These
functions help you to get feeds from the URI that is appropriate for the respective service.
function getNextPage($feed) {
$nextURL = $feed->getLink('next');
if ($nextURL !== null) {
return $gdata->getFeed($nextURL);
} else {
return null;
}
}
If you would prefer not to work with pages in your application, pass the first page of the feed
into Zend_Gdata_App::retrieveAllEntriesForFeed(), which will consolidate all entries
from each page into a single feed. This example shows how to use this function:
750
Zend_Gdata
Keep in mind when calling this function that it may take a long time to complete on large feeds.
You may need to increase PHP's execution time limit by calling set_time_limit().
You can use the data model classes for each service to construct the appropriate
entry to post to Google's services. The insertEntry() function will accept a child of
Zend_Gdata_App_Entry as data to post to the service. The method returns a child of
751
Zend_Gdata
Zend_Gdata_App_Entry which represents the state of the entry as it was returned from the
server.
Alternatively, you could construct the XML structure for an entry as a string and pass the string
to the insertEntry() function.
$entry = $gdata->newEntry();
$entry->title = $gdata->newTitle('Playing football at the park');
$content =
$gdata->newContent('We will visit the park and play football');
$content->setType('text');
$entry->content = $content;
$entryResult = $gdata->insertEntry($entry,
'http://www.blogger.com/feeds/blogID/posts/default');
To post entries, you must be using an authenticated Zend_Http_Client that you created using
the Zend_Gdata_AuthSub or Zend_Gdata_ClientLogin classes.
Option 2: Alternatively, you can call $entry->delete() on an entry retrieved from a Google
service.
To delete entries, you must be using an authenticated Zend_Http_Client that you created
using the Zend_Gdata_AuthSub or Zend_Gdata_ClientLogin classes.
La documentation Google indique que le mécanisme ClientLogin est approprié dans le cas
d'applications embarquées, à la différence du mécanisme AuthSub, utilisé pour les applications
Web ayant recours à une authentification extérieure. AuthSub récupère des identifiant d'un
752
Zend_Gdata
Les identifiants utilisés par le processus AuthSub sont fournis par l'utilisateur de l'application, et
non par l'application elle-même.
Après que le serveur d'authentification de Google ait redirigé le navigateur de l'utilisateur vers
votre application, un paramètre GET est ajouté, il s'agit du jeton d'authentification. Ce jeton
servira à éviter de demander une authentification à chaque requête future.
Ci-dessous un exemple d'une application PHP qui effectue une authentification afin d'utiliser
le service Google Calendar. Elle crée un objet client Zend_Gdata utilisant le client HTTP
fraîchement authentifié.
$my_calendar =
'http://www.google.com/calendar/feeds/default/private/full';
if (!isset($_SESSION['cal_token'])) {
if (isset($_GET['token'])) {
// Vous pouvez convertir le jeton unique en jeton de session.
$session_token =
Zend_Gdata_AuthSub::getAuthSubSessionToken($_GET['token']);
// Enregistre le jeton de session, dans la session PHP.
$_SESSION['cal_token'] = $session_token;
} else {
// Affiche le lien permettant la génération du jeton unique.
$googleUri = Zend_Gdata_AuthSub::getAuthSubTokenUri(
'http://'. $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'],
$my_calendar, 0, 1);
echo "Cliquez <a href='$googleUri'>ici</a>"
. " pour autoriser votre application.";
exit();
}
}
753
Zend_Gdata
if (isset($_GET['logout'])) {
Zend_Gdata_AuthSub::AuthSubRevokeToken($_SESSION['cal_token']);
unset($_SESSION['cal_token']);
header('Location: ' . $php_self);
exit();
}
Notes de sécurité
Le traitement effectué pour la variable $php_self dans l'exemple ci-dessus
est une règle de sécurité générale, elle n'est pas spécifique à l'utilisation
de Zend_Gdata. Vous devriez systématiquement filtrer le contenu que vous
envoyez en tant qu'en-tête HTTP.
Your client application can use the Book Search Data API to issue full-text searches for books
and to retrieve standard book information, ratings, and reviews. You can also access individual
users' library collections and public reviews. Finally, your application can submit authenticated
requests to enable users to create and modify library collections, ratings, labels, reviews, and
other account-specific entities.
For more information on the Book Search Data API, please refer to the official PHP Developer's
Guide on code.google.com.
754
Zend_Gdata
The most common action is to retrieve a list of books that match a search query. To do so you
create a VolumeQuery object and pass it to the Books::getVolumeFeed method.
For example, to perform a keyword query, with a filter on viewability to restrict the results to partial
or full view books, use the setMinViewability and setQuery methods of the VolumeQuery
object. The following code snippet prints the title and viewability of all volumes whose metadata
or text matches the query term "domino":
$query->setQuery('domino');
$query->setMinViewability('partial_view');
$feed = $books->getVolumeFeed($query);
The Query class, and subclasses like VolumeQuery, are responsible for constructing feed
URLs. The VolumeQuery shown above constructs a URL equivalent to the following:
http://www.google.com/books/feeds/volumes?q=keyword&min-viewability=partial
Note: Since Book Search results are public, you can issue a Book Search query without
authentication.
Here are some of the most common VolumeQuery methods for setting search parameters:
setQuery: Specifies a search query term. Book Search searches all book metadata and full
text for books matching the term. Book metadata includes titles, keywords, descriptions, author
names, and subjects. Note that any spaces, quotes or other punctuation in the parameter value
must be URL-escaped. (Use a plus (+) for a space.) To search for an exact phrase, enclose the
phrase in quotation marks. For example, to search for books matching the phrase "spy plane", set
the q parameter to %22spy+plane%22. You can also use any of the advanced search operators
supported by Book Search. For example, jane+austen+-inauthor:austen returns matches
that mention (but are not authored by) Jane Austen.
setStartIndex: Specifies the index of the first matching result that should be included
in the result set. This parameter uses a one-based index, meaning the first result is 1, the
second result is 2 and so forth. This parameter works in conjunction with the max-results
parameter to determine which results to return. For example, to request the third set of 10
results—results 21-30—set the start-index parameter to 21 and the max-results parameter
to 10. Note: This isn't a general cursoring mechanism. If you first send a query with ?start-
index=1&max-results=10 and then send another query with ?start-index=11&max-
results=10, the service cannot guarantee that the results are equivalent to ?start-
index=1&max-results=20, because insertions and deletions could have taken place in
between the two queries.
755
Zend_Gdata
setMaxResults: Specifies the maximum number of results that should be included in the
result set. This parameter works in conjunction with the start-index parameter to determine which
results to return. The default value of this parameter is 10 and the maximum value is 20.
setMinViewability: Allows you to filter the results according to the books' viewability status.
This parameter accepts one of three values: 'none' (the default, returning all matching books
regardless of viewability), 'partial_view' (returning only books that the user can preview
or view in their entirety), or 'full_view' (returning only books that the user can view in their
entirety).
If you are a partner who wants to do Co-Branded Search using the Book Search Data API, you
may do so by modifying the feed URL above to point to your Co-Branded Search implementation.
If, for example, a Co-Branded Search is available at the following URL:
http://www.google.com/books/p/PARTNER_COBRAND_ID?q=ball
then you can obtain the same results using the Book Search Data API at the following URL:
http://www.google.com/books/feeds/p/PARTNER_COBRAND_ID/volumes?q=ball+-soccer
To specify an alternate URL when querying a volume feed, you can provide an extra parameter
to newVolumeQuery
$query =
$books->newVolumeQuery('http://www.google.com/books/p/PARTNER_COBRAND_ID');
To add a rating, add a Rating object to a VolumeEntry and post it to the annotation feed. In
the example below, we start from an empty VolumeEntry object.
3.3.2. Reviews
In addition to ratings, authenticated users can submit reviews or edit their reviews. For information
on how to request previously submitted reviews, see Retrieving annotations.
To add a review, add a Review object to a VolumeEntry and post it to the annotation feed. In
the example below, we start from an existing VolumeEntry object.
756
Zend_Gdata
$annotationUrl = $entry->getAnnotationLink()->href;
$review = new Zend_Gdata_Books_Extension_Review();
To update an existing review, first you retrieve the review you want to update, then you modify
it, and then you submit it to the annotation feed.
$entryUrl = $entry->getId()->getText();
$review = new Zend_Gdata_Books_Extension_Review();
3.3.3. Labels
You can use the Book Search Data API to label volumes with keywords. A user can submit,
retrieve and modify labels. See Retrieving annotations for how to read previously submitted
labels.
To submit labels, add a Category object with the scheme LABELS_SCHEME to a VolumeEntry
and post it to the annotation feed.
$annotationUrl = $entry->getAnnotationLink()->href;
$category = new Zend_Gdata_App_Extension_Category(
'rated',
'http://schemas.google.com/books/2008/labels');
$entry->setCategory(array($category));
$books->insertVolume($entry, Zend_Gdata_Books::MY_ANNOTATION_FEED_URI);
$feed = $books->getVolumeFeed(
'http://www.google.com/books/feeds/users/USER_ID/volumes');
<i>(or)</i>
$feed = $books->getUserAnnotationFeed();
757
Zend_Gdata
For a list of the supported query parameters, see the query parameters section.
$books->deleteVolume($entry);
The most important collection is the user's My Library, which represents the books the user would
like to remember, organize, and share with others. This is the collection the user sees when
accessing his or her My Library page.
To retrieve the user's books, send a query to the My Library feed. To get the library of the
authenticated user, use me in place of USER_ID.
$feed = $books->getUserLibraryFeed();
Note: The feed may not contain all of the user's books, because there's a default limit on the
number of results returned. For more information, see the max-results query parameter in
Searching for books.
Just as you can search across all books, you can do a full-text search over just the books in a
user's library. To do this, just set the appropriate paramters on the VolumeQuery object.
For example, the following query returns all the books in your library that contain the word "bear":
$query = $books->newVolumeQuery(
'http://www.google.com/books/feeds/users' .
'/USER_ID/collections/library/volumes');
$query->setQuery('bear');
$feed = $books->getVolumeFeed($query);
For a list of the supported query parameters, see the query parameters section. In addition, you
can search for books that have been labeled by the user:
$query = $books->newVolumeQuery(
'http://www.google.com/books/feeds/users/' .
'USER_ID/collections/library/volumes');
$query->setCategory(
758
Zend_Gdata
$query->setCategory('favorites');
$feed = $books->getVolumeFeed($query);
After authenticating, you can add books to the current user's library.
You can either create an entry from scratch if you know the volume ID, or insert an entry read
from any feed.
The following example creates a new entry and adds it to the library:
$books->insertVolume(
$entry,
Zend_Gdata_Books::MY_LIBRARY_FEED_URI
);
To remove a book from a user's library, call deleteVolume on the VolumeEntry object.
$books->deleteVolume($entry);
La documentation Google indique que le mécanisme ClientLogin est approprié dans le cas
d'applications embarquées, à la différence du mécanisme AuthSub, utilisé pour les applications
Web ayant recours à une authentification extérieure. AuthSub récupère des identifiant d'un
utilisateur de l'application Web, et un navigateur réagissant aux redirections est requis. Le
processus ClientLogin, lui, utilise du code PHP tel qu'écrit dans l'application elle-même.
L'utilisateur de l'application n'entre pas en jeu pour fournir des identifiants de manière interactive.
Les identifiants fournis au mécanisme ClientLogin doivent correspondre à des identifiants valides
pour les services Google, mais il n'est pas nécessaire qu'ils correspondent à ceux de l'utilisateur
de l'application.
759
Zend_Gdata
Le troisième paramètre optionnel est le nom du service Google Data. Par exemple, il peut être
"cl" pour Google Calendar. Par défaut il s'agit de "xapi", ce qui correspond au service générique
de Google Data.
La quatrième paramètre optionnel est une instance de Zend_Http_Client. Vous pouvez alors
configurer votre client à part (par exemple lui ajouter des options pour la gestion d'un Proxy). Si
vous passez NULL à ce paramètre, alors un client Zend_Http_Client générique est crée.
Le cinquième paramètre optionnel est le nom du client que les serveurs Google Data identifieront
en interne. Par défaut il s'agit de "Zend-ZendFramework".
Le sixième paramètre, toujours optionnel, est l'ID pour le challenge CAPTCHA™ retourné par le
serveur. Ce paramètre n'est nécessaire que si vous avez reçu un challenge lors d'un processus
d'authentification passé, et que vous le renvoyez pour résolution..
Ci dessous, un exemple d'une application PHP qui s'authentifie auprès du service Google
Calendar et crée un objet client Zend_Gdata utilisant l'objet Zend_Http_Client fraîchement
authentifié :
760
Zend_Gdata
Before any transactions can occur, this connection needs to be made. Creating a connection
to the calendar servers involves two steps: creating an HTTP client and binding a
Zend_Gdata_Calendar service instance to that client.
5.1.1. Authentication
The Google Calendar API allows access to both public and private calendar feeds. Public feeds
do not require authentication, but are read-only and offer reduced functionality. Private feeds
offers the most complete functionality but requires an authenticated connection to the calendar
servers. There are three authentication schemes that are supported by Google Calendar:
• AuthSub allows authentication to the calendar servers via a Google proxy server. This provides
the same level of convenience as ClientAuth but without the security risk, making this an ideal
choice for web-based applications.
• MagicCookie allows authentication based on a semi-random URL available from within the
Google Calendar interface. This is the simplest authentication scheme to implement, but
requires that users manually retrieve their secure URL before they can authenticate, doesn't
provide access to calendar lists, and is limited to read-only access.
The Zend_Gdata library provides support for all three authentication schemes. The rest of
this chapter will assume that you are familiar the authentication schemes available and how to
create an appropriate authenticated connection. For more information, please see section the
Authentication section of this manual or the Authentication Overview in the Google Data API
Developer's Guide.
The example below shows how to create a Calendar service class using ClientAuth
authentication:
761
Zend_Gdata
A Calendar service using AuthSub can be created in a similar, though slightly more lengthy
fashion:
/*
* Retrieve the current URL so that the AuthSub server knows where to
* redirect the user after authentication is complete.
*/
function getCurrentUrl()
{
global $_SERVER;
if (isset($_SERVER['HTTPS']) &&
strtolower($_SERVER['HTTPS']) == 'on') {
$protocol = 'https://';
} else {
$protocol = 'http://';
}
$host = $_SERVER['HTTP_HOST'];
if ($_SERVER['HTTP_PORT'] != '' &&
(($protocol == 'http://' && $_SERVER['HTTP_PORT'] != '80') ||
($protocol == 'https://' && $_SERVER['HTTP_PORT'] != '443'))) {
$port = ':' . $_SERVER['HTTP_PORT'];
} else {
$port = '';
}
return $protocol . $host . $port . $php_request_uri;
}
/**
* Obtain an AuthSub authenticated HTTP client, redirecting the user
* to the AuthSub server to login if necessary.
*/
function getAuthSubHttpClient()
{
global $_SESSION, $_GET;
762
Zend_Gdata
$scope = "http://www.google.com/calendar/feeds/";
$secure = false;
$session = true;
$authSubUrl = Zend_Gdata_AuthSub::getAuthSubTokenUri($next,
$scope,
$secure,
$session);
header("HTTP/1.0 307 Temporary redirect");
exit();
}
// Make sure that the user has a valid session, so we can record the
// AuthSub session token once it is available.
session_start();
Finally, an unauthenticated server can be created for use with either public feeds or MagicCookie
authentication:
Note that MagicCookie authentication is not supplied with the HTTP connection, but is instead
specified along with the desired visibility when submitting queries. See the section on retrieving
events below for an example.
763
Zend_Gdata
The calendar list is always private and must be accessed over an authenticated connection. It is
not possible to retrieve another user's calendar list and it cannot be accessed using MagicCookie
authentication. Attempting to access a calendar list without holding appropriate credentials will
fail and result in a 401 (Authentication Required) status code.
$service = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Calendar($client);
try {
$listFeed= $service->getCalendarListFeed();
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getMessage();
}
5.3.1. Queries
When retrieving events using the Calendar API, specially constructed query URLs are used to
describe what events should be returned. The Zend_Gdata_Calendar_EventQuery class
simplifies this task by automatically constructing a query URL based on provided parameters. A
full list of these parameters is available at the Queries section of the Google Data APIs Protocol
Reference. However, there are three parameters that are worth special attention:
• User is used to specify the user whose calendar is being searched for, and is specified as an
email address. If no user is provided, "default" will be used instead to indicate the currently
authenticated user (if authenticated).
• Visibility specifies whether a users public or private calendar should be searched. If using an
unauthenticated session and no MagicCookie is available, only the public feed will be available.
• Projection specifies how much data should be returned by the server and in what format. In
most cases you will want to use the "full" projection. Also available is the "basic" projection,
which places most meta-data into each event's content field as human readable text, and the
"composite" projection which includes complete text for any comments alongside each event.
The "composite" view is often much larger than the "full" view.
764
Zend_Gdata
The example below illustrates the use of the Zend_Gdata_Query class and specifies the
private visibility feed, which requires that an authenticated connection is available to the calendar
servers. If a MagicCookie is being used for authentication, the visibility should be instead set
to "private-magicCookieValue", where magicCookieValue is the random string obtained
when viewing the private XML address in the Google Calendar UI. Events are requested
chronologically by start time and only events occurring in the future are returned.
$query = $service->newEventQuery();
$query->setUser('default');
// Set to $query->setVisibility('private-magicCookieValue') if using
// MagicCookie auth
$query->setVisibility('private');
$query->setProjection('full');
$query->setOrderby('starttime');
$query->setFutureevents('true');
Additional properties such as ID, author, when, event status, visibility, web content, and content,
among others are available within Zend_Gdata_Calendar_EventEntry. Refer to the Zend
Framework API Documentation and the Calendar Protocol Reference for a complete list.
To print out all events within a certain range, for example from December 1, 2006 through
December 15, 2007, add the following two lines to the previous sample. Take care to remove
"$query->setFutureevents('true')", since futureevents will override startMin and
startMax.
$query->setStartMin('2006-12-01');
$query->setStartMax('2006-12-16');
Note that startMin is inclusive whereas startMax is exclusive. As a result, only events
through 2006-12-15 23:59:59 will be returned.
To print out all events which contain a specific word, for example "dogfood", use the setQuery()
method when creating the query.
$query->setQuery("dogfood");
765
Zend_Gdata
$query = $service->newEventQuery();
$query->setUser('default');
$query->setVisibility('private');
$query->setProjection('full');
$query->setEvent($eventId);
try {
$event = $service->getCalendarEventEntry($query);
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getMessage();
}
In a similar fashion, if the event URL is known, it can be passed directly into
getCalendarEntry() to retrieve a specific event. In this case, no query object is required
since the event URL contains all the necessary information to retrieve the event.
$eventURL = "http://www.google.com/calendar/feeds/default/private"
. "/full/g829on5sq4ag12se91d10uumko";
try {
$event = $service->getCalendarEventEntry($eventURL);
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getMessage();
}
• Title provides the headline that will appear above the event within the Google Calendar UI.
• When indicates the duration of the event and, optionally, any reminders that are associated
with it. See the next section for more information on this attribute.
• Author provides information about the user who created the event.
• Content provides additional information about the event which appears when the event details
are requested from within Google Calendar.
766
Zend_Gdata
• Transparency indicates whether the event should be consume time on the user's free/busy list.
• Visibility allows the event to be hidden from the public event lists.
For a complete list of event attributes, refer to the Zend Framework API Documentation and the
Calendar Protocol Reference. Attributes that can contain multiple values, such as where, are
implemented as arrays and need to be created accordingly. Be aware that all of these attributes
require objects as parameters. Trying instead to populate them using strings or primitives will
result in errors during conversion to XML.
Once the event has been populated, it can be uploaded to the calendar server by passing it as
an argument to the calendar service's insertEvent() function.
// Create a new entry using the calendar service's magic factory method
$event= $service->newEventEntry();
$when = $service->newWhen();
$when->startTime = "{$startDate}T{$startTime}:00.000{$tzOffset}:00";
$when->endTime = "{$endDate}T{$endTime}:00.000{$tzOffset}:00";
$event->when = array($when);
All-day events can be scheduled by specifying only the date omitting the time when setting
startTime and endTime. Likewise, zero-duration events can be specified by omitting the
endTime. In all cases, date/time values should be provided in RFC3339 format.
767
Zend_Gdata
The when attribute also controls when reminders are sent to a user. Reminders are stored in an
array and each event may have up to find reminders associated with it.
For a reminder to be valid, it needs to have two attributes set: method and a time. Method
can accept one of the following strings: "alert", "email", or "sms". The time should be entered as
an integer and can be set with either the property minutes, hours, days, or absoluteTime.
However, a valid request may only have one of these attributes set. If a mixed time is desired,
convert to the most precise unit available. For example, 1 hour and 30 minutes should be entered
as 90 minutes.
Due to the complexity of parsing recurrence patterns, further information on this them is outside
the scope of this document. However, more information can be found in the Common Elements
section of the Google Data APIs Developer Guide, as well as in RFC 2445.
// Create a new entry using the calendar service's magic factory method
$event= $service->newEventEntry();
$recurrence = "DTSTART;VALUE=DATE:20070501\r\n" .
"DTEND;VALUE=DATE:20070502\r\n" .
"RRULE:FREQ=WEEKLY;BYDAY=Tu;UNTIL=20070904\r\n";
768
Zend_Gdata
$event->recurrence = $service->newRecurrence($recurrence);
// Create a new entry using the calendar service's magic factory method
$event= $service->newEventEntry();
In the event another user has modified the event since the local copy was retrieved, save()
will fail and the server will return a 409 (Conflict) status code. To resolve this a fresh copy of the
event must be retrieved from the server before attempting to resubmit any modifications.
In either case, the deleted event will still show up on a user's private event feed if an
updateMin query parameter is provided. Deleted events can be distinguished from regular
events because they will have their eventStatus property set to "http://schemas.google.com/
g/2005#event.canceled".
769
Zend_Gdata
// Option 2: Events can be deleted supplying the edit URL of the event
// to the calendar service, if known
$service->delete($event->getEditLink()->href);
Working with comments is fundamentally similar to working with events, with the only significant
difference being that a different feed and event class should be used and that the additional meta-
data for events such as where and when does not exist for comments. Specifically, the comment's
author is stored in the author property, and the comment text is stored in the content property.
// Extract the comment URL from the first event in a user's feed list
$event = $eventFeed[0];
$commentUrl = $event->comments->feedLink->url;
$service = Zend_Gdata_Docs::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
770
Zend_Gdata
/**
* Upload the specified document
*
* @param Zend_Gdata_Docs $docs The service object to use for communicating
* with the Google Documents server.
* @param boolean $html True if output should be formatted for display in a
* web browser.
* @param string $originalFileName The name of the file to be uploaded. The
* MIME type of the file is determined from the extension on this file
* name. For example, test.csv is uploaded as a comma separated volume
* and converted into a spreadsheet.
* @param string $temporaryFileLocation (optional) The file in which the
* data for the document is stored. This is used when the file has been
* uploaded from the client's machine to the server and is stored in
* a temporary file which does not have an extension. If this parameter
* is null, the file is read from the originalFileName.
*/
function uploadDocument($docs, $html, $originalFileName,
$temporaryFileLocation) {
$fileToUpload = $originalFileName;
if ($temporaryFileLocation) {
$fileToUpload = $temporaryFileLocation;
}
// Upload the file and convert it into a Google Document. The original
// file name is used as the title of the document and the MIME type
// is determined based on the extension on the original file name.
$newDocumentEntry = $docs->uploadFile($fileToUpload, $originalFileName,
null, Zend_Gdata_Docs::DOCUMENTS_LIST_FEED_URI);
if ($html) {
// Find the URL of the HTML view of this document.
$alternateLink = '';
foreach ($newDocumentEntry->link as $link) {
if ($link->getRel() === 'alternate') {
$alternateLink = $link->getHref();
}
}
// Make the title link to the document on docs.google.com.
echo "<a href=\"$alternateLink\">\n";
}
echo $newDocumentEntry->title."\n";
if ($html) {echo "</a>\n";}
771
Zend_Gdata
$feed = $docs->getDocumentListFeed(
'http://docs.google.com/feeds/documents/private/full/-/document');
$feed = $docs->getDocumentListFeed(
'http://docs.google.com/feeds/documents/private/full/-/spreadsheet');
• Read a user's Google Health profile or query for medical records that match particular criteria
and then use the results to provide personalized functionality based on the data.
• Add new medical records to a user's profile by including CCR data when sending a notice
to a user's profile. Note: The CCR data is stored as an XML blob within the <atom> entry.
The library does not provide direct accessors to the object model but it does have helpers for
extracting specific fields.
There are three main feeds, each of which requires authentication. Unlike other Google
Data APIs, each Google Health feed has a limited set of HTTP operations you can
perform on it, depending on which authentication method you are using (ClientLogin or
AuthSub/OAuth). For a list of permitted operations, see http://code.google.com/apis/health/
reference.html#Authentication.
• Profile Feed use the profile feed to query a user's health profile for specific information.
• Register Feed use the register feed to reconcile new CCR data into a profile.
772
Zend_Gdata
• Profile List Feed the profile list feed should be used to determine which of the user's Health
profiles to interact with. This feed is only available when using ClientLogin.
See http://code.google.com/apis/health for more information about the Google Health API.
Before any transactions can occur, a connection needs to be made. Creating a connection to the
Health servers involves two steps: creating an HTTP client and binding a Zend_Gdata_Health
service instance to that client.
7.1.1. Authentication
The Google Health API allows programmatic access to a user's Health profile. There are three
authentication schemes that are supported by Google Health:
• AuthSub allows a user to authorize the sharing of their private data. This provides the same
level of convenience as ClientLogin but without the security risk, making it an ideal choice for
web-based applications. For Google Health, AuthSub must be used in registered and secure
mode--meaning that all requests to the API must be digitally signed.
See Authentication Overview in the Google Data API documentation for more information on
each authentication method.
To test against the H9 Developer's (/h9) instead of Google Health (/health), the
Zend_Gdata_Health constructor takes an optional third argument for you to specify the H9
service name 'weaver'.
The example below shows how to create a Health service class using ClientLogin authentication:
773
Zend_Gdata
A Health service using AuthSub can be created in a similar, though slightly more lengthy fashion.
AuthSub is the recommend interface to communicate with Google Health because each token
is directly linked to a specific profile in the user's account. Unlike other Google Data APIs, it is
required that all requests from your application be digitally signed.
/*
* Retrieve the current URL so that the AuthSub server knows where to
* redirect the user after authentication is complete.
*/
function getCurrentUrl() {
$phpRequestUri = htmlentities(substr($_SERVER['REQUEST_URI'],
0,
strcspn($_SERVER['REQUEST_URI'],
"\n\r")),
ENT_QUOTES);
/*
* Redirect a user to AuthSub if they do not have a valid session token.
* If they're coming back from AuthSub with a single-use token, instantiate
* a new HTTP client and exchange the token for a long-lived session token
* instead.
*/
function setupClient($singleUseToken = null) {
$client = null;
774
Zend_Gdata
$secure,
$session,
$authSubHandler);
$sessionToken =
Zend_Gdata_AuthSub::getAuthSubSessionToken(trim($singleUseToken),
$client);
session_start();
$client = setupClient(@$_GET['token']);
NOTE: the remainder of this document will assume you are using AuthSub for authentication.
When retrieving the profile using the Health API, specifically constructed query URLs are used to
describe what (CCR) data should be returned. The Zend_Gdata_Health_Query class helps
simplify this task by automatically constructing a query URL based on the parameters you set.
775
Zend_Gdata
$profileFeed = $healthService->getHealthProfileFeed($query);
Using setDigest("true") returns all of user's CCR data in a single Atom <entry>.
A full list of supported query parameters is available in the query parameters section of the Health
API Reference Guide.
To retrieve the full CCR information from an entry, make a call to the
Zend_Gdata_Health_ProfileEntry class's getCcr() method. That returns a
Zend_Gdata_Health_Extension_CCR:
$entries = $profileFeed->getEntries();
foreach ($entries as $entry) {
$medications = $entry->getCcr()->getMedications();
//$conditions = $entry->getCcr()->getConditions();
//$immunizations = $entry->getCcr()->getImmunizations();
// print the CCR xml (this will just be the entry's medications)
foreach ($medications as $med) {
$xmlStr = $med->ownerDocument->saveXML($med);
echo "<pre>" . $xmlStr . "</pre>";
}
}
Here, the getCcr() method is used in conjunction with a magic helper to drill down and extract
just the medication data from the entry's CCR. The formentioned magic helper takes the form
getCATEGORYNAME(), where CATEGORYNAME is a supported Google Health category. See the
Google Health reference Guide for the possible categories.
To be more efficient, you can also use category queries to only return the necessary CCR from
the Google Health servers. Then, iterate through those results:
776
Zend_Gdata
// Since the query contained digest=true, only one Atom entry is returned
$entry = $profileFeed->entry[0];
$conditions = $entry->getCcr()->getConditions();
// print the CCR xml (this will just be the profile's conditions)
foreach ($conditions as $cond) {
$xmlStr = $cond->ownerDocument->saveXML($cond);
echo "<pre>" . $xmlStr . "</pre>";
}
Since ClientLogin requires a profile ID with each of its feeds, applications will likely want to query
this feed first in order to select the appropriate profile. The profile list feed returns Atom entries
corresponding each profile in the user's Google Health account. The profile ID is returned in the
Atom <content> and the profile name in the <title> element.
$client = Zend_Gdata_ClientLogin::getHttpClient('user@gmail.com',
'pa$$word',
'health');
$healthService = new Zend_Gdata_Health($client);
$feed = $healthService->getHealthProfileListFeed();
Once you've determined which profile to use, call setProfileID() with the profileID as an
argument. This will restrict subsequent API requests to be against that particular profile:
$profileFeed = $healthService->getHealthProfileFeed();
$profileID = $healthService->getProfileID();
echo '<p><b>Queried profileID</b>: ' . $profileID . '</p>';
777
Zend_Gdata
might be sent to remind users to pick up a prescription, or they might contain lab results in the
CCR format.
$responseEntry = $healthService->sendHealthNotice($subject,
$body,
"html",
$ccr);
778
Zend_Gdata
$service = Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$spreadsheetService = new Zend_Gdata_Spreadsheets($client);
$feed = $spreadsheetService->getSpreadsheetFeed();
The following sections describe how to get a list-based feed, add a row to a worksheet, and send
queries with various query parameters.
The list feed makes some assumptions about how the data is laid out in the spreadsheet.
In particular, the list feed treats the first row of the worksheet as a header row; Spreadsheets
dynamically creates XML elements named after the contents of header-row cells. Users who
want to provide Gdata feeds should not put any data other than column headers in the first row
of a worksheet.
The list feed contains all rows after the first row up to the first blank row. The first blank row
terminates the data set. If expected data isn't appearing in a feed, check the worksheet manually
to see whether there's an unexpected blank row in the middle of the data. In particular, if the
second row of the spreadsheet is blank, then the list feed will contain no data.
779
Zend_Gdata
$query->setWorksheetId($worksheetId);
$listFeed = $spreadsheetService->getListFeed($query);
$rowData = $listFeed->entries[1]->getCustom();
foreach($rowData as $customEntry) {
echo $customEntry->getColumnName() . " = " . $customEntry->getText();
}
An alternate version of this array, customByName, allows direct access to an entry's cells by
name. This is convenient when trying to access a specific header:
$customEntry = $listFeed->entries[1]->getCustomByName('my_heading');
echo $customEntry->getColumnName() . " = " . $customEntry->getText();
Note that if you want to order (or reverse sort) by a particular column, rather than by position in the
worksheet, you can set the orderby value of the Zend_Gdata_Spreadsheets_ListQuery
object to column:<the header of that column>.
780
Zend_Gdata
$insertedListEntry = $spreadsheetService->insertRow($rowData,
$spreadsheetKey,
$worksheetId);
The $rowData parameter contains an array of column keys to data values. The method returns
a Zend_Gdata_Spreadsheets_SpreadsheetsEntry object which represents the inserted
row.
Spreadsheets inserts the new row immediately after the last row that appears in the list-based
feed, which is to say immediately before the first entirely blank row.
$updatedListEntry = $spreadsheetService->updateRow($oldListEntry,
$newRowData);
The $oldListEntry parameter contains the list entry to be updated. $newRowData contains
an array of column keys to data values, to be used as the new row data. The method returns
a Zend_Gdata_Spreadsheets_SpreadsheetsEntry object which represents the updated
row.
$spreadsheetService->deleteRow($listEntry);
Alternatively, you can call the delete method of the entry itself:
$listEntry->delete();
Note that we don't recommend interacting with both a cell-based feed and a list-based feed for
the same worksheet at the same time.
781
Zend_Gdata
foreach($cellFeed as $cellEntry) {
$row = $cellEntry->cell->getRow();
$col = $cellEntry->cell->getColumn();
$val = $cellEntry->cell->getText();
echo "$row, $col = $val\n";
}
$updatedCell = $spreadsheetService->updateCell($row,
$col,
$inputValue,
$spreadsheetKey,
$worksheetId);
The new data is placed in the specified cell in the worksheet. If the specified cell contains data
already, it will be overwritten. Note: Use updateCell to change the data in a cell, even if the
cell is empty.
This library implements version 2.0 of the Provisioning API. Access to your account via the
Provisioning API must be manually enabled for each domain using the Google Apps control
panel. Only certain account types are able to enable this feature.
For more information on the Google Apps Provisioning API, including instructions for enabling
API access, refer to the Provisioning API V2.0 Reference.
Authentication
The Provisioning API does not support authentication via AuthSub and
anonymous access is not permitted. All HTTP connections must be authenticated
using ClientAuth authentication.
782
Zend_Gdata
$domain = "example.com";
$gdata = new Zend_Gdata_Gapps($client, $domain);
$domain = "example.com";
$query = new Zend_Gdata_Gapps_UserQuery($domain, $arg);
When using a service class factory method to create a query, the service class will automatically
set the query's domain to match its own domain. As a result, it is not necessary to specify the
domain as part of the constructor arguments.
$domain = "example.com";
$gdata = new Zend_Gdata_Gapps($client, $domain);
$query = $gdata->newUserQuery($arg);
Users can also be created by instantiating UserEntry, providing a username, given name, family
name, and password, then calling insertUser() on a service object to upload the entry to
the server.
$user = $gdata->newUserEntry();
$user->login = $gdata->newLogin();
$user->login->username = 'foo';
$user->login->password = '••••••••';
$user->name = $gdata->newName();
$user->name->givenName = 'Random';
$user->name->familyName = 'User';
$user = $gdata->insertUser($user);
783
Zend_Gdata
The user's password should normally be provided as cleartext. Optionally, the password can be
provided as an SHA-1 digest if login->passwordHashFunction is set to 'SHA-1'.
$user = $gdata->retrieveUser('foo');
$query = $gdata->newUserQuery('foo');
$user = $gdata->getUserEntry($query);
If the specified user cannot be located a ServiceException will be thrown with an error
code of Zend_Gdata_Gapps_Error::ENTITY_DOES_NOT_EXIST. ServiceExceptions will be
covered in Section 9.6, « Handling errors ».
$feed = $gdata->retrieveAllUsers();
This will create a Zend_Gdata_Gapps_UserFeed object which holds each user on the domain.
Alternatively, call getUserFeed() with no options. Keep in mind that on larger domains this
feed may be paged by the server. For more information on paging, see Section 1.9, « Working
with Multi-page Feeds ».
784
Zend_Gdata
$feed = $gdata->getUserFeed();
$user = $gdata->retrieveUser('foo');
$user->name->givenName = 'Foo';
$user->name->familyName = 'Bar';
$user = $user->save();
A user's password can be reset to a new value by updating the login->password property.
$user = $gdata->retrieveUser('foo');
$user->login->password = '••••••••';
$user = $user->save();
Note that it is not possible to recover a password in this manner as stored passwords are not
made available via the Provisioning API for security reasons.
A user can be forced to change their password at their next login by setting the login-
>changePasswordAtNextLogin property to TRUE.
$user = $gdata->retrieveUser('foo');
$user->login->changePasswordAtNextLogin = true;
$user = $user->save();
Users can be restricted from logging in without deleting their user account by instead suspending
their user account. Accounts can be suspended or restored by using the suspendUser() and
restoreUser() convenience methods:
$gdata->suspendUser('foo');
$gdata->restoreUser('foo');
$user = $gdata->retrieveUser('foo');
$user->login->suspended = true;
$user = $user->save();
785
Zend_Gdata
Users can be granted the ability to administer your domain by setting their login->admin
property to TRUE.
$user = $gdata->retrieveUser('foo');
$user->login->admin = true;
$user = $user->save();
And as expected, setting a user's login->admin property to FALSE revokes their administrative
rights.
$user = $gdata->retrieveUser('foo');
$user->delete();
If you do not have access to a UserEntry object for an account, use the deleteUser()
convenience method.
$gdata->deleteUser('foo');
$gdata->createNickname('foo', 'bar');
Nicknames can also be created by instantiating NicknameEntry, providing the nickname with a
name and an owner, then calling insertNickname() on a service object to upload the entry
to the server.
$nickname = $gdata->newNicknameEntry();
$nickname->login = $gdata->newLogin('foo');
$nickname->nickname = $gdata->newNickname('bar');
$nickname = $gdata->insertNickname($nickname);
786
Zend_Gdata
$nickname = $gdata->retrieveNickname('bar');
$query = $gdata->newNicknameQuery('bar');
$nickname = $gdata->getNicknameEntry($query);
$feed = $gdata->retrieveNicknames('foo');
$query = $gdata->newNicknameQuery();
$query->setUsername('foo');
$feed = $gdata->getNicknameFeed($query);
$feed = $gdata->retrieveAllNicknames();
787
Zend_Gdata
$feed = $gdata->getNicknameFeed();
$nickname = $gdata->retrieveNickname('bar');
$nickname->delete();
For nicknames which you do not hold a NicknameEntry for, use the deleteNickname()
convenience method.
$gdata->deleteNickname('bar');
$gdata->createEmailList('friends');
Email lists can also be created by instantiating EmailListEntry, providing a name for the list, then
calling insertEmailList() on a service object to upload the entry to the server.
$list = $gdata->newEmailListEntry();
$list->emailList = $gdata->newEmailList('friends');
$list = $gdata->insertEmailList($list);
$feed = $gdata->retrieveEmailLists('baz@somewhere.com');
788
Zend_Gdata
This will create a Zend_Gdata_Gapps_EmailListFeed object which holds each email list
associated with the specified recipient.
$query = $gdata->newEmailListQuery();
$query->setRecipient('baz@somewhere.com');
$feed = $gdata->getEmailListFeed($query);
$feed = $gdata->retrieveAllEmailLists();
This will create a Zend_Gdata_Gapps_EmailListFeed object which holds each email list on
the domain.
$feed = $gdata->getEmailListFeed();
$gdata->deleteEmailList('friends');
789
Zend_Gdata
$gdata->addRecipientToEmailList('bar@somewhere.com', 'friends');
$feed = $gdata->retrieveAllRecipients('friends');
$query = $gdata->newEmailListRecipientQuery();
$query->setEmailListName('friends');
$feed = $gdata->getEmailListRecipientFeed($query);
$gdata->removeRecipientFromEmailList('baz@somewhere.com', 'friends');
Each ServiceException instance may hold one or more Error objects. Each of these objects
contains an error code, reason, and (optionally) the input which triggered the exception. A
complete list of known error codes is provided in the Zend Framework API documentation
under Zend_Gdata_Gapps_Error. Additionally, the authoritative error list is available online
at Google Apps Provisioning API V2.0 Reference: Appendix D.
While the complete list of errors received is available within ServiceException as an array by
calling getErrors(), often it is convenient to know if one specific error occurred. For these
cases the presence of an error can be determined by calling hasError().
The following example demonstrates how to detect if a requested resource doesn't exist and
handle the fault gracefully:
790
Zend_Gdata
$query = $gdata->newUserQuery($username);
try {
$user = $gdata->getUserEntry($query);
} catch (Zend_Gdata_Gapps_ServiceException $e) {
// Set the user to null if not found
if ($e->hasError(Zend_Gdata_Gapps_Error::ENTITY_DOES_NOT_EXIST)) {
$user = null;
} else {
throw $e;
}
}
return $user;
}
There are two item feeds: snippets feed and customer items feeds. The snippets feed contains all
Google Base data and is available to anyone to query against without a need for authentication.
The customer items feed is a customer-specific subset of data and only a customer/owner can
access this feed to insert, update, or delete their own data. Queries are constructed the same
way against both types of feeds.
See http://code.google.com/apis/base for more information about the Google Base API.
Before any transactions can occur, this connection needs to be made. Creating a connection to
the base servers involves two steps: creating an HTTP client and binding a Zend_Gdata_Gbase
service instance to that client.
10.1.1. Authentication
The Google Base API allows access to both public and private base feeds. Public feeds do not
require authentication, but are read-only and offer reduced functionality. Private feeds offers the
most complete functionality but requires an authenticated connection to the base servers. There
are three authentication schemes that are supported by Google Base:
• ClientAuth provides direct username/password authentication to the base servers. Since this
scheme requires that users provide your application with their password, this authentication is
only recommended when other authentication schemes are insufficient.
• AuthSub allows authentication to the base servers via a Google proxy server. This provides
the same level of convenience as ClientAuth but without the security risk, making this an ideal
choice for web-based applications.
791
Zend_Gdata
The Zend_Gdata library provides support for all three authentication schemes. The rest of this
chapter will assume that you are familiar the authentication schemes available and how to create
an appropriate authenticated connection. For more information, please see section Section 1.4,
« Google Data Client Authentication ». or the Authentication Overview in the Google Data API
Developer's Guide.
The example below shows how to create a Base service class using ClientAuth authentication:
A Base service using AuthSub can be created in a similar, though slightly more lengthy fashion:
/*
* Retrieve the current URL so that the AuthSub server knows where to
* redirect the user after authentication is complete.
*/
function getCurrentUrl()
{
global $_SERVER;
if (isset($_SERVER['HTTPS']) &&
strtolower($_SERVER['HTTPS']) == 'on') {
$protocol = 'https://';
} else {
$protocol = 'http://';
}
$host = $_SERVER['HTTP_HOST'];
if ($_SERVER['HTTP_PORT'] != '' &&
(($protocol == 'http://' && $_SERVER['HTTP_PORT'] != '80') ||
792
Zend_Gdata
/**
* Obtain an AuthSub authenticated HTTP client, redirecting the user
* to the AuthSub server to login if necessary.
*/
function getAuthSubHttpClient()
{
global $_SESSION, $_GET;
$authSubUrl = Zend_Gdata_AuthSub::getAuthSubTokenUri($next,
$scope,
$secure,
$session);
header("HTTP/1.0 307 Temporary redirect");
exit();
}
793
Zend_Gdata
Finally, an unauthenticated server can be created for use with snippets feeds:
You can send a structured query to retrieve items from your own customer items feed or from
the public snippets feed.
When retrieving items using the Base API, specially constructed query URLs are used
to describe what events should be returned. The Zend_Gdata_Gbase_ItemQuery and
Zend_Gdata_Gbase_SnippetQuery classes simplify this task by automatically constructing
a query URL based on provided parameters.
To execute a query against the customer items feed, invoke newItemQuery() and
getGbaseItemFeed() methods:
A full list of these parameters is available at the Query parameters section of the Customer Items
Feed documentation.
To execute a query against the public snippets feed, invoke newSnippetQuery() and
getGbaseSnippetFeed() methods:
A full list of these parameters is available at the Query parameters section of the Snippets Feed
documentation.
794
Zend_Gdata
To iterate through all attributes of a given item, invoke getGbaseAttributes() and iterate
through the results:
Or, you can look for specific attribute name and iterate through the results that match:
You can test a feed operation before it is actually executed by setting the dry-run flag ($dryRun)
to TRUE. Once you are sure that you want to submit the data, set it to FALSE to execute the
operation.
// Add title
$title = "PHP Developer Handbook";
$newEntry->title = $service->newTitle(trim($title));
795
Zend_Gdata
$dryRun = true;
$createdEntry = $service->insertGbaseItem($newEntry, $dryRun);
796
Zend_Gdata
// $service->updateGbaseItem($entry, $dryRun);
$dryRun = false;
$service->deleteGbaseItem($entry, $dryRun);
$dryRun = false;
$entry->delete($dryRun);
L'accès public à un album, en lecture donc, n'est pas sujet à demande d'authentification. En
revanche, toute autre manipulation telle que la mise à jour ou la suppression, nécessitera que
vous vous authentifiez.
Pour plus d'informations sur l'API, voyez l'API Picasa Web Albums.
Authentification
L'API propose les deux modes d'authentification, AuthSub (recommandé) et
ClientAuth. Pour toute opération d'écriture vers le service, une authentification
sera demandée, la lecture est elle, libre, au regard de l'API.
Avant tout, il faut donc se connecter. Ceci se fait en deux étapes : créer un client HTTP, et insérer
un Zend_Gdata_Photos dans celui-ci.
11.1.1. Authentification
L'API propose un accès à la fois aux données publiques, et aux données privées. Les données
publiques ne requièrent pas d'authentification, mais ne sont accessibles qu'en lecture seule.
L'écriture et l'accès aux données privées requièrent une authentification, qui peut s'effectuer de
trois manières différentes :
797
Zend_Gdata
• AuthSub permet l'authentification en passant par un serveur proxy de Google. Les risques liés
à la sécurité sont donc moindre avec cette méthode.
Une fois que vous avez choisi une méthode d'authentification, vous devez créer une
instance de Zend_Gdata_Photos. Le constructeur prend en paramètre une instance de
Zend_Http_Client. Cette classe est l'interface AuthSub ou ClientAuth authentification. Si
vous ne passez pas cette instance en argument, alors une instance de Zend_Http_Client
sera crée automatiquement, mais en mode non authentifié.
Voici un exemple qui démontre comment créer une classe vers le service avec le procédé
d'authentification ClientAuth :
session_start();
/**
* Retourne l'URL complet de la page actuelle,
* en fonction des variables d'environnement
*
* Env variables utilisées:
* $_SERVER['HTTPS'] = (on|off|)
* $_SERVER['HTTP_HOST'] = value of the Host: header
* $_SERVER['SERVER_PORT'] = port number (only used if not http/80,https/443)
* $_SERVER['REQUEST_URI'] = the URI after the method of the HTTP request
*
* @return string Current URL
*/
function getCurrentUrl()
{
global $_SERVER;
/**
* Filtre php_self pour éviter des problèmes de sécurité
*/
$php_request_uri = htmlentities(substr($_SERVER['REQUEST_URI'], 0,
strcspn($_SERVER['REQUEST_URI'], "\n\r")), ENT_QUOTES);
798
Zend_Gdata
if (isset($_SERVER['HTTPS'])
&& strtolower($_SERVER['HTTPS']) == 'on') {
$protocol = 'https://';
} else {
$protocol = 'http://';
}
$host = $_SERVER['HTTP_HOST'];
if ($_SERVER['SERVER_PORT'] != '' &&
(($protocol == 'http://' && $_SERVER['SERVER_PORT'] != '80') ||
($protocol == 'https://' && $_SERVER['SERVER_PORT'] != '443'))) {
$port = ':' . $_SERVER['SERVER_PORT'];
} else {
$port = '';
}
return $protocol . $host . $port . $php_request_uri;
}
/**
* Retourne l'URL AuthSub que l'utilisateur doit visiter
* pour authentifier ses requêtes
*
* Utilise getCurrentUrl() pour récupérer le prochain URL
* vers lequel l'utilisateur sera redirigé après
* s'être authentifié.
*
* @return string AuthSub URL
*/
function getAuthSubUrl()
{
$next = getCurrentUrl();
$scope = 'http://picasaweb.google.com/data';
$secure = false;
$session = true;
return Zend_Gdata_AuthSub::getAuthSubTokenUri($next,
$scope,
$secure,
$session);
}
/**
* Retourne un objet servant de client HTTP avec les bons en-têtes,
* permettant de communiquer avec les services Google, et utilisant
* l'authentification AuthSub.
*
* Utilise $_SESSION['sessionToken'] pour stocker le jeton de session
* AuthSub après l'avoir obtenu. $_GET['token'] récupère ce jeton
* après la redirection d'authentification
*
* @return Zend_Http_Client
*/
function getAuthSubHttpClient()
{
global $_SESSION, $_GET;
if (!isset($_SESSION['sessionToken']) && isset($_GET['token'])) {
$_SESSION['sessionToken'] =
Zend_Gdata_AuthSub::getAuthSubSessionToken($_GET['token']);
}
$client =
Zend_Gdata_AuthSub::getHttpClient($_SESSION['sessionToken']);
799
Zend_Gdata
return $client;
}
/**
* Créer une instance du service, redirigeant l'utilisateur
* vers le serveur AuthSub si nécéssaire.
*/
$service = new Zend_Gdata_Photos(getAuthSubHttpClient());
• User Cette classe requêtera tout ce qui concerne un utilisateur du service. Sans spécifier
d'utilisateur, "default" sera utilisé.
• Album Cette classe va servir de base pour toutes les requêtes concernant les albums Picasa.
• Photo Cette classe va servir de base pour toutes les requêtes concernant les photos Picasa.
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
Pour chaque requête, des paramètres de limitations de la recherche peuvent être passés grâce
aux méthodes get(Paramètre) and set(Paramètre) :
• Projection spécifie le format de retour des données dans le flux. Peut être "api" ou "base". En
temps normal, "api" est conseillé, c'est la valeur par défaut d'ailleurs.
• Access détermine la visibilité des éléments retournés, "all"(défaut), "public", ou "private". Les
éléments non publics ne seront retournés que si le client est authentifié.
• ImgMax spécifie un filtre par dimension maximale sur les éléments retournés.
• User spécifie l'utilisateur dont les éléments sont recherchés. Par défaut, "default".
• AlbumId spécifie l'identifiant de l'album recherché. Ceci ne s'applique qu'aux requêtes album
et photo. Dans le cas d'une recherche de photo, ceci indique l'album dans lequel effectuer la
requête de recherche. Ce paramètre annule et remplace AlbumName, si spécifié.
800
Zend_Gdata
• AlbumName spécifie le nom de l'album recherché. Ceci ne s'applique qu'aux requêtes album
et photo. Dans le cas d'une recherche de photo, ceci indique l'album dans lequel effectuer la
requête de recherche. Ce paramètre annule et remplace AlbumId, si spécifié.
• PhotoId spécifie l'identifiant de la photo recherchée. Ceci ne s'applique qu'aux requêtes photo.
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
try {
$userFeed = $service->getUserFeed("sample.user");
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getMessage();
}
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
try {
$userFeed = $service->getUserFeed(null, $query);
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getMessage();
}
Construire une requête donne aussi accès aux éléments d'un utilisateur :
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
try {
$userEntry = $service->getUserEntry($query);
801
Zend_Gdata
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
try {
$albumFeed = $service->getAlbumFeed($query);
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getMessage();
}
L'objet de requête accepte aussi un nom d'album avec setAlbumName. Attention, ceci annule
un identifiant d'album éventuellement précédemment spécifié.
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
try {
$albumEntry = $service->getAlbumEntry($query);
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getMessage();
}
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
802
Zend_Gdata
try {
$photoFeed = $service->getPhotoFeed($query);
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getMessage();
}
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
try {
$photoEntry = $service->getPhotoEntry($query);
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getMessage();
}
Voici comment effectuer des actions sur les commentaires récupérés d'une photo :
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
try {
$photoFeed = $service->getPhotoFeed($query);
803
Zend_Gdata
Voici comment effectuer des actions sur les mots-clés récupérés d'une photo :
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
try {
$photoFeed = $service->getPhotoFeed($query);
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
$service->insertAlbumEntry($entry);
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
804
Zend_Gdata
$fd = $service->newMediaFileSource($photo["tmp_name"]);
$fd->setContentType($photo["type"]);
$albumEntry = $service->getAlbumEntry($albumQuery);
$service->insertPhotoEntry($entry, $albumEntry);
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
$photoEntry = $service->getPhotoEntry($photoQuery);
$service->insertCommentEntry($entry, $photoEntry);
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
$photoEntry = $service->getPhotoEntry($photoQuery);
$service->insertTagEntry($entry, $photoEntry);
805
Zend_Gdata
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
$entry = $service->getAlbumEntry($albumQuery);
$service->deleteAlbumEntry($entry, true);
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
$entry = $service->getPhotoEntry($photoQuery);
$service->deletePhotoEntry($entry, true);
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
$entry = $service->getCommentEntry($path);
$service->deleteCommentEntry($entry, true);
806
Zend_Gdata
$service = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata_Photos($client);
$photoFeed = $service->getPhotoFeed($query);
$service->deleteTagEntry($tagEntry, true);
Voici un exemple de gestion des versions et accès concurrent sur un effacement avec
deleteAlbumEntry:
807
Zend_Gdata
For more information on the YouTube Data API, please refer to the official PHP Developer's
Guide on code.google.com.
12.1. Authentication
The YouTube Data API allows read-only access to public data, which does not require
authentication. For any write requests, a user needs to authenticate either using ClientLogin or
AuthSub authentication. Please refer to the Authentication section in the PHP Developer's Guide
for more detail.
808
Zend_Gdata
For more details on the different query parameters, please refer to the Reference Guide.
The available helper functions in Zend_Gdata_YouTube_VideoQuery for each of these
parameters are described in more detail in the PHP Developer's Guide.
There are also query parameters to specify the time period over which the standard feed is
computed.
Alternatively, you could just retrieve the feed using the URL:
809
Zend_Gdata
Comments can also be retrieved for a video if you have a copy of the
Zend_Gdata_YouTube_VideoEntry object:
810
Zend_Gdata
$feedUrl = $playlistEntry->getPlaylistVideoFeedUrl();
$playlistVideoFeed = $yt->getPlaylistVideoFeed($feedUrl);
811
Zend_Gdata
Property Value
Title My Test Movie
Category Autos
Keywords cars, funny
Description My description
Filename mytestmovie.mov
File MIME type video/quicktime
Video private? FALSE
Video location 37, -122 (lat, long)
Developer Tags mydevelopertag, anotherdevelopertag
812
Zend_Gdata
$filesource = $yt->newMediaFileSource('mytestmovie.mov');
$filesource->setContentType('video/quicktime');
$filesource->setSlug('mytestmovie.mov');
$myVideoEntry->setMediaSource($filesource);
try {
$newEntry = $yt->insertEntry($myVideoEntry,
$uploadUrl,
'Zend_Gdata_YouTube_VideoEntry');
} catch (Zend_Gdata_App_HttpException $httpException) {
echo $httpException->getRawResponseBody();
} catch (Zend_Gdata_App_Exception $e) {
echo $e->getMessage();
}
813
Zend_Gdata
video's meta-data to receive back a token element which can be used to construct an HTML
upload form.
$tokenHandlerUrl = 'http://gdata.youtube.com/action/GetUploadToken';
$tokenArray = $yt->getFormUploadToken($myVideoEntry, $tokenHandlerUrl);
$tokenValue = $tokenArray['token'];
$postUrl = $tokenArray['url'];
The above code prints out a link and a token that is used to construct an HTML form to display
in the user's browser. A simple example form is shown below with $tokenValue representing the
content of the returned token element, as shown being retrieved from $myVideoEntry above.
In order for the user to be redirected to your website after submitting the form, make sure to
append a $nextUrl parameter to the $postUrl above, which functions in the same way as the
$next parameter of an AuthSub link. The only difference is that here, instead of a single-use
token, a status and an id variable are returned in the URL.
814
Zend_Gdata
try {
$control = $videoEntry->getControl();
} catch (Zend_Gdata_App_Exception $e) {
echo $e->getMessage();
}
Please refer to the full documentation available in the PHP Developer's Guide on
code.google.com.
try {
$client =
Zend_Gdata_ClientLogin::getHttpClient($username, $password);
} catch(Zend_Gdata_App_Exception $ex) {
// Affiche l'exception à l'utilisateur
die($ex->getMessage());
}
815
Zend_Gdata
déterminer la cause exacte de l'erreur, alors que $e->getMessage() ne montre pas autant
de détails.
Vous pouvez ainsi utiliser ces sous-classes d'exceptions pour les gérer chacune différemment.
Référez vous à l'API pour savoir quel composant Zend_Gdata envoie quel type d'exception.
try {
$client =
Zend_Gdata_ClientLogin::getHttpClient($username,
$password,
$service);
} catch(Zend_Gdata_App_AuthException $authEx) {
// identifiants fournis incorrects
// Vous pourriez par exemple offrir une
// seconde chance à l'utilisateur ici
...
} catch(Zend_Gdata_App_HttpException $httpEx) {
// les serveurs Google Data sont injoignables
die($httpEx->getMessage);
}
816
Zend_Http
1. Introduction
Zend_Http_Client fournit une interface qui permet d'utiliser le protocole HTTP (Hyper-Text
Transfer Protocol). Zend_Http_Client supporte les fonctionnalités de base d'un client HTTP,
ainsi que des fonctionnalités plus avancées, comme l'authentification ou l'upload de fichiers.
Toutes les requêtes retournent un objet Zend_Http_Response, avec lequel on pourra accéder
au corps ou aux en-têtes de la réponse HTTP (voyez Section 5, « Zend_Http_Response »).
// OU
$client = new Zend_Http_Client();
$client->setUri('http://example.org');
$client->setConfig(array(
'maxredirects' => 0,
'timeout' => 30));
// You can also use a Zend_Config object to set the client's configuration
$config = new Zend_Config_Ini('httpclient.ini, 'secure');
$client->setConfig($config);
817
Zend_Http
818
Zend_Http
Si aucune méthode de requêtage HTTP n'est définie, alors la dernière utilisée via setMethod()
sera utilisée. Si setMethod() n'a jamais été appelée, GET est alors utilisée par défaut.
// requête POST
$response = $client->request('POST');
Coté POST, c'est très similaire à GET, sauf que les paramètres POST doivent faire partie du
corps de la requête. Il n'est donc pas possible de les ajouter dans l'URL. Utilisez simplement
setParameterPost() de la même manière que sa soeur setParameterGet().
1
Voyez la RFC 2616 - http://www.w3.org/Protocols/rfc2616/rfc2616.html.
819
Zend_Http
Notez qu'en plus de paramètres POST, vous pouvez ajouter des paramètres GET à une requête
POST. Le contraire n'est pas possible, ainsi les paramètres POST ajoutés à une requête GET
seront acceptés certes, mais ignorés.
Conformément à la RFC HTTP/1.1, les codes réponse HTTP 301 et 302 doivent être traités par
le client en envoyant à nouveau la même requête à l'adresse spécifiée - en utilisant la même
méthode de requête. Cependant, la plupart des clients ne réagissent pas correctement et redirige
toujours via une requête GET. Par défaut, Zend_Http_Client agit de même - Lors d'une
redirection basée sur la réception d'un code 301 ou 302, tous les paramètres GET et POST sont
remis à zéro, et une requête GET est envoyée à la nouvelle adresse. Ce comportement peut
être modifié en positionnant le paramètre de configuration "strictredirects" à TRUE :
Exemple 417. Forcer des redirections conformes au RFC 2616 lors de la réception
d'un code statut 301 and 302
// Redirections strictes
$client->setConfig(array('strictredirects' => true)
Il est toujours possible d'obtenir le nombre de redirections effectuées après l'envoi d'une requête
en invoquant la méthode getRedirectionsCount().
820
Zend_Http
Pour plus d'information sur les objets Zend_Http_Cookie, voir Section 4, « Zend_Http_Cookie
and Zend_Http_CookieJar ».
Pour plus d'information sur la classe Zend_Http_CookieJar, voir Section 4.5, « Classe
Zend_Http_CookieJar : Instanciation ».
821
Zend_Http
setHeader() peut aussi être facilement utilisé pour définir des en-têtes multiples en un seul
appel, en fournissant un tableau d'en-têtes comme paramètre unique :
822
Zend_Http
Dans le premier exemple, la variable $texte est envoyée et sera disponible dans
$_FILES['upload'] côté serveur. Dans le second exemple, le fichier existant "/tmp/
Backup.tar.gz" est envoyé au serveur et sera disponible dans $_FILES['bufile']. Son
type sera défini automatiquement si possible. Sinon, le type sera défini comme "application/
octet-stream".
Envoi de fichiers
$xml = '<book>' .
' <title>Islands in the Stream</title>' .
' <author>Ernest Hemingway</author>' .
' <year>1970</year>' .
'</book>';
$client->setRawData($xml, 'text/xml')->request('POST');
Les données seront disponible côté serveur via la variable PHP $HTTP_RAW_POST_DATA ou
via le flux php://input.
Définir des données brutes POST pour une requête écrasera tout autre
paramètre POST ou envoi de fichiers. Il est recommandé de ne pas utiliser les
823
Zend_Http
Lorsqu'on génère plusieurs requêtes vers le même hôte, il est chaudement recommandé
d'activer la variable de configuration "keepalive". De cette manière, si le serveur supporte le mode
de connexion "keep-alive", la connexion au serveur sera fermée après l'exécution de toutes les
requêtes et la destruction de l'instance. Ceci permet d'éviter au serveur d'ouvrir et de fermer de
multiples connexions TCP.
Lorsqu'on génère plusieurs requêtes avec le même client, mais qu'on souhaite s'assurer que tous
les paramètres spécifiques de chacune des requêtes sont effacés, on peut utiliser la méthode
resetParameters(). Ceci permet de supprimer tous les paramètres GET et POST, le contenu
des requêtes et les en-têtes spécifiques de manière à ce qu'ils ne soient pas réutilisés lors de
la requête suivante.
Notez que les en-têtes spécifiques non liés à la requête ne sont pas réinitialisés
par défaut quand la méthode resetParameters est invoquée. En fait, seuls
les en-têtes "Content-length" et "Content-type" sont supprimés. Ceci permet
de définir une seule fois les en-têtes comme "Accept-language" ou "Accept-
encoding".
824
Zend_Http
Pour effacer tous les entêtes et toutes les données excepté l'URI et la méthode,
utilisez resetParameters(true).
Une autre fonctionnalité spécifique aux requêtes consécutives est l'objet Magasin de Cookies
("Cookie Jar"). Il permet de sauver automatiquement les cookies définis par le serveur lors de
la première requête et de les renvoyer de manière transparente lors de chacune des requêtes
suivantes. Ceci permet, par exemple, de passer une étape d'authentification avant d'envoyer
d'autres requêtes.
Si votre application nécessite une requête d'authentification par utilisateur, et que d'autres
requêtes peuvent être effectuées via plusieurs scripts différents, il peut se révéler pratique de
stocker le Magasin de cookies dans la session utilisateur. De cette manière, il sera possible de
ne s'identifier qu'une seule fois par session.
$client->setCookieJar($_SESSION['cookiejar']);
} else {
// Sinon, Identifions-nous et stockons le cookie
$client->setCookieJar();
$client->setUri('http://www.exemple.com/connexion.php');
$client->setParameterPost(array(
'user' => 'shahar',
'pass' => 'secret'
));
$client->request(Zend_Http_Client::POST);
$reponse = $client->request(Zend_Http_Client::GET);
In order to use stream to pass data to Zend_Http_Client, use setRawData() method with
data argument being stream resource (e.g., result of fopen()).
825
Zend_Http
In order to receive data from the server as stream, use setStream(). Optional argument
specifies the filename where the data will be stored. If the argument is just TRUE (default),
temporary file will be used and will be deleted once response object is destroyed. Setting
argument to FALSE disables the streaming functionality.
You can either write the response to pre-defined file, or use temporary file for storing it and send
it out or write it to another file using regular stream functions.
• Zend_Http_Client_Adapter_Socket (défaut)
• Zend_Http_Client_Adapter_Proxy
• Zend_Http_Client_Adapter_Curl
• Zend_Http_Client_Adapter_Test
L'objet Zend_Http_Client se voit spécifié un adaptateur via son constructeur avec le tableau
d'options, à l'index 'adapter'. Fournissez alors une chaîne représentant la classe d'adaptateur à
utiliser (par exemple 'Zend_Http_Client_Adapter_Socket'), ou un objet directement (par exemple
new Zend_Http_Client_Adapter_Test). Vous pouvez de même passer un adaptateur plus
tard, avec la méthode Zend_Http_Client->setConfig().
826
Zend_Http
L'adaptateur Socket peut être configuré avec des options, passées par Zend_Http_Client-
>setConfig() ou au constructeur du client.
Bien que les réglages par défaut du mode SSL fonctionneront pour la plupart
des applications, vous pourrez avoir besoin de les changer si le serveur, auquel
vous vous connectez, requière un paramétrage particulier du client. Dans ce cas,
vous devriez lire les sections sur la couche de transport SSL et ses options à
cette adresse.
827
Zend_Http
Le résultat ci-dessus sera similaire à l'ouverture d'une connexion TCP avec la commande PHP
suivante :
fsockopen('tls://www.example.com', 443)
You can access the stream context using the following methods of
Zend_Http_Client_Adapter_Socket:
• getStreamContext() Get the stream context of the adapter. If no stream context was set,
will create a default stream context and return it. You can then set or get the value of different
context options using regular PHP stream context functions.
828
Zend_Http
Exemple 429. Setting stream context options for the Socket adapter
// Array of options
$options = array(
'socket' => array(
// Bind local socket side to a specific interface
'bindto' => '10.1.2.3:50505'
),
'ssl' => array(
// Verify server side certificate,
// do not accept invalid or self-signed SSL certificates
'verify_peer' => true,
'allow_self_signed' => false,
// Method 3: get the default stream context and set the options on it
$context = $adapter->getStreamContext();
stream_context_set_option($context, $options);
// If everything went well, you can now access the context again
$opts = stream_context_get_options($adapter->getStreamContext());
echo $opts['ssl']['peer_certificate'];
Note that you must set any stream context options before using the adapter
to preform actual requests. If no context is set before preforming HTTP
requests with the Socket adapter, a default stream context will be created. This
context resource could be accessed after preforming any requests using the
getStreamContext() method.
829
Zend_Http
proxy_host devrait toujours être fourni. Si ça n'est pas le cas, alors le client retournera sur une
connexion Socket par défaut. proxy_port est par défaut à "8080".
// Paramètres de configuration
$config = array(
'adapter' => 'Zend_Http_Client_Adapter_Proxy',
'proxy_host' => 'proxy.int.zend.com',
'proxy_port' => 8000,
'proxy_user' => 'shahar.e',
'proxy_pass' => 'bananashaped'
);
Comme déjà dit, si proxy_host n'est pas rempli ou défini en tant que chaîne vide, alors le client
utilisera l'adaptateur Socket par défaut. Ceci est utile si le proxy est utilisé optionnellement, ou
par intermittence.
830
Zend_Http
occur for a HTTP client and make it a perfect choice for a HTTP adapter. It supports secure
connections, proxy, all sorts of authentication mechanisms and shines in applications that move
large files around between servers.
$config = array(
'adapter' => 'Zend_Http_Client_Adapter_Curl',
'curloptions' => array(CURLOPT_FOLLOWLOCATION => true),
);
$client = new Zend_Http_Client($uri, $config);
By default the cURL adapter is configured to behave exactly like the Socket Adapter and it
also accepts the same configuration parameters as the Socket and Proxy adapters. You can
also change the cURL options by either specifying the 'curloptions' key in the constructor of the
adapter or by calling setCurlOption($name, $value). The $name key corresponds to the
CURL_* constants of the cURL extension. You can get access to the Curl handle by calling
$adapter->getHandle();
You can use cURL to transfer very large files over HTTP by filehandle.
$putFileSize = filesize("filepath");
$putFileHandle = fopen("filepath", "r");
831
Zend_Http
$response = $client->request('GET');
// ... continuez à parser $response...
L'exemple ci dessus montre comment préconfigurer la réponse qui sera retournée lors d'une
requête de votre objet client. Ainsi lors des tests, votre application continuera de se comporter
normalement, elle aura tout simplement été trompée (mock). Aucune connexion HTTP n'est dans
ce cas là nécessaire.
Quelques fois, plusieurs transactions HTTP peuvent être nécessaires. Une réponse peut
demander une redirection, vers une autre. Dans ce cas, utiliser setResponse() toute seule
n'est pas possible car il ne sera pas possible de spécifier les réponses suivantes, nécessaires
alors à l'application.
832
Zend_Http
Dans l'exemple ci-dessus, l'adaptateur est configuré pour un scénario de test de redirections
302. En fonction de votre application, le suivi d'une redirection peut être ou non désiré. Dans
notre exemple, nous nous attendons à ce que la redirection soit suivie et nous configurons
notre adaptateur de tests pour ceci. La réponse de redirection originelle (302) est définie avec
la méthode setResponse(), quant à la réponse non redirigeante (200) suivante, elles est
définie avec la méthode addResponse(). Lorsque votre objet client est configuré, vous pouvez
l'injecter dans votre application à tester, et voir le résultat et les comportements.
If you need the adapter to fail on demand you can use setNextRequestWillFail($flag).
The method will cause the next call to connect() to throw an
Zend_Http_Client_Adapter_Exception exception. This can be useful when your
application caches content from an external site (in case the site goes down) and you want to
test this feature.
833
Zend_Http
try {
// This call will result in a Zend_Http_Client_Adapter_Exception
$client->request();
} catch (Zend_Http_Client_Adapter_Exception $e) {
// ...
}
834
* Définit le tableau de configuration pour cet adaptateur
*
* @param array $config
*/
Zend_Http
public function setConfig($config = array())
{
// Ceci change rarement, vous devriez copier l'implémentation
// présente dans Zend_Http_Client_Adapter_Socket.
Exemple 436. Création de votre propre adaptateur de connexion
}
/**
* Connecte à une serveur distant
*
* @param string $host
* @param int $port
* @param boolean $secure
*/
public function connect($host, $port = 80, $secure = false)
{
// Etablit la connexion au serveur
}
/**
* Envoie une requête au serveur
*
* @param string $method
* @param Zend_Uri_Http $url
* @param string $http_ver
* @param array $headers
* @param string $body
* @return string Request as text
*/
public function write($method,
$url,
$http_ver = '1.1',
$headers = array(),
$body = '')
{
// Envoie la requête au serveur distant. Cette fonction devrait
// retourner la requête complète (en-tête et corps) as a string
}
/**
* Lit la réponse du serveur
*
* @return string
*/
public function read()
{
// Lit la réponse du serveur distant, et la retourne sous forme
// de chaine de caractères
}
/**
* Ferme la connexion avec le serveur
*
*/
public function close()
{
// Ferme la connexion, appelée en dernière.
}
}
835
Zend_Http
• $path: Chemin de validité du cookie, par ex. "/foo/bar/" (optionnel, défaut : "/")
• $secure: Booléen, Si le cookie doit être transmis via connexion sécurisée (HTTPS)
uniquement (optionnel, défaut à FALSE)
836
Zend_Http
// Va afficher 'foo=two+words;' :
echo $cookie->__toString();
837
Zend_Http
• boolean isExpired(int $time = null): Vérifie si le cookie est expirés. Si il n'y a pas
de date d'expiration, cette méthode retournera toujours TRUE. Si $time est fourni, alors la
date du cookie sera comparée à ce $time, et non plus au temps actuel.
• boolean isSessionCookie(): Vérifie si le cookie est un cookie dit 'de session'. C'est un
cookie sans date d'expiration, sensé être détruit à la fin de la session de travail actuelle (à la
fermeture du navigateur).
• mixed $uri : un objet Zend_Uri_Http avec un domaine et un chemin à vérifier. Une chaîne
représentant une URL peut aussi être utilisée. Le cookie sera déclaré bon si le schéma de
l'URL (HTTP ou HTTPS) correspond, ainsi que le chemin (path).
838
Zend_Http
• int $now : timestamp UNIX passé pour vérifier l'expiration du cookie. Si non spécifié, alors
le temps actuel sera pris en considération.
$cookie->match('https://www.example.com/somedir/foo.php');
// Retournera true
$cookie->match('http://www.example.com/somedir/foo.php');
// Retournera false, car la connexion n'est pas sécurisée
$cookie->match('https://otherexample.com/somedir/foo.php');
// Retournera false, le domaine est incorrect
$cookie->match('https://example.com/foo.php');
// Retournera false, le chemin est incorrect
$cookie->match('https://www.example.com/somedir/foo.php', false);
// Retournera false, car les cookies de session ne sont pas pris en compte
$cookie->match('https://sub.domain.example.com/somedir/otherdir/foo.php');
// Retournera true
$cookie->match('http://www.example.com/');
// Retournera true
$cookie->match('https://www.example.com/');
// Will return true - non secure cookies can go
// over secure connexions as well!
$cookie->match('http://subdomain.example.com/');
// Retournera false, domaine incorrect
839
Zend_Http
Si vous voulez tout de même instancier manuellement un objet CookieJar, appelez son
constructeur avec "new Zend_Http_CookieJar()", sans paramètres. Sinon il est possible
aussi de passer par la méthode statique Zend_Http_CookieJar::fromResponse() qui
prend, elle, deux paramètres : un objet Zend_Http_Response et un URI de référence
(un objet Zend_Uri_Http ou une chaîne). Cette méthode retourne alors un objet
Zend_Http_CookieJar qui contiendra les cookies de la réponse HTTP passée. L'URI de
référence servira à remplir les paramètres "domain" et "path" des cookies, si jamais ils n'ont pas
été définis dans les en-têtes "Set-Cookie".
• Zend_Http_CookieJar->addCookiesFromResponse($response, $ref_uri):
Ajoute tous les cookies présents dans une réponse HTTP au conteneur. La réponse HTTP doit
être un objet Zend_Http_Response contenant au moins un en-tête "Set-Cookie". $ref_uri
est un URI (un objet Zend_Uri_Http ou une chaîne), servant de référence pour remplir les
paramètres du cookie "domain" et "path", si ceux-ci ne sont pas trouvés dans la réponse.
Il est important de noter que chacune de ces trois méthodes, prend un paramètre spécial destiné
à déterminer le type que chaque méthode retournera. Ce paramètre peut avoir 3 valeurs:
• Zend_Http_CookieJar::COOKIE_STRING_CONCAT: Similaire à
COOKIE_STRING_ARRAY, mais si plusieurs cookies devaient être retournés, alors il ne
s'agira plus d'un tableau, mais d'une chaîne concaténant tous les cookies, séparés par des
point-virgule (;). Ceci est très utile pour passer tous les cookies d'un coup, dans l'en-tête HTTP
"Cookie".
840
Zend_Http
• Zend_Http_CookieJar->getMatchingCookies($uri[,
$matchSessionCookies[, $ret_as[, $now]]]): Retourne tous les cookies ayant une
correspondance pour un scénario donné, à savoir un URI et une date d'expiration.
• $ret_as spécifie le type de retour, comme vu plus haut (par défaut COOKIE_OBJECT).
• $now est un entier représentant un timestamp UNIX à considérer comme 'maintenant'. Ainsi
tous les cookies expirant avant ce temps là, ne seront pas pris en compte. Si vous ne
spécifiez pas ce paramètre, alors c'est le temps actuel qui sera pris comme référence.
Vous pouvez en apprendre plus sur la correspondance des cookies ici : Section 4.4,
« Zend_Http_Cookie: Correspondance de scénario ».
5. Zend_Http_Response
5.1. Introduction
Zend_Http_Response fournit un accès simplifié aux réponses HTTP d'un message,
ainsi qu'un ensemble de méthodes statiques pour analyser ces réponses HTTP.
Habituellement Zend_Http_Response est utilisé en tant qu'objet retourné par une requête
Zend_Http_Client.
Dans la plupart des cas, un objet Zend_Http_Response sera instancié en utilisant la méthode
fromString(), qui lit une chaîne contenant une réponse HTTP, et retourne un nouvel objet
Zend_Http_Response :
$str = '';
$sock = fsockopen('www.exemple.com', 80);
$req = "GET / HTTP/1.1\r\n" .
"Host: www.exemple.com\r\n" .
"Connection: close\r\n" .
"\r\n";
fwrite($sock, $req);
while ($buff = fread($sock, 1024))
$str .= $sock;
$response = Zend_Http_Response::fromString($str);
Vous pouvez aussi utiliser le constructeur pour créer un nouvel objet de réponse HTTP, en
spécifiant tous les paramètres de la réponse :
841
Zend_Http
• $headers : un tableau associatif d'en-têtes de réponse HTTP (par ex. "Host" =>
"exemple.com")
• $message : le message de la réponse HTTP (par ex. "OK", "Internal Server Error"). Si non
spécifié, le message sera paramétré suivant le code de la réponse.
• isSuccessful() : la requête est réussie ou non. Retourne true pour les codes de
réponses HTTP 1xx et 2xx.
• isError() : la requête implique une erreur ou non. Retourne true pour les codes de
réponses HTTP 4xx (erreurs du client) et 5xx (erreurs du serveur).
• isRedirect() : la requête est une redirection ou non. Retourne true pour les codes de
réponses HTTP 3xx.
if ($response->isError()) {
echo "Erreur de transmission des données.\n"
echo "Les infos Serveur sont : "
. $response->getStatus()
. " " . $response->getMessage()
. "\n";
}
// ... traiter la réponse ici ...
• int getStatus() : récupère le code de la réponse HTTP (par ex. 200, 504, etc.)
• string getMessage() : récupère le message de la réponse HTTP (par ex. "Not Found",
"Authorization Required")
• array getHeaders() : récupère les en-têtes de la réponse HTTP sous la forme d'un tableau
associatif (par ex. 'Content-type' => 'text/html')
842
Zend_Http
if ($response->getStatus() == 200) {
echo "La requête retourne les informations suivantes :<br />";
echo $response->getBody();
} else {
echo "Une erreur est apparue lors de la recherche des données :<br />";
echo $response->getStatus() . ": " . $response->getMessage();
}
$ctype = $response->getHeader('Content-type');
if (is_array($ctype)) $ctype = $ctype[0];
$body = $response->getBody();
if ($ctype == 'text/html' || $ctype == 'text/xml') {
$body = htmlentities($body);
}
echo $body;
843
Zend_Http
844
Zend_InfoCard
1. Introduction
The Zend_InfoCard component implements relying-party support for Information Cards.
Information Cards are used for identity management on the internet and authentication of users
to web sites. The web sites that the user ultimately authenticates to are called relying-parties.
Detailed information about information cards and their importance to the internet identity
metasystem can be found on the IdentityBlog.
In the example above, the requiredClaims <param> tag is used to identify pieces of
information known as claims (i.e. person's first name, last name) which the web site (a.k.a "relying
party") needs in order a user to authenticate using an information card. For your reference, the
full URI (for instance the givenname claim) is as follows: http://schemas.xmlsoap.org/
ws/2005/05/identity/claims/givenname
When the above HTML is activated by a user (clicks on it), the browser will bring up a card
selection program which not only shows them which information cards meet the requirements of
the site, but also allows them to select which information card to use if multiple meet the criteria.
This information card is transmitted as an XML document to the specified POST URL and is
ready to be processed by the Zend_InfoCard component.
Note, Information cards can only be HTTP POSTed to SSL-encrypted URLs. Please consult your
web server's documentation on how to set up SSL encryption.
845
Zend_InfoCard
<?php
if (isset($_POST['xmlToken'])) {
$adapter->addCertificatePair('/usr/local/Zend/apache2/conf/server.key',
'/usr/local/Zend/apache2/conf/server.crt');
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($adapter);
switch ($result->getCode()) {
case Zend_Auth_Result::SUCCESS:
$claims = $result->getIdentity();
print "Given Name: {$claims->givenname}<br />";
print "Surname: {$claims->surname}<br />";
print "Email Address: {$claims->emailaddress}<br />";
print "PPI: {$claims->getCardID()}<br />";
break;
case Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID:
print "The Credential you provided did not pass validation";
break;
default:
case Zend_Auth_Result::FAILURE:
print "There was an error processing your credentials.";
break;
}
if (count($result->getMessages()) > 0) {
print "<pre>";
var_dump($result->getMessages());
print "</pre>";
}
}
?>
<hr />
<div id="login" style="font-family: arial; font-size: 2em;">
<p>Simple Login Demo</p>
<form method="post">
<input type="submit" value="Login" />
<object type="application/x-informationCard" name="xmlToken">
<param name="tokenType"
value="urn:oasis:names:tc:SAML:1.0:assertion" />
<param name="requiredClaims"
value="http://.../claims/givenname
http://.../claims/surname
http://.../claims/emailaddress
http://.../claims/privatepersonalidentifier" />
</object>
</form>
</div>
846
Zend_InfoCard
Once the adapter has been configured, you can then use the standard Zend_Auth facilities to
validate the provided information card token and authenticate the user by examining the identity
provided by the getIdentity() method.
<?php
if (isset($_POST['xmlToken'])) {
$infocard = new Zend_InfoCard();
$infocard->addCertificatePair('/usr/local/Zend/apache2/conf/server.key',
'/usr/local/Zend/apache2/conf/server.crt');
$claims = $infocard->process($_POST['xmlToken']);
if($claims->isValid()) {
print "Given Name: {$claims->givenname}<br />";
print "Surname: {$claims->surname}<br />";
print "Email Address: {$claims->emailaddress}<br />";
print "PPI: {$claims->getCardID()}<br />";
} else {
print "Error Validating identity: {$claims->getErrorMsg()}";
}
}
?>
<hr />
<div id="login" style="font-family: arial; font-size: 2em;">
<p>Simple Login Demo</p>
<form method="post">
<input type="submit" value="Login" />
<object type="application/x-informationCard" name="xmlToken">
<param name="tokenType"
value="urn:oasis:names:tc:SAML:1.0:assertion" />
<param name="requiredClaims"
value="http://.../claims/givenname
http://.../claims/surname
http://.../claims/emailaddress
http://.../claims/privatepersonalidentifier" />
</object>
</form>
</div>
In the example above, we use the Zend_InfoCard component independently to validate the
token provided by the user. As was the case with the Zend_Auth_Adapter_InfoCard, we
create an instance of Zend_InfoCard and then set one or more SSL certificate public/private
key pairs used by the web server. Once configured, we can use the process() method to
process the information card and return the results.
847
Zend_InfoCard
your web site when the user authenticated. As shown in the examples above, the validity of
the information card can be ascertained by calling the Zend_InfoCard_Claims::isValid()
method. Claims themselves can either be retrieved by simply accessing the identifier desired
(i.e. givenname) as a property of the object or through the getClaim() method.
In most cases you will never need to use the getClaim() method. However, if your
requiredClaims mandate that you request claims from multiple different sources/namespaces
then you will need to extract them explicitly using this method (simply pass it the full URI of the
claim to retrieve its value from within the information card). Generally speaking however, the
Zend_InfoCard component will set the default URI for claims to be the one used the most
frequently within the information card itself and the simplified property-access method can be
used.
As part of the validation process, it is the developer's responsibility to examine the issuing
source of the claims contained within the information card and to decide if that source is
a trusted source of information. To do so, the getIssuer() method is provided within the
Zend_InfoCard_Claims object which returns the URI of the issuer of the information card
claims.
// ...
public function submitinfocardAction()
{
if (!isset($_REQUEST['xmlToken'])) {
throw new ZBlog_Exception('Expected an encrypted token ' .
'but was not provided');
}
try {
$claims = $infoCard->process($request['xmlToken']);
} catch(Zend_InfoCard_Exception $e) {
// TODO Error processing your request
throw $e;
}
if ($claims->isValid()) {
$db = ZBlog_Data::getAdapter();
$ppi = $db->quote($claims->getCardID());
$fullname = $db->quote("{$claims->givenname} {$claims->surname}");
848
Zend_InfoCard
try {
$db->query($query);
} catch(Exception $e) {
// TODO Failed to store in DB
}
$this->view->render();
return;
} else {
throw new
ZBlog_Exception("Infomation card failed security checks");
}
}
The Zend_InfoCard adapter is used as a callback mechanism within the component to perform
various tasks, such as storing and retrieving Assertion IDs for information cards when they are
processed by the component. While storing the assertion IDs of submitted information cards
is not necessary, failing to do so opens up the possibility of the authentication scheme being
compromised through a replay attack.
849
Zend_InfoCard
$claims = $infoCard->process($_POST['xmlToken']);
850
Zend_Json
1. Introduction
Zend_Json fournit des méthodes pratiques permettant de convertir du code PHP natif en
notation JSON, et vice versa. Pour plus d'informations concernant JSON, visitez le site du projet
JSON.
La notation JSON (JavaScript Object Notation [Ndt : Notation-Objet JavaScript]) peut être utilisée
comme un système d'échange de données entre JavaScript et d'autres langages. Comme la
notation JSON peut être évaluée directement par JavaScript, c'est une alternative plus simple
que XML pour les interfaces AJAX.
De plus, Zend_Json fournit une manière utile pour convertir n'importe quel chaîne arbitraire
formatée en XML en une chaîne formatée en JSON. Cette caractéristique permettra aux
développeurs PHP de transformer les données encodées en format XML en un format JSON
avant de l'envoyer aux navigateurs basés sur des applications client Ajax. Zend_Json fournit
une fonction facilitant la conversion de données dynamiques du code côté serveur évitant ainsi
l'analyse syntaxique inutile réalisée dans les applications côté client. Il offre une fonction utilitaire
agréable qui aboutit aux techniques plus simples de traitement de données d'applications
spécifiques.
2. Utilisation de base
L'utilisation de Zend_Json implique l'emploi des deux méthodes statiques publiques
disponibles : Zend_Json::encode() et Zend_Json::decode().
851
Zend_Json
JSON ne vous permet pas de référencer des objets, donc le soin devra être pris pour
ne pas coder des objets avec des références récursives. Si vous avez des problèmes
de récursivité, Zend_Json::encode() et Zend_Json_Encoder::encode() autorisent un
deuxième paramètre facultatif afin de vérifier la récursivité ; si un objet est sérialisé deux fois,
une exception sera levée.
Bien que les objets Javascript correspondent de très près aux tableau associatifs de PHP,
décoder des objets JSON pose une légère difficulté. Certains suggèrent qu'un identifiant de
classe soit passé, et qu'une instance de cette classe soit créée et définie avec les paires clé/
valeur des objets JSON ; d'autres pensent que cela pourrait poser un risque de sécurité potentiel.
Par défaut, Zend_Json décodera des objets JSON comme en tableaux associatifs. Cependant,
si vous désirez avoir un objet en retour, vous pouvez le spécifier :
Tous les objets sont ainsi décodés et retournés comme des objets de type StdClass, avec
leurs propriétés correspondantes aux paires clé/valeur de la notation JSON.
La recommandation de Zend Framework est que le développeur doit décider comment décoder
les objets JSON. Si un objet d'un type spécifié doit être créé, il peut être créé dans le code du
développeur et définit avec les valeurs décodées en utilisant Zend_Json.
Still sometimes you might want to use the internal encoder/decoder even if you have ext/json
installed. You can achieve this by calling:
Zend_Json::$useBuiltinEncoderDecoder = true:
852
Zend_Json
naturally does. With the Expression support for Zend_Json support you can encode JSON
objects with valid javascript callbacks. This works for both json_encode() or the internal
encoder.
A javascript callback is represented using the Zend_Json_Expr object. It implements the value
object pattern and is immutable. You can set the javascript expression as the first constructor
argument. By default Zend_Json::encode does not encode javascript callbacks, you have to
pass the option 'enableJsonExprFinder' = true into the encode function. If enabled the
expression support works for all nested expressions in large object structures. A usage example
would look like:
$data = array(
'onClick' => new Zend_Json_Expr('function() {'
. 'alert("I am a valid javascript callback '
. 'created by Zend_Json"); }'),
'other' => 'no expression',
);
$jsonObjectWithExpression = Zend_Json::encode(
$data,
false,
array('enableJsonExprFinder' => true)
);
Ce qui suit est un exemple simple qui montre à la fois la chaîne XML fournie et la chaîne JSON
retournée en résultat de la fonction Zend_Json::fromXml(). Cet exemple utilise le paramètre
facultatif pour ne pas ignorer les attributs XML pendant la conversion. Par conséquent, vous
pouvez noter que la chaîne résultante JSON inclut une représentation des attributs XML actuels
de la chaîne XML fournie.
853
Zend_Json
<book id="2">
<title>PHP Hacks</title>
<author><first>Jack</first><last>Herrington</last></author>
<publisher>O'Reilly</publisher>
</book>
<book id="3">
<title>Podcasting Hacks</title>
<author><first>Jack</first><last>Herrington</last></author>
<publisher>O'Reilly</publisher>
</book>
</books>
{
"books" : {
"book" : [ {
"@attributes" : {
"id" : "1"
},
"title" : "Code Generation in Action",
"author" : {
"first" : "Jack", "last" : "Herrington"
},
"publisher" : "Manning"
}, {
"@attributes" : {
"id" : "2"
},
"title" : "PHP Hacks", "author" : {
"first" : "Jack", "last" : "Herrington"
},
"publisher" : "O'Reilly"
}, {
"@attributes" : {
"id" : "3"
},
"title" : "Podcasting Hacks", "author" : {
"first" : "Jack", "last" : "Herrington"
},
"publisher" : "O'Reilly"
}
]}
}
Plus de détails au sujet de ce dispositif xml2json peuvent être trouvés dans la proposition
originale elle-même. Jetez un oeil à la proposition Zend_xml2json.
854
Zend_Json
JSON-RPC is a lightweight Remote Procedure Call protocol that utilizes JSON for its messaging
envelopes. This JSON-RPC implementation follows PHP's SoapServer API. This means, in a
typical situation, you will simply:
Zend_Json_Server listens for POST requests only at this time; fortunately, most JSON-RPC
client implementations in the wild at the time of this writing will only POST requests as it is. This
makes it simple to utilize the same server end point to both handle requests as well as to deliver
the service SMD, as is shown in the next example.
855
* Calculator - sample class to expose via JSON-RPC
*/
First,
classlet's define a class we wish to expose via the JSON-RPC server. We'll call the class
Calculator
'Calculator',
{ and define methodsZend_Json
for 'add', 'subtract', 'multiply', and 'divide':
/**
* Return sum of two variables
*
Exemple
* @param 445.int
Zend_Json_Server
$x Usage
* @param int $y
* @return int
*/
public function add($x, $y)
{
return $x + $y;
}
/**
* Return difference of two variables
*
* @param int $x
* @param int $y
* @return int
*/
public function subtract($x, $y)
{
return $x - $y;
}
/**
* Return product of two variables
*
* @param int $x
* @param int $y
* @return int
*/
public function multiply($x, $y)
{
return $x * $y;
}
$server
/** =
$server = new
new Zend_Json_Server();
Zend_Json_Server();
$server->setClass('Calculator');
* Return the division of two variables
However, this will not address the issue of returning an SMD so that the JSON-RPC client
// Indicate
* what functionality is available:
can
if autodiscover
('GET' methods. That can be accomplished
== $_SERVER['REQUEST_METHOD'])
$server->setClass('Calculator'); { by determining the HTTP request
* @param int $x
method,
//
Now we'll and then
Indicate specifying
create aint
* @param the URL some server
endpoint, metadata:
and
script$yto handle the requests:the JSON-RPC version used:
$server->setTarget('/json-rpc.php')
// Handle the request:
* @return float
->setEnvelope(Zend_Json_Server_Smd::ENV_JSONRPC_2);
$server->handle();
*/
Note that each method has a docblock with entries indicating each parameter and its
public function divide($x, $y)
type,//
{as Grab
well as
theanSMDentry for the return value. This is absolutely critical when utilizing
$smdreturn $x /or $y;
any other server component in Zend Framework, for that matter.
= $server->getServiceMap();
Zend_Json_Server
$server = new Zend_Json_Server();
}
$server->setClass('Calculator');
} // Return the SMD to the client
header('Content-Type: application/json');
If
ifutilizing
('GET' the
== JSON-RPC server with Dojo toolkit,
$_SERVER['REQUEST_METHOD']) { you will also need to set a special
echo $smd;
compatibility flag to ensure that the
$server->setTarget('/json-rpc.php')
return; two interoperate properly:
} ->setEnvelope(Zend_Json_Server_Smd::ENV_JSONRPC_2);
$smd = $server->getServiceMap();
$server->handle();
// Set Dojo compatibility:
$smd->setDojoCompatible(true);
header('Content-Type: application/json');
echo $smd;
return;
}
$server->handle();
856
Zend_Json
5.1.1. Zend_Json_Server
Zend_Json_Server is the core class in the JSON-RPC offering; it handles all requests and
returns the response payload. It has the following methods:
• setClass($class): Specify a class or object to attach to the server; all public methods of
that item will be exposed as JSON-RPC methods.
5.1.2. Zend_Json_Server_Request
The JSON-RPC request environment is encapsulated in the Zend_Json_Server_Request
object. This object allows you to set necessary portions of the JSON-RPC request, including the
request ID, parameters, and JSON-RPC specification version. It has the ability to load itself via
JSON or a set of options, and can render itself as JSON via the toJson() method.
• addParam($value, $key = null): Add a parameter to use with the method call.
Parameters can be just the values, or can optionally include the parameter name.
857
Zend_Json
• isMethodError(): Determine whether or not the request is malformed and would result in
an error.
• setId($name): Set the request identifier (used by the client to match requests to responses).
• setVersion($version): Set the JSON-RPC specification version the request conforms to.
May be either '1.0' or '2.0'.
5.1.3. Zend_Json_Server_Response
The JSON-RPC response payload is encapsulated in the Zend_Json_Server_Response
object. This object allows you to set the return value of the request, whether or not the response
is an error, the request identifier, the JSON-RPC specification version the response conforms
to, and optionally the service map.
• setId($name): Set the request identifier (so the client may match the response with the
original request).
858
Zend_Json
• toJson(): Serialize the response to JSON. If the response is an error response, serializes
the error object.
5.1.4. Zend_Json_Server_Error
JSON-RPC has a special format for reporting error conditions. All errors need to provide,
minimally, an error message and error code; optionally, they can provide additional data, such
as a backtrace.
Error codes are derived from those recommended by the XML-RPC EPI project.
Zend_Json_Server appropriately assigns the code based on the error condition. For
application exceptions, the code '-32000' is used.
• setCode($code): Set the error code; if the code is not in the accepted XML-RPC error code
range, -32000 will be assigned.
• setData($data): Set auxiliary data further qualifying the error, such as a backtrace.
• toArray(): Cast the error to an array. The array will contain the keys 'code', 'message', and
'data'.
5.1.5. Zend_Json_Server_Smd
SMD stands for Service Mapping Description, a JSON schema that defines how a client can
interact with a particular web service. At the time of this writing, the specification has not yet been
formally ratified, but it is in use already within Dojo toolkit as well as other JSON-RPC consumer
clients.
At its most basic, a Service Mapping Description indicates the method of transport (POST,
GET, TCP/IP, etc), the request envelope type (usually based on the protocol of the server), the
target URL of the service provider, and a map of services available. In the case of JSON-RPC,
the service map is a list of available methods, which each method documenting the available
parameters and their types, as well as the expected return value type.
Zend_Json_Server_Smd provides an object oriented way to build service maps. At its most
basic, you pass it metadata describing the service using mutators, and specify services (methods
and functions).
859
Zend_Json
Zend_Json_Server actually does all of this behind the scenes for you, by using reflection on
the attached classes and functions; you should create your own service maps only if you need
to provide custom functionality that class and function introspection cannot offer.
• setTransport($transport): Set the transport used to access the service; only POST is
currently supported.
• setContentType($type): Set the content type requests should use (by default, this is
'application/json').
• getContentType(): Get the current content type for requests to the service.
• setId($id): Typically, this is the URL endpoint of the service (same as the target).
• getId(): Retrieve the service ID (typically the URL endpoint of the service).
• isDojoCompatible(): Returns the value of the Dojo compatibility flag (FALSE, by default).
860
Zend_Json
• toDojoArray(): Cast the service map to an array compatible with Dojo Toolkit.
• setOptions(array $options): Set object state from an array. Any mutator (methods
beginning with 'set') may be used as a key and set via this method.
• setName($name): Set the service name (typically, the function or method name).
• setTarget($target): Set the URL endpoint of the service (typically, this will be the same
as the overall SMD to which the service is attached).
861
Zend_Json
862
Zend_Layout
1. Introduction
Zend_Layout utilise le design pattern Two Step View, ce qui permet d'encapsuler le contenu
d'une vue dans une autre, généralement appelée template. D'autres projets les appellent aussi
layouts, ainsi Zend Framework utilise ce terme.
• Automatiser le rendu des layouts lorsqu'ils sont utilisés avec les composants MVC de Zend
Framework.
• Fournir un cadre à part entière pour les variables du layout, au même titre que les variables
de vue.
• Permettre la configuration du nom des layouts, la recherche des scripts leurs correspondant
(inflexion), ainsi que leurs chemins d'accès.
• Permettre de désactiver les layouts temporairement, changer leur configuration ; tout ceci
depuis les contrôleurs ou les scripts de vue.
• Utiliser les mêmes règles de résolution (inflexion) que le ViewRenderer, mais sans empêcher
de les personnaliser à part.
• Une intégration sous forme d'aides/plugin dans le modèle MVC de Zend Framework.
Par exemple :
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Mon Site</title>
</head>
<body>
<?php
// récupère la clé "content" via l'aide de vue layout :
echo $this->layout()->content;
863
Zend_Layout
Toutes les aides de vue enregistrées sont accessibles dans Zend_Layout car il utilise
Zend_View pour son propre rendu. Vous pouvez aussi accéder aux variables de la vue. Les
aides placeholder sont très pratiques pour l'accès aux éléments tels que <head>, les scripts,
les méta, etc. :
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<?php echo $this->headTitle() ?>
<?php echo $this->headScript() ?>
<?php echo $this->headStyle() ?>
</head>
<body>
<?php echo $this->render('header.phtml') ?>
<div id="nav"><?php echo $this->placeholder('nav') ?></div>
Regardons d'abord comment initialiser Zend_Layout afin de l'utiliser dans un contexte MVC
Dans le contrôleur d'action, vous pouvez donc accéder à l'instance de layout via l'aide d'action :
864
Zend_Layout
Dans vos scripts de vue, utiliser l'aide layout pour accéder à l'instance de Zend_Layout.
Notez que cette aide est différente des autres car elle ne retourne pas une chaîne, mais bien un
objet. Vous pouvez donc enchaîner une méthode immédiatement :
Autrement, vous pouvez n'importe où accéder à votre instance de Zend_Layout via la méthode
statique getMvcInstance() :
<body>
<!-- rend /nav/menu -->
<div id="nav"><?php echo $this->layout()->nav ?></div>
Cette approche est particulièrement utile avec l'aide d'action ActionStack et son plugin du même
nom. Vous pouvez les utiliser pour gérer une pile d'actions et ainsi décomposer vos processus.
865
Zend_Layout
// rendu du layout :
echo $layout->render();
866
Zend_Layout
867
Zend_Layout
Avec cette approche, vous pouvez régler vos mises en forme CSS. En positionnement absolu,
par exemple, vous pourriez rendre la barre de navigation plus tard, en fin. Le mécanisme
d'obtention du contenu reste le même cependant.
• layout : le nom du script de layout. L'inflecteur traduit alors ceci en nom de fichier. Par
défaut, il s'agit de "layout" traduit vers "layout.phtml". Les accesseurs sont setLayout() et
getLayout().
• layoutPath : l'url de base vers les scripts de layout. Les accesseurs sont setLayoutPath()
et getLayoutPath().
• mvcSuccessfulActionOnly : si une action envoie une exception et que cette option vaut
TRUE, alors le layout ne sera pas rendu. (Ceci évite un double rendu alors que le
plugin ErrorHandler est activé). Par défaut cette option est à TRUE. Ses accesseurs :
setMvcSuccessfulActionOnly() et getMvcSuccessfulActionOnly().
• view : l'objet de vue (Zend_View) utilisée par le layout pour rendre son script. Utilisé avec
MVC, Zend_Layout cherchera à récupérer la vue via l'aide ViewRenderer, si aucun objet de
vue ne lui est passé explicitement. Les accesseurs sont setView() et getView().
• inflector : l'inflecteur utilisé pour la résolution des noms de layout vers les scripts de
layout. Voyez la documentation spécifique pour plus de détails. Les accesseurs sont
setInflector() et getInflector().
3.1. Exemples
Les exemples sont basés sur les paramètres $options et $config suivants :
868
Zend_Layout
$options = array(
'layout' => 'foo',
'layoutPath' => '/chemin/vers/layouts',
'contentKey' => 'CONTENT'
// ignorés si MVC n'est pas utilisé
);
/**
[layout]
layout = "foo"
layoutPath = "/chemin/vers/layouts"
contentKey = "CONTENT"
*/
$config = new Zend_Config_Ini('/chemin/vers/layout.ini', 'layout');
// Avec le constructeur :
$layout = new Zend_Layout($options);
// Avec startMvc():
$layout = Zend_Layout::startMvc($options);
// Cas du constructeur:
$layout = new Zend_Layout($config);
// Via startMvc():
$layout = Zend_Layout::startMvc($config);
869
Zend_Layout
Enfin, vous pouvez aussi configurer votre objet de Zend_Layout grâce à ses accesseurs.
Ils peuvent s'utiliser chaînés (interface fluide):
$layout->setLayout('foo')
->setLayoutPath('/chemin/vers/layouts')
->setContentKey('CONTENT');
• Objet de vue personnalisé. Zend_Layout accepte tout objet de vue implémentant l'interface
Zend_View_Interface.
• Plugin contrôleur frontal personnalisé. Zend_Layout est livré avec un plugin qui rend le layout
automatiquement avant de renvoyer la réponse (utilisation MVC). Vous pouvez injecter votre
propre plugin.
• Aide d'action personnalisée. Zend_Layout est livrée avec une aide d'action qui devrait en
théorie suffire dans la majorité des cas. C'est un proxy vers l'objet Zend_Layout. Vous pouvez
cependant utiliser votre propre classe d'aide.
Si vous avez besoin d'un objet Zend_View personnalisé qui ne supporte pas
ces aides de vue, vous devrez alors trouver un moyen de passer les variables
du layout à la vue. Par exemple, en étendant l'objet Zend_Layout et en
rédéfinissant la méthode render() en passant des variables à la vue. Aussi,
vous pouvez créer votre propre plugin qui s'occupe de passer ces variables avant
le rendu du layout.
870
Zend_Layout
Si vous voulez utiliser votre propre classe, celle-ci devra alors étendre
Zend_Controller_Action_Helper_Abstract. Passez le nom de la classe dans l'option
helperClass de la méthode startMvc().
L'inflecteur par défaut de Zend_Layout utilise des marqueurs statiques pour la cible et le
suffixe. 2 accesseurs vous sont donc proposés :
// Affecte le suffixe:
$layout->setViewSuffix('php');
871
Zend_Layout
Les inflecteurs fonctionnent avec un cible et plusieurs règles. La cible par défaut utilisée
pour Zend_Layout est ":script.:suffix" ; ":script" représente le nom du script de layout, et
":suffix" est une règle statique.
Imaginons que vous vouliez que le suffixe du script de layout soit "html", et que vous vouliez
séparer les mots en CasseMélangée ou en notationCamel avec des tirets-bats au lieu des
tirets. De plus, vous voulez chercher vos scripts dans un sous-dossier "layouts" :
$layout->getInflector()->setTarget('layouts/:script.:suffix')
->setStaticRule('suffix', 'html')
->setFilterRule(array('Word_CamelCaseToUnderscore'));
Dans la plupart des cas, modifier l'inflecteur sera suffisant. Vous pouvez cependant créer
votre propre inflecteur, pour l'utiliser à différents endroits par exemple, et le passer à
Zend_Layout:
872
Zend_Ldap
1. Introduction
Zend_Ldap is a class for performing LDAP operations including but not limited to binding,
searching and modifying entries in an LDAP directory.
The component provides several helper classes to perform operations on LDAP entries
(Zend_Ldap_Attribute) such as setting and retrieving attributes (date values, passwords,
boolean values, ...), to create and modify LDAP filter strings (Zend_Ldap_Filter) and to
manipulate LDAP distinguished names (DN) (Zend_Ldap_Dn).
Additionally the component abstracts LDAP schema browsing for OpenLDAP and ActiveDirectoy
servers Zend_Ldap_Node_Schema and server information retrieval for OpenLDAP-,
ActiveDirectory- and Novell eDirectory servers (Zend_Ldap_Node_RootDse).
Using the Zend_Ldap class depends on the type of LDAP server and is best summarized with
some simple examples.
If you are using OpenLDAP, a simple example looks like the following (note that the
bindRequiresDn option is important if you are not using AD):
$options = array(
'host' => 's0.foo.net',
'username' => 'CN=user1,DC=foo,DC=net',
'password' => 'pass1',
'bindRequiresDn' => true,
'accountDomainName' => 'foo.net',
'baseDn' => 'OU=Sales,DC=foo,DC=net',
);
$ldap = new Zend_Ldap($options);
$acctname = $ldap->getCanonicalAccountName('abaker',
Zend_Ldap::ACCTNAME_FORM_DN);
echo "$acctname\n";
$options = array(
'host' => 'dc1.w.net',
'useStartTls' => true,
'username' => 'user1@w.net',
'password' => 'pass1',
'accountDomainName' => 'w.net',
'accountDomainNameShort' => 'W',
'baseDn' => 'CN=Users,DC=w,DC=net',
873
Zend_Ldap
);
$ldap = new Zend_Ldap($options);
$acctname = $ldap->getCanonicalAccountName('bcarter',
Zend_Ldap::ACCTNAME_FORM_DN);
echo "$acctname\n";
Note that we use the getCanonicalAccountName() method to retrieve the account DN here
only because that is what exercises the most of what little code is currently present in this class.
The following example illustrates how the non-DN username 'abaker' can be used with bind():
$options = array(
'host' => 's0.foo.net',
'username' => 'CN=user1,DC=foo,DC=net',
'password' => 'pass1',
'bindRequiresDn' => true,
'accountDomainName' => 'foo.net',
'baseDn' => 'OU=Sales,DC=foo,DC=net',
);
$ldap = new Zend_Ldap($options);
$ldap->bind('abaker', 'moonbike55');
$acctname = $ldap->getCanonicalAccountName('abaker',
Zend_Ldap::ACCTNAME_FORM_DN);
echo "$acctname\n";
The bind() call in this example sees that the username 'abaker' is not in DN form, finds
bindRequiresDn is TRUE, uses 'CN=user1,DC=foo,DC=net' and 'pass1' to bind, retrieves
the DN for 'abaker', unbinds and then rebinds with the newly discovered 'CN=Alice
Baker,OU=Sales,DC=foo,DC=net'.
874
Zend_Ldap
The default canonicalization depends on what account domain name options were supplied.
If accountDomainNameShort was supplied, the default accountCanonicalForm value is
ACCTNAME_FORM_BACKSLASH. Otherwise, if accountDomainName was supplied, the default is
ACCTNAME_FORM_PRINCIPAL.
Account name canonicalization ensures that the string used to identify an account is consistent
regardless of what was supplied to bind(). For example, if the user supplies an account name
of abaker@example.com or just abaker and the accountCanonicalForm is set to 3, the resulting
canonicalized name would be EXAMPLE\abaker.
Consider the following example that illustrates the technique required to implement multi-domain
authentication and failover:
$acctname = 'W\\user2';
$password = 'pass2';
$multiOptions = array(
'server1' => array(
'host' => 's0.foo.net',
'username' => 'CN=user1,DC=foo,DC=net',
'password' => 'pass1',
'bindRequiresDn' => true,
'accountDomainName' => 'foo.net',
'accountDomainNameShort' => 'FOO',
'accountCanonicalForm' => 4, // ACCT_FORM_PRINCIPAL
'baseDn' => 'OU=Sales,DC=foo,DC=net',
),
'server2' => array(
'host' => 'dc1.w.net',
'useSsl' => true,
'username' => 'user1@w.net',
'password' => 'pass1',
'accountDomainName' => 'w.net',
'accountDomainNameShort' => 'W',
'accountCanonicalForm' => 4, // ACCT_FORM_PRINCIPAL
'baseDn' => 'CN=Users,DC=w,DC=net',
),
);
$ldap->setOptions($options);
try {
$ldap->bind($acctname, $password);
$acctname = $ldap->getCanonicalAccountName($acctname);
875
Zend_Ldap
If the bind fails for any reason, the next set of server options is tried.
The getCanonicalAccountName() call gets the canonical account name that the application
would presumably use to associate data with such as preferences. The accountCanonicalForm
= 4 in all server options ensures that the canonical form is consistent regardless of which server
was ultimately used.
The above code is very similar to code used within Zend_Auth_Adapter_Ldap. In fact, we
recommend that you simply use that authentication adapter for multi-domain + failover LDAP
based authentication (or copy the code).
2. API overview
2.1. Configuration / options
The Zend_Ldap component accepts an array of options either supplied to the constructor or
through the setOptions() method. The permitted options are as follows:
876
Zend_Ldap
Name Description
option should be favored over useSsl but not all
servers support this newer mechanism.
useSsl Whether or not the LDAP client should use
SSL encrypted transport. The useSsl and
useStartTls options are mutually exclusive.
username The default credentials username. Some
servers require that this be in DN form. This
must be given in DN form if the LDAP server
requires a DN to bind and binding should be
possible with simple usernames.
password The default credentials password (used only
with username above).
bindRequiresDn If TRUE, this instructs Zend_Ldap to retrieve
the DN for the account used to bind if the
username is not already in DN form. The default
value is FALSE.
baseDn The default base DN used for searching (e.g.,
for accounts). This option is required for most
account related operations and should indicate
the DN under which accounts are located.
accountCanonicalForm A small integer indicating the form to which
account names should be canonicalized. See
the Account Name Canonicalization section
below.
accountDomainName The FQDN domain for which the target LDAP
server is an authority (e.g., example.com).
accountDomainNameShort The 'short' domain for which the target LDAP
server is an authority. This is usually used
to specify the NetBIOS domain name for
Windows networks but may also be used by
non-AD servers.
accountFilterFormat The LDAP search filter used to search for
accounts. This string is a sprintf() style
expression that must contain one '%s' to
accommodate 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))'.
Users of custom schemas may need to change
this option.
allowEmptyPassword Some LDAP servers can be configured to
accept an empty string password as an
anonymous bind. This behavior is almost
always undesirable. For this reason, empty
passwords are explicitly disallowed. Set this
value to TRUE to allow an empty string
password to be submitted during the bind.
877
Zend_Ldap
Name Description
optReferrals If set to TRUE, this option indicates to the LDAP
client that referrals should be followed. The
default value is FALSE.
tryUsernameSplit If set to FALSE, this option indicates that the
given username should not be split at the first
@ or \ character to separate the username
from the domain during the binding-procedure.
This allows the user to use usernames that
contain an @ or \ character that do not inherit
some domain-information, e.g. using email-
addresses for binding. The default value is
TRUE.
2.2.1. Zend_Ldap
Zend_Ldap is the base interface into a LDAP server. It provides connection and binding methods
as well as methods to operate on the LDAP tree.
Method Description
string filterEscape(string $str) Escapes a value to be used in a
LDAP filter according to RFC 2254.
This method is deprecated, please use
Zend_Ldap_Filter_Abstract::escapeValue()
instead.
boolean explodeDn($dn, array &$keys Checks if a given DN $dn is malformed. If
= null, array &$vals = null) $keys or $keys and $vals are given, these
arrays will be filled with the appropriate DN keys
and values. This method is deprecated, please
use Zend_Ldap_Dn::checkDn() instead.
__construct($options) Constructor. The $options parameter is
optional and can be set to an array or
a Zend_Config instance. If no options
are provided at instantiation, the connection
parameters must be passed to the instance
using Zend_Ldap::setOptions(). The
allowed options are specified in Zend_Ldap
Options
resource getResource() Returns the raw LDAP extension (ext/ldap)
resource.
integer getLastErrorCode() Returns the LDAP error number of the last
LDAP command.
878
Zend_Ldap
Method Description
string getLastError(integer & Returns the LDAP error message of the last
$errorCode, array &$errorMessages) LDAP command. The optional $errorCode
parameter is set to the LDAP error number
when given. The optional $errorMessages
array will be filled with the raw error messages
when given. The various LDAP error retrieval
functions can return different things, so they are
all collected if $errorMessages is given.
Zend_Ldap setOptions($options) Sets the LDAP connection and binding
parameters. $options can be an array or
an instance of Zend_Config. The allowed
options are specified in Zend_Ldap Options
array getOptions() Returns the current connection and binding
parameters.
string getBaseDn() Returns the base DN this LDAP connection is
bound to.
string Returns the canonical account name of the
getCanonicalAccountName(string given account name $acctname. $form
$acctname, integer $form) specifies the format into which the account
name is canonicalized. See Account Name
Canonicalization for more details.
Zend_Ldap disconnect() Disconnects the Zend_Ldap instance from the
LDAP server.
Zend_Ldap connect(string $host, Connects the Zend_Ldap instance to the
integer $port, boolean $useSsl, given LDAP server. All parameters are
boolean $useStartTls) optional and will be taken from the LDAP
connection and binding parameters passed
to the instance via the construtor or via
Zend_Ldap::setOptions() when set to
NULL.
Zend_Ldap bind(string $username, Authenticates $username with $password
string $password) at the LDAP server. If both paramaters are
omitted the binding will be carried out with
the credentials given in the connection and
binding parameters. If no credentials are given
in the connection and binding parameters
an anonymous bind will be performed. Note
that this requires anonymous binds to be
allowed on the LDAP server. An empty
string '' can be passed as $password
together with a username if, and only if,
allowEmptyPassword is set to TRUE in the
connection and binding parameters.
Zend_Ldap_Collection Searches the LDAP tree with the given
search(string| $filter and the given search parameters.
Zend_Ldap_Filter_Abstract $filter,
string|Zend_Ldap_Dn $basedn, string| The filter
integer $scope, array Zend_Ldap_Filter_Abstract string to
$filter be used in
879
Zend_Ldap
Method Description
$attributes, string $sort, string the search,
$collectionClass) e.g.
(objectClass=posixAcc
880
Zend_Ldap
Method Description
in the
returned
entries. To
include all
possible
attributes
(ACL
restrictions
can
disallow
certain
attribute to
be
retrieved by
a given
user) pass
either an
empty
array
array()
or
array('*')
to the
method. On
some
LDAP
servers you
can retrieve
special
internal
attributes
by passing
array('*',
'+') to the
method.
881
Zend_Ldap
Method Description
ext/ldap
function
ldap_sort().
882
Zend_Ldap
Method Description
need to be called by the end-user as it's
implicitly called on every data modification
method.
Zend_Ldap add(string|Zend_Ldap_Dn Adds the entry identified by $dn with its
$dn, array $entry) attributes $entry to the LDAP tree. Throws a
Zend_Ldap_Exception if the entry could not
be added.
Zend_Ldap update(string| Updates the entry identified by $dn with its
Zend_Ldap_Dn $dn, array $entry) attributes $entry to the LDAP tree. Throws a
Zend_Ldap_Exception if the entry could not
be modified.
Zend_Ldap save(string|Zend_Ldap_Dn Saves the entry identified by $dn with its
$dn, array $entry) attributes $entry to the LDAP tree. Throws a
Zend_Ldap_Exception if the entry could not
be saved. This method decides by querying the
LDAP tree if the entry will be added or updated.
Zend_Ldap delete(string| Deletes the entry identified by $dn
Zend_Ldap_Dn $dn, boolean from the LDAP tree. Throws a
$recursively) Zend_Ldap_Exception if the entry could
not be deleted. $recursively is FALSE
by default. If set to TRUE the deletion will
be carried out recursively and will effectively
delete a complete subtree. Deletion will fail if
$recursively is FALSE and the entry $dn is
not a leaf entry.
Zend_Ldap moveToSubtree(string| Moves the entry identified by $from to
Zend_Ldap_Dn $from, string| a location below $to keeping its RDN
Zend_Ldap_Dn $to, boolean unchanged. $recursively specifies if the
$recursively, boolean operation will be carried out recursively (FALSE
$alwaysEmulate) by default) so that the entry $from and all its
descendants will be moved. Moving will fail if
$recursively is FALSE and the entry $from
is not a leaf entry. $alwaysEmulate controls
whether the ext/ldap function ldap_rename()
should be used if available. This can only work
for leaf entries and for servers and for ext/ldap
supporting this function. Set to TRUE to always
use an emulated rename operation.
883
Zend_Ldap
Method Description
also are by no means atomic.
Please keep this in mind.
Zend_Ldap move(string|Zend_Ldap_Dn This is an alias for Zend_Ldap::rename().
$from, string|Zend_Ldap_Dn $to,
boolean $recursively, boolean
$alwaysEmulate)
Zend_Ldap rename(string| Renames the entry identified by $from to $to.
Zend_Ldap_Dn $from, string| $recursively specifies if the operation will
Zend_Ldap_Dn $to, boolean be carried out recursively (FALSE by default) so
$recursively, boolean that the entry $from and all its descendants will
$alwaysEmulate) be moved. Moving will fail if $recursively is
FALSE and the entry $from is not a leaf entry.
$alwaysEmulate controls whether the ext/
ldap function ldap_rename() should be used
if available. This can only work for leaf entries
and for servers and for ext/ldap supporting
this function. Set to TRUE to always use an
emulated rename operation.
Zend_Ldap copyToSubtree(string| Copies the entry identified by $from to
Zend_Ldap_Dn $from, string| a location below $to keeping its RDN
Zend_Ldap_Dn $to, boolean unchanged. $recursively specifies if the
$recursively) operation will be carried out recursively (FALSE
by default) so that the entry $from and all its
descendants will be copied. Copying will fail if
$recursively is FALSE and the entry $from
is not a leaf entry.
Zend_Ldap copy(string|Zend_Ldap_Dn Copies the entry identified by $from to $to.
$from, string|Zend_Ldap_Dn $to, $recursively specifies if the operation will
boolean $recursively) be carried out recursively (FALSE by default) so
that the entry $from and all its descendants will
be copied. Copying will fail if $recursively is
FALSE and the entry $from is not a leaf entry.
Zend_Ldap_Node getNode(string| Returns the entry $dn wrapped in a
Zend_Ldap_Dn $dn) Zend_Ldap_Node.
Zend_Ldap_Node getBaseNode() Returns the entry for the base DN $baseDn
wrapped in a Zend_Ldap_Node.
Zend_Ldap_Node_RootDse Returns the RootDSE for the current server.
getRootDse()
Zend_Ldap_Node_Schema getSchema() Returns the LDAP schema for the current
server.
2.2.1.1. Zend_Ldap_Collection
884
Zend_Ldap
2.2.2. Zend_Ldap_Attribute
Zend_Ldap_Attribute is a helper class providing only static methods to manipulate arrays
suitable to the structure used in Zend_Ldap data modification methods and to the data format
required by the LDAP server. PHP data types are converted the following way:
object and array The value will be converted to a string by using serialize().
others All other data types (namely non-stream resources) will be ommitted.
others All other strings won't be automatically converted and are passed as they are.
885
Zend_Ldap
Method Description
returned if the attribute does not exist in the
given array. If an integer index is specified the
corresponding value at the given index will be
returned. If the index is out of bounds, NULL will
be returned. Conversion will take place.
boolean attributeHasValue(array & Checks if the attribute $attribName in $data
$data, string $attribName, mixed| has the value(s) given in $value. The method
array $value) returns TRUE only if all values in $value are
present in the attribute. Comparison is done
strictly (respecting the data type).
void Removes all duplicates from the attribute
removeDuplicatesFromAttribute(array$attribName in $data.
&$data, string $attribName)
void removeFromAttribute(array & Removes the value(s) given in $value from
$data, string $attribName, mixed| the attribute $attribName in $data.
array $value)
string|null Converts a PHP data type into its LDAP
convertToLdapValue(mixed $value) representation. See introduction for details.
mixed convertFromLdapValue(string Converts an LDAP value into its PHP data type.
$value) See introduction for details.
string|null Converts a timestamp into its LDAP date/time
convertToLdapDateTimeValue(integer representation. If $utc is TRUE (FALSE by
$value, boolean $utc) default) the resulting LDAP date/time string will
be in UTC, otherwise a local date/time string
will be returned.
integer|null Converts LDAP date/time representation into
convertFromLdapDateTimeValue(stringa timestamp. The method returns NULL if
$value) $value can not be converted back into a PHP
timestamp.
void setPassword(array &$data, Sets a LDAP password for the attribute
string $password, string $hashType, $attribName in $data. $attribName
string $attribName) defaults to 'userPassword' which is
the standard password attribute. The
password hash can be specified with
$hashType. The default value here is
Zend_Ldap_Attribute::PASSWORD_HASH_MD5
with
Zend_Ldap_Attribute::PASSWORD_HASH_SHA
as the other possibilty.
string createPassword(string Creates a LDAP password. The
$password, string $hashType) password hash can be specified with
$hashType. The default value here is
Zend_Ldap_Attribute::PASSWORD_HASH_MD5
with
Zend_Ldap_Attribute::PASSWORD_HASH_SHA
as the other possibilty.
void setDateTimeAttribute(array & Sets the attribute $attribName in $data
$data, string $attribName, integer| to the date/time value $value. If $append
886
Zend_Ldap
Method Description
array $value, boolean $utc, boolean is TRUE (FALSE by default) $value will
$append) be appended to the attribute. $value
can be an integer value or an array of
integers. Date-time-conversion according to
Zend_Ldap_Attribute::convertToLdapDateTimeValue
will take place.
array|integer Returns the date/time attribute $attribName
getDateTimeAttribute(array $data, from $data. If $index is NULL (default) an
string $attribName, integer|null array will be returned containing all the date/
$index) time values for the given attribute. An empty
array will be returned if the attribute does
not exist in the given array. If an integer
index is specified the corresponding date/time
value at the given index will be returned.
If the index is out of bounds, NULL will be
returned. Date-time-conversion according to
Zend_Ldap_Attribute::convertFromLdapDateTimeVal
will take place.
2.2.3. Zend_Ldap_Dn
Zend_Ldap_Dn provides an object-oriented interface to manipulating LDAP distinguished
names (DN). The parameter $caseFold that is used in several methods determines the way
DN attributes are handled regarding their case. Allowed values for this paraneter are:
The class implements ArrayAccess to allow indexer-access to the different parts of the
DN. The ArrayAccess-methods proxy to Zend_Ldap_Dn::get($offset, 1, null)
for offsetGet(integer $offset), to Zend_Ldap_Dn::set($offset, $value)
for offsetSet() and to Zend_Ldap_Dn::remove($offset, 1) for offsetUnset().
offsetExists() simply checks if the index is within the bounds.
887
Zend_Ldap
Method Description
to the array structure detailed under
Zend_Ldap_Dn::implodeDn().
array getRdn(string|null Gets the RDN of the current DN. The return
$caseFold) value is an array with the RDN attribute names
its keys and the RDN attribute values.
string getRdnString(string|null Gets the RDN of the current DN. The return
$caseFold) value is a string.
Zend_Ldap_Dn getParentDn(integer Gets the DN of the current DN's ancestor
$levelUp) $levelUp levels up the tree. $levelUp
defaults to 1.
array get(integer $index, integer Returns a slice of the current DN determined
$length, string|null $caseFold) by $index and $length. $index starts with
0 on the DN part from the left.
Zend_Ldap_Dn set(integer $index, Replaces a DN part in the current DN. This
array $value) operation manipulates the current instance.
Zend_Ldap_Dn remove(integer Removes a DN part from the current DN. This
$index, integer $length) operation manipulates the current instance.
$length defaults to 1
Zend_Ldap_Dn append(array $value) Appends a DN part to the current DN. This
operation manipulates the current instance.
Zend_Ldap_Dn prepend(array $value) Prepends a DN part to the current DN. This
operation manipulates the current instance.
Zend_Ldap_Dn insert(integer Inserts a DN part after the index $index to
$index, array $value) the current DN. This operation manipulates the
current instance.
void setCaseFold(string|null Sets the case-folding option to the
$caseFold) current DN instance. If $caseFold is
NULL the default case-folding setting
(Zend_Ldap_Dn::ATTR_CASEFOLD_NONE
by default or set via
Zend_Ldap_Dn::setDefaultCaseFold()
will be set for the current instance.
string toString(string|null Returns DN as a string.
$caseFold)
array toArray(string|null Returns DN as an array.
$caseFold)
string __toString() Returns DN as a string - proxies to
Zend_Ldap_Dn::toString(null).
void setDefaultCaseFold(string Sets the default case-folding option used by
$caseFold) all instances on creation by default. Already
existing instances are not affected by this
setting.
array escapeValue(string|array Escapes a DN value according to RFC 2253.
$values)
array unescapeValue(string|array Undoes the conversion done by
$values) Zend_Ldap_Dn::escapeValue().
888
Zend_Ldap
Method Description
array explodeDn(string $dn, array Explodes the DN $dn into an array containing
&$keys, array &$vals, string|null all parts of the given DN. $keys optinally
$caseFold) receive DN keys (e.g. CN, OU, DC, ...). $vals
optionally receive DN values. The resulting
array will be of type
array(
array("cn" => "name1", "uid" => "user"),
array("cn" => "name2"),
array("dc" => "example"),
array("dc" => "org")
)
for a DN of
cn=name1+uid=user,cn=name2,dc=example,dc=org.
boolean checkDn(string $dn, array Checks if a given DN $dn is malformed. If
&$keys, array &$vals, string|null $keys or $keys and $vals are given, these
$caseFold) arrays will be filled with the appropriate DN keys
and values.
string implodeRdn(array $part, Returns a DN part in the form $attribute=
string|null $caseFold) $value
string implodeDn(array $dnArray, Implodes an array in the form delivered
string|null $caseFold, string by Zend_Ldap_Dn::explodeDn() to a DN
$separator) string. $separator defaults to ',' but some
LDAP servers also understand ';'. $dnArray
must of type
array(
array("cn" => "name1", "uid" => "user"),
array("cn" => "name2"),
array("dc" => "example"),
array("dc" => "org")
)
2.2.4. Zend_Ldap_Filter
Tableau 74. Zend_Ldap_Filter API
Method Description
Zend_Ldap_Filter equals(string Creates an 'equals' filter: (attr=value).
$attr, string $value)
Zend_Ldap_Filter begins(string Creates an 'begins with' filter:
$attr, string $value) (attr=value*).
Zend_Ldap_Filter ends(string Creates an 'ends with' filter: (attr=*value).
$attr, string $value)
Zend_Ldap_Filter contains(string Creates an 'contains' filter: (attr=*value*).
$attr, string $value)
889
Zend_Ldap
Method Description
Zend_Ldap_Filter greater(string Creates an 'greater' filter: (attr>value).
$attr, string $value)
Zend_Ldap_Filter Creates an 'greater or equal' filter:
greaterOrEqual(string $attr, (attr>=value).
string $value)
Zend_Ldap_Filter less(string Creates an 'less' filter: (attr<value).
$attr, string $value)
Zend_Ldap_Filter Creates an 'less or equal' filter:
lessOrEqual(string $attr, string (attr<=value).
$value)
Zend_Ldap_Filter approx(string Creates an 'approx' filter: (attr~=value).
$attr, string $value)
Zend_Ldap_Filter any(string $attr) Creates an 'any' filter: (attr=*).
Zend_Ldap_Filter string(string Creates a simple custom string filter. The user
$filter) is responsible for all value-escaping as the filter
is used as is.
Zend_Ldap_Filter mask(string Creates a filter from a string mask. All $value
$mask, string $value,...) parameters will be escaped and substituted
into $mask by using sprintf()
Zend_Ldap_Filter Creates an 'and' filter from all arguments given.
andFilter(Zend_Ldap_Filter_Abstract
$filter,...)
Zend_Ldap_Filter Creates an 'or' filter from all arguments given.
orFilter(Zend_Ldap_Filter_Abstract
$filter,...)
__construct(string $attr, string Constructor. Creates an arbitrary filter
$value, string $filtertype, string| according to the parameters supplied. The
null $prepend, string|null $append) resulting filter will be a concatenation $attr .
$filtertype . $prepend . $value .
$append. Normally this constructor is not
needed as all filters can be created by using the
appropriate factory methods.
string toString() Returns a string representation of the filter.
string __toString() Returns a string representation
of the filter. Proxies to
Zend_Ldap_Filter::toString().
Zend_Ldap_Filter_Abstract negate() Negates the current filter.
Zend_Ldap_Filter_Abstract Creates an 'and' filter from the current filter and
addAnd(Zend_Ldap_Filter_Abstract all filters passed in as the arguments.
$filter,...)
Zend_Ldap_Filter_Abstract Creates an 'or' filter from the current filter and
addOr(Zend_Ldap_Filter_Abstract all filters passed in as the arguments.
$filter,...)
string|array escapeValue(string| Escapes the given $values according to
array $values) RFC 2254 so that they can be safely used
in LDAP filters. If a single string is given,
890
Zend_Ldap
Method Description
a string is returned - otherwise an array is
returned. Any control characters with an ASCII
code < 32 as well as the characters with
special meaning in LDAP filters "*", "(", ")",
and "\" (the backslash) are converted into the
representation of a backslash followed by two
hex digits representing the hexadecimal value
of the character.
string|array unescapeValue(string| Undoes the conversion done
array $values) by Zend_Ldap_Filter::escapeValue().
Converts any sequences of a backslash
followed by two hex digits into the
corresponding character.
2.2.5. Zend_Ldap_Node
Zend_Ldap_Node includes the magic property accessors __set(), __get(),
__unset() and __isset() to access the attributes by their name. They
proxy to Zend_Ldap_Node::setAttribute(), Zend_Ldap_Node::getAttribute(),
Zend_Ldap_Node::deleteAttribute() and Zend_Ldap_Node::existsAttribute()
respectively. Furthermore the class implements ArrayAccess for array-style-access to the
attributes. Zend_Ldap_Node also implements Iterator and RecursiveIterato to allow for
recursive tree-traversal.
Method Description
Zend_Ldap getLdap() Returns the current LDAP connection. Throws
Zend_Ldap_Exception if current node is
in detached mode (not connected to a
Zend_Ldap instance).
Zend_Ldap_Node Attach the current node to the
attachLdap(Zend_Ldap $ldap) $ldap Zend_Ldap instance. Throws
Zend_Ldap_Exception if $ldap is not
responsible for the current node (node is not a
child of the $ldap base DN).
Zend_Ldap_Node detachLdap() Detach node from LDAP connection.
boolean isAttached() Checks if the current node is attached to a
LDAP connection.
Zend_Ldap_Node create(string| Factory method to create a new detached
array|Zend_Ldap_Dn $dn, array Zend_Ldap_Node for a given DN. Creates a
$objectClass) new Zend_Ldap_Node with the DN $dn and
the object-classes $objectClass.
Zend_Ldap_Node fromLdap(string| Factory method to create an attached
array|Zend_Ldap_Dn $dn, Zend_Ldap Zend_Ldap_Node for a given DN. Loads an
$ldap) existing Zend_Ldap_Node with the DN $dn
from the LDAP connection $ldap.
Zend_Ldap_Node fromArray((array Factory method to create a detached
$data, boolean $fromDataSource) Zend_Ldap_Node from array data $data.
If $fromDataSource is TRUE (FALSE by
891
Zend_Ldap
Method Description
default), the data is treated as beeing present
in a LDAP tree.
boolean isNew() Tells if the node is consiedered as new (not
present on the server). Please note, that this
doesn't tell if the node is really present on the
server. Use Zend_Ldap_Node::exists()
to see if a node is already there.
boolean willBeDeleted() Tells if this node is going to be deleted once
Zend_Ldap_Node::update() is called.
Zend_Ldap_Node delete() Marks this node as to be
deleted. Node will be deleted on
calling Zend_Ldap_Node::update() if
Zend_Ldap_Node::willBeDeleted() is
TRUE.
boolean willBeMoved() Tells if this node is going to be moved once
Zend_Ldap_Node::update() is called.
Zend_Ldap_Node update(Zend_Ldap Sends all pending changes to the LDAP
$ldap) server. If $ldap is omitted the current
LDAP connection is used. If the current
node is detached from a LDAP connection
a Zend_Ldap_Exception will be thrown. If
$ldap is provided the current node will be
attached to the given LDAP connection.
Zend_Ldap_Dn getCurrentDn() Gets the current DN of the current node as a
Zend_Ldap_Dn. This does not reflect possible
rename-operations.
Zend_Ldap_Dn getDn() Gets the original DN of the current node as a
Zend_Ldap_Dn. This reflects possible rename-
operations.
string getDnString(string Gets the original DN of the current node
$caseFold) as a string. This reflects possible rename-
operations.
array getDnArray(string $caseFold) Gets the original DN of the current node
as an array. This reflects possible rename-
operations.
string getRdnString(string Gets the RDN of the current node as a string.
$caseFold) This reflects possible rename-operations.
array getRdnArray(string Gets the RDN of the current node as an array.
$caseFold) This reflects possible rename-operations.
Zend_Ldap_Node setDn(Zend_Ldap_Dn| Sets the new DN for this node
string|array $newDn) effectively moving the node once
Zend_Ldap_Node::update() is called.
Zend_Ldap_Node move(Zend_Ldap_Dn| This is an alias for
string|array $newDn) Zend_Ldap_Node::setDn().
Zend_Ldap_Node This is an alias for
rename(Zend_Ldap_Dn|string|array Zend_Ldap_Node::setDn().
$newDn)
892
Zend_Ldap
Method Description
array getObjectClass() Returns the objectClass of the node.
Zend_Ldap_Node Sets the objectClass attribute.
setObjectClass(array|string
$value)
Zend_Ldap_Node Appends to the objectClass attribute.
appendObjectClass(array|string
$value)
string toLdif(array $options) Returns a LDIF representation of the current
node. $options will be passed to the
Zend_Ldap_Ldif_Encoder.
array getChangedData() Gets changed node data. The array
contains all changed attributes. This format
can be used in Zend_Ldap::add() and
Zend_Ldap::update().
array getChanges() Returns all changes made.
string toString() Returns the DN of the current node - proxies to
Zend_Ldap_Dn::getDnString().
string __toString() Casts to string representation - proxies to
Zend_Ldap_Dn::toString().
array toArray(boolean Returns an array representation of the current
$includeSystemAttributes) node. If $includeSystemAttributes is
FALSE (defaults to TRUE) the system specific
attributes are stripped from the array. Unlike
Zend_Ldap_Node::getAttributes() the
resulting array contains the DN with key 'dn'.
string toJson(boolean Returns a JSON representation of the current
$includeSystemAttributes) node using Zend_Ldap_Node::toArray().
array getData(boolean Returns the node's attributes. The array
$includeSystemAttributes) contains all attributes in its internal format (no
conversion).
boolean existsAttribute(string Checks whether a given attribute exists. If
$name, boolean $emptyExists) $emptyExists is FALSE empty attributes
(containing only array()) are treated as non-
existent returning FALSE. If $emptyExists is
TRUE empty attributes are treated as existent
returning TRUE. In this case the method returns
FALSE only if the attribute name is missing in
the key-collection.
boolean attributeHasValue(string Checks if the given value(s) exist in the
$name, mixed|array $value) attribute. The method returns TRUE only if all
values in $value are present in the attribute.
Comparison is done strictly (respecting the
data type).
integer count() Returns the number of attributes in the node.
Implements Countable.
893
Zend_Ldap
Method Description
mixed getAttribute(string $name, Gets a LDAP attribute. Data conversion is
integer|null $index) applied using
Zend_Ldap_Attribute::getAttribute().
array getAttributes(boolean Gets all attributes of node. If
$includeSystemAttributes) $includeSystemAttributes is FALSE
(defaults to TRUE) the system specific attributes
are stripped from the array.
Zend_Ldap_Node setAttribute(string Sets a LDAP attribute. Data conversion is
$name, mixed $value) applied using
Zend_Ldap_Attribute::setAttribute().
Zend_Ldap_Node Appends to a LDAP attribute.
appendToAttribute(string $name, Data conversion is applied using
mixed $value) Zend_Ldap_Attribute::setAttribute().
array|integer Gets a LDAP date/time attribute.
getDateTimeAttribute(string $name, Data conversion is applied using
integer|null $index) Zend_Ldap_Attribute::getDateTimeAttribute().
Zend_Ldap_Node Sets a LDAP date/time attribute.
setDateTimeAttribute(string $name, Data conversion is applied using
integer|array $value, boolean $utc) Zend_Ldap_Attribute::setDateTimeAttribute().
Zend_Ldap_Node Appends to a LDAP date/time attribute.
appendToDateTimeAttribute(string Data conversion is applied using
$name, integer|array $value, Zend_Ldap_Attribute::setDateTimeAttribute().
boolean $utc)
Zend_Ldap_Node Sets a LDAP password on $attribName
setPasswordAttribute(string (defaults to 'userPassword') to $password
$password, string $hashType, string with the hash type $hashType (defaults to
$attribName) Zend_Ldap_Attribute::PASSWORD_HASH_MD5).
Zend_Ldap_Node Deletes a LDAP attribute.
deleteAttribute(string $name)
void Removes duplicate values from a LDAP
attribute.
removeDuplicatesFromAttribute(string
$name)
void removeFromAttribute(string Removes the given values from a LDAP
$attribName, mixed|array $value) attribute.
boolean exists(Zend_Ldap $ldap) Checks if the current node exists on the given
LDAP server (current server is used if NULL is
passed).
Zend_Ldap_Node reload(Zend_Ldap Reloads the current node's attributes from the
$ldap) given LDAP server (current server is used if
NULL is passed).
Zend_Ldap_Node_Collection Searches the nodes's subtree with the given
searchSubtree(string| $filter and the given search parameters.
Zend_Ldap_Filter_Abstract $filter, See Zend_Ldap::search() for details on
integer $scope, string $sort) the parameters $scope and $sort.
integer countSubtree(string| Count the nodes's subtree items matching the
Zend_Ldap_Filter_Abstract $filter, given $filter and the given search scope.
integer $scope)
894
Zend_Ldap
Method Description
See Zend_Ldap::search() for details on
the $scope parameter.
integer countChildren() Count the nodes's children.
Zend_Ldap_Node_Collection Searches the nodes's children
searchChildren(string| matching the given $filter. See
Zend_Ldap_Filter_Abstract $filter, Zend_Ldap::search() for details on the
string $sort) $sort parameter.
boolean hasChildren() Returns whether the current node has children.
Zend_Ldap_Node_ChildrenIterator Returns all children of the current node.
getChildren()
Zend_Ldap_Node getParent(Zend_Ldap Returns the parent of the current node using
$ldap) the LDAP connection $ldap (uses the current
LDAP connection if omitted).
2.2.6. Zend_Ldap_Node_RootDse
The following methods are available on all vendor-specific subclasses.
Method Description
Zend_Ldap_Dn getDn() Gets the DN of the current node as a
Zend_Ldap_Dn.
string getDnString(string Gets the DN of the current node as a string.
$caseFold)
array getDnArray(string $caseFold) Gets the DN of the current node as an array.
string getRdnString(string Gets the RDN of the current node as a string.
$caseFold)
array getRdnArray(string Gets the RDN of the current node as an array.
$caseFold)
array getObjectClass() Returns the objectClass of the node.
string toString() Returns the DN of the current node - proxies to
Zend_Ldap_Dn::getDnString().
string __toString() Casts to string representation - proxies to
Zend_Ldap_Dn::toString().
array toArray(boolean Returns an array representation of the current
$includeSystemAttributes) node. If $includeSystemAttributes is
FALSE (defaults to TRUE) the system specific
attributes are stripped from the array. Unlike
895
Zend_Ldap
Method Description
Zend_Ldap_Node_RootDse::getAttributes()
the resulting array contains the DN with key
'dn'.
string toJson(boolean Returns a JSON representation
$includeSystemAttributes) of the current node using
Zend_Ldap_Node_RootDse::toArray().
array getData(boolean Returns the node's attributes. The array
$includeSystemAttributes) contains all attributes in its internal format (no
conversion).
boolean existsAttribute(string Checks whether a given attribute exists. If
$name, boolean $emptyExists) $emptyExists is FALSE, empty attributes
(containing only array()) are treated as non-
existent returning FALSE. If $emptyExists is
TRUE, empty attributes are treated as existent
returning TRUE. In this case the method returns
FALSE only if the attribute name is missing in
the key-collection.
boolean attributeHasValue(string Checks if the given value(s) exist in the
$name, mixed|array $value) attribute. The method returns TRUE only if all
values in $value are present in the attribute.
Comparison is done strictly (respecting the
data type).
integer count() Returns the number of attributes in the node.
Implements Countable.
mixed getAttribute(string $name, Gets a LDAP attribute. Data conversion is
integer|null $index) applied using
Zend_Ldap_Attribute::getAttribute().
array getAttributes(boolean Gets all attributes of node. If
$includeSystemAttributes) $includeSystemAttributes is FALSE
(defaults to TRUE) the system specific attributes
are stripped from the array.
array|integer Gets a LDAP date/time attribute.
getDateTimeAttribute(string $name, Data conversion is applied using
integer|null $index) Zend_Ldap_Attribute::getDateTimeAttribute().
Zend_Ldap_Node_RootDse Reloads the current node's attributes from the
reload(Zend_Ldap $ldap) given LDAP server.
Zend_Ldap_Node_RootDse Factory method to create the RootDSE.
create(Zend_Ldap $ldap)
array getNamingContexts() Gets the namingContexts.
string|null getSubschemaSubentry() Gets the subschemaSubentry.
boolean supportsVersion(string| Determines if the LDAP version is supported.
int|array $versions)
boolean Determines if the sasl mechanism is supported.
supportsSaslMechanism(string|array
$mechlist)
integer getServerType() Gets the server type. Returns
896
Zend_Ldap
Method Description
for
Zend_Ldap_Node_RootDse::SERVER_TYPE_GENERIC
unknown
LDAP
servers
for
Zend_Ldap_Node_RootDse::SERVER_TYPE_OPENLDAP
OpenLDAP
servers
for
Zend_Ldap_Node_RootDse::SERVER_TYPE_ACTIVEDIREC
Microsoft
ActiveDirectory
servers
For Novell
Zend_Ldap_Node_RootDse::SERVER_TYPE_EDIRECTORY
eDirectory
servers
Zend_Ldap_Dn getSchemaDn() Returns the schema DN.
2.2.6.1. OpenLDAP
Method Description
integer getServerType() Gets the server type. Returns
Zend_Ldap_Node_RootDse::SERVER_TYPE_OPENLDAP
string|null getConfigContext() Gets the configContext.
string|null getMonitorContext() Gets the monitorContext.
boolean supportsControl(string| Determines if the control is supported.
array $oids)
boolean supportsExtension(string| Determines if the extension is supported.
array $oids)
boolean supportsFeature(string| Determines if the feature is supported.
array $oids)
2.2.6.2. ActiveDirectory
897
Zend_Ldap
Method Description
integer getServerType() Gets the server type. Returns
Zend_Ldap_Node_RootDse::SERVER_TYPE_ACTIVEDIREC
string|null Gets the configurationNamingContext.
getConfigurationNamingContext()
string|null getCurrentTime() Gets the currentTime.
string|null Gets the defaultNamingContext.
getDefaultNamingContext()
string|null getDnsHostName() Gets the dnsHostName.
string|null Gets the domainControllerFunctionality.
getDomainControllerFunctionality()
string|null Gets the domainFunctionality.
getDomainFunctionality()
string|null getDsServiceName() Gets the dsServiceName.
string|null Gets the forestFunctionality.
getForestFunctionality()
string|null Gets the highestCommittedUSN.
getHighestCommittedUSN()
string|null Gets the isGlobalCatalogReady.
getIsGlobalCatalogReady()
string|null getIsSynchronized() Gets the isSynchronized.
string|null getLdapServiceName() Gets the ldapServiceName.
string|null Gets the rootDomainNamingContext.
getRootDomainNamingContext()
string|null Gets the schemaNamingContext.
getSchemaNamingContext()
string|null getServerName() Gets the serverName.
boolean supportsCapability(string| Determines if the capability is supported.
array $oids)
boolean supportsControl(string| Determines if the control is supported.
array $oids)
boolean supportsPolicy(string| Determines if the version is supported.
array $policies)
2.2.6.3. eDirectory
898
Zend_Ldap
2.2.7. Zend_Ldap_Node_Schema
The following methods are available on all vendor-specific subclasses.
899
Zend_Ldap
Method Description
array getObjectClass() Returns the objectClass of the node.
string toString() Returns the DN of the current node - proxies to
Zend_Ldap_Dn::getDnString().
string __toString() Casts to string representation - proxies to
Zend_Ldap_Dn::toString().
array toArray(boolean Returns an array representation of the current
$includeSystemAttributes) node. If $includeSystemAttributes is
FALSE (defaults to TRUE), the system specific
attributes are stripped from the array. Unlike
Zend_Ldap_Node_Schema::getAttributes(),
the resulting array contains the DN with key
'dn'.
string toJson(boolean Returns a JSON representation
$includeSystemAttributes) of the current node using
Zend_Ldap_Node_Schema::toArray().
array getData(boolean Returns the node's attributes. The array
$includeSystemAttributes) contains all attributes in its internal format (no
conversion).
boolean existsAttribute(string Checks whether a given attribute exists. If
$name, boolean $emptyExists) $emptyExists is FALSE, empty attributes
(containing only array()) are treated as non-
existent returning FALSE. If $emptyExists is
TRUE, empty attributes are treated as existent
returning TRUE. In this case the method returns
FALSE only if the attribute name is missing in
the key-collection.
boolean attributeHasValue(string Checks if the given value(s) exist in the
$name, mixed|array $value) attribute. The method returns TRUE only if all
values in $value are present in the attribute.
Comparison is done strictly (respecting the
data type).
integer count() Returns the number of attributes in the node.
Implements Countable.
mixed getAttribute(string $name, Gets a LDAP attribute. Data conversion is
integer|null $index) applied using
Zend_Ldap_Attribute::getAttribute().
array getAttributes(boolean Gets all attributes of node. If
$includeSystemAttributes) $includeSystemAttributes is FALSE
(defaults to TRUE) the system specific attributes
are stripped from the array.
array|integer Gets a LDAP date/time attribute.
getDateTimeAttribute(string $name, Data conversion is applied using
integer|null $index) Zend_Ldap_Attribute::getDateTimeAttribute().
Zend_Ldap_Node_Schema Reloads the current node's attributes from the
reload(Zend_Ldap $ldap) given LDAP server.
Zend_Ldap_Node_Schema Factory method to create the Schema node.
create(Zend_Ldap $ldap)
900
Zend_Ldap
Method Description
array getAttributeTypes() Gets the attribute types as an array of .
array getObjectClasses() Gets the object classes as an array of
Zend_Ldap_Node_Schema_ObjectClass_Interface.
Method Description
string getName() Gets the attribute name.
string getOid() Gets the attribute OID.
string getSyntax() Gets the attribute syntax.
int|null getMaxLength() Gets the attribute maximum length.
boolean isSingleValued() Returns if the attribute is single-valued.
string getDescription() Gets the attribute description
Method Description
string getName() Returns the objectClass name.
string getOid() Returns the objectClass OID.
array getMustContain() Returns the attributes that this objectClass
must contain.
array getMayContain() Returns the attributes that this objectClass may
contain.
string getDescription() Returns the attribute description
integer getType() Returns the objectClass type. The method
returns one of the following values:
for
Zend_Ldap_Node_Schema::OBJECTCLASS_TYPE_UNKNOWN
unknown
class types
for
Zend_Ldap_Node_Schema::OBJECTCLASS_TYPE_STRUCTU
structural
classes
for abstract
Zend_Ldap_Node_Schema::OBJECTCLASS_TYPE_ABSTRAC
classes
for auxiliary
Zend_Ldap_Node_Schema::OBJECTCLASS_TYPE_AUXILIA
classes
array getParentClasses() Returns the parent objectClasses of this class.
This includes structural, abstract and auxiliary
objectClasses.
901
Zend_Ldap
Method Description
array getData() Gets all the underlying data from the schema
information node.
integer count() Returns the number of attributes in this schema
information node. Implements Countable.
2.2.7.1. OpenLDAP
Method Description
array getLdapSyntaxes() Gets the LDAP syntaxes.
array getMatchingRules() Gets the matching rules.
array getMatchingRuleUse() Gets the matching rule use.
Method Description
Returns the parent attribute type in the
Zend_Ldap_Node_Schema_AttributeType_OpenLdap|
null getParent() inheritance tree if one exists.
Method Description
array getParents() Returns the parent object classes in
the inheritance tree if one exists.
The returned array is an array of
Zend_Ldap_Node_Schema_ObjectClass_OpenLdap.
2.2.7.2. ActiveDirectory
902
Zend_Ldap
2.2.8. Zend_Ldif_Encoder
Method Description
array decode(string $string) Decodes the string $string into an array of
LDIF items.
string encode(scalar|array| Encode $value into a LDIF representation.
Zend_Ldap_Node $value, array $options is an array that may contain the
$options) following keys:
3. Usage Scenarios
3.1. Authentication scenarios
3.1.1. OpenLDAP
3.1.2. ActiveDirectory
903
Zend_Ldap
904
Zend_Ldap
Exemple 460. Move a LDAP entry recursively with all its descendants to a
different subtree
905
Zend_Ldap
4. Tools
4.1. Creation and modification of DN strings
// (&(name=value)(name=value*)(name=*value))
$f5 = Zend_Ldap_Filter::andFilter($f2, $f3, $f4);
// (|(name=value)(name=value*)(name=*value))
$f6 = Zend_Ldap_Filter::orFilter($f2, $f3, $f4);
906
Zend_Ldap
• OpenLDAP
• Microsoft ActiveDirectory
• Novell eDirectory
6.2.1. OpenLDAP
907
Zend_Ldap
6.2.2. ActiveDirectory
908
Zend_Ldap
cn:: 5bCP56yg5Y6fIOODreODieODi+ODvA==
title:: 5Za25qWt6YOoIOmDqOmVtw==
givenname;lang-ja;phonetic:: 44KN44Gp44Gr44O8
sn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJ
cn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJIOOCjeOBqeOBq+ODvA==
title;lang-ja;phonetic:: 44GI44GE44GO44KH44GG44G2IOOBtuOBoeOCh+OBhg==
givenname;lang-en: Rodney
sn;lang-en: Ogasawara
cn;lang-en: Rodney Ogasawara
title;lang-en: Sales, Director
*/
909
Zend_Ldap
910
Zend_Loader
1. Charger les fichiers et les classes dynamiquement
La classe Zend_Loader inclut des méthodes afin de vous aider à charger des fichiers
dynamiquement.
L'argument $dirs définit les dossiers où rechercher le fichier. Si NULL, la recherche s'effectuera
uniquement dans les dossiers définis par la variable include_path. Si c'est une chaîne ou
un tableau, le ou les répertoires spécifiés seront scannés, ainsi que les dossiers définis par la
variable include_path.
Zend_Loader::loadClass('Container_Tree',
array(
'/home/production/mylib',
'/home/production/myapp'
)
);
911
Zend_Loader
La chaîne spécifiant la classe est convertie en chemin relatif en remplaçant les tirets bas
("_") par le séparateur de dossier puis en ajoutant le bloc ".php". Dans l'exemple ci-dessus,
"Container_Tree" devient "Container\\Tree.php".
Si le fichier n'est pas trouvé ou que la classe n'existe pas après le chargement,
Zend_Loader::loadClass() lèvera une exception Zend_Exception
• The fully-qualified namespace and class is suffixed with ".php" when loading
from the file system.
As examples:
if (Zend_Loader::isReadable($filename)) {
// puis manipulation avec $filename
}
912
Zend_Loader
L'argument $filename spécifie le nom du fichier à vérifier. Il peut contenir des informations
concernant le chemin d'accès. Cette méthode enveloppe la fonction PHP is_readable().
La fonction PHP ne recherche pas le fichier spécifié dans les répertoires de l'include_path,
contrairement à Zend_Loader::isReadable().
Zend_Loader::registerAutoload();
Après avoir inscrit le callback vers l'autoload de Zend Framework, vous pouvez appeler une
classe de Zend Framework sans l'avoir explicitement chargé auparavant. La méthode autoload
utilise automatiquement Zend_Loader::loadClass() quand vous appelez une classe.
Si vous avez étendu la classe Zend_Loader, vous pouvez passer un argument optionnel
à registerAutoload(), pour spécifier la classe de laquelle vous souhaitez enregistrer la
méthode autoload().
Zend_Loader::registerAutoload('Mon_Chargeur');
913
Zend_Loader
2. L'autoloader
Zend_Loader_Autoloader propose une solution intelligente et souple d'auto-chargement
(autoload) pour Zend Framework. Il a été conçu pour remplir plusieurs objectifs :
• Proposer un autoloader à base d'espaces de noms (auparavant, les espaces de noms étaient
interceptés).
• Proposer d'enregistrer des autoloads personnalisés, et les gérer comme une pile. (A l'heure
actuelle, ceci permet de s'affranchir de certaines contraintes avec spl_autoload, qui ne
permet pas le ré-enregistrement d'une fonction à base d'objet).
• Proposer un autoload optimisé pour les espaces de noms, qui permet une meilleure résolution
des noms de classes.
$autoloader = Zend_Loader_Autoloader::getInstance();
Par défaut, l'autoloader est configuré pour capturer les espaces de noms "Zend_" et "ZendX_".
Si votre propre librairie de code utilise un espace de noms différent, vous devez alors enregistrer
celui-ci avec la méthode registerNamespace(). Par exemple, si votre librairie possède
l'espace de noms "My_", vous devriez agir comme cela :
$autoloader->registerNamespace('My_');
Il est aussi possible que vous enregistriez vos propres fonctions d'autoload, optionnellement
avec un espace de noms spécifique, Zend_Loader_Autoloader va alors tenter de l'utiliser
lorsque nécessaire (lors de l'auto-chargement).
Par exemple, vous pourriez avoir besoin d'un ou plusieurs composants eZcomponents avec
votre application Zend Framework. Pour utiliser ses capacités d'autoload, ajoutez son autoloader
à votre pile grâce à pushAutoloader() :
Ceci indique que les classes dont le nom commence par "ezc" devra utiliser cette fonction
d'autoload.
914
Zend_Loader
le comportement recherché. Cependant, si vous voulez faire apparaître les éventuelles erreurs
de chargement, appelez alors suppressNotFoundWarnings() :
$autoloader->suppressNotFoundWarnings(true);
Enfin, il se peut que vous vouliez que l'autoloader par défaut charge toutes les classes de tous
les espaces de noms. Par exemple, les librairies PEAR ne partagent pas un espace de noms
commun, ce qui rend la tâche difficile si on veut associer chacun des espaces de noms internes.
Utilisez alors setFallbackAutoloader() pour rendre l'autoloader "global" et charger tous
les espaces de noms :
$autoloader->setFallbackAutoloader(true);
Starting in version 1.10.0, Zend Framework now allows loading classes from
PHP namespaces. This support follows the same guidelines and implementation
as that found in the PHP Framework Interop Group PSR-0 reference
implementation.
• The fully-qualified namespace and class is suffixed with ".php" when loading
from the file system.
As examples:
• During development, you want to track the latest version of Zend Framework you have
installed, so that you can ensure the application works when you upgrade between versions.
915
Zend_Loader
When pushing to Quality Assurance, however, you need to have slightly more stability, so you
want to use the latest installed revision of a specific minor version.
Finally, when you push to production, you want to pin to a specific installed version, to ensure
no breakage occurs if or when you add new versions of Zend Framework to you server.
The autoloader allows you to do this with the method setZfPath(). This method takes two
arguments, a path to a set of Zend Framework installations, and a version to use. Once invoked,
it prepends a path to the include_path pointing to the appropriate Zend Framework installation
library.
The directory you specify as your path should have a tree such as the following:
ZendFramework/
|-- 1.9.2/
| |-- library/
|-- ZendFramework-1.9.1-minimal/
| |-- library/
|-- 1.8.4PL1/
| |-- library/
|-- 1.8.4/
| |-- library/
|-- ZendFramework-1.8.3/
| |-- library/
|-- 1.7.8/
| |-- library/
|-- 1.7.7/
| |-- library/
|-- 1.7.6/
| |-- library/
Note that each subdirectory should contain the directory library, which contains the actual
Zend Framework library code. The individual subdirectory names may be version numbers, or
simply be the untarred contents of a standard Zend Framework distribution tarball/zipfile.
Now, let's address the use cases. In the first use case, in development, we want to track the
latest source install. We can do that by passing "latest" as the version:
$autoloader->setZfPath($path, 'latest');
In the example from above, this will map to the directory ZendFramework/1.9.2/library/;
you can verify this by checking the return value of getZfPath().
In the second situation, for quality assurance, let's say we want to pin to the 1.8 minor release,
using the latest install you have for that release. You can do so as follows:
$autoloader->setZfPath($path, '1.8');
In the final case, for production, we'll pin to a specific version -- 1.7.7, since that was what was
available when Quality Assurance tested prior to our release.
$autoloader->setZfPath($path, '1.7.7');
916
Zend_Loader
You can also specify these values in the configuration file you use with Zend_Application.
To do so, you'd specify the following information:
[production]
autoloaderZfPath = "path/to/ZendFramework"
autoloaderZfVersion = "1.7.7"
[qa]
autoloaderZfVersion = "1.8"
[development]
autoloaderZfVersion = "latest"
Note the different environment sections, and the different version specified in each environment;
these factors will allow Zend_Application to configure the autoloader appropriately.
Performance implications
For best performance, either do not use this feature, or specify a specific Zend
Framework version (i.e., not "latest", a major revision such as "1", or a minor
revision such as "1.8"). Otherwise, the autoloader will need to scan the provided
path for directories matching the criteria -- a somewhat expensive operation to
perform on each request.
interface Zend_Loader_Autoloader_Interface
{
public function autoload($class);
}
$autoloader->pushAutoloader($foo, 'Foo_');
917
Zend_Loader
918
Zend_Loader
919
Zend_Loader
3. Autoloaders de ressources
Les autoloaders de ressources servent à manipuler du code de librairies dans des espaces
de noms, respectant les conventions de codage du Zend Framework, mais n'ayant pas une
correspondance 1:1 entre le nom de la classe et la structure du dossier. Leur but est de faciliter
le chargement du code des ressources de l'application, comme les modèles, les ACLs, les
formulaires...
Les autoloaders de ressources s'enregistrent dans l'autoloader à leur instanciation, avec l'espace
de noms auxquels ils sont rattachés. Ceci permet de facilement isoler du code dans des dossiers,
sous l'espace de noms, tout en gardant les bénéfices de l'autoload.
path/to/some/directory/
acls/
Site.php
forms/
920
Zend_Loader
Login.php
models/
User.php
Au sein de ce répertoire, toutes les classes sont préfixées par l'espace de noms "My_". Dans le
dossier "acls", le préfixe de composant "Acl_" est ajouté, ce qui donne un nom de classe final
"My_Acl_Site". Aussi, le dossier "forms" correspond à "Form_", ce qui donne "My_Form_Login".
Le dossier "models" n'a pas d'espace de noms particulier, donnant donc "My_User".
Pour instancier un autoloader de ressoucres, il faut au minimum lui passer son dossier de travail
(base path), et le nom de l'espace de noms correspondant :
Maintenant que notre autoloader est configuré, nous pouvons ajouter des composants à auto-
charger. Ceci se fait via la méthode addResourceType(), qui accepte 3 arguments : un "type"
de ressource, utiliser en interne comme nom de référence ; le sous dossier dans lequel la
ressource en question est logé, et l'espace de noms du composant à rajouter à l'espace de noms
général. Voici un exemple :
Aussi, vous auriez pu effectuer la même action avec un tableau PHP. addResourceTypes()
est alors appropriée :
$resourceLoader->addResourceTypes(array(
'acl' => array(
'path' => 'acls/',
'namespace' => 'Acl',
),
'form' => array(
'path' => 'forms/',
'namespace' => 'Form',
),
'model' => array(
'path' => 'models/',
),
));
Enfin, vous pouvez spécifier tout cela d'un seul coup avec des tableaux nichés. La clé doit alors
être "resourceTypes" :
921
Zend_Loader
Par exemple, avec un module dont le préfixe est "Blog_", le chargement de la classe
"Blog_Form_Entry" mènerait au chargement du fichier "forms/Entry.php".
4. Chargeur de Plugins
Zend Framework vous propose l'utilisation de composants "pluggables", que vous créez
vous même. Ceux-ci ne sont pas forcément dans l'include_path. De même, ils ne
suivent pas forcément les mêmes règles de nommage que les composants de Zend
Framework.Zend_Loader_PluginLoader propose une solution à ce problème.
PluginLoader suit la convention "une classe par fichier" et les tirets bas sont utilisés comme
séparateurs de dossiers. Il accepte aussi qu'un préfixe optionnel lui soit passé, afin de charger
922
Zend_Loader
une classe. Tous les chemins sont analysés en ordre LIFO. Grâce à ces deux spécificités, vous
pouvez "namespacer" vos plugins, et écraser les plugins enregistrés plus tôt.
application/
modules/
foo/
views/
helpers/
FormLabel.php
FormSubmit.php
bar/
views/
helpers/
FormSubmit.php
library/
Zend/
View/
Helper/
FormLabel.php
FormSubmit.php
FormText.php
Maintenant créons un chargeur de plugins pour utiliser les différentes classes d'aides de vue :
Il devient alors possible de charger une aide de vue en spécifiant juste le nom de sa classe :
923
Zend_Loader
LIFO (dernier arrivé, premier sorti). Ceci est pratique pour court-circuiter ses
composants et utiliser ceux en incubateur, par exemple.
• getPaths($prefix = null) retourne les chemin sous la forme prefix / path si $prefix
n'est pas renseigné. Sinon, ce sont les chemins enregistrés pour le préfixe en question qui
sont renvoyés.
Une autre utilisation de PluginLoader peut être de récupérer le nom des classes des plugins
chargés.getClassName() vous le permet. Utilisée en conjonction avec isLoaded(), vous
pouvez écrire par exemple ceci :
if ($loader->isLoaded('Adapter')) {
$class = $loader->getClassName('Adapter');
$adapter = call_user_func(array($class, 'getInstance'));
}
924
Zend_Loader
un fichier qui correspond - et qui définit de plus la classe voulue. Dans le cas où le fichier existe
mais ne défini pas la classe, une erreur sera ajouté à la pile d'erreur PHP, opération qui est elle
aussi consommatrice. La question qui vient à l'esprit est : comment maintenir la flexibilité des
plugins et la performance ?
Pour utiliser le fichier de cache des inclusions de classe, collez simplement le code suivant
dans votre fichier d'initialisation :
Cette technique vous permet de restreindre les modifications au seul fichier de configuration
plutôt que dans votre code.
925
Zend_Locale
1. Introduction
Zend_Locale is the Frameworks answer to the question, "How can the same application be
used around the whole world?" Most people will say, "That's easy. Let's translate all our output to
several languages." However, using simple translation tables to map phrases from one language
to another is not sufficient. Different regions will have different conventions for first names,
surnames, salutory titles, formatting of numbers, dates, times, currencies, etc.
When you are using Zend_Locale you will not have this limitations, because
Zend_Locale is not related to or coupled with PHP's setlocale().
• Zend_Locale - Backend support of locales available for localization support within other Zend
Framework components.
926
Zend_Locale
A locale string or object identifying a supported locale gives Zend_Locale and its subclasses
access to information about the language and region expected by the user. Correct formatting,
normalization, and conversions are made based on this information.
Be aware that there exist not only locales with 2 characters as most people
think. Also there are languages and regions which are not only abbreviated with
2 characters. Therefor you should NOT strip the region and language yourself,
but use Zend_Locale when you want to strip language or region from a locale
string. Otherwise you could have unexpected behaviour within your code when
you do this yourself.
A user from USA would expect the language English and the region USA, yielding the locale
identifier "en_US". A user in Germany would expect the language German and the region
Germany, yielding the locale identifier "de_DE". See the list of pre-defined locale and region
combinations, if you need to select a specific locale within Zend Framework.
A German user in America might expect the language German and the region USA, but these
non-standard mixes are not supported directly as recognized "locales". Instead, if an invalid
combination is used, then it will automatically be truncated by dropping the region code. For
example, "de_IS" would be truncated to "de", and "xh_RU" would be truncated to "xh", because
neither of these combinations are valid. Additionally, if the base language code is not supported
(e.g. "zz_US") or does not exist, then a default "root" locale will be used. The "root" locale
has default definitions for internationally recognized representations of dates, times, numbers,
currencies, etc. The truncation process depends on the requested information, since some
combinations of language and region might be valid for one type of data (e.g. dates), but not for
another (e.g. currency format).
Beware of historical changes, as Zend Framework components do not know about or attempt to
track the numerous timezone changes made over many years by many regions. For example,
we can see a historical list showing dozens of changes made by governments to when and
if a particular region observes Daylight Savings Time, and even which timezone a particular
927
Zend_Locale
geographic area belongs. Thus, when performing date math, the math performed by Zend
Framework components will not adjust for these changes, but instead will give the correct time
for the timezone using current, modern rules for DST and timezone assignment for geographic
regions.
The search algorithm used by Zend_Locale for automatic selection of a locale uses three
sources of information:
1. const Zend_Locale::BROWSER - The user's Web browser provides information with each
request, which is published by PHP in the global variable HTTP_ACCEPT_LANGUAGE. If
no matching locale can be found, then preference is given to ENVIRONMENT and lastly
FRAMEWORK.
2. const Zend_Locale::ENVIRONMENT - PHP publishes the host server's locale via the PHP
internal function setlocale(). If no matching locale can be found, then preference is given
to FRAMEWORK and lastly BROWSER.
1. 'browser' - Zend_Locale should work with the information which is provided by the user's
Web browser. It is published by PHP in the global variable HTTP_ACCEPT_LANGUAGE.
928
Zend_Locale
If a user provides more than one locale within his browser, Zend_Locale will use the first
found locale. If the user does not provide a locale or the script is being called from the
command line the automatic locale 'environment' will automatically be used and returned.
2. 'environment' - Zend_Locale should work with the information which is provided by the
host server. It is published by PHP via the internal function setlocale().
If a environment provides more than one locale, Zend_Locale will use the first found locale.
If the host does not provide a locale the automatic locale 'browser' will automatically be
used and returned.
3. 'auto' - Zend_Locale should automatically detect any locale which can be worked with. It
will first search for a users locale and then, if not successful, search for the host locale.
If no locale can be detected, it will throw an exception and tell you that the automatic detection
has been failed.
In such cases Zend_Locale will normally throw an exception with a message that the automatic
detection of any locale was not successful. You have two options to handle such a situation.
Either through setting a new locale per hand, or defining a default locale.
But this has one big negative effect. You will have to set your locale object within every class
using Zend_Locale. This could become very unhandy if you are using multiple classes.
Since Zend Framework Release 1.5 there is a much better way to handle this. You can set a
default locale which the static setDefault() method. Of course, every unknown or not full
929
Zend_Locale
qualified locale will also throw an exception. setDefault() should be the first call before you
initiate any class using Zend_Locale. See the following example for details:
In the case that no locale can be detected, automatically the locale de will be used. Otherwise,
the detected locale will be used.
To override this default behavior, and force locale-aware Zend Framework components to use
specific locales, regardless of the origin of your website visitors, explicitly specify a locale as the
third argument to the constructor.
If you know many objects should all use the same default locale, explicitly specify the default
locale to avoid the overhead of each object determining the default locale.
930
Zend_Locale
The 'date_format' option can be used to specify a default date format string, but beware of using
getDate(), checkdateFormat() and getTime() after using setOptions() with a 'date_format'. To
use these four methods with the default date format for a locale, use array('date_format' => null,
'locale' => $locale) for their options.
931
Zend_Locale
When no cache is set, then Zend_Locale will automatically set a cache itself. Sometimes it is
wished to prevent that a cache is set, even if this degrades performance. In this case the static
disableCache(true) method should be used. It does not only disable the actual set cache,
without erasing it, but also prevents that a cache is automatically generated when no cache is set.
2. Using Zend_Locale
Zend_Locale also provides localized information about locales for each locale, including
localized names for other locales, days of the week, month names, etc.
2.2. Equality
Zend_Locale also provides a convenience function to compare two locales. All locale-aware
classes should provide a similar equality check.
932
Zend_Locale
To obtain only the default locales relevant to the BROWSER, ENVIRONMENT, or FRAMEWORK ,
use the corresponding method:
• getEnvironment()
• getBrowser()
• getLocale()
// Actual locale
print $locale->toString();
// new locale
$locale->setLocale('aa_DJ');
print $locale->toString();
933
Zend_Locale
The requested list of information is always returned as named array. If you want to give more
than one value to a explicit type where you wish to receive values from, you have to give an
array instead of multiple values.
print_r ($list);
// example key -> value pairs...
// [de] -> Deutsch
// [en] -> Englisch
// use one of the returned key as value for the getTranslation() method
// of another language
print Zend_Locale::getTranslation('de', 'language', 'zh');
// returns the translation for the language 'de' in chinese
You can receive this informations for all languages. But not all of the informations are completely
available for all languages. Some of these types are also available through an own function for
simplicity. See this list for detailed informations.
Tableau 91. Details for getTranslationList($type = null, $locale = null, $value = null)
Type Description
Language Returns a localized list of all languages. The
language part of the locale is returned as key
and the translation as value
Script Returns a localized list of all scripts. The script
is returned as key and the translation as value
Territory Returns a localized list of all territories. This
contains countries, continents and territories.
To get only territories and continents use '1'
as value. To get only countries use '2' as
value. The country part of the locale is used
as key where applicable. In the other case the
official ISO code for this territory is used. The
translated territory is returned as value. When
you omit the value you will get a list with both.
934
Zend_Locale
Type Description
Variant Returns a localized list of known variants of
scripts. The variant is returned as key and the
translation as value
Key Returns a localized list of known keys. This
keys are generic values used in translation.
These are normally calendar, collation and
currency. The key is returned as array key and
the translation as value
Type Returns a localized list of known types of
keys. These are variants of types of calendar
representations and types of collations. When
you use 'collation' as value you will get all types
of collations returned. When you use 'calendar'
as value you will get all types of calendars
returned. When you omit the value you will get
a list all both returned. The type is used as key
and the translation as value
Layout Returns a list of rules which describes how to
format special text parts
Characters Returns a list of allowed characters within this
locale
Delimiters Returns a list of allowed quoting characters for
this locale
Measurement Returns a list of known measurement values.
This list is depreciated
Months Returns a list of all month representations
within this locale. There are several different
representations which are all returned as sub
array. If you omit the value you will get a
list of all months from the 'gregorian' calendar
returned. You can give any known calendar as
value to get a list of months from this calendar
returned. Use Zend_Date for simplicity
Month Returns a localized list of all month names for
this locale. If you omit the value you will get
the normally used gregorian full name of the
months where each month number is used as
key and the translated month is returned as
value. You can get the months for different
calendars and formats if you give an array
as value. The first array entry has to be the
calendar, the second the used context and the
third the width to return. Use Zend_Date for
simplicity
Days Returns a list of all day representations
within this locale. There are several different
representations which are all returned as sub
array. If you omit the value you will get a list of
all days from the 'gregorian' calendar returned.
935
Zend_Locale
Type Description
You can give any known calendar as value to
get a list of days from this calendar returned.
Use Zend_Date for simplicity
Day Returns a localized list of all day names for this
locale. If you omit the value you will get the
normally used gregorian full name of the days
where the english day abbreviation is used as
key and the translated day is returned as value.
You can get the days for different calendars
and formats if you give an array as value. The
first array entry has to be the calendar, the
second the used context and the third the width
to return. Use Zend_Date for simplicity
Week Returns a list of values used for proper week
calculations within a locale. Use Zend_Date for
simplicity
Quarters Returns a list of all quarter representations
within this locale. There are several different
representations which are all returned as sub
array. If you omit the value you will get a list
of all quarters from the 'gregorian' calendar
returned. You can give any known calendar as
value to get a list of quarters from this calendar
returned
Quarter Returns a localized list of all quarter names for
this locale. If you omit the value you will get
the normally used gregorian full name of the
quarters where each quarter number is used
as key and the translated quarter is returned
as value. You can get the quarters for different
calendars and formats if you give an array
as value. The first array entry has to be the
calendar, the second the used context and the
third the width to return
Eras Returns a list of all era representations within
this locale. If you omit the value you will get
a list of all eras from the 'gregorian' calendar
returned. You can give any known calendar as
value to get a list of eras from this calendar
returned
Era Returns a localized list of all era names for this
locale. If you omit the value you will get the
normally used gregorian full name of the eras
where each era number is used as key and the
translated era is returned as value. You can get
the eras for different calendars and formats if
you give an array as value. The first array entry
has to be the calendar and the second the width
to return
936
Zend_Locale
Type Description
Date Returns a localized list of all date formats for
this locale. The name of the dateformat is used
as key and the format itself as value.If you omit
the value you will get the date formats for the
gregorian calendar returned. You can get the
date formats for different calendars if you give
the wished calendar as string. Use Zend_Date
for simplicity
Time Returns a localized list of all time formats for
this locale. The name of the timeformat is used
as key and the format itself as value. If you omit
the value you will get the time formats for the
gregorian calendar returned. You can get the
time formats for different calendars if you give
the wished calendar as string. Use Zend_Date
for simplicity
DateTime Returns a localized list of all known date-time
formats for this locale. The name of the date-
time format is used as key and the format
itself as value. If you omit the value you will
get the date-time formats for the gregorian
calendar returned. You can get the date-time
formats for different calendars if you give the
wished calendar as string. Use Zend_Date for
simplicity
DateItem Returns a list of default formats for given date
or time items
DateInterval Returns a list of date or time formats which
are used when you want to display intervals.
The list is a multidimentional array where the
first dimension is the interval format, and the
second dimension is the token with the greatest
difference.
Field Returns a localized list of date fields which can
be used to display calendars or date strings
like 'month' or 'year' in a wished language. If
you omit the value you will get this list for the
gregorian calendar returned. You can get the
list for different calendars if you give the wished
calendar as string
Relative Returns a localized list of relative dates which
can be used to display textual relative dates like
'yesterday' or 'tomorrow' in a wished language.
If you omit the value you will get this list for the
gregorian calendar returned. You can get the
list for different calendars if you give the wished
calendar as string
Symbols Returns a localized list of characters used for
number representations
937
Zend_Locale
Type Description
NameToCurrency Returns a localized list of names for currencies.
The currency is used as key and the translated
name as value. Use Zend_Currency for
simplicity
CurrencyToName Returns a list of currencies for localized names.
The translated name is used as key and the
currency as value. Use Zend_Currency for
simplicity
CurrencySymbol Returns a list of known localized currency
symbols for currencies. The currency is used
as key and the symbol as value. Use
Zend_Currency for simplicity
Question Returns a list of localized strings for
acceptance ('yes') and negotation ('no').
Use Zend_Locale's getQuestion method for
simplicity
CurrencyFraction Returns a list of fractions for currency values.
The currency is used as key and the fraction as
integer value. Use Zend_Currency for simplicity
CurrencyRounding Returns a list of how to round which currency.
The currency is used as key and the rounding
as integer value. Use Zend_Currency for
simplicity
CurrencyToRegion Returns a list of currencies which are known
to be used within a region. The ISO3166
value ('region') is used as array key and the
ISO4217 value ('currency') as array value. Use
Zend_Currency for simplicity
RegionToCurrency Returns a list of regions where a currency is
used . The ISO4217 value ('currency') is used
as array key and the ISO3166 value ('region')
as array value. When a currency is used in
several regions these regions are separated
with a whitespace. Use Zend_Currency for
simplicity
RegionToTerritory Returns a list of territories with the countries
or sub territories which are included within that
territory. The ISO territory code ('territory') is
used as array key and the ISO3166 value
('region') as array value. When a territory
contains several regions these regions are
separated with a whitespace
TerritoryToRegion Returns a list of regions and the territories
where these regions are located. The ISO3166
code ('region') is used as array key and the ISO
territory code ('territory') as array value. When
a region is located in several territories these
territories are separated with a whitespace
938
Zend_Locale
Type Description
ScriptToLanguage Returns a list of scripts which are used within a
language. The language code is used as array
key and the script code as array value. When a
language contains several scripts these scripts
are separated with a whitespace
LanguageToScript Returns a list of languages which are using a
script. The script code is used as array key
and the language code as array value. When
a script is used in several languages these
languages are separated with a whitespace
TerritoryToLanguage Returns a list of countries which are using
a language. The country code is used as
array key and the language code as array
value. When a language is used in several
countries these countries are separated with a
whitespace
LanguageToTerritory Returns a list of countries and the languages
spoken within these countries. The country
code is used as array key and the language
code as array value. When a territory is
using several languages these languages are
separated with a whitespace
TimezoneToWindows Returns a list of windows timezones and the
related ISO timezone. The windows timezone
is used as array key and the ISO timezone as
array value
WindowsToTimezone Returns a list of ISO timezones and the related
windows timezone. The ISO timezone is used
as array key and the windows timezone as
array value
TerritoryToTimezone Returns a list of regions or territories and the
related ISO timezone. The ISO timezone is
used as array key and the territory code as
array value
TimezoneToTerritory Returns a list of timezones and the related
region or territory code. The region or territory
code is used as array key and the ISO timezone
as array value
CityToTimezone Returns a localized list of cities which can
be used as translation for a related timezone.
Not for all timezones is a translation available,
but for a user is the real city written in his
languages more accurate than the ISO name
of this timezone. The ISO timezone is used as
array key and the translated city as array value
TimezoneToCity Returns a list of timezones for localized city
names. The localized city is used as array key
and the ISO timezone name as array value
939
Zend_Locale
Type Description
PhoneToTerritory Returns a list of phone codes which are known
to be used within a territory. The territory
(region) is used as array key and the telephone
code as array value
TerritoryToPhone Returns a list of territories where a phone is
used . The phone code is used as array key
and the territory (region) as array value. When
a phone code is used in several territories these
territories are separated with a whitespace
NumericToTerritory Returns a list of 3 digit number codes for
territories. The territory (region) is used as array
key and the 3 digit number code as array value
TerritoryToNumeric Returns a list of territories with their 3 digit
number code. The 3 digit number code is used
as array key and the territory (region) as array
value
Alpha3ToTerritory Returns a list of 3 sign character codes for
territories. The territory (region) is used as array
key and the 3 sign character code as array
value
TerritoryToAlpha3 Returns a list of territories with their 3 sign
character code. The 3 sign character code is
used as array key and the territory (region) as
array value
PostalToTerritory Returns a list of territories with a regex for
postal codes which are included within that
territory. The ISO territory code ('territory') is
used as array key and the regex as array value.
NumberingSystem Returns a list of scripts with the notation for
digits used within the script
FallbackToChar Returns a list of replacement characters for
often used unicode characters. This can be
used to replace "©" with "(C)" for example
CharToFallback Returns a list of unicode characters for often
used replacement characters. This can be used
to replace "(C)" with "©" for example
LocaleUpgrade Returns a list of locale dependencies which can
be used to upgrade a language to a full qualified
locale
Unit Returns a list of localized calendar units. This
can be used to translate the strings "day",
"month" and so on automatically
If you are in need of a single translated value, you can use the getTranslation() method. It
returns always a string but it accepts some different types than the getTranslationList()
method. Also value is the same as before with one difference. You have to give the detail you
want to get returned as additional value.
940
Zend_Locale
Because you have almost always give a value as detail this parameter has to be
given as first parameter. This differs from the getTranslationList() method.
Tableau 92. Details for getTranslation($value = null, $type = null, $locale = null)
Type Description
Language Returns a translation for a language. To select
the wished translation you must give the
language code as value
Script Returns a translation for a script. To select
the wished translation you must give the script
code as value
Territory or Country Returns a translation for a territory. This can be
countries, continents and territories. To select
the wished variant you must give the territory
code as value
Variant Returns a translation for a script variant. To
select the wished variant you must give the
variant code as value
Key Returns translation for a known keys. This keys
are generic values used in translation. These
are normally calendar, collation and currency.
To select the wished key you must give the key
code as value
DefaultCalendar Returns the default calendar for the given
locale. For most locales this will be 'gregorian'.
Use Zend_Date for simplicity
MonthContext Returns the default context for months which is
used within the given calendar. If you omit the
value the 'gregorian' calendar will be used. Use
Zend_Date for simplicity
DefaultMonth Returns the default format for months which is
used within the given calendar. If you omit the
value the 'gregorian' calendar will be used. Use
Zend_Date for simplicity
Month Returns a translation for a month. You have
to give the number of the month as integer
value. It has to be between 1 and 12. If
you want to receive data for other calendars,
contexts or formats, then you must give
an array instead of an integer with the
expected values. The array has to look like
this: array( 'calendar', 'context',
'format', 'month number'). If you give
only an integer then the default values are the
'gregorian' calendar, the context 'format' and
the format 'wide'. Use Zend_Date for simplicity
941
Zend_Locale
Type Description
DayContext Returns the default context for ´days which is
used within the given calendar. If you omit the
value the 'gregorian' calendar will be used. Use
Zend_Date for simplicity
DefaultDay Returns the default format for days which is
used within the given calendar. If you omit the
value the 'gregorian' calendar will be used. Use
Zend_Date for simplicity
Day Returns a translation for a day. You have
to give the english abbreviation of the day
as string value ('sun', 'mon', etc.). If you
want to receive data for other calendars,
contexts or format, then you must give
an array instead of an integer with the
expected values. The array has to look like
this: array('calendar', 'context',
'format', 'day abbreviation'). If you
give only an string then the default values are
the 'gregorian' calendar, the context 'format'
and the format 'wide'. Use Zend_Date for
simplicity
Quarter Returns a translation for a quarter. You
have to give the number of the quarter as
integer and it has to be between 1 and
4. If you want to receive data for other
calendars, contexts or formats, then you must
give an array instead of an integer with
the expected values. The array has to look
like this: array('calendar', 'context',
'format', 'quarter number'). If you
give only an string then the default values are
the 'gregorian' calendar, the context 'format'
and the format 'wide'
Am Returns a translation for 'AM' in a expected
locale. If you want to receive data for other
calendars an string with the expected calendar.
If you omit the value then the 'gregorian'
calendar will be used. Use Zend_Date for
simplicity
Pm Returns a translation for 'PM' in a expected
locale. If you want to receive data for other
calendars an string with the expected calendar.
If you omit the value then the 'gregorian'
calendar will be used. Use Zend_Date for
simplicity
Era Returns a translation for an era within a locale.
You have to give the era number as string
or integer. If you want to receive data for
other calendars or formats, then you must give
an array instead of the era number with the
942
Zend_Locale
Type Description
expected values. The array has to look like this:
array('calendar', 'format', 'era
number'). If you give only an string then the
default values are the 'gregorian' calendar and
the 'abbr' format
DefaultDate Returns the default date format which is used
within the given calendar. If you omit the
value the 'gregorian' calendar will be used. Use
Zend_Date for simplicity
Date Returns the date format for an given calendar
or format within a locale. If you omit the value
then the 'gregorian' calendar will be used with
the 'medium' format. If you give a string then the
'gregorian' calendar will be used with the given
format. Or you can also give an array which will
have to look like this: array('calendar',
'format'). Use Zend_Date for simplicity
DefaultTime Returns the default time format which is used
within the given calendar. If you omit the
value the 'gregorian' calendar will be used. Use
Zend_Date for simplicity
Time Returns the time format for an given calendar
or format within a locale. If you omit the value
then the 'gregorian' calendar will be used with
the 'medium' format. If you give a string then the
'gregorian' calendar will be used with the given
format. Or you can also give an array which will
have to look like this: array('calendar',
'format'). Use Zend_Date for simplicity
DateTime Returns the datetime format for the given locale
which indicates how to display date with times
in the same string within the given calendar. If
you omit the value the 'gregorian' calendar will
be used. Use Zend_Date for simplicity
DateItem Returns the default format for a given date or
time item
DateInterval Returns the interval format for a given
date or time format. The first value is the
calendar format, normally 'gregorian'. The
second value is the interval format and the third
value the token with the greatest difference.
For example: array('gregorian', 'yMMMM', 'y')
returns the interval format for the date format
'yMMMM' where 'y' has the greatest difference.
Field Returns a translated date field which can be
used to display calendars or date strings like
'month' or 'year' in a wished language. You
must give the field which has to be returned
as string. In this case the 'gregorian' calendar
943
Zend_Locale
Type Description
will be used. You can get the field for other
calendar formats if you give an array which has
to look like this: array('calendar', 'date
field')
Relative Returns a translated date which is relative
to today which can include date strings like
'yesterday' or 'tomorrow' in a wished language.
You have to give the number of days relative
to tomorrow to receive the expected string.
Yesterday would be '-1', tomorrow '1' and so
on. This will use the 'gregorian' calendar. If you
want to get relative dates for other calendars
you will have to give an array which has to look
like this: array('calendar', 'relative
days'). Use Zend_Date for simplicity
DecimalNumber Returns the format for decimal numbers within
a given locale. Use Zend_Locale_Format for
simplicity
ScientificNumber Returns the format for scientific numbers within
a given locale
PercentNumber Returns the format for percentage numbers
within a given locale
CurrencyNumber Returns the format for displaying currency
numbers within a given locale. Use
Zend_Currency for simplicity
NameToCurrency Returns the translated name for a given
currency. The currency has to be given in
ISO format which is for example 'EUR' for
the currency 'euro'. Use Zend_Currency for
simplicity
CurrencyToName Returns a currency for a given localized name.
Use Zend_Currency for simplicity
CurrencySymbol Returns the used symbol for a currency within
a given locale. Not for all currencies exists a
symbol. Use Zend_Currency for simplicity
Question Returns a localized string for acceptance ('yes')
and negotation ('no'). You have to give either
'yes' or 'no' as value to receive the expected
string. Use Zend_Locale's getQuestion method
for simplicity
CurrencyFraction Returns the fraction to use for a given currency.
You must give the currency as ISO value. Use
Zend_Currency for simplicity
CurrencyRounding Returns how to round a given currency. You
must give the currency as ISO value. If you omit
the currency then the 'DEFAULT' rounding will
be returned. Use Zend_Currency for simplicity
944
Zend_Locale
Type Description
CurrencyToRegion Returns the currency for a given region. The
region code has to be given as ISO3166
string for example 'AT' for austria. Use
Zend_Currency for simplicity
RegionToCurrency Returns the regions where a currency is used.
The currency has to be given as ISO4217 code
for example 'EUR' for euro. When a currency
is used in multiple regions, these regions are
separated with a whitespace character. Use
Zend_Currency for simplicity
RegionToTerritory Returns the regions for a given territory. The
territory has to be given as ISO4217 string
for example '001' for world. The regions within
this territory are separated with a whitespace
character
TerritoryToRegion Returns the territories where a given region
is located. The region has to be given in
ISO3166 string for example 'AT' for austria.
When a region is located in multiple territories
then these territories are separated with a
whitespace character
ScriptToLanguage Returns the scripts which are used within a
given language. The language has to be given
as ISO language code for example 'en' for
english. When multiple scripts are used within a
language then these scripts are separated with
a whitespace character
LanguageToScript Returns the languages which are used within a
given script. The script has to be given as ISO
script code for example 'Latn' for latin. When a
script is used in multiple languages then these
languages are separated with a whitespace
character
TerritoryToLanguage Returns the territories where a given language
is used. The language has to be given
as ISO language code for example 'en' for
english. When multiple territories exist where
this language is used then these territories are
separated with a whitespace character
LanguageToTerritory Returns the languages which are used within
a given territory. The territory has to be given
as ISO3166 code for example 'IT' for italia.
When a language is used in multiple territories
then these territories are separated with a
whitespace character
TimezoneToWindows Returns a ISO timezone for a given windows
timezone
945
Zend_Locale
Type Description
WindowsToTimezone Returns a windows timezone for a given ISO
timezone
TerritoryToTimezone Returns the territory for a given ISO timezone
TimezoneToTerritory Returns the ISO timezone for a given territory
CityToTimezone Returns the localized city for a given ISO
timezone. Not for all timezones does a city
translation exist
TimezoneToCity Returns the ISO timezone for a given localized
city name. Not for all cities does a timezone
exist
PhoneToTerritory Returns the telephone code for a given territory
(region). The territory code has to be given as
ISO3166 string for example 'AT' for austria
TerritoryToPhone Returns the territory (region) where a telephone
code is used. The telephone code has to be
given as plain integer code for example '43'
for +43. When a telephone code is used in
multiple territories (regions), these territories
are separated with a whitespace character
NumericToTerritory Returns the 3 digit number code for a given
territory (region). The territory code has to be
given as ISO3166 string for example 'AT' for
austria
TerritoryToNumeric Returns the territory (region) for a 3 digit
number code. The 3 digit number code has to
be given as plain integer code for example '43'
Alpha3ToTerritory Returns the 3 sign character code for a given
territory (region). The territory code has to be
given as ISO3166 string for example 'AT' for
austria
TerritoryToAlpha3 Returns the territory (region) for a 3 sign
character code
PostalToTerritory Returns the a regex for postal codes for a
given territory. The territory has to be given as
ISO4217 string for example '001' for world
NumberingSystem Returns a scripts with the notation for digits
used within this script
FallbackToChar Returns a replacement character for a often
used unicode character. This can be used to
replace "©" with "(C)" for example
CharToFallback Returns a unicode character for a often used
replacement character. This can be used to
replace "(C)" with "©" for example
LocaleUpgrade Returns a locale dependencies for a given
language which can be used to upgrade this
language to a full qualified locale
946
Zend_Locale
Type Description
Unit Returns a localized calendar unit. This can be
used to translate the strings "day", "month" and
so on automatically. The first parameter has to
be the type, and the second parameter has to
be the count
With Zend Framework 1.5 several old types have been renamed. This has to
be done because of several new types, some misspelling and to increase the
usability. See this table for a list of old to new types:
The example below demonstrates how to obtain the names of things in different languages.
The next example shows how to find the name of a language in another language, when the two
letter iso country code is not known.
947
Zend_Locale
$code2name = Zend_Locale::getLanguageTranslationList('en_US');
$name2code = array_flip($code2name);
$frenchCode = $name2code['French'];
echo Zend_Locale::getLanguageTranslation($frenchCode, 'de_AT');
// output is the German name of the French language
$list = Zend_Locale::getLanguageTranslationList('auto');
• yes and no: A generic string representation for yes and no responses. This will contain the first
and most generic response from yesarray and noarray.
yesarray and noarray: An array with all known yes and no responses. Several languages have
more than just two responses. In general this is the full string and its abbreviation.
yesexpr and noexpr: An generated regex which allows you to handle user response, and
search for yes or no.
All of this informations are of course localized and depend on the set locale. See the following
example for the informations you can receive:
948
Zend_Locale
- - - Output - - -
Array
(
[yes] => ja
[no] => nein
[yesarray] => Array
(
[0] => ja
[1] => j
)
Until 1.0.3 yesabbr from the underlaying locale data was also available. Since 1.5
this information is no longer standalone available, but you will find the information
from it within yesarray.
$localelist = Zend_Locale::getLocaleList();
Note that the locales are returned as key of the array you will receive. The value
is always a boolean TRUE.
949
Zend_Locale
$input = 'to_RU';
if (Zend_Locale::isLocale($input)) {
print "'{$input}' is a locale";
} else {
print "Sorry... the given input is no locale";
}
As you can see, the output of this method is always a boolean. There is only one reason you
could get an exception when calling this method. When your system does not provide any locale
and Zend Framework is not able to detect it automatically. Normally this shows that there is a
problem with your OS in combination with PHP's setlocale().
You should also note that any given locale string will automatically be degraded if the region part
does not exist for this locale. In our previous example the language 'to' does not exist in the
region 'RU', but you will still get TRUE returned as Zend_Locale can work with the given input.
Still it's sometimes useful to prevent this automatic degrading, and this is where the second
parameter of isLocale() comes in place. The strict parameter defaults to FALSE and can
be used to prevent degrading when set to TRUE.
$input = 'to_RU';
if (Zend_Locale::isLocale($input, true)) {
print "'{$input}' is a locale";
} else {
print "Sorry... the given input is no locale";
}
Now that you are able to detect if a given string is a locale you could add locale aware behaviour
to your own classes. But you will soon detect that this will always leads to the same 15 lines of
code. Something like the following example:
With Zend Framework 1.8 we added a static findLocale() method which returns you a locale
string which you can work with. It processes the following tasks:
950
Zend_Locale
• Detects the locale from browser when the previous detections failed
• Detects the locale from environment when the previous detections failed
• Detects the locale from framework when the previous detections failed
The following example shows how these checks and the above code can be simplified with one
single call:
$locale = Zend_Locale::findLocale($inputstring);
The exact same string containing a number or a date might mean different things to people with
different customs and conventions. Disambiguation of numbers and dates requires rules about
how to interpret these strings and normalize the values into a standardized data structure. Thus,
all methods in Zend_Locale_Format require a locale in order to parse the input data.
951
Zend_Locale
Also, the precision of the resulting decimal representation can be rounded to a desired length
with getNumber() with the option 'precision'. If no precision is given, no rounding occurs.
Use only PHP integers to specify the precision.
$number = Zend_Locale_Format::getNumber('13.524,678',
array('number_format' => '#.00',
'locale' => $locale)
);
print $number; // will return 13524.67
Unlimited length
The same way as within getNumber(), toNumber() handles precision. If no precision is given,
the complete localized number will be returned.
952
Zend_Locale
Using the option 'number_format' a self defined format for generating a number can be defined.
The format itself has to be given in CLDR format as described below. The locale is used to get
separation, precision and other number formatting signs from it. German for example defines ','
as precision separation and in English the '.' sign is used.
$number = Zend_Locale_Format::toNumber(13547.3,
array('number_format' => '#,##0.00',
'locale' => 'de')
);
953
Zend_Locale
954
Zend_Locale
955
Zend_Locale
not to be confused with Eastern Arabic numerals sometimes used with the Arabic language to
express numerals. Attempts to use an unsupported numeral system will result in an exception,
to avoid accidentally performing an incorrect conversion due to a spelling error. All characters
in the input, which are not numerals for the selected numeral system, are copied to the output
with no conversion provided for unit separator characters. Zend_Locale* components rely on
the data provided by CLDR (see their list of scripts grouped by language).
In CLDR and hereafter, the Europena/Latin numerals will be referred to as "Latin" or by the
assigned 4-letter code "Latn". Also, the CLDR refers to this numeral systems as "scripts".
Suppose a web form collected a numeric input expressed using Eastern Arabic
digits "####". Most software and PHP functions expect input using Arabic numerals.
Fortunately, converting this input to its equivalent Latin numerals "100" requires little
effort using convertNumerals($inputNumeralString, $sourceNumeralSystem,
$destNumeralSystem) , which returns the $input with numerals in the script
$sourceNumeralSystem converted to the script $destNumeralSystem.
Similarly, any of the supported numeral systems may be converted to any other supported
numeral system.
Exemple 511. Converting numerals from Latin script to Eastern Arabic script
$latinScript = '123';
$arabicScript = Zend_Locale_Format::convertNumerals($latinScript,
'Latn',
'Arab');
Exemple 512. Getting 4 letter CLDR script code using a native-language name of
the script
956
Zend_Locale
value meaning
0 nothing to fix
1 fixed false month
2 swapped day and year
3 swapped month and year
4 swapped month and day
For those needing to specify explicitly the format of the date string, the following format token
specifiers are supported. If an invalid format specifier is used, such as the PHP 'i' specifier when
in ISO format mode, then an error will be thrown by the methods in Zend_Locale_Format that
support user-defined formats.
These specifiers (below) are a small subset of the full "ISO" set supported by Zend_Date's
toString(). If you need to use PHP date() compatible format specifiers, then first
call setOptions(array('format_type' => 'php')). And if you want to convert
only one special format string from PHP date() compatible format to "ISO" format use
convertPhpToIsoFormat(). Currently, the only practical difference relates to the specifier
for minutes ('m' using the ISO default, and 'i' using the PHP date format).
957
Zend_Locale
$dateString = Zend_Locale_Format::getDate('13.04.2006',
array('date_format' =>
'dd.MM.yyyy')
);
print_r($dateString); // outputs:
Array
(
[format] => dd.MM.yyyy
[day] => 13
[month] => 4
[year] => 2006
)
print_r($date); // outputs:
Array
(
[format] => dd.MM.yyyy
[day] => 13
[month] => 4
[year] => 2006
[fixed] => 4
)
Since getDate() is "locale-aware", specifying the $locale is sufficient for date strings
adhering to that locale's format. The option 'fix_date' uses simple tests to determine if the day
or month is not valid, and then applies heuristics to try and correct any detected problems. Note
the use of 'Zend_Locale_Format::STANDARD' as the value for 'date_format' to prevent the
958
Zend_Locale
use of a class-wide default date format set using setOptions(). This forces getDate to use
the default date format for $locale.
print_r ($date);
A complete date and time is returned when the input contains both a date and time in the expected
format.
print_r ($date);
If a specific format is desired, specify the $format argument, without giving a $locale. Only
single-letter codes (H, m, s, y, M, d), and MMMM and EEEE are supported in the $format.
$date = Zend_Locale_Format::getDate('13200504T551422',
array('date_format' =>
'ddyyyyMM ssmmHH')
);
print_r ($date);
959
Zend_Locale
The option 'fix_date' uses simple tests to determine if the day or month is not valid, and then
applies heuristics to try and correct any detected problems. getDate() automatically detects
and corrects some kinds of problems with input, such as misplacing the year:
$date = Zend_Locale_Format::getDate('41.10.20',
array('date_format' => 'ddMMyy',
'fix_date' => true)
);
960
Zend_Locale
5. Supported locales
Zend_Locale provides information on several locales. The following table shows all languages
and their related locales, sorted by language:
961
Zend_Locale
962
Zend_Locale
963
Zend_Locale
964
Zend_Locale
965
Zend_Locale
966
Zend_Locale
967
Zend_Locale
968
Zend_Locale
969
Zend_Locale
970
Zend_Locale
971
Zend_Locale
972
Zend_Log
1. Présentation
Zend_Log est un composant destiné à tous les usages du log. Il supporte l'écriture multiple
centralisée, formate les messages envoyés vers les logs, et les filtre. Ces fonctions sont divisées
en objets suivants :
• Un enregistreur (instance de Zend_Log) est l'objet que votre application emploie le plus. Vous
pouvez avoir autant d'objets d'enregistreur que vous voulez ; ils n'agissent pas l'un sur l'autre.
Un objet enregistreur doit contenir au moins un rédacteur (Writer), et peut facultativement
contenir un ou plusieurs filtres.
$logger->addWriter($redacteur);
Il est important de noter que l'enregistreur doit avoir au moins un rédacteur. Vous pouvez ajouter
tout nombre de rédacteurs en utilisant la méthode addWriter().
Le premier paramètre de la méthode log() est une chaîne message et le deuxième paramètre
est une priority fourni en nombre entier. La priorité doit être l'une des priorités identifiées par
l'instance de l'enregistreur. Ceci est expliqué dans la prochaine section.
973
Zend_Log
Un raccourci est également disponible. Au lieu d'appeler la méthode log(), vous pouvez
appeler une méthode par le même nom que la priorité :
$logger = null;
Explicitement détruire le log de cette façon est facultatif et est exécuté automatiquement à la
fermeture de PHP.
Ces priorités sont toujours disponibles, et une méthode de convenance de même nom est
disponible pour chacun.
Les priorités ne sont pas arbitraires. Elles viennent du protocole BSD syslog, qui est décrit dans
la RFC-3164. Les noms et les niveaux de priorité correspondants sont également compatibles
avec un autre système de log de PHP, PEAR Log, ce qui favorise l'interopérabilité entre lui et
Zend_Log.
Les numéros de priorité descendent par ordre d'importance. EMERG (0) est la priorité la plus
importante. DEBUG (7) est la priorité la moins importante des priorités intégrées. Vous pouvez
définir des priorités d'importance inférieure que DEBUG. En choisissant la priorité pour votre
message de log, faîtes attention à cette hiérarchie prioritaire et choisissez convenablement.
$logger->addPriority('ESSAI', 8);
974
Zend_Log
L'extrait ci-dessus crée une nouvelle priorité, ESSAI, dont la valeur est 8. La nouvelle priorité
est alors disponible pour l'enregistreur :
$logger->setEventItem('pid', getmypid());
L'exemple ci-dessus place un nouvel élément nommé pid et lui donne comme valeur le PID du
processus courant. Une fois qu'un nouvel élément a été placé, il est disponible automatiquement
pour tous les rédacteurs avec toutes les autres données d'événement pendant l'enregistrement.
Un élément peut être surchargé à tout moment en appelant une nouvelle fois la méthode
setEventItem().
Le réglage d'un nouvel élément d'événement avec setEventItem() entraîne que le nouvel
élément sera envoyé à tous les rédacteurs de l'enregistreur. Cependant, ceci ne garantit pas
que les rédacteurs utilisent réellement l'élément. C'est parce que les rédacteurs ne sauront pas
quoi faire avec lui à moins qu'un objet formateur soit informé du nouvel élément. Veuillez vous
reporter à la section sur des formateurs pour en apprendre davantage.
2. Rédacteurs (Writers)
Un rédacteur est un objet qui hérite de Zend_Log_Writer_Abstract. La responsabilité d'un
rédacteur est d'enregistrer des données de log dans un stockage particulier.
Pour écrire des données de log dans le buffer d'affichage de PHP, il faut utiliser l'URL php://
output. Alternativement, vous pouvez préférer envoyer des données de log directement à un
flux comme STDERR (php://stderr).
$logger->info("Message d'information");
975
Zend_Log
Pour écrire des données dans un fichier, employer un des Filesystem URLs:
$logger->info("Message d'information");
Par défaut, le flux s'ouvre en mode d'ajout ("a"). Pour l'ouvrir avec un mode différent, le
constructeur de Zend_Log_Writer_Stream accepte un deuxième paramètre facultatif pour
le mode.
$logger->info("Message d'information");
Vous ne pouvez pas indiquer le mode pour des ressources existantes de flux. Le faire entraînera
une Zend_Log_Exception.
$logger->info("Message d'information");
L'exemple ci-dessus écrit une ligne unique de données de log dans la table appelée
nom_de_la_table_de_log. La colonne de base de données appelée niveau reçoit le niveau
de priorité et la colonne appelée msg reçoit le message de log.
976
Zend_Log
Éléments requis :
• L'extension Firefox nommée Firebug qui peut être téléchargée à cette adresse https://
addons.mozilla.org/en-US/firefox/addon/1843.
• L'extension Firefox nommée FirePHP ui peut être téléchargée à cette adresse https://
addons.mozilla.org/en-US/firefox/addon/6149.
977
Zend_Log
$logger->addPriority('FOO', 8);
$writer->setPriorityStyle(8, 'TRACE');
$logger->foo('Foo Message');
Le style par défaut pour les priorités définies par l'utilisateur peut être paramétrer avec la méthode
setDefaultPriorityStyle().
$writer->setDefaultPriorityStyle('TRACE');
978
Zend_Log
Toute variable PHP peut être journalisée avec les priorités incorporées, un formatage spécial
est requis si vous utilisez des styles d'historisation un peu plus spécialisé.
Les styles LOG, INFO, WARN, ERROR et TRACE ne requièrent pas de formatage spécial.
Vous pouvez aussi journaliser des données en les formatant comme un tableau. Les colonnes
sont automatiquement reconnues et la première ligne de données devient automatiquement la
ligne d'en-têtes.
$writer->setPriorityStyle(8, 'TABLE');
$logger->addPriority('TABLE', 8);
979
Zend_Log
// Essai
$log->error('unable to connect to database');
Zend_Log_Writer_Mail utilisera un corps de message en texte plein (plain text) par défaut.
Le filtre est géré. Par exemple si le filtre est réglé sur Warnings, et que 2 évènements warnings
et 5 évènements erreurs se produisent, alors 7 évènements seront envoyés.
// essai
$log->error('unable to connect to database');
980
Zend_Log
2.4.3. Attention
Envoyer des rapports d'erreurs par emails peut être dangereux. Si votre système de surveillance
d'erreurs n'est pas correct ou a un problème, vous risquez de vous retrouver inondé de tonnes
d'emails en provenance de votre application.
Encore une fois, l'unique but de Zend_Log_Writer_Mail est la notification d'un humain au
sujet d'une erreur. Si ce système est clairement contrôlé, alors il peut devenir un avantage très
appréciable.
Par défaut, tous les messages gérés sont préfixés par "Zend_Log". Si vous souhaitez changer
ce nom, utilisez le constructeur ou l'accesseur:
// A l'instanciation
$writer = new Zend_Log_Writer_Syslog(array('application' => 'FooBar'));
// Plus tard:
$writer->setApplicationName('BarBaz');
Le journal système vous aidera aussi à identifier les messages par types d'application ("facility"),
les programmes de journalisation système vont générer des fichiers journaux différents en
fonction des types d'application, ce qui là encore, peut aider dans l'administration.
Pour spécifier le type d'application, utilisez le constructeur ou l'accesseur. Cette option peut être
l'une des constantes utilisées par openlog(), définies dans la page du manuel de openlog().
// A l'instanciation
$writer = new Zend_Log_Writer_Syslog(array('facility' => LOG_AUTH));
// Plus tard
$writer->setFacility(LOG_USER);
En utilisant l'objet de log, continuez d'utiliser les constantes de Zend_Log, elles vont être
converties en leurs valeurs par syslog().
981
Zend_Log
Une caractéristique particulière de l'API Monitor est que vous pouvez spécifier n'importe quelle
information dans le journal. Par exemple, journaliser une exception est possible en journalisant
tout l'objet Exception d'un coup et pas juste son message. L'objet sera alors visible et analysable
via le moniteur d'évènement de Zend Server.
Pour utiliser cet objet d'écriture, Zend Monitor doit petre installé et activé. Si ce
n'est pas le cas, alors l'objet d'écriture agira de manière transparente et ne fera
rien du tout.
$log->info('Voici un message');
Vous pouvez ajouter des informations à journaliser, passez les comme second paramètre:
Ce deuxième paramètre peut être de type scalaire, objet, ou tableau; si vous souhaitez passer
plusieurs informations d'un seul coup, utilisez un tableau.
982
Zend_Log
983
Zend_Log
Sur cette image, les deux premiers évènements listés sont des évènements personnalisés
enregistré via l'objet d'écriture ZendMonitor. Cliquez alors sur un évènement pour voir toutes
ses informations.
984
Zend_Log
985
Zend_Log
Cliquer sur le sous menu "Personnalisé"(Custom) montre les détails, c'est à dire ce que vous
avez passé comme deuxième argument à la méthode de journalisation. Cet information est
enregistrée sous la clé info; et vous pouvez voir que l'objet de requête a été enregistré dans
cet exemple.
Par défaut, les commandes zf.sh et zf.bat ajoute une configuration pour la
ressource 'log' deZend_Application , elle inclut la configuration pour l'objet
d'écriture du journal ZendMonitor. Aussi, le ErrorController utilise ce
journal pour journaliser les exceptions de l'application.
Comme dit précedemment, si l'API de Zend Monitor API n'est pas détectée sur
votre installation de PHP, alors le journal ne fera rien du tout.
// va nulle part
$logger->info("Message d'information");
$logger->info("Message d'information");
var_dump($mock->events[0]);
// Array
// (
// [timestamp] => 2007-04-06T07:16:37-07:00
// [message] => Message d'information
// [priority] => 6
// [priorityName] => INFO
// )
Pour effacer les événements notés dans le simulacre, il faut simplement réaliser $simulacre-
>events = array().
986
Zend_Log
$redacteur1 =
new Zend_Log_Writer_Stream('/chemin/vers/premier/fichierdelog');
$redacteur2 =
new Zend_Log_Writer_Stream('/chemin/vers/second/fichierdelog');
Quelques rédacteurs ("Writers") ne fonctionnent pas en terme de ligne et ne peuvent pas utiliser
un formateur. Un exemple est le rédacteur de base de données, qui insère les items d'événement
directement en colonnes de base de données. Pour les rédacteurs qui ne peuvent pas supporter
un formateur, une exception est levée si vous essayez d'affecter un formateur.
$logger->info('Monde');
987
Zend_Log
$logger->info("Message d'information");
Le code ci-dessus affiche le XML suivant (des espaces supplémentaires sont ajoutés pour la
clarté) :
<logEntry>
<timestamp>2007-04-06T07:24:37-07:00</timestamp>
<message>Message d'information</message>
<priority>6</priority>
<priorityName>INFO</priorityName>
</logEntry>
Il est possible d'adapter l'élément racine comme indiquent faire correspondre les éléments XML
au tableau de données d'évènements. Le constructeur de Zend_Log_Formatter_Xml accepte
une chaîne avec le nom de l'élément racine comme premier paramètre et un tableau associatif
avec les éléments de correspondance comme deuxième paramètre :
$logger->info("Message d'information");
Le code ci-dessus change l'élément racine par défaut de logEntry en log. Il fait correspondre
également les éléments msg au message de l'item de donnée d'événement. Ceci a comme
conséquence l'affichage suivant :
<log>
<msg>Message d'information</msg>
<niveau>INFO</niveau>
</log>
4. Filtres
Un objet Filter bloque les messages avant l'écriture dans le log.
988
Zend_Log
// bloqué
$logger->info("Message d'information");
Quand vous ajoutez un ou plusieurs filtres à l'objet enregistreur, le message doit passer par tous
les filtres avant que tous les rédacteurs le reçoive.
$redacteur1 =
new Zend_Log_Writer_Stream('/chemin/vers/premier/fichierdelog');
$logger->addWriter($redacteur1);
$redacteur2 =
new Zend_Log_Writer_Stream('/chemin/vers/second/fichierdelog');
$logger->addWriter($redacteur2);
Un exemple:
$logger = Zend_Log::factory(array(
array(
'writerName' => 'Stream',
'writerParams' => array(
'stream' => '/tmp/zend.log',
),
'filterName' => 'Priority',
'filterParams' => array(
989
Zend_Log
Le code ci-dessus instancie un objet journal et 2 objets d'écriture dont un vers un fichier local,
puis un vers Firebug. Chacun possède un filtre.
filterName (optionnel) Le nom "court" d'un filtre à utiliser sur l'objet d'écriture.
Voyez "filterNamespace" pour plus de détails. Exemples:
"Message", "Priority".
columnMap Tableau associatif de correspondance entre les noms des colonnes de la table et
les champs des évènements du journal.
990
Zend_Log
991
Zend_Log
$default = array(
'bar' => null,
'baz' => null,
);
$config = array_merge($default, $config);
Aussi, il est possible d'appeler les setters après l'instanciation, mais avant de retourner l'instance:
992
Zend_Log
993
Zend_Mail
1. Introduction
1.1. Pour commencer
Zend_Mail fournit des fonctionnalités génériques pour écrire et envoyer des émail au format
texte et MIME. Un émail peut-être envoyé avec Zend_Mail via le transporteur par défaut
Zend_Mail_Transport_Sendmail ou via Zend_Mail_Transport_Smtp.
Un émail simple est composé d'un destinataire, d'un sujet, d'un message et d'un expéditeur.
Pour envoyer ce genre de messages en utilisant Zend_Mail_Transport_Sendmail,
vous pouvez faire comme ceci :
Définitions minimales
Pour envoyer un émail avec Zend_Mail, vous devez spécifier au moins un
destinataire, un expéditeur (avec setFrom()), et un message (texte et/ou
HTML).
Pour la plupart des attributs de l'émail, il y a des méthodes "get" pour lire les informations
stockées dans l'objet mail. Pour plus de détails, merci de vous référer à la documentation de
l'API. Une méthode spéciale est getRecipients(). Elle retourne un tableau avec toutes les
adresses émail des destinataires qui ont été ajoutés avant l'appel de cette méthode.
Pour des raisons de sécurité, Zend_Mail filtre tous les champs d'en-tête pour éviter tout
problème d'injection d'en-têtes avec des caractères de nouvelles lignes (\n). Les guillemets
doubles sont changés en guillemets simples et les crochets en parenthèses dans le nom des
émetteurs et des destinataires. Si ces caractères sont dans l'adresse émail, ils sont enlevés.
Vous pouvez aussi utiliser la plupart des méthodes de l'objet Zend_Mail via une interface fluide.
994
Zend_Mail
mail(). Si vous souhaitez fournir des paramètres additionnels à la fonction mail(), créez
simplement une nouvelle instance du transporteur et fournissez vos paramètres au constructeur.
La nouvelle instance du transporteur peut ainsi devenir le transporteur par défaut Zend_Mail,
ou il peut être fourni à la méthode send() de Zend_Mail.
Ainsi si vous voulez utiliser BCC sur un serveur Windows, utilisez le transport
SMTP pour l'envoi !
995
Zend_Mail
Optionally, you can also define a default From email address and name, as well as a default
reply-to header. This can be done through the static methods setDefaultFrom() and
setDefaultReplyTo(). These defaults will be used when you don't specify a From/Reply-
to Address or -Name until the defaults are reset (cleared). Resetting the defaults can be done
through the use of the clearDefaultFrom() and clearDefaultReplyTo.
// Créer un transport
$config = array('name' => 'sender.example.com');
$transport = new Zend_Mail_Transport_Smtp('mail.example.com', $config);
// Ajouter les nom et adresses "From" & "Reply-To" pour tous les émails
// à envoyer
Zend_Mail::setDefaultFrom('sender@example.com', 'John Doe');
Zend_Mail::setDefaultReplyTo('replyto@example.com','Jane Doe');
Si vous voulez avoir une connexion SMTP séparée pour chaque distribution d'émail, vous devez
créer et détruire votre transport avant et après chaque appel de la méthode send(). Ou sinon,
vous pouvez manipuler la connexion entre chaque distribution en accédant à l'objet de protocole
de transport.
996
Zend_Mail
// Créer un transport
$transport = new Zend_Mail_Transport_Smtp();
$transport->setConnection($protocol);
$protocol->quit();
$protocol->disconnect();
Transports additionnels
5. Émail HTML
Pour envoyer un émail au format HTML, définissez le corps du message en utilisant la
méthode setBodyHTML() à la place de setBodyText(). Le type de contenu MIME sera
997
Zend_Mail
automatiquement définit à text/html. Si vous utilisez les formats textes et HTML, un message
MIME de type multipart/alternative sera automatiquement généré :
6. Fichiers joints
Des fichiers peuvent-être attachés à un émail en utilisant la méthode createAttachment().
Le comportement par défaut de Zend_Mail est de définir que le fichier joint est un objet
binaire (application/octet-stream), qui devra être transféré avec un encodage de type base64,
et est définit comme une pièce jointe. Ce comportement peut être redéfinit en passant plus de
paramètres à createAttachment() :
Si vous voulez contrôler la partie MIME générée pour un fichier joint, vous pouvez utiliser
la valeur retournée de createAttachment() pour modifier ses attributs. La méthodes
createAttachment() retourne un objet de type Zend_Mime_Part :
$at = $mail->createAttachment($monImage);
$at->type = 'image/gif';
$at->disposition = Zend_Mime::DISPOSITION_INLINE;
$at->encoding = Zend_Mime::ENCODING_8BIT;
$at->filename = 'test.gif';
$mail->send();
Une façon alternative est de créer une instance de Zend_Mime_Part et de l'ajouter avec la
méthode addAttachment() :
998
Zend_Mail
$mail->addAttachment($at);
$mail->send();
Paramètres additionnels
addTo() et addCc() acceptent un second paramètre optionnel, qui est utilisé
comme un nom de destinataire humainement lisible. Le guillemet double est
changé en simple guillemet et les crochets en parenthèses dans le paramètre.
Utilisation optionnelle
Ces trois méthodes peuvent aussi accepter un tableau d'adresses émails plutôt
que de les ajouter une par une. Dans le cas de addTo() et addCc(), il peut
s'agir de tableaux associatifs où la clé est un nom de destinataire humainement
lisible.
9. En-têtes additionnels
Zend_Mail provides several methods to set additional Mail Headers:
• setDate($date = null): sets the Date: header. This method uses current time stamp by
default. Or You can pass time stamp, date string or Zend_Date instance to this method.
• setMessageId($id = true): sets the Message-Id: header. This method can generate
message ID automatically by default. Or You can pass your message ID string to this method.
This method call createMessageId() internally.
999
Zend_Mail
Return-Path
If you set Return-Path on your mail, see Configuring sendmail transport.
Unfortunately, setReturnPath($email) method does not perform this
purpose.
Des en-têtes arbitraires peuvent être définis en utilisant la méthode addHeader(). Elle a besoin
de deux paramètres contenant le nom et la valeur du champ d'en-tête. Un troisième paramètre
optionnel détermine si l'en-tête doit avoir une ou plusieurs valeurs :
The following example is how to use Zend_Mail in Japanese. This is one of CJK (aka
CJKV ) languages. If you use Chinese, you may use HZ-GB-2312 instead of ISO-2022-JP.
11. Encodage
Par défaut, le corps des messages textes et HTML est encodé via le mécanisme "quoted-
printable". Les en-têtes du message sont aussi encodés avec le mécanisme "quoted-printable"
si vous ne spécifiez pas base64 avec setHeaderEncoding(). Si vous utilisez une langue
qui n'est pas sur des lettres de type romaines, la base64 sera plus convenable. Tous les
fichiers joints sont encodés via base64 si aucun autre encodage n'est spécifié lors de l'appel à
1000
Zend_Mail
addAttachment() ou assigné plus tard à la partie MIME de l'objet. Les encodages 7Bit et 8Bit
ne se font pour l'instant que sur les données binaires.
L'encodage des en-têtes, spécialement l'encodage du sujet, est toujours délicat. Zend_Mime
implémente actuellement son propre algorithme pour encoder les en-têtes "quoted-printable"
suivant la RFC-2045. Ceci est du à un problème des fonctions iconv_mime_encode et
mb_encode_mimeheader avec certaines tables de caractères. Cet algorithme ne coupe les en-
têtes qu'au niveau des espaces, ce qui peut entraîner des problèmes quand la longueur excède
la longueur de 76 caractères. Dans ce cas, il est suggéré de basculer en encodage BASE64
pour les en-têtes comme décrit dans les exemples suivants :
Type d'authentification
Le type d'authentification est sensible à la casse mais ne contient pas de
ponctuation. Par exemple, pour utiliser CRAM-MD5 vous devez passer 'auth'
=> 'crammd5' dans le constructeur de Zend_Mail_Transport_Smtp.
1001
Zend_Mail
Si vous voulez lire un fichier Mbox, vous devez juste donner le nom du fichier au constructeur
de Zend_Mail_Storage_Mbox:
1002
Zend_Mail
$mail =
new Zend_Mail_Storage_Mbox(array('filename' => '/home/test/mail/inbox'));
$mail =
new Zend_Mail_Storage_Maildir(array('dirname' => '/home/test/mail/'));
// connexion à Pop3
$mail = new Zend_Mail_Storage_Pop3(array('host' => 'exemple.com'
'user' => 'test',
'password' => 'test'));
// connexion à Imap
$mail = new Zend_Mail_Storage_Imap(array('host' => 'exemple.com'
'user' => 'test',
'password' => 'test'));
Pour ces deux stockages SSL et TLS sont supportés. Si vous utilisez SSL le port par défaut
change comme indiqué dans la RFC.
// utiliser TLS
$mail = new Zend_Mail_Storage_Pop3(array('host' => 'exemple.com'
'user' => 'test',
'password' => 'test',
'ssl' => 'TLS'));
1003
Zend_Mail
$message = $mail->getMessage($numeroDeMessage);
L'accès sous forme de tableau est aussi supporté, mais cet méthode d'accès ne supporte pas les
paramètres additionnels qui aurait pu être ajouté à getMessage(). Tant que vous n'en n'avez
pas besoin et que vous pouvez vivre avec les valeurs par défaut, vous pouvez utiliser :
$message = $mail[$numeroDeMessage];
Pour compter les messages dans le stockage, vous pouvez soit utiliser la méthode
countMessages() ou utiliser l'accès de type tableau :
// par méthode
$maxMessage = $mail->countMessages();
// type tableau
$maxMessage = count($mail);
Pour supprimer un mail vous pouvez utiliser la méthode removeMessage() ou l'accès de type
tableau :
// méthode
$mail->removeMessage($numeroDeMessage);
// type tableau
unset($mail[$messageNum]);
1004
Zend_Mail
$type = $message->contentType;
Si vous avez plusieurs en-têtes avec le même nom, par exemple les en-têtes "Received", vous
pourriez les vouloir sous la forme d'un tableau plutôt qu'en tant que chaîne. Ceci est possible
avec la méthode getHeader().
La méthode getHeaders() retourne tous les headers sous forme de tableau avec des clés en
minuscules et des valeurs en tant que tableau pour des en-têtes multiples ou une chaîne pour
des en-têtes uniques.
Si vous n'avez pas de message de type multipart, extraire le contenu est facilité avec
getContent(). A la différence des en-têtes, le contenu est seulement extrait en cas de besoin
(alias late-fetch).
Vérifier si un message est de type multipart est réalisé avec la méthode isMultipart().
Si vous avez un message de type multipart vous pouvez récupérer une instance de
1005
Zend_Mail
1006
Zend_Mail
// ...
// vérifie d'autres drapeaux
// ...
default:
echo $flag . '(drapeau inconnu) ';
}
}
Comme IMAP autorise les drapeaux définis par client ou l'utilisateur, vous pouvez obtenir ces
drapeaux même s'ils n'ont pas de constante dans Zend_Mail_Storage. Au lieu de cela ils sont
retournés comme une chaîne et peuvent être vérifiés de la même manière avec hasFlag().
Pour les stockages locaux vous devez employer les classes séparées appelées
Zend_Mail_Storage_Folder_Mbox ou Zend_Mail_Storage_Folder_Maildir. Tous
les deux ont besoin d'un paramètre nommé dirname avec le nom du dossier de base. Le format
pour le maildir est comme définie dans maildir++ (avec un point comme délimiteur par défaut),
Mbox est une hiérarchie de dossiers avec des fichiers Mbox. Si vous n'avez pas un dossier de
Mbox appelé INBOX dans votre dossier de base Mbox vous devez placer un autre dossier dans
le constructeur.
Zend_Mail_Storage_Imap supporte déjà des dossiers par défaut. Exemples pour ouvrir ces
stockages :
1007
Zend_Mail
Si vous employez l'itérateur, la clé de l'élément courant est le nom local. Le nom global est
également retourné par la méthode magique __toString(). Quelques dossiers peuvent ne
pas être sélectionnables, ce qui veut dire qu'ils ne peuvent pas stocker des messages et les
choisir entraînerait une erreur. Ceci peut être vérifié avec la méthode isSelectable(). Ainsi
il est très facile de produire l'arbre entier dans une vue :
1008
Zend_Mail
Les dossiers choisis courants sont retournés par la méthode getSelectedFolder(). Changer
de dossier est fait avec la méthode selectFolder(), qui a besoin du nom global comme
paramètre. Si vous voulez éviter d'écrire des délimiteurs vous pouvez également employer les
propriétés d'une instance de Zend_Mail_Storage_Folder :
// faire qqch...
1009
Zend_Mail
$signal_file = '/home/test/.mail.last_change';
$mbox_basedir = '/home/test/mail/';
$cache_id = 'exemple de mail en cache ' . $mbox_basedir . $signal_file;
$cache->set($cache_id, $mail);
Si vous avez besoin de fonctionnalités additionnelles de protocole vous pouvez étendre la classe
de protocole et l'employer dans le constructeur de la classe principale. Supposer par exemple
que nous devons joindre différents ports avant que nous puissions nous relier à POP3.
Zend_Loader::loadClass('Zend_Mail_Storage_Pop3');
1010
Zend_Mail
// initialise le parent
parent::__construct($protocol);
}
}
Comme vous voyez nous supposons toujours que nous sommes reliés, identifiés et, si supporté,
un dossier est choisi dans le constructeur de la classe principale. Ainsi si vous assignez votre
propre classe de protocole vous devez toujours vous assurer que c'est fait ou la prochaine
méthode échouera si le serveur ne la permet pas dans l'état actuel.
1011
Zend_Mail
$quota = $mail->checkQuota(true);
echo 'Vous êtes ',
$quota['over_quota'] ? 'hors quota' : 'dans le quota',
"\n";
echo 'Vous avez ',
$quota['count'],
' de ',
$quota['quota']['count'],
' messages et vous utilisez ';
echo $quota['size'], ' de ', $quota['quota']['size'], ' octets';
Si vous voulez spécifier votre propre quota plutôt que d'utiliser celui spécifié dans le fichier de
contrôle de la taille du dossier de mails, vous pouvez le faire avec setQuota() :
Pour ajouter vos propres vérifications de quota, utilisez des caractères uniques en tant que
clé et ils seront préservés (mais évidemment non vérifié). Il est aussi possible d'étendre
Zend_Mail_Storage_Writable_Maildir pour définir votre propre quota seulement si le
fichier de contrôle de la taille du dossier de mails est absent (qui peut se produire dans Maildir
++) :
1012
Zend_Markup
1. Introduction
The Zend_Markup component provides an extensible way for parsing text and rendering
lightweight markup languages like BBcode and Textile. It is available as of Zend Framework
version 1.10.
Ensuite, nous utiliserons la méthode render() afin de convertir un bout de BBCode vers
du HTML.
1013
Zend_Markup
$bbcode = Zend_Markup::factory('Bbcode');
$input = <<<EOT
[list]
[*]Zend Framework
[*]Foobar
[/list]
EOT;
echo $bbcode->render($input);
/*
Devrait afficher quelque chose comme:
<ul>
<li>Zend Framework</li>
<li>Foobar</li>
</ul>
*/
En plus d'analyser et rendre des codes de type BBCode, Zend_Markup peut aussi traiter
les entrées non valides. La plupart des processeurs de BBCode ne sont pas capables de
rendre toute l'entrée sous forme de XHTML valide. Zend_Markup corrige les entrées non
valides et ajoute des tags de fermetures si nécessaire:
$bbcode = Zend_Markup::factory('Bbcode');
[b]foo[i]bar[/i][/b]baz
• [b]
• foo
• [i]
• bar
1014
Zend_Markup
• baz
Notez que les tags de fermeture n'existent plus dans l'arbre généré. Ceci car ils n'ont pas de
valeur particulière pour la sémantique, ils ne sont pas perdus mais stockés grâce au seul tag
d'ouverture. Notez aussi que ceci n'est qu'une vue simplifiée de l'arbre réel qui contient en réalité
bien plus d'informations comme les attributs éventuels du tag et son nom.
[name(=(value|"value"))( attribute=(value|"value"))*]
[b]
[list=1]
[code file=Zend/Markup.php]
[url="http://framework.zend.com/" title="Zend Framework!"]
Par défaut, tous les tags sont fermés avec la syntaxe '[/tagname]'.
Entrée Sortie
*foo* <strong>foo</strong>
_foo_ <em>foo</em>
??foo?? <cite>foo</cite>
-foo- <del>foo</del>
+foo+ <ins>foo</ins>
^foo^ <sup>foo</sup>
~foo~ <sub>foo</sub>
%foo% <span>foo</span>
PHP(PHP Hypertext Preprocessor) <acronym title="PHP Hypertext
Preprocessor">PHP</acronym>
"Zend Framework":http:// <a href="http://framework.zend.com/">Zend
framework.zend.com/ Framework</a>
h1. foobar <h1>foobar</h1>
h6. foobar <h6>foobar</h6>
!http://framework.zend.com/images/logo.gif! <img src="http://framework.zend.com/images/
logo.gif" />
L'analyseur Textile encapsule tous les tags dans des paragraphes; un paragraphe se termine par
deux nouvelles lignes, et s'il y a des tags supplémentaires, un nouveau paragraphe sera ajouté.
1015
Zend_Markup
3.3.1. Listes
L'analyseur Textile supporte aussi deux types de listes. Le type numérique, utilisant le caractère
"#" et le type anonyme qui utilise lui l'étoile "*". Exemple des deux listes:
# Item 1
# Item 2
* Item 1
* Item 2
Notez que créer vos propres tags n'est utile que si l'analyseur syntaxique en tient compte.
Actuellement, seul BBCode supporte cela. Textile ne supporte pas les tags personnalisés.
Certains moteurs de rendu (comme le moteur HTML) supporte un paramètre nommé 'tag'. Cela
remplace les paramètres 'start' et 'end', et il rend le tag avec des attributs par défaut ainsi que
le tag de fin.
1016
Zend_Markup
Il est possible maintenant d'ajouter le tag 'upper', avec comme fonction de rappel, une instance
de My_Markup_Renderer_Html_Upper. Voici un exemple:
1017
Zend_Markup
1018
Zend_Measure
1. Introduction
Les classes Zend_Measure_* fournissent un moyen générique et facile de travailler avec les
mesures. En utilisant des classes de Zend_Measure_*, vous pouvez convertir des mesures en
différentes unités du même type. Elles peuvent être ajoutées, soustraites et comparées les uns
contre les autres. À partir d'une entrée donnée faite dans la langue maternelle de l'utilisateur,
l'unité de la mesure peut être automatiquement extraite. Des unités de mesure supportées sont
nombreuses.
Le mètre est utilisé pour mesurer des longueurs, ainsi son type constante peut être trouvé
dans la classe des longueurs. Pour se rapporter à cette unité de mesure, la notation
Length::METER doit être utilisée. L'unité de visualisation est m.
echo Zend_Measure_Length::STANDARD;
// affiche 'Length::METER'
echo Zend_Measure_Length::KILOMETER;
// affiche 'Length::KILOMETER'
1019
Zend_Measure
chaîne sans unités, ou une chaîne régionale avec une (des) unité(s) spécifiée(s). Le
deuxième paramètre définit le type de la mesure. Les deux paramètres sont obligatoires. La
langue peut être indiquée comme troisième paramètre optionnel.
$mesure = 1234.7;
$unite = new Zend_Measure_Length((integer)$mesure,
Zend_Measure_Length::STANDARD);
echo $unite;
// affiche '1234 m' (mètres)
$machaine = "10010100111010111010100001011011101010001";
$unit = new Zend_Measure_Number($machaine, Zend_Measure_Number::BINARY);
echo $unit;
1020
Zend_Measure
$machaine = "1,234.50";
$unite = new Zend_Measure_Length($machaine,
Zend_Measure_Length::STANDARD, 'en_US');
echo $unite; // affiche "1234.50 m"
Récupération automatique
Affichage de la mesure
L'affichage peut être réalisé simplement en utilisant echo ou print.
1021
Zend_Measure
• Convertir
• Ajouter et soustraire
1022
Zend_Measure
4.1. Convertir
Le dispositif le plus important est probablement la conversion dans différentes unités de la
mesure. La conversion d'une unité peut être faite à tout moment en utilisant la méthode
convertTo(). Des unités de mesure peuvent seulement être converties en d'autres unités
du même type (classe). Par conséquent, il n'est pas possible de convertir (par exemple : une
longueur en poids), qui encouragerait des pratiques de programmation pauvres et entraînerait
la propagation d'erreurs sans lever d'exception.
La méthode convertTo accepte un paramètre facultatif. Avec ce paramètre vous pouvez définir
une précision pour l'élément retourné. La précision par défaut est "2".
Conversion automatique
1023
Zend_Measure
if ($unite1->equals($unite2)) {
print "Les mesures sont identiques";
} else {
print "Les mesures sont différentes";
}
// affiche "Les mesures sont différentes"
$unite2->setType(Zend_Measure_Length::CENTIMETER);
if ($unite1->equals($unite2)) {
print "Les mesures sont identiques";
} else {
print "Les mesures sont différentes";
} // affiche "Les mesures sont identiques"
1024
Zend_Measure
$unite->setValue(1.2);
echo $unite; // affiche "1.2 m"
$unite->setValue(1.2, Zend_Measure_Length::KILOMETER);
echo $unite; // affiche "1200 km"
$unite->setValue("1.234,56", Zend_Measure_Length::MILLIMETER,$locale);
echo $unite; // affiche "1234.56 mm"
$unite->setType(Zend_Measure_Length::KILOMETER);
echo $unite; // affiche "1000 km"
5. Types de mesures
Tous les types de mesures supportées sont listées ci-dessous, chacun avec une exemple
d'utilisation standard pour ce type de mesure.
1025
Zend_Measure
1026
Zend_Measure
1027
Zend_Measure
Dans la classe BINARY les deux notations peuvent être trouvées, comme KILOBYTE =
1024 - binary computer conversion KIBIBYTE = 1024 - new notation
KILO_BINARY_BYTE = 1024 - new, ou la notation, en format long KILOBYTE_SI = 1000
- SI notation for kilo (1000). Les DVDs par exemple sont identifiés par la SI-notation,
mais presque tous les disques durs sont marqués dans la numérotation binaire des ordinateurs.
1028
Zend_Measure
$valeur_grande = '_X';
$locale = new Zend_Locale('en');
$unite = new Zend_Measure_Number($valeur_grande,
Zend_Measure_Number::ROMAN,
$locale);
1029
Zend_Memory
1. Présentation
1.1. Introduction
Le composant Zend_Memory est destiné à gérer des données dans un environnement où la
mémoire est limitée.
Les objets mémoire (conteneurs de mémoire) sont produits par le manager de mémoire sur
demande et mis en cache/chargés d'une manière transparente quand c'est nécessaire.
Par exemple, si la création ou le chargement d'un objet entraîne une utilisation de mémoire totale
excédant la limite que vous spécifiez, certains objets gérés sont copiés en cache à l'extérieur
de la mémoire. De cette façon, la mémoire totale utilisée par les objets gérés n'excède pas la
limite que vous devez mettre en application.
$backendOptions = array(
'cache_dir' => './tmp/'
// Dossier où les blocks de mémoire peuvent être stockés
);
$loadedFiles = array();
$loadedFiles[] = $memoryManager->create($data);
}
echo $loadedFiles[$index1]->value;
$loadedFiles[$index2]->value = $newValue;
$loadedFiles[$index3]->value[$charIndex] = '_';
• Manager de mémoire
• Conteneur de mémoire
1030
Zend_Memory
Vous pouvez exploiter cet attribut value comme une propriété d'objet :
$memObject = $memoryManager->create($data);
echo $memObject->value;
$memObject->value = $newValue;
$memObject->value[$index] = '_';
echo ord($memObject->value[$index1]);
Si vous utilisez une version de PHP inférieure à 5.2, utilisez la méthode getRef()
au lieu d'accéder directement à la valeur de la propriété.
Le manager de mémoire ne met pas en cache des objets ayant une taille plus petite que
le minimum spécifié dans un soucis de performances. Voir Section 2.3.2, « MinSize (taille
minimum) » pour plus de détails.
2. Manager de mémoire
2.1. Créer un manager de mémoire
Vous pouvez créer un nouveau manager de mémoire (objet Zend_Memory_Manager) en
utilisant la méthode Zend_Memory::factory($backendName [, $backendOprions]).
Le premier argument $backendName est le nom d'un type de backend supporté par
Zend_Cache
1031
Zend_Memory
$backendOptions = array(
'cache_dir' => './tmp/'
// Dossier où les blocks de mémoire peuvent être stockés
);
Vous pouvez de plus utiliser le nom spécial 'None' en tant que nom de backend supplémentaire
de Zend_Cache.
$memoryManager = Zend_Memory::factory('None');
Si vous utilisez "None", alors le manager de mémoire ne mettra pas en cache les blocks de
mémoire. Ceci est intéressant si vous savez que la mémoire n'est pas limitée ou la taille complète
des objets n'atteint jamais la limite de mémoire.
$memObject = $memoryManager->create($data);
L'argument $data est optionnel et utilisé pour initialiser la valeur de l'objet. Si l'argument $data
est omis, la valeur est une chaîne vide.
$memObject = $memoryManager->createLocked($data);
L'argument $data est optionnel et utilisé pour initialiser la valeur de l'objet. Si l'argument $data
est omis, la valeur est une chaîne vide.
function foo()
{
1032
Zend_Memory
...
$memObject1 = $memoryManager->create($data1);
$memObject2 = $memoryManager->create($data2);
$memObject3 = $memoryManager->create($data3);
...
$memList[] = $memObject3;
...
...
// $memObject1 est détruit ici
// mais $memObject3 est toujours référencé par $memList
// et n'est pas détruit
}
Si le chargement ou la création d'un objet entraîne l'utilisation de mémoire excédant cette limite,
alors le manager met en cache un certain nombre d'objet.
$oldLimit = $memoryManager->getMemoryLimit();
// Récupére la mémoire limite en octets
$memoryManager->setMemoryLimit($newLimit);
// Règle la mémoire limite en octets
La valeur par défaut est deux-tiers de la valeur de "memory_limit" dans le php.ini ou "no
limit" (-1) si "memory_limit" n'est pas réglé dans le php.ini.
Vous pouvez récupérer et régler la taille minimale en utilisant les méthodes getMinSize() et
setMinSize($newSize) :
$oldMinSize = $memoryManager->getMinSize();
// Récupère la taille minimale en octets
1033
Zend_Memory
$memoryManager->setMinSize($newSize);
// Règle la taille minimale en octets
3. Objet mémoire
3.1. Mobile
Créer des objets mémoires mobiles en utilisant la méthode create([$data]) du manager de
mémoire :
$memObject = $memoryManager->create($data);
"Mobile" signifie que de tels objets peuvent être mis en cache et déchargés de la mémoire et
chargés ensuite quand le code de l'application accède à l'objet.
3.2. Verrouillé
Créer des objets mémoires verrouillés en utilisant la méthode createLocked([$data]) du
manager de mémoire :
$memObject = $memoryManager->createLocked($data);
"Verrouillé" signifie que de tels objets ne sont jamais mis en cache et déchargés de la mémoire.
Les objets verrouillés fournissent la même interface que des objets mobiles
(Zend_Memory_Container_Interface). Donc l'objet verrouillé peut être utilisé en n'importe
quel endroit à la place des objets mobiles.
Il est utile si une application ou un développeur peut décider, que quelques objets ne devraient
jamais être mis en cache, en se basant sur des considérations de performance.
L'accès aux objets verrouillés est plus rapide, parce que le manager de mémoire ne doit pas
suivre à la trace des changements pour ces objets.
$memObject = $memoryManager->create($data);
echo $memObject->value;
$memObject->value = $newValue;
$memObject->value[$index] = '_';
echo ord($memObject->value[$index1]);
1034
Zend_Memory
Une autre manière d'accéder aux données d'objet mémoire est d'utiliser la méthode getRef().
Cette méthode doit être utilisée pour les versions de PHP inférieure à 5.2. Il devrait aussi être
utilisé dans quelques autres cas pour des raisons de performance.
Des objets mobiles sont chargés du cache à ce moment si l'objet n'est pas déjà dans la mémoire.
Si l'objet est chargé du cache, cela pourrait entraîner la mise en cache d'autres objets si la limite
de mémoire était dépassée en ayant tous les objets en mémoire.
La méthode getRef() doit être utilisée pour accéder aux données d'objet mémoire si la version
de PHP est inférieure à 5.2
Suivre à la trace les changements de ces données nécessitent des ressources supplémentaires.
La méthode getRef() retourne une référence à une chaîne, qui est changé directement par
l'utilisateur de l'application. Ainsi, c'est une bonne idée d'utiliser la méthode getRef() pour le
traitement des données :
$memObject = $memoryManager->create($data);
$value = &$memObject->getRef();
La méthode touch() devrait être employée en commun avec getRef(). Elle signale que la
valeur d'objet a été changé :
$memObject = $memoryManager->create($data);
...
$value = &$memObject->getRef();
1035
Zend_Memory
$memObject->touch();
La méthode lock() verrouille l'objet en mémoire. Elle devrait être utilisé pour empêcher la mise
en cache des objets que vous choisissez. Normalement, ce n'est pas nécessaire, parce que le
manager de mémoire utilise un algorithme intelligent pour choisir des candidats à la mise en
cache. Mais si vous savez exactement, qu'à cette endroit du code certains objets ne devraient
pas être mis en cache, vous pouvez les verrouiller.
Le verrouillage d'objets dans la mémoire garantit aussi que la référence retournée par la méthode
getRef() est valable jusqu'à ce que vous déverrouiller l'objet :
$memObject1 = $memoryManager->create($data1);
$memObject2 = $memoryManager->create($data2);
...
$memObject1->lock();
$memObject2->lock();
$value1 = &$memObject1->getRef();
$value2 = &$memObject2->getRef();
$memObject1->touch();
$memObject1->unlock();
$memObject2->unlock();
La méthode unlock() déverrouille l'objet quand il n'est plus nécessaire de maintenir verrouillé.
Voir l'exemple ci-dessus.
La méthode isLocked() peut être utilisée pour vérifier si l'objet est verrouillé. Il retourne TRUE
si l'objet est verrouillé, ou FALSE s'il n'est pas verrouillé. C'est toujours TRUE pour les objets
"verrouillés" et peut être TRUE ou FALSE pour des objets "mobiles".
1036
Zend_Mime
1. Zend_Mime
1.1. Introduction
Zend_Mime est une classe de support pour gérer les messages MIME en plusieurs parties. Elle
est utilisé par Zend_Mail et Zend_Mime_Message, est peut-être utilisée dans l'application qui
nécessite un support MIME.
Zend_Mime définit un jeu de constantes communément utilisé avec des messages MIME :
• Zend_Mime::TYPE_OCTETSTREAM: "application/octet-stream"
• Zend_Mime::TYPE_TEXT: "text/plain"
• Zend_Mime::TYPE_HTML: "text/html"
• Zend_Mime::ENCODING_7BIT: "7bit"
• Zend_Mime::ENCODING_8BIT: "8bit"
• Zend_Mime::ENCODING_QUOTEDPRINTABLE: "quoted-printable"
• Zend_Mime::ENCODING_BASE64: "base64"
• Zend_Mime::DISPOSITION_ATTACHMENT: "attachment"
• Zend_Mime::DISPOSITION_INLINE: "inline"
• Zend_Mime::MULTIPART_ALTERNATIVE: 'multipart/alternative'
• Zend_Mime::MULTIPART_MIXED: 'multipart/mixed'
1037
Zend_Mime
• Zend_Mime::MULTIPART_RELATED: 'multipart/related'
2. Zend_Mime_Message
2.1. Introduction
Zend_Mime_Message représente un message compatible MIME qui peut contenir une
ou plusieurs parties séparées (représentées par des objets Zend_Mime_Part) Avec
Zend_Mime_Message, les messages multiparts compatibles MIME peuvent être générés à
partir de Zend_Mime_Part. L'encodage et la gestion des frontières sont gérées de manière
transparente par la classe. Les objets Zend_Mime_Message peuvent aussi être reconstruits à
partir de chaînes de caractères données (expérimental). Utilisés par Zend_Mail.
La fonction ->isMultiPart() retournera TRUE si plus d'une partie est enregistrée avec l'objet
Zend_Mime_Message, l'objet pourra ainsi régénérer un objet Multipart-Mime-Message lors de
la génération de la sortie.
1038
Zend_Mime
Généralement, vous n'aurez pas besoin de faire cela. ->setMime(Zend_Mime $mime) définit
une instance spéciale de Zend_Mime pour qu'elle soit utilisée par ce Message.
->getMime() retourne l'instance de Zend_Mime qui sera utilisée pour générer le message
lorsque generateMessage() est appelée.
3. Zend_Mime_Part
3.1. Introduction
Cette classe représente une seule partie d'un message MIME. Elle contient le contenu actuel
de la partie du message ainsi que des informations sur son encodage, le type de contenu
("content-type") et le nom de fichier original. Elle fournie une méthode pour générer une chaîne
de caractères à partir des données stockées. Les objets Zend_Mime_Part peuvent-être ajoutés
à Zend_Mime_Message pour assembler un message multipart complet.
3.2. Instanciation
Zend_Mime_Part est instanciée avec une chaîne de caractères qui représente le contenu de
cette nouvelle partie. Le type doit être OCTET-STREAM, et l'encodage 8 bits. Après instanciation
de Zend_Mime_Part, les métas-informations peuvent être définies en accédant directement
aux attributs :
1039
Zend_Mime
getHeaders() retourne les Mime-Headers d'un MimePart générés à partir des attributs
accessibles publiquement. Les attributs de l'objet doivent être paramétrés correctement avant
que cette méthode ne soit appelée.
• $charset doit définir l'encodage actuel du contenu, si c'est un type texte (Texte ou HTML).
• $id doit être défini pour identifier un content-id pour les images d'un mail HTML.
• $disposition définit si le fichier doit être traité comme une pièce jointe ou s'il est inclus
dans le mail (HTML).
• $location peut être utilisé comme URI d'une ressource URI qui a une relation avec le
contenu.
1040
Zend_Navigation
1. Introduction
Zend_Navigation est un composant gérant arbres et menus pour les pages webs. Il permet
de créer des menus, des fils, des liens et des cartographies de sites (sitemaps), ou encore toute
autre représentation concernant la navigation.
1.1.1. Pages
Une page (Zend_Navigation_Page) dans Zend_Navigation – dans sa forme la plus simple
– est un objet pointant vers une page web. En plus d'un pointeur vers une page web, l'objet page
contient d'autres informations utiles à la navigation comme un label, un titre title, etc.
1.1.2. Conteneurs
Un conteneur de navigation (Zend_Navigation_Container) est une classe contenant
des pages. Elle possède des méthodes pour ajouter, supprimer, récupérer et itrérer au
travers de pages. Elle implémente les interfaces de la SPL RecursiveIterator et
Countable, et peuvent ainsi être parcourues avec les itérateurs de la SPL tels que
RecursiveIteratorIterator.
Pour plus d'informations sur le rendu des éléments, lisez leur section.
2. Pages
Zend_Navigation est fournie par défaut avec deux types de page:
Les pages MVC proposeront des liens pour l'application courante et utilisent les paramètres
MVC (action, controller, module, route, params). Les pages URI utilisent elles une seule
option,uri, ce qui vous offre la possibilité de créer des liens vers des sites externes ou encore
de créer des liens personnalisés comme par exemple <a href="#">foo<a>.
1041
Zend_Navigation
Les noms des options (en clés) sont dirigées vers les setters appropriés set. Ceci signifie
qu'une option appelée order sera passée à la méthode setOrder(), et une option
nommée reset_params sera dirigée vers setResetParams(). Si aucune méthode setter ne
correspond, l'option sera alors évaluée comme un attribut personnalisé de la page.
1042
Zend_Navigation
1043
Zend_Navigation
Propriétés personnalisées
Toutes les pages supportent la gestion de propriétés personnalisées, ceci
via les méthodes magiques __set($name, $value), __get($name),
__isset($name) et __unset($name). Ces propriétés peuvent prendre
n'importe quelle valeur et seront incluses dans le tableau retourné par $page-
>toArray(), ce qui signifie que les pages peuvent être dé/sérialisées même si
elles comportent des propriétés non natives à leur classe.
Que les pages soient natives ou personnalisées par vos soins, les propriétés
peuvent être gérées au moyen des méthodes $page->set($name, $value)
et $page->get($name), ou encore via des méthodes magiques.
Cet exemple montre comment les propriétés personnalisées des pages peuvent être
utilisées.
echo $page->foo;
if ($page->meaning != 42) {
// quelque chose à faire ici
}
2.2. Zend_Navigation_Page_Mvc
Les pages de type MVC utilisent des paramètres MVC issus du composant Zend_Controller.
Une page MVC utilisera en interne Zend_Controller_Action_Helper_Url dans la
1044
Zend_Navigation
méthode getHref() pour générer des cibles (hrefs), et la méthode isActive() utilisera
les paramètres issus de Zend_Controller_Request_Abstract et les comparera aux
paramètres internes à la page.
Les trois exemples qui suivent supposent une configuration MVC par défaut, avec
une route default.
1045
Zend_Navigation
// getHref() retourne /
$page = new Zend_Navigation_Page_Mvc(array(
'action' => 'index',
'controller' => 'index'
));
1046
Zend_Navigation
Cet exemple montre que les pages de type MVC utilisent l'objet de requête afin de
déterminer si elles sont actives ou non.
/*
* Requête dispatchée:
* - module: default
* - controller: index
* - action: index
*/
$page1 = new Zend_Navigation_Page_Mvc(array(
'action' => 'index',
'controller' => 'index'
));
/*
* Requête dispatchée:
* - module: blog
* - controller: post
* - action: view
* - id: 1337
*/
$page = new Zend_Navigation_Page_Mvc(array(
'action' => 'view',
'controller' => 'post',
'module' => 'blog'
));
/*
* Requête dispatchée:
* - module: blog
* - controller: post
* - action: view
*/
$page = new Zend_Navigation_Page_Mvc(array(
'action' => 'view',
'controller' => 'post',
'module' => 'blog',
'params' => array('id' => null)
));
1047
Zend_Navigation
Les routes sont utilisables dans les pages de type MVC. Si une page a une route, elle sera
utilisée par getHref() pour générer l'URL de la page.
// retourne: /a/42
$page->getHref();
2.3. Zend_Navigation_Page_Uri
Les pages de type Zend_Navigation_Page_Uri peuvent être utilisées pour pointer vers des
sites externes, ou des pages internes personnalisées. Les pages URI sont simples, en plus
des options classiques des pages, les pages URI utilisent une seule option : uri. L' uri sera
retourné à l'appel de $page->getHref() et retournera une chaine ou NULL.
1048
Zend_Navigation
La seule chose à définir dans une page personnalisée est la méthode getHref().
1049
Zend_Navigation
Ajouter des propriétés à vos pages étendues ne nécessite pas de réécrire setOptions()
ou setConfig().
// ...or
$page = Zend_Navigation_Page::factory(array(
'type' => 'My_Navigation_Page',
'label' => 'Les noms des propriétés sont dirigés vers les setters',
'foo' => 'bar',
'foo_bar' => 'baz'
));
1050
Zend_Navigation
type URI sera créee. Si un ou plusieurs paramètres concernant MVC sont passés, une page de
type MVC sera retournée.
Si le paramètre type est passé, la fabrique l'utilisera pour déterminer le nom de la classe à
utiliser. Les valeurs mvc ou uri créeront des pages de types MVC/URI.
$page = Zend_Navigation_Page::factory(array(
'label' => 'My MVC page',
'action' => 'index'
));
$page = Zend_Navigation_Page::factory(array(
'label' => 'Search blog',
'action' => 'index',
'controller' => 'search',
'module' => 'blog'
));
$page = Zend_Navigation_Page::factory(array(
'label' => 'Home',
'action' => 'index',
'controller' => 'index',
'module' => 'index',
'route' => 'home'
));
$page = Zend_Navigation_Page::factory(array(
'type' => 'mvc',
'label' => 'My MVC page'
));
$page = Zend_Navigation_Page::factory(array(
'label' => 'My URI page',
'uri' => 'http://www.example.com/'
));
$page = Zend_Navigation_Page::factory(array(
'label' => 'Search',
'uri' => 'http://www.example.com/search',
'active' => true
));
$page = Zend_Navigation_Page::factory(array(
'label' => 'My URI page',
'uri' => '#'
));
$page = Zend_Navigation_Page::factory(array(
'type' => 'uri',
'label' => 'My URI page'
));
1051
Zend_Navigation
$page = Zend_Navigation_Page::factory(array(
'type' => 'My_Navigation_Page',
'label' => 'My custom page',
'foo_bar' => 'foo bar'
));
3. Containers
Containers have methods for adding, retrieving, deleting and iterating pages. Containers
implement the SPL interfaces RecursiveIterator and Countable, meaning that a container
can be iterated using the SPL RecursiveIteratorIterator class.
1052
'label' => 'Page 5.1',
'uri' => '#',
'pages' => array(
array(
Zend_Navigation
'label' => 'Page 5.1.1',
'uri' => '#',
'pages' => array(
array(
Exemple 565. Creating a container using an array
'label' => 'Page 5.1.2',
'uri' => '#',
// let's say this page is active
'active' => true
)
)
)
)
)
)
),
array(
'label' => 'ACL page 1 (guest)',
'uri' => '#acl-guest',
'resource' => 'nav-guest',
'pages' => array(
array(
'label' => 'ACL page 1.1 (foo)',
'uri' => '#acl-foo',
'resource' => 'nav-foo'
),
array(
'label' => 'ACL page 1.2 (bar)',
'uri' => '#acl-bar',
'resource' => 'nav-bar'
),
array(
'label' => 'ACL page 1.3 (baz)',
'uri' => '#acl-baz',
'resource' => 'nav-baz'
),
array(
'label' => 'ACL page 1.4 (bat)',
'uri' => '#acl-bat',
'resource' => 'nav-bat'
)
)
),
array(
'label' => 'ACL page 2 (member)',
'uri' => '#acl-member',
'resource' => 'nav-member'
),
array(
'label' => 'ACL page 3 (admin',
'uri' => '#acl-admin',
'resource' => 'nav-admin',
'pages' => array(
array(
'label' => 'ACL page 3.1 (nothing)',
'uri' => '#acl-nada'
)
)
),
array(
'label' => 'Zend Framework',
'route' => 'zf-route'
)
));
1053
<uri>page3</uri>
<pages>
<page3_1>
Zend_Navigation
<label>Page 3.1</label>
<uri>page3/page3_1</uri>
<resource>guest</resource>
</page3_1>
Exemple 566. Creating a container using a config object
<page3_2>
<label>Page 3.2</label>
<uri>page3/page3_2</uri>
<resource>member</resource>
<pages>
<page3_2_1>
<label>Page 3.2.1</label>
<uri>page3/page3_2/page3_2_1</uri>
</page3_2_1>
<page3_2_2>
<label>Page 3.2.2</label>
<uri>page3/page3_2/page3_2_2</uri>
<resource>admin</resource>
</page3_2_2>
</pages>
</page3_2>
<page3_3>
<label>Page 3.3</label>
<uri>page3/page3_3</uri>
<resource>special</resource>
<pages>
<page3_3_1>
<label>Page 3.3.1</label>
<uri>page3/page3_3/page3_3_1</uri>
<visible>0</visible>
</page3_3_1>
<page3_3_2>
<label>Page 3.3.2</label>
<uri>page3/page3_3/page3_3_2</uri>
<resource>admin</resource>
</page3_3_2>
</pages>
</page3_3>
</pages>
</page3>
<home>
<label>Home</label>
<order>-100</order>
<module>default</module>
<controller>index</controller>
<action>index</action>
</home>
</nav>
</config>
*/
1054
Zend_Navigation
// create container
$container = new Zend_Navigation();
$pages = array(
array(
'label' => 'Save'
'action' => 'save',
),
array(
'label' => 'Delete',
'action' => 'delete'
)
);
1055
Zend_Navigation
The finder methods can also be used magically by appending the property name to findBy,
findOneBy, or findAllBy, e.g. findOneByLabel('Home') to return the first matching
page with label Home. Other combinations are findByLabel(...), findOnyByTitle(...),
findAllByController(...), etc. Finder methods also work on custom properties, such as
findByFoo('bar').
1056
array(
'label' => 'Page 1.2',
'uri' => 'page-1.2',
'class' => 'my-class',
), Zend_Navigation
array(
'type' => 'uri',
'label' => 'Page 1.3',
Exemple 569. Finding pages in a container
'uri' => 'page-1.3',
'action' => 'about'
)
)
),
array(
'label' => 'Page 2',
'id' => 'page_2_and_3',
'class' => 'my-class',
'module' => 'page2',
'controller' => 'index',
'action' => 'page1'
),
array(
'label' => 'Page 3',
'id' => 'page_2_and_3',
'module' => 'page3',
'controller' => 'index'
)
));
1057
Zend_Navigation
/*
* Create a container from an array
*/
$container = new Zend_Navigation(array(
array(
'label' => 'Page 1',
'uri' => '#'
),
array(
'label' => 'Page 2',
'uri' => '#',
'pages' => array(
array(
'label' => 'Page 2.1',
'uri' => '#'
),
array(
'label' => 'Page 2.2',
'uri' => '#'
)
)
)
array(
'label' => 'Page 3',
'uri' => '#'
)
));
1058
Zend_Navigation
The toArray() method converts the container and the pages in it to an array. This can be
useful for serializing and debugging.
1059
["pages"]=> array(0) {
}
["uri"]=> string(1) "#"
}
[1]=> array(15) { Zend_Navigation
["label"]=> string(6) "Page 2"
["id"]=> NULL
["class"]=> NULL
Exemple 571. Converting a container to an array
["title"]=> NULL
["target"]=> NULL
["rel"]=> array(0) {
}
["rev"]=> array(0) {
}
["order"]=> NULL
["resource"]=> NULL
["privilege"]=> NULL
["active"]=> bool(false)
["visible"]=> bool(true)
["type"]=> string(23) "Zend_Navigation_Page_Uri"
["pages"]=> array(2) {
[0]=> array(15) {
["label"]=> string(8) "Page 2.1"
["id"]=> NULL
["class"]=> NULL
["title"]=> NULL
["target"]=> NULL
["rel"]=> array(0) {
}
["rev"]=> array(0) {
}
["order"]=> NULL
["resource"]=> NULL
["privilege"]=> NULL
["active"]=> bool(false)
["visible"]=> bool(true)
["type"]=> string(23) "Zend_Navigation_Page_Uri"
["pages"]=> array(0) {
}
["uri"]=> string(1) "#"
}
[1]=>
array(15) {
["label"]=> string(8) "Page 2.2"
["id"]=> NULL
["class"]=> NULL
["title"]=> NULL
["target"]=> NULL
["rel"]=> array(0) {
}
["rev"]=> array(0) {
}
["order"]=> NULL
["resource"]=> NULL
["privilege"]=> NULL
["active"]=> bool(false)
["visible"]=> bool(true)
["type"]=> string(23) "Zend_Navigation_Page_Uri"
["pages"]=> array(0) {
}
["uri"]=> string(1) "#"
}
}
["uri"]=> string(1) "#"
}
}
*/
1060
Zend_Oauth
1. Introduction to OAuth
OAuth allows you to approve access by any application to your private data stored a website
without being forced to disclose your username or password. If you think about it, the practice of
handing over your username and password for sites like Yahoo Mail or Twitter has been endemic
for quite a while. This has raised some serious concerns because there's nothing to prevent
other applications from misusing this data. Yes, some services may appear trustworthy but that
is never guaranteed. OAuth resolves this problem by eliminating the need for any username and
password sharing, replacing it with a user controlled authorization process.
This authorization process is token based. If you authorize an application (and by application we
can include any web based or desktop application) to access your data, it will be in receipt of
an Access Token associated with your account. Using this Access Token, the application can
access your private data without continually requiring your credentials. In all this authorization
delegation style of protocol is simply a more secure solution to the problem of accessing private
data via any web service API.
OAuth is not a completely new idea, rather it is a standardized protocol building on the existing
properties of protocols such as Google AuthSub, Yahoo BBAuth, Flickr API, etc. These all
to some extent operate on the basis of exchanging user credentials for an Access Token of
some description. The power of a standardized specification like OAuth is that it only requires
a single implementation as opposed to many disparate ones depending on the web service.
This standardization has not occurred independently of the major players, and indeed many now
support OAuth as an alternative and future replacement for their own solutions.
Zend Framework's Zend_Oauth currently implements a full OAuth Consumer conforming to the
OAuth Core 1.0 Revision A Specification (24 June 2009) via the Zend_Oauth_Consumer class.
By chance, our User has decided that they want to utilise a new service called TweetExpress
which claims to be capable of reposting your blog posts to Twitter in a manner of seconds.
TweetExpress is a registered application on Twitter meaning that it has access to a Consumer
Key and a Consumer Secret (all OAuth applications must have these from the Provider they will
be accessing) which identify its requests to Twitter and that ensure all requests can be signed
using the Consumer Secret to verify their origin.
To use TweetExpress you are asked to register for a new account, and after your registration is
confirmed you are informed that TweetExpress will seek to associate your Twitter account with
the service.
In the meantime TweetExpress has been busy. Before gaining your approval from Twitter, it has
sent a HTTP request to Twitter's service asking for a new unauthorized Request Token. This
token is not User specific from Twitter's perspective, but TweetExpress may use it specifically
for the current User and should associate it with their account and store it for future use.
1061
Zend_Oauth
TweetExpress now redirects the User to Twitter so they can approve TweetExpress' access. The
URL for this redirect will be signed using TweetExpress' Consumer Secret and it will contain the
unauthorized Request Token as a parameter.
At this point the User may be asked to log into Twitter and will now be faced with a Twitter screen
asking if they approve this request by TweetExpress to access Twitter's API on the User's behalf.
Twitter will record the response which we'll assume was positive. Based on the User's approval,
Twitter will record the current unauthorized Request Token as having been approved by the User
(thus making it User specific) and will generate a new value in the form of a verification code.
The User is now redirected back to a specific callback URL used by TweetExpress (this callback
URL may be registered with Twitter or dynamically set using an oauth_callback parameter in
requests). The redirect URL will contain the newly generated verification code.
TweetExpress' callback URL will trigger an examination of the response to determine whether the
User has granted their approval to Twitter. Assuming so, it may now exchange it's unauthorized
Request Token for a fully authorized Access Token by sending a request back to Twitter including
the Request Token and the received verification code. Twitter should now send back a response
containing this Access Token which must be used in all requests used to access Twitter's API
on behalf of the User. Twitter will only do this once they have confirmed the attached Request
Token has not already been used to retrieve another Access Token. At this point, TweetExpress
may confirm the receipt of the approval to the User and delete the original Request Token which
is no longer needed.
From this point forward, TweetExpress may use Twitter's API to post new tweets on the User's
behalf simply by accessing the API endpoints with a request that has been digitally signed (via
HMAC-SHA1) with a combination of TweetExpress' Consumer Secret and the Access Key being
used.
Although Twitter do not currently expire Access Tokens, the User is free to deauthorize
TweetExpress from their Twitter account settings. Once deauthorized, TweetExpress' access
will be cut off and their Access Token rendered invalid.
These signature methods are quite easy to understand. As you can imagine, a PLAINTEXT
signature method does nothing that bears mentioning since it relies on HTTPS. If you were to
use PLAINTEXT over HTTP, you are left with a significant problem: there's no way to be sure that
the content of any OAuth enabled request (which would include the OAuth Access Token) was
altered en route. This is because unsecured HTTP requests are always at risk of eavesdropping,
Man In The Middle (MITM) attacks, or other risks whereby a request can be retooled so to speak
to perform tasks on behalf of the attacker by masquerading as the origin application without being
noticed by the service provider.
HMAC-SHA1 and RSA-SHA1 alleviate this risk by digitally signing all OAuth requests with the
original application's registered Consumer Secret. Assuming only the Consumer and the Provider
know what this secret is, a middle-man can alter requests all they wish - but they will not be
able to validly sign them and unsigned or invalidly signed requests would be discarded by both
parties. Digital signatures therefore offer a guarantee that validly signed requests do come from
1062
Zend_Oauth
the expected party and have not been altered en route. This is the core of why OAuth can operate
over an unsecure connection.
How these digital signatures operate depends on the method used, i.e. HMAC-SHA1, RSA-SHA1
or perhaps another method defined by the service provider. HMAC-SHA1 is a simple mechanism
which generates a Message Authentication Code (MAC) using a cryptographic hash function (i.e.
SHA1) in combination with a secret key known only to the message sender and receiver (i.e. the
OAuth Consumer Secret and the authorized Access Key combined). This hashing mechanism
is applied to the parameters and content of any OAuth requests which are concatenated into a
"base signature string" as defined by the OAuth specification.
RSA-SHA1 operates on similar principles except that the shared secret is, as you would expect,
each parties' RSA private key. Both sides would have the other's public key with which to verify
digital signatures. This does pose a level of risk compared to HMAC-SHA1 since the RSA method
does not use the Access Key as part of the shared secret. This means that if the RSA private
key of any Consumer is compromised, then all Access Tokens assigned to that Consumer are
also. RSA imposes an all or nothing scheme. In general, the majority of service providers offering
OAuth authorization have therefore tended to use HMAC-SHA1 by default, and those who offer
RSA-SHA1 may offer fallback support to HMAC-SHA1.
While digital signatures add to OAuth's security they are still vulnerable to other forms of attack,
such as replay attacks which copy earlier requests which were intercepted and validly signed at
that time. An attacker can now resend the exact same request to a Provider at will at any time
and intercept its results. This poses a significant risk but it is quiet simple to defend against -
add a unique string (i.e. a nonce) to all requests which changes per request (thus continually
changing the signature string) but which can never be reused because Providers actively track
used nonces within the a certain window defined by the timestamp also attached to a request.
You might first suspect that once you stop tracking a particular nonce, the replay could work but
this ignore the timestamp which can be used to determine a request's age at the time it was
validly signed. One can assume that a week old request used in an attempted replay should be
summarily discarded!
As a final point, this is not an exhaustive look at the security architecture in OAuth. For example,
what if HTTP requests which contain both the Access Token and the Consumer Secret are
eavesdropped? The system relies on at one in the clear transmission of each unless HTTPS
is active, so the obvious conclusion is that where feasible HTTPS is to be preferred leaving
unsecured HTTP in place only where it is not possible or affordable to do so.
Assuming we have obtained a key and secret, we can start the OAuth workflow by setting up
a Zend_Oauth_Consumer instance as follows passing it a configuration (either an array or
Zend_Config object).
$config = array(
'callbackUrl' => 'http://example.com/callback.php',
'siteUrl' => 'http://twitter.com/oauth',
'consumerKey' => 'gg3DsFTW9OU9eWPnbuPzQ',
'consumerSecret' => 'tFB0fyWLSMf74lkEu9FTyoHXcazOWpbrAjTCCK48A'
);
$consumer = new Zend_Oauth_Consumer($config);
1063
Zend_Oauth
The callbackUrl is the URI we want Twitter to request from our server when sending information.
We'll look at this later. The siteUrl is the base URI of Twitter's OAuth API endpoints. The full list of
endpoints include http://twitter.com/oauth/request_token, http://twitter.com/oauth/access_token,
and http://twitter.com/oauth/authorize. The base siteUrl utilises a convention which maps to
these three OAuth endpoints (as standard) for requesting a request token, access token or
authorization. If the actual endpoints of any service differ from the standard set, these three URIs
can be separately set using the methods setRequestTokenUrl(), setAccessTokenUrl(),
and setAuthorizeUrl() or the configuration fields requestTokenUrl, accessTokenUrl and
authorizeUrl.
The consumerKey and consumerSecret are retrieved from Twitter when your application is
registered for OAuth access. These also apply to any OAuth enabled service, so each one will
provide a key and secret for your application.
All of these configuration options may be set using method calls simply by converting from, e.g.
callbackUrl to setCallbackUrl().
In addition, you should note several other configuration values not explicitly used:
requestMethod and requestScheme. By default, Zend_Oauth_Consumer sends
requests as POST (except for a redirect which uses GET). The customised client
(see later) also includes its authorization by way of a header. Some services may,
at their discretion, require alternatives. You can reset the requestMethod (which
defaults to Zend_Oauth::POST) to Zend_Oauth::GET, for example, and reset the
requestScheme from its default of Zend_Oauth::REQUEST_SCHEME_HEADER to one of
Zend_Oauth::REQUEST_SCHEME_POSTBODY or
Zend_Oauth::REQUEST_SCHEME_QUERYSTRING. Typically the defaults should work fine
apart from some exceptional cases. Please refer to the service provider's documentation for
more details.
The first part of the OAuth workflow is obtaining a request token. This is accomplished using:
$config = array(
'callbackUrl' => 'http://example.com/callback.php',
'siteUrl' => 'http://twitter.com/oauth',
'consumerKey' => 'gg3DsFTW9OU9eWPnbuPzQ',
'consumerSecret' => 'tFB0fyWLSMf74lkEu9FTyoHXcazOWpbrAjTCCK48A'
);
$consumer = new Zend_Oauth_Consumer($config);
$config = array(
1064
Zend_Oauth
The user will now be redirected to Twitter. They will be asked to authorize the request token
attached to the redirect URI's query string. Assuming they agree, and complete the authorization,
they will be again redirected, this time to our Callback URL as previously set (note that the
callback URL is also registered with Twitter when we registered our application).
Before redirecting the user, we should persist the request token to storage. For simplicity I'm just
using the user's session, but you can easily use a database for the same purpose, so long as you
tie the request token to the current user so it can be retrieved when they return to our application.
The redirect URI from Twitter will contain an authorized Access Token. We can include code to
parse out this access token as follows - this source code would exist within the executed code of
our callback URI. Once parsed we can discard the previous request token, and instead persist
the access token for future use with the Twitter API. Again, we're simply persisting to the user
session, but in reality an access token can have a long lifetime so it should really be stored to
a database.
$config = array(
'callbackUrl' => 'http://example.com/callback.php',
'siteUrl' => 'http://twitter.com/oauth',
'consumerKey' => 'gg3DsFTW9OU9eWPnbuPzQ',
'consumerSecret' => 'tFB0fyWLSMf74lkEu9FTyoHXcazOWpbrAjTCCK48A'
);
$consumer = new Zend_Oauth_Consumer($config);
// Now that we have an Access Token, we can discard the Request Token
$_SESSION['TWITTER_REQUEST_TOKEN'] = null;
} else {
// Mistaken request? Some malfeasant trying something?
exit('Invalid callback request. Oops. Sorry.');
}
Success! We have an authorized access token - so it's time to actually use the Twitter API.
Since the access token must be included with every single API request, Zend_Oauth_Consumer
offers a ready-to-go HTTP client (a subclass of Zend_Http_Client) to use either by itself or
by passing it as a custom HTTP Client to another library or component. Here's an example of
using it standalone. This can be done from anywhere in your application, so long as you can
access the OAuth configuration and retrieve the final authorized access token.
1065
Zend_Oauth
$config = array(
'callbackUrl' => 'http://example.com/callback.php',
'siteUrl' => 'http://twitter.com/oauth',
'consumerKey' => 'gg3DsFTW9OU9eWPnbuPzQ',
'consumerSecret' => 'tFB0fyWLSMf74lkEu9FTyoHXcazOWpbrAjTCCK48A'
);
$token = unserialize($_SESSION['TWITTER_ACCESS_TOKEN']);
$client = $token->getHttpClient($configuration);
$client->setUri('http://twitter.com/statuses/update.json');
$client->setMethod(Zend_Http_Client::POST);
$client->setParameterPost('status', $statusMessage);
$response = $client->request();
$data = Zend_Json::decode($response->getBody());
$result = $response->getBody();
if (isset($data->text)) {
$result = 'true';
}
echo $result;
As a note on the customised client, this can be passed to most Zend Framework service or other
classes using Zend_Http_Client displacing the default client they would otherwise use.
1066
Zend_OpenId
1. Introduction
Zend_OpenId est le composant de Zend Framework qui propose une API simple pour
l'utilisation de la technologie OpenID dans son applicatif internet.
Visitez donc le site officiel de OpenID pour plus d'information, voyez aussi le OpenID Book, par
Rafeeq Rehman ou OpenIDFrance.
1. L'authentification est initialisée par l'utilisateur final, qui passe son identifiant OpenID, via son
navigateur.
3. Le service protégé par OpenID établit optionnellement une connexion avec le serveur OpenID
en utilisant des clés Diffie-Hellman. Les deux parties partagent alors un "secret" utilisé pour
signer et vérifier les messages suivants.
4. Le service protégé par OpenID redirige le navigateur vers l'URL du serveur OpenID, en
effectuant une requête d'authentification.
7. Le serveur OpenID effectue une vérification pour savoir si l'utilisateur a le droit d'accéder au
service protégé par OpenID.
1067
Zend_OpenId
8. L'utilisateur final accepte alors ou non de partager ses informations d'identité pour le service
protégé par OpenID.
9. Le serveur OpenID redirige le navigateur vers le service protégé par OpenID, avec un
message "authentification approuvée" ou "échouée".
10.Le service protégé par OpenID vérifie l'information du serveur OpenID en utilisant le "secret"
obtenu étape 3 ou en envoyant des informations supplémentaires.
Les seuls points communs entre ces deux classes sont l'extension Simple Registration
Extension, implémentée par la classe Zend_OpenId_Extension_Sreg, ainsi que les
fonctions utilitaires présentes dans la classe Zend_OpenId.
2. Zend_OpenId_Consumer Basics
Zend_OpenId_Consumer can be used to implement OpenID authentication for web sites.
The OpenID authentication protocol actually requires more steps, but many of them are
encapsulated inside Zend_OpenId_Consumer and are therefore transparent to the developer.
The end user initiates the OpenID authentication process by submitting his or her identification
credentials with the appropriate form. The following example shows a simple form that accepts
an OpenID identifier. Note that the example only demonstrates a login.
1068
Zend_OpenId
<html><body>
<form method="post" action="example-1_2.php"><fieldset>
<legend>OpenID Login</legend>
<input type="text" name="openid_identifier">
<input type="submit" name="openid_action" value="login">
</fieldset></form></body></html>
This form passes the OpenID identity on submission to the following PHP script
that performs the second step of authentication. The PHP script need only call the
Zend_OpenId_Consumer::login() method in this step. The first argument of this method is
an accepted OpenID identity, and the second is the URL of a script that handles the third and
last step of authentication.
An OpenID provider usually asks users for their password (if they weren't previously logged-
in), whether the user trusts this site and what information may be returned to the site. These
interactions are not visible to the OpenID consumer, so it can not obtain the user's password or
other information that the user did not has not directed the OpenID provider to share with it.
The third step of authentication is initiated by the response from the OpenID provider, after it has
authenticated the user's password. This response is passed indirectly, as an HTTP redirection
using the end user's web browser. The consumer must now simply check that this response is
valid.
1069
Zend_OpenId
<?php
$status = "";
if (isset($_POST['openid_action']) &&
$_POST['openid_action'] == "login" &&
!empty($_POST['openid_identifier'])) {
In addition, this code differentiates between cancelled and invalid authentication responses. The
provider returns a cancelled response if the identity provider is not aware of the supplied identity,
the user is not logged in, or the user doesn't trust the site. An invalid response indicates that the
response is not conformant to the OpenID protocol or is incorrectly signed.
By default, the realm URL is automatically set to the URL of the directory in which the login script
resides. This default value is useful for most, but not all, cases. Sometimes an entire domain,
and not a directory should be trusted. Or even a combination of several servers in one domain.
1070
Zend_OpenId
To override the default value, developers may pass the realm URL as a third argument to the
Zend_OpenId_Consumer::login method. In the following example, a single interaction asks
for trusted access to all php.net sites.
This example implements only the second step of authentication; the first and third steps are
similar to the examples above.
This example implements only the second step of authentication; the first and third steps are
similar to the examples above.
2.5. Zend_OpenId_Consumer_Storage
There are three steps in the OpenID authentication procedure, and each step is performed by
a separate HTTP request. To store information between requests, Zend_OpenId_Consumer
uses internal storage.
The following example demonstrates a simple storage mechanism that uses Zend_Db as its
backend and exposes three groups of functions. The first group contains functions for working
with associations, while the second group caches discovery information, and the third group can
1071
Zend_OpenId
be used to check whether a response is unique. This class can easily be used with existing or
new databases; if the required tables don't exist, it will create them.
1072
$version,
$expires)
{
$table = $this->_discovery_table;
Zend_OpenId
$this->_db->insert($table, array(
'id' => $id,
'realId' => $realId,
'server' => $server,
Exemple 578. Database Storage
'version' => $version,
'expires' => $expires,
));
return true;
}
if (is_array($res)) {
$realId = $res['realId'];
$server = $res['server'];
$version = $res['version'];
$expires = $res['expires'];
return true;
}
return false;
}
$db = Zend_Db::factory('Pdo_Sqlite',
array('dbname'=>'/tmp/openid_consumer.db'));
$storage = new DbStorage($db);
$consumer = new Zend_OpenId_Consumer($storage);
1073
Zend_OpenId
This example doesn't list the OpenID authentication code itself, but this code would be the same
as that for other examples in this chapter. examples.
• nickname - any UTF-8 string that the end user uses as a nickname
• email - the email address of the user as specified in section 3.4.1 of RFC2822
• dob - the user's date of birth in the format 'YYYY-MM-DD'. Any values whose representation
uses fewer than the specified number of digits in this format should be zero-padded. In other
words, the length of this value must always be 10. If the end user does not want to reveal any
particular part of this value (i.e., year, month or day), it must be set to zero. For example, if
the user wants to specify that his date of birth falls in 1980, but not specify the month or day,
the value returned should be '1980-00-00'.
• gender - the user's gender: "M" for male, "F" for female
• postcode - a UTF-8 string that conforms to the postal system of the user's country
An OpenID-enabled web site may ask for any combination of these fields. It may also strictly
require some information and allow users to provide or hide additional information. The following
example instantiates the Zend_OpenId_Extension_Sreg class, requiring a nickname and
optionally requests an email and a fullname.
1074
Zend_OpenId
is required; TRUE means the field is required and FALSE means the field is optional. The
Zend_OpenId_Consumer::login method accepts an extension or an array of extensions as
its fourth argument.
If the Zend_OpenId_Extension_Sreg object was created without any arguments, the user
code should check for the existence of the required data itself. However, if the object is created
with the same list of required fields as on the second step, it will automatically check for the
existence of required data. In this case, Zend_OpenId_Consumer::verify will return FALSE
if any of the required fields are missing.
The big difference between this adapter and existing ones, is that it works on two HTTP requests
and includes a dispatch code to perform the second or third step of OpenID authentication.
1075
<?php Zend_OpenId
class OpenIdAdapter implements Zend_Auth_Adapter_Interface {
private $_id = null;
Exemple 581. Zend_Auth Adapter for OpenID
public function __construct($id = null) {
$this->_id = $id;
}
$status = "";
$auth = Zend_Auth::getInstance();
if ((isset($_POST['openid_action']) &&
$_POST['openid_action'] == "login" &&
!empty($_POST['openid_identifier'])) ||
isset($_GET['openid_mode'])) {
$adapter = new OpenIdAdapter(@$_POST['openid_identifier']);
$result = $auth->authenticate($adapter);
if ($result->isValid()) {
Zend_OpenId::redirect(Zend_OpenId::selfURL());
} else {
$auth->clearIdentity();
foreach ($result->getMessages() as $message) {
$status .= "$message<br>\n";
}
}
} else if ($auth->hasIdentity()) {
if (isset($_POST['openid_action']) &&
$_POST['openid_action'] == "logout") {
$auth->clearIdentity();
} else {
$status = "You are logged in as " . $auth->getIdentity() . "<br>\n";
}
}
?>
<html><body>
<?php echo htmlspecialchars($status);?>
<form method="post"><fieldset>
<legend>OpenID Login</legend>
<input type="text" name="openid_identifier" value="">
<input type="submit" name="openid_action" value="login">
<input type="submit" name="openid_action" value="logout">
</fieldset></form></body></html>
1076
Zend_OpenId
With Zend_Auth the end-user's identity is saved in the session's data. It may be checked with
Zend_Auth::hasIdentity and Zend_Auth::getIdentity.
3. Zend_OpenId_Provider
Zend_OpenId_Provider can be used to implement OpenID servers. This chapter provides
examples that demonstrate how to build a very basic server. However, for implementation of
a production OpenID server (such as www.myopenid.com) you may have to deal with more
complex issues.
The code before the <html> tag is just a trick that automatically creates a user account. You
won't need such code when using real identities.
<?php
// Set up test identity
define("TEST_SERVER", Zend_OpenId::absoluteURL("example-8.php"));
define("TEST_ID", Zend_OpenId::selfURL());
define("TEST_PASSWORD", "123");
$server = new Zend_OpenId_Provider();
if (!$server->hasUser(TEST_ID)) {
$server->register(TEST_ID, TEST_PASSWORD);
}
?>
<html><head>
<link rel="openid.server" href="<?php echo TEST_SERVER;?>" />
</head><body>
<?php echo TEST_ID;?>
</body></html>
The following identity server script handles two kinds of requests from OpenID-enabled
sites (for association and authentication). Both of them are handled by the same method:
1077
Zend_OpenId
It is a good idea to use a secure connection (HTTPS) for these scripts- especially
for the following interactive scripts- to prevent password disclosure.
The following script implements a login screen for an identity server using
Zend_OpenId_Provider and redirects to this page when a required user has not yet logged
in. On this page, a user will enter his password to login.
You should use the password "123" that was used in the identity script above.
On submit, the script calls Zend_OpenId_Provider::login with the accepted user's identity
and password, then redirects back to the main identity provider's script. On success, the
Zend_OpenId_Provider::login establishes a session between the user and the identity
provider and stores the information about the user, who is now logged in. All following requests
from the same user won't require a login procedure- even if they come from another OpenID
enabled web site.
Note that this session is between end-user and identity provider only. OpenID
enabled sites know nothing about it.
1078
Zend_OpenId
<?php
$server = new Zend_OpenId_Provider();
The fact that the user is now logged in doesn't mean that the authentication must necessarily
succeed. The user may decide not to trust particular OpenID enabled sites. The following trust
screen allows the end user to make that choice. This choice may either be made only for current
requests or forever. In the second case, information about trusted/untrusted sites is stored in
an internal database, and all following authentication requests from this site will be handled
automatically without user interaction.
1079
Zend_OpenId
<?php
$server = new Zend_OpenId_Provider();
if (isset($_POST['allow'])) {
if (isset($_POST['forever'])) {
$server->allowSite($server->getSiteRoot($_GET));
}
$server->respondToConsumer($_GET);
} else if (isset($_POST['deny'])) {
if (isset($_POST['forever'])) {
$server->denySite($server->getSiteRoot($_GET));
}
Zend_OpenId::redirect($_GET['openid_return_to'],
array('openid.mode'=>'cancel'));
}
}
?>
<html>
<body>
<p>A site identifying as
<a href="<?php echo htmlspecialchars($server->getSiteRoot($_GET));?>">
<?php echo htmlspecialchars($server->getSiteRoot($_GET));?>
</a>
has asked us for confirmation that
<a href="<?php echo htmlspecialchars($server->getLoggedInUser());?>">
<?php echo htmlspecialchars($server->getLoggedInUser());?>
</a>
is your identity URL.
</p>
<form method="post">
<input type="checkbox" name="forever">
<label for="forever">forever</label><br>
<input type="hidden" name="openid_action" value="trust">
<input type="submit" name="allow" value="Allow">
<input type="submit" name="deny" value="Deny">
</form>
</body>
</html>
Production OpenID servers usually support the Simple Registration Extension that allows
consumers to request some information about the user from the provider. In this case, the trust
page can be extended to allow entering requested fields or selecting a specific user profile.
The following example is not complete. It doesn't provide GUI code for the user,
instead performing an automatic login and trust relationship instead. This is done
1080
Zend_OpenId
just to simplify the example; a production server should include some code from
previous examples.
define("TEST_ID", Zend_OpenId::absoluteURL("example-9-id.php"));
define("TEST_PASSWORD", "123");
If you compare this example with previous examples split in to separate pages, you will see only
the one difference besides the dispatch code: unset($_GET['openid_action']). This call
to unset is necessary to route the next request to main handler.
1081
Zend_OpenId
<?php
define("TEST_SERVER", Zend_OpenId::absoluteURL("example-10.php"));
define("TEST_ID", Zend_OpenId::selfURL());
define("TEST_PASSWORD", "123");
$server = new Zend_OpenId_Provider();
if (!$server->hasUser(TEST_ID)) {
$server->register(TEST_ID, TEST_PASSWORD);
$server->login(TEST_ID, TEST_PASSWORD);
$sreg = new Zend_OpenId_Extension_Sreg(array(
'nickname' =>'test',
'email' => 'test@test.com'
));
$root = Zend_OpenId::absoluteURL(".");
Zend_OpenId::normalizeUrl($root);
$server->allowSite($root, $sreg);
$server->logout();
}
?>
<html>
<head>
<link rel="openid.server" href="<?php echo TEST_SERVER;?>" />
</head>
<body>
<?php echo TEST_ID;?>
</body>
</html>
You should now pass this identity to the OpenID-enabled web site (use the Simple Registration
Extension example from the previous section), and it should use the following OpenID server
script.
This script is a variation of the script in the "Everything Together" example. It uses the
same automatic login mechanism, but doesn't contain any code for a trust page. The
user already trusts the example scripts forever. This trust was established by calling the
Zend_OpenId_Provider::allowSite() method in the identity script. The same method
associates the profile with the trusted URL. This profile will be returned automatically for a request
from the trusted URL.
1082
Zend_OpenId
define("TEST_ID", Zend_OpenId::absoluteURL("example-10-id.php"));
define("TEST_PASSWORD", "123");
• A set of methods to build an end-user GUI that allows users to register and manage their
trusted sites and profiles
• An abstract storage layer to store information about users, their sites and their profiles. It also
stores associations between the provider and OpenID-enabled sites. This layer is very similar
to that of the Zend_OpenId_Consumer class. It also uses file storage by default, but may
used with another backend.
• An abstract user-association layer that may associate a user's web browser with a logged-
in identity
The Zend_OpenId_Provider class doesn't attempt to cover all possible features that can be
implemented by OpenID servers, e.g. digital certificates, but it can be extended easily using
Zend_OpenId_Extensions or by standard object-oriented extension.
1083
Zend_Paginator
1. Introduction
Zend_Paginator est un composant flexible pour paginer des collections de données et
présenter ces données aux utilisateurs.
• paginer des données arbitraires, et pas simplement issues de base de données relationnelle ;
• ne pas forcer les utilisateurs à adhérer à une seule manière d'afficher les données ou de
retourner les contrôles de pagination ;
2. Utilisation
2.1. Paginer des collections de données
Afin de pouvoir paginer des éléments, Zend_Paginator doit posséder une manière générique
d'accéder aux sources de données. De ce fait, tous les accès aux données se font via des
adaptateurs de sources. Plusieurs adaptateurs existent par défaut :
Plutôt que de sélectionner chaque ligne correspondant à une requête fournie, les
adaptateurs DbSelect et DbTableSelect récupèrent seulement la quantité de
données nécessaire pour l'affichage de la page courante.
1084
Zend_Paginator
Pour créer une instance de Zend_Paginator, vous devez spécifier un adaptateur à son
constructeur:
$paginator = Zend_Paginator::factory($array);
Bien que l'instance soit techniquement utilisable dans l'état, dans votre contrôleur d'action vous
devrez informer le paginateur du numéro de page demandé par l'utilisateur. Ceci lui permet
d'avancer à travers les données paginées.
$paginator->setCurrentPageNumber($page);
La manière la plus simple de suivre et scruter cette valeur est via l'URL. Nous recommandons
l'utilisation d'un routeur compatible avec Zend_Controller_Router_Interface, mais ceci
n'est pas nécessaire.
Voici une route que vous pourriez définir dans un fichier de configuration INI:
routes.example.route = articles/:articleName/:page
routes.example.defaults.controller = articles
routes.example.defaults.action = view
routes.example.defaults.page = 1
routes.example.reqs.articleName = \w+
routes.example.reqs.page = \d+
Avec une telle route (et en utilisant les composants MVC de Zend Framework), vous pourriez
spécifier le numéro de la page de cette manière :
$paginator->setCurrentPageNumber($this->_getParam('page'));
Enfin, il faut passer l'instance du paginateur à votre vue. Si vous utilisez Zend_View avec l'aide
d'action ViewRenderer, ceci fonctionnera :
$this->view->paginator = $paginator;
1085
Zend_Paginator
To use the DbSelect and DbTableSelect adapters you don't have to retrieve the data upfront from
the database. Both adapters do the retrieval for you, aswell as the counting of the total pages.
If additional work has to be done on the database results the adapter getItems() method has
to be extended in your application.
Additionally these adapters do not fetch all records from the database Instead, the adapters
manipulates the original query to produce the corresponding COUNT query. Paginator then
executes that COUNT query to get the number of rows. This does require an extra round-trip to
the database, but this is many times faster than fetching an entire result set and using count().
Especially with large collections of data.
The database adapters will try and build the most efficient query that will execute on pretty much
all modern databases. However, depending on your database or even your own schema setup,
there might be more efficient ways to get a rowcount. For this scenario the database adapters
allow you to set a custom COUNT query. For example, if you keep track of the count of blog
posts in a separate table, you could achieve a faster count query with the following setup:
This approach will probably not give you a huge performance gain on small collections and/or
simple select queries. However, with complex queries and large collections, a similar approach
could give you a significant performance boost.
<html>
<body>
<h1>Example</h1>
<?php if (count($this->paginator)): ?>
<ul>
<?php foreach ($this->paginator as $item): ?>
<li><?php echo $item; ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<?php echo $this->paginationControl($this->paginator,
'Sliding',
'my_pagination_control.phtml'); ?>
</body>
</html>
Notez l'appel à l'aide de vue en fin de script. PaginationControl accepte jusqu'à quatre
paramètres : l'instance du paginateur, un type de défilement (optionnel), un partial de vue
(optionnel) et un tableau de paramètres additionnels.
1086
Zend_Paginator
Les second et troisième paramètres sont très importants. Alors que le partial est utiliser pour
déterminer comment présenter les données, le type de défilement définira la manière dont ils se
comportent. Disons que le partial ressemble à un contrôle de recherche, comme ce qui suit :
Que se passe-t-il lorsque l'utilisateur clique sur le lien "next" quelques fois? Plusieurs choses
peuvent arriver. Le numéro de la page courante pourrait rester au milieu (comme c'est le cas sur
Yahoo!), ou il pourrait aussi bien avancer à la fin de la fourchette des pages et apparaître encore
à gauche lorsque l'utilisateur clique alors sur "next". Le nombre de pages pourrait alors s'étendre
ou se comprimer alors que l'utilisateur avance ("scroll") à travers (comme chez Google).
Le quatrième et dernier paramètre est réservé pour un tableau associatif optionnel de variables
supplémentaires que vous voulez rendre disponible dans vos partiels de vues (disponible via
$this). Par exemple, ces valeurs permettent d'inclure des paramètres d'URL supplémentaires
pour les liens de pagination.
En spécifiant le partial de vue par défaut, le défilement et l'instance de vue, vous pouvez alors
vous affranchir totalement des appels à PaginationControl :
Zend_Paginator::setDefaultScrollingStyle('Sliding');
Zend_View_Helper_PaginationControl::setDefaultViewPartial(
'my_pagination_control.phtml'
);
$paginator->setView($view);
Utilisez dès lors un simple echo dans le script de vue pour le rendu du paginateur complet:
1087
Zend_Paginator
$smarty->assign('pages', $paginator->getPages());
{$pages->pageCount}
Pagination de recherche :
<!--
Voir http://developer.yahoo.com/ypatterns/pattern.php?pattern=searchpagination
-->
Pagination d'objets :
<!--
Voir http://developer.yahoo.com/ypatterns/pattern.php?pattern=itempagination
-->
1088
Zend_Paginator
Pagination Dropdown :
1089
Zend_Paginator
3. Configuration
Zend_Paginator possède de nombreuses méthodes de configuration qui peuvent être
appelées :
1090
Zend_Paginator
Méthode Description
setView Règle l'instance de la vue, pour une facilité de
rendu.
4. Utilisation avancée
4.1. Adaptateurs de source de données personnalisée
À partir d'un moment, vous pourriez avoir besoin de parcourir un type de données qui n'est pas
couvert par les adaptateurs fournis par défaut. Dans ce cas, vous devrez écrire vos propres
adaptateurs.
• count()
• getItems($offset, $itemCountPerPage)
De plus, vous voudrez peut-être implémenter un constructeur qui prend votre source de données
comme paramètre et le stocke comme propriété protégée ou privée. La manière suivant laquelle
vous allez spécifiquement faire ceci, vous incombe.
Si vous avez déjà utilisé l'interface SPL Countable, vous êtes familier avec count(). Utilisé
avec Zend_Paginator, il s'agit du nombre total d'éléments dans la collection de données. De
plus, l'instance Zend_Paginator fournit une méthode countAllItems() qui proxie vers la
méthode count() de l'adaptateur.
La méthode getItems() est seulement légèrement plus compliquée. Pour ceci, les paramètres
sont un point de départ et un nombre d'éléments à afficher par page. Vous devez retourner la
portion appropriée de données. Pour un tableau, il s'agirait :
Cette méthode doit calculer des bornes inférieures et supérieures des numéros de page dans la
plage des pages dites "local" (c'est-à-dire qui sont proches de la page courante).
1091
Zend_Paginator
Il n'y a rien de spécial au sujet de cet appel ; c'est simplement une méthode pratique pour vérifier
la validité de la limite inférieure et supérieure et pour renvoyer un tableau de ces bornes au
paginateur.
Quand vous êtes prêt à utiliser votre style de défilement, vous devez informer Zend_Paginator
dans quel dossier le chercher, en réalisant ceci :
$prefix = 'Mon_Paginator_StyleDefilement';
$path = 'Mon/Paginator/StyleDefilement/';
Zend_Paginator::addScrollingStylePrefixPath($prefix, $path);
$paginator = Zend_Paginator::factory($someData);
$fO = array('lifetime' => 3600, 'automatic_serialization' => true);
$bO = array('cache_dir'=>'/tmp');
$cache = Zend_cache::factory('Core', 'File', $fO, $bO);
Zend_Paginator::setCache($cache);
Tant que Zend_Paginator possède une instance de Zend_Cache_Core, les données seront
mises en cache. Parfois vous ne voudrez pas mettre en cache les données même si vous avez
déjà fourni un instance de cache. Vous pourrez alors utiliser la méthode setCacheEnable() :
$paginator = Zend_Paginator::factory($someData);
// $cache est une instance de Zend_Cache_Core
Zend_Paginator::setCache($cache);
// ... plus loin dans le script
$paginator->setCacheEnable(false);
// le cache est maintenant désactivé
Quand un cache est paramétré, les données y sont automatiquement stockées et extraites. Il
peut alors être utile de vider le cache manuellement. Vous pouvez réaliser ceci en appelant
clearPageItemCache($pageNumber). Si vous ne passer aucun paramètre, le cache entier
sera vidé. Vous pouvez fournir optionnellement un paramètre représentant le numéro de page
à enlever du cache :
$paginator = Zend_Paginator::factory($someData);
Zend_Paginator::setCache($cache);
$items = $paginator->getCurrentItems();
// la page 1 est maintenant en cache
$page3Items = $paginator->getItemsByPage(3);
// la page 3 est maintenant en cache
Changer le nombre d'éléments par page videra tout le cache comme s'il était devenu invalide :
1092
Zend_Paginator
$paginator = Zend_Paginator::factory($someData);
Zend_Paginator::setCache($cache);
// récupérer des éléments
$items = $paginator->getCurrentItems();
Il est aussi possible de voir les données en cache et de les appeler directement grâce à la
méthode getPageItemCache() :
$paginator = Zend_Paginator::factory($someData);
$paginator->setItemCountPerPage(3);
Zend_Paginator::setCache($cache);
interface Zend_Paginator_AdapterAggregate
{
/**
* Return a fully configured Paginator Adapter from this method.
*
* @return Zend_Paginator_Adapter_Abstract
*/
public function getPaginatorAdapter();
}
The interface is fairly small and only expects you to return an instance of
Zend_Paginator_Adapter_Abstract. An Adapter Aggregate instance is then recognized
by both Zend_Paginator::factory and the constructor of Zend_Paginator and handled
accordingly.
1093
Zend_Pdf
1. Introduction
Zend_Pdf est un composant entièrement écrit en PHP5 permettant la manipulation de
documents PDF (Portable Document Format). Il peut charger des documents, créer, modifier
et les sauvegarder. Cela peut aider toute les applications PHP à générer dynamiquement des
documents PDF en modifiant un modèle existant ou en générant un document à partir de rien.
Zend_Pdf supporte les fonctionnalités suivantes :
1
• Créer un nouveau document ou en charger un qui existe déjà.
• Manipuler les pages d'un document. Changer l'ordre des pages, ajouter des nouvelles pages,
retirer des pages.
• Dessiner du texte en utilisant une des 14 polices standard ou vos propres polices TrueType.
• Rotations.
2
• Inclure des images.
Pour créer un nouveau document, un nouvel objet Zend_Pdf doit être créé.
La classe Zend_Pdf fournis deux méthodes statiques pour charger un PDF existant. Ce sont
les méthodes Zend_Pdf::load() et Zend_Pdf::parse(). Les deux retournent un objet
Zend_Pdf ou lève une exception en cas d'erreur.
...
/// Crée un nouveau document PDF.
$pdf1 = new Zend_Pdf();
Le format de fichier PDF supporte la mise à jour incrémentale d'un document. Ainsi chaque
fois que le document est mis à jour, une nouvelle version du document est créée. Le module
Zend_Pdf supporte la récupération d'une version spécifique.
1094
Zend_Pdf
...
// Charge la version précédente d'un document PDF.
$pdf1 = Zend_Pdf::load($fileName, 1);
...
// Charge un document PDF.
$pdf = Zend_Pdf::load($fileName);
...
// Met à jour le document
$pdf->save($fileName, true);
// Sauvegarde le document dans un nouveau fichier.
$pdf->save($newFileName);
Les pages d'un PDF proviennent d'un PDF existant, ou sont créées à partir de rien.
3
La méthode Zend_Pdf::rollback() doit être appelée avant tout changement. Sinon le comportement est indéfini.
1095
Zend_Pdf
Une nouvelle page peut-être obtenu en créant un nouvel objet Zend_Pdf_Page ou en appelant
la méthode Zend_Pdf::newPage()qui retourne un objet Zend_Pdf_Page. La différence est
que la méthode Zend_Pdf::newPage() crée une page directement attachée au document. A
la différence des pages non attachées à un document, elle ne peut-être utilisée dans plusieurs
4
documents PDF, mais est un peu plus performante. . C'est à vous de choisir quel approche
doit-être utilisée.
• Zend_Pdf_Page::SIZE_A4
• Zend_Pdf_Page::SIZE_A4_LANDSCAPE
• Zend_Pdf_Page::SIZE_LETTER
• Zend_Pdf_Page::SIZE_LETTER_LANDSCAPE
Les pages du document sont stockées dans l'attribut public $pages de la classe Zend_Pdf.
C'est un tableau d'objet Zend_Pdf_Page. Il définit l'ensemble des pages, ainsi que l'ordre de
celle-ci et peut-être manipulé comme un tableau classique :
...
// Inverse l'ordre des pages
$pdf->pages = array_reverse($pdf->pages);
...
// Ajoute une nouvelle page
$pdf->pages[] = new Zend_Pdf_Page(Zend_Pdf_Page::SIZE_A4);
// Ajoute une nouvelle page
$pdf->pages[] = $pdf->newPage(Zend_Pdf_Page::SIZE_A4);
4
C'est une limitation de la version courante du module Zend_Pdf. Ce sera corrigé dans une future version. Mais les pages non
attachées à un document donneront toujours de meilleurs résultats pour partager une page entre plusieurs documents.
1096
Zend_Pdf
...
// Stocke le modèle dans une variable séparée
$template = $pdf->pages[$templatePageIndex];
...
// Ajoute une nouvelle page
$page1 = new Zend_Pdf_Page($template);
$pdf->pages[] = $page1;
...
C'est pratique si plusieurs pages doivent être créées sur le même modèle.
5. Dessiner
5.1. Géométrie
Le format PDF utilise la même géométrie que le format PostScript. Elle démarre d'en bas à
gauche et est mesuré en points (1/72 inch soit 0,352778 mm).
$width = $pdfPage->getWidth();
$height = $pdfPage->getHeight();
5.2. Couleurs
Le format PDF a d'excellentes capacités dans la représentation des couleurs. Le module
Zend_Pdf supporte les espaces de couleur : niveaux de gris, RGB et CMJN. Chacun
d'entre eux peut-être utilisé à chaque fois qu'un objet Zend_Pdf_Color est requis. Les
classes Zend_Pdf_Color_GrayScale, Zend_Pdf_Color_RGB et Zend_Pdf_Color_CMYK
fournissent cette fonctionnalité :
1097
Zend_Pdf
Les différentes couleurs HTML sont aussi fourni avec la classe Zend_Pdf_Color_Html :
/**
* Dessine une ligne de x1,y1 à x2,y2.
*
* @param float $x1
* @param float $y1
* @param float $x2
* @param float $y2
* @return Zend_Pdf_Page
*/
public function drawLine($x1, $y1, $x2, $y2);
/**
* Draw a rounded rectangle.
*
* Fill types:
* Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE - fill rectangle and stroke (default)
* Zend_Pdf_Page::SHAPE_DRAW_STROKE - stroke rectangle
* Zend_Pdf_Page::SHAPE_DRAW_FILL - fill rectangle
*
* radius is an integer representing radius of the four corners, or an array
* of four integers representing the radius starting at top left, going
* clockwise
*
* @param float $x1
* @param float $y1
* @param float $x2
* @param float $y2
* @param integer|array $radius
* @param integer $fillType
* @return Zend_Pdf_Page
*/
public function drawRoundedRectangle($x1, $y1, $x2, $y2, $radius,
$fillType = Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE);
/**
* Dessine un rectangle.
*
* Type de remplissage:
* Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE
* - remplit le rectangle et dessine le contour (par défaut)
* Zend_Pdf_Page::SHAPE_DRAW_STROKE
* - trace uniquement le contour du rectangle
* Zend_Pdf_Page::SHAPE_DRAW_FILL
* - remplit le rectangle
1098
Zend_Pdf
*
* @param float $x1
* @param float $y1
* @param float $x2
* @param float $y2
* @return Zend_Pdf_Page
* @param integer $fillType
* @return Zend_Pdf_Page
*/
public function drawRectangle(
$x1, $y1, $x2, $y2, $fillType = Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE);
/**
* Dessine un polygone.
*
* Si $fillType est Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE
* ou Zend_Pdf_Page::SHAPE_DRAW_FILL,
* le polygone est automatiquement fermé.
* Regardez la description détaillée de ces méthodes dans la
* documentation du format PDF
* (section 4.4.2 Path painting Operators, Filling)
*
* @param array $x - tableau de float (les coordonnés X des sommets)
* @param array $y - tableau de float (les coordonnés Y des sommets)
* @param integer $fillType
* @param integer $fillMethod
* @return Zend_Pdf_Page
*/
public function drawPolygon(
$x, $y,
$fillType = Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE,
$fillMethod = Zend_Pdf_Page::FILL_METHOD_NON_ZERO_WINDING);
/**
* Dessine un cercle avec comme centre x, y et comme rayon radius.
*
* Les angles sont en radian
*
* Signatures des méthodes:
* drawCircle($x, $y, $radius);
* drawCircle($x, $y, $radius, $fillType);
* drawCircle($x, $y, $radius, $startAngle, $endAngle);
* drawCircle($x, $y, $radius, $startAngle, $endAngle, $fillType);
*
*
* Ce n'est pas réellement un cercle, car le format supporte
* uniquement des courbe de Bezier cubique.
* Mais c'est une très bonne approximation.
* La différence avec un vrai cercle est de au maximum 0.00026 radians
* (avec les angles PI/8, 3*PI/8, 5*PI/8, 7*PI/8, 9*PI/8, 11*PI/8,
* 13*PI/8 et 15*PI/8).
* Avec les angles 0, PI/4, PI/2, 3*PI/4, PI, 5*PI/4, 3*PI/2 et 7*PI/4
* c'est exactement la tangente d'un cercle.
*
* @param float $x
* @param float $y
* @param float $radius
* @param mixed $param4
* @param mixed $param5
1099
Zend_Pdf
/**
* Dessine une ellipse dans le rectangle spécifié.
*
* Signatures des méthodes:
* drawEllipse($x1, $y1, $x2, $y2);
* drawEllipse($x1, $y1, $x2, $y2, $fillType);
* drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle);
* drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle, $fillType);
*
* Les angles sont en radians
*
* @param float $x1
* @param float $y1
* @param float $x2
* @param float $y2
* @param mixed $param5
* @param mixed $param6
* @param mixed $param7
* @return Zend_Pdf_Page
*/
public function drawEllipse(
$x1, $y1, $x2, $y2, $param5 = null, $param6 = null, $param7 = null);
/**
* Dessine une ligne de texte à la position x,y.
*
* @param string $text
* @param float $x
* @param float $y
* @param string $charEncoding (optionnel) encodage des caractères du texte.
* Par défaut le réglage système est utilisé.
* @throws Zend_Pdf_Exception
* @return Zend_Pdf_Page
*/
public function drawText($text, $x, $y, $charEncoding = '');
...
$pdfPage->drawText('Bonjour le monde!', 72, 720);
...
Par défaut, les chaînes de texte sont interprétées en utilisant l'encodage du système. Si vous
avez une chaîne qui utilise un encodage différent (comme les chaînes UTF-8 lues depuis
1100
Zend_Pdf
une fichier sur le disque, ou une chaîne MacRoman obtenue depuis une base de données),
vous pouvez indiquer l'encodage au moment du dessin et Zend_Pdf gérera la conversion pour
vous. Vous pouvez fournir des chaînes dans n'importe quel encodage supporté par la fonction
iconv() de PHP:
...
// Lit une chaîne UTF-8 à partir du disque
$unicodeString = fread($fp, 1024);
/**
* Choisit la police courante.
*
* @param Zend_Pdf_Resource_Font $font
* @param float $fontSize
* @return Zend_Pdf_Page
*/
public function setFont(Zend_Pdf_Resource_Font $font, $fontSize);
Les documents PDF supportent PostScript Type 1 et les polices TrueType, mais également
deux types spécifiques PDF, Type3 et les polices composites. Il y a aussi 14 polices Type 1
standard intégré dans tout lecteur de PDF : Courier (4 styles), Helvetica (4 styles), Times (4
styles), Symbol, et Zapf Dingbats.
Zend_Pdf supporte actuellement les 14 polices standard mais également vos propres
police TrueType. Les objets de police obtenus via une des deux fabriques méthodes :
Zend_Pdf_Font::fontWithName($fontName) pour les 14 polices PDF standard ou
Zend_Pdf_Font::fontWithPath($filePath) pour les polices personnalisées.
...
// Crée une nouvelle police
$font = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA);
// Applique la police
$pdfPage->setFont($font, 36);
...
Les constantes pour les 14 polices standard sont définis dans la classe Zend_Pdf_Font :
• Zend_Pdf_Font::FONT_COURIER
• Zend_Pdf_Font::FONT_COURIER_BOLD
• Zend_Pdf_Font::FONT_COURIER_ITALIC
1101
Zend_Pdf
• Zend_Pdf_Font::FONT_COURIER_BOLD_ITALIC
• Zend_Pdf_Font::FONT_TIMES
• Zend_Pdf_Font::FONT_TIMES_BOLD
• Zend_Pdf_Font::FONT_TIMES_ITALIC
• Zend_Pdf_Font::FONT_TIMES_BOLD_ITALIC
• Zend_Pdf_Font::FONT_HELVETICA
• Zend_Pdf_Font::FONT_HELVETICA_BOLD
• Zend_Pdf_Font::FONT_HELVETICA_ITALIC
• Zend_Pdf_Font::FONT_HELVETICA_BOLD_ITALIC
• Zend_Pdf_Font::FONT_SYMBOL
• Zend_Pdf_Font::FONT_ZAPFDINGBATS
Vous pouvez aussi prendre n'importe quelle police TrueType (extension habituelle ".ttf") ou
OpenType (".otf") si elles ont une silhouette TrueType. Pour l'instant non supportée, les polices
Mac Os X ".dfont" et les collections TrueType Microsoft (".ttc") seront intégrées dans une version
future.
Pour utiliser une police TrueType, vous devez fournir le chemin de fichier complet vers cette
police. Si la police ne peut pas être lue pour une quelconque raison, ou si ce n'est pas une police
TrueType, la méthode lèvera une exception :
...
// Crée la nouvelle police
$goodDogCoolFont = Zend_Pdf_Font::fontWithPath('/chemin/vers/GOODDC__.TTF');
Par défaut, les polices personnalisées seront incorporées dans le document PDF résultant. Cela
permet aux destinataires de voir la page comme prévu, même s'ils ne font pas installer les polices
appropriées sur leur système. En cas de problème avec la taille du fichier généré, vous pouvez
demander que la police ne soit pas incorporé en passant l'option 'ne pas inclure' à la méthode
de création :
Exemple 598. Créer une police TrueType sans l'incorporer dans le document PDF
...
// Crée la nouvelle police
$goodDogCoolFont =
Zend_Pdf_Font::fontWithPath('/chemin/vers/GOODDC__.TTF',
Zend_Pdf_Font::EMBED_DONT_EMBED);
1102
Zend_Pdf
Si les polices ne sont pas incorporées mais que le destinataire du fichier PDF a ces polices
installées sur son système, il verra le document comme prévu. Si la police correcte n'est pas
installée, l'application de visualisation du PDF fera de son mieux pour synthétiser une police de
remplacement.
Quelques polices ont les règles de licence très spécifiques qui les empêchent d'être incorporées
dans des documents PDF. Donc vous devez faire attention, si vous essayez d'utiliser une police
qui ne peut pas être incorporée, la méthode de création lèvera une exception.
Vous pouvez toujours utiliser ces polices, mais vous devez passer le paramètre 'ne pas inclure'
comme décrit ci-dessous, ou vous pouvez simplement bloquer l'exception :
Exemple 599. Ne pas lever d'exception pour les polices ne pouvant être
incorporées
...
$font =
Zend_Pdf_Font::fontWithPath('/chemin/vers/PoliceNonIncorporable.ttf',
Zend_Pdf_Font::EMBED_SUPPRESS_EMBED_EXCEPTION);
...
Cette technique de suppression est préférée si vous permettez aux utilisateurs de choisir leurs
propres polices. Les polices qui peuvent être incorporées dans le document PDF le seront ; les
autres ne le seront pas.
Les fichiers de police peuvent être assez grands, certains peuvent atteindre des dizaines de
méga-octets. Par défaut, toutes les polices incorporées sont comprimées en utilisant le schéma
de compression Flate, ayant pour résultat un gain d'espace de 50% en moyenne. Si, pour une
quelconque raison, vous ne voulez pas comprimer la police, vous pouvez le neutraliser avec
une option :
...
$font =
Zend_Pdf_Font::fontWithPath('/chemin/vers/PoliceDeGrandeTaille.ttf',
Zend_Pdf_Font::EMBED_DONT_COMPRESS);
...
...
$font = Zend_Pdf_Font::fontWithPath(
$cheminVersPoliceQuelconque,
(Zend_Pdf_Font::EMBED_SUPPRESS_EMBED_EXCEPTION |
Zend_Pdf_Font::EMBED_DONT_COMPRESS));
...
1103
Zend_Pdf
Zend_Pdf utilise l'encodage CP1252 (WinLatin1) pour tracer les textes avec les polices
standard.
Le texte peut encore être fourni dans n'importe quel autre encodage, qui doit être spécifié s'il
diffère de celui en cours. Seulement les caractères WinLatin1 seront tracés réellement.
...
$font = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_COURIER);
$pdfPage->setFont($font, 36)
->drawText('Euro sign - €', 72, 720, 'UTF-8')
->drawText('Text with umlauts - à è ì', 72, 650, 'UTF-8');
...
Ceci peut être utile lors des mises à jour de document avec ajout de texte. Sans cette
fonctionnalité vous devez attacher et probablement intégrer la police dans le document chaque
fois que vous voulez le mettre à jour.
Les objets Zend_Pdf et Zend_Pdf_Page fournissent une méthode spéciale pour extraire toutes
les polices mentionnés à l'intérieur d'un document ou d'une page :
...
$pdf = Zend_Pdf::load($cheminVersDocument);
...
// Récupère toutes les polices du document
$listePolice = $pdf->extractFonts();
$pdf->pages[] = ($page = $pdf->newPage(Zend_Pdf_Page::SIZE_A4));
$yPosition = 700;
foreach ($listePolice as $police) {
$page->setFont($police, 15);
$page->drawText(
$police->getFontName(Zend_Pdf_Font::NAME_POSTSCRIPT, 'fr', 'UTF-8')
. ': Le renard brun rapide saute par-dessus le chien paresseux',
100,
$yPosition,
'UTF-8');
$yPosition -= 30;
}
...
// Récupère toutes les polices référencées dans la première page du document
$firstPage = reset($pdf->pages);
$firstPageFonts = $firstPage->extractFonts();
...
1104
Zend_Pdf
Exemple 604. Extraction d'une police à partir d'un document chargé en spécifiant
le nom de police
...
$pdf = new Zend_Pdf();
...
$pdf->pages[] = ($page = $pdf->newPage(Zend_Pdf_Page::SIZE_A4));
$police = Zend_Pdf_Font::fontWithPath($cheminVersPolices);
$page->setFont($police, $taillePolice);
$page->drawText($texte, $x, $y);
...
// Ce nom de police peut être stocké quelquepart...
$fontName = $font->getFontName(Zend_Pdf_Font::NAME_POSTSCRIPT, 'fr', 'UTF-8');
...
$pdf->save($cheminVersDocument);
...
...
$pdf = Zend_Pdf::load($cheminVersDocument);
...
$pdf->pages[] = ($page = $pdf->newPage(Zend_Pdf_Page::SIZE_A4));
$police = $pdf->extractFont($nomPolice);
/* $pageSource->extractFont($nomPolice) peut aussi être utilisé ici */
$page->setFont($police, $taillePolice);
$page->drawText($texte, $x, $y);
...
$pdf->save($cheminVersDocument, true /* mise à jour de type incrémental */);
...
Les polices extraites peuvent être utilisées à la place de n'importe quelle autre police avec les
limitations suivantes :
• La police extraite peut être employée seulement dans le cadre du document à partir duquel
elle a été extraite.
• Les possibles programmes de polices incorporées ne sont pas extraits réellement. La police
ainsi extraite ne peut pas fournir la métrique correcte de police et la police originale doit être
utilisée pour les calculs de largeur des textes :
...
$police = $pdf->extractFont($fontName);
$policeOriginal = Zend_Pdf_Font::fontWithPath($cheminVersPolices);
1105
Zend_Pdf
/**
* Insère une image à la position spécifiée dans la page
*
* @param Zend_Pdf_Resource_Image $image
* @param float $x1
* @param float $y1
* @param float $x2
* @param float $y2
* @return Zend_Pdf_Page
*/
public function drawImage(Zend_Pdf_Resource_Image $image, $x1, $y1, $x2, $y2);
...
//Charger une image
$image = Zend_Pdf_Image::imageWithPath('mon_image.jpg');
Important ! Le support JPEG nécessite que l'extension PHP GD soit installé. Important ! Le
support PNG nécessite que l'extension ZLIB soit configuré pour accepter les images avec canaux
Alpha.
/**
* Choisit le modèle de tiret.
*
* modele est un tableau de floats: array(longueur_visible,
* longueur_invisible, longueur_visible, longueur_invisible,
* ...)
* phase est le décalage à partir du début de la ligne.
*
* @param array $modele
1106
Zend_Pdf
The nonzero winding number rule determines whether a given point is inside
a path by conceptually drawing a ray from that point to infinity in any direction
and then examining the places where a segment of the path crosses the ray.
Starting with a count of 0, the rule adds 1 each time a path segment crosses
the ray from left to right and subtracts 1 each time a segment crosses from
right to left. After counting all the crossings, if the result is 0 then the point is
outside the path; otherwise it is inside. Note: The method just described does
not specify what to do if a path segment coincides with or is tangent to the
chosen ray. Since the direction of the ray is arbitrary, the rule simply chooses
a ray that does not encounter such problem intersections. For simple convex
paths, the nonzero winding number rule defines the inside and outside as
one would intuitively expect. The more interesting cases are those involving
complex or self-intersecting paths like the ones shown in Figure 4.10 (in a
PDF Reference). For a path consisting of a five-pointed star, drawn with five
connected straight line segments intersecting each other, the rule considers
the inside to be the entire area enclosed by the star, including the pentagon in
the center. For a path composed of two concentric circles, the areas enclosed
by both circles are considered to be inside, provided that both are drawn in
the same direction. If the circles are drawn in opposite directions, only the
"doughnut" shape between them is inside, according to the rule; the "doughnut
hole" is outside.
• Zend_Pdf_Page::FILL_METHOD_EVEN_ODD
An alternative to the nonzero winding number rule is the even-odd rule. This
rule determines the "insideness" of a point by drawing a ray from that point
in any direction and simply counting the number of path segments that cross
the ray, regardless of direction. If this number is odd, the point is inside; if
even, the point is outside. This yields the same results as the nonzero winding
1107
Zend_Pdf
number rule for paths with simple shapes, but produces different results for
more complex shapes. Figure 4.11 (in a PDF Reference) shows the effects of
applying the even-odd rule to complex paths. For the five-pointed star, the rule
considers the triangular points to be inside the path, but not the pentagon in
the center. For the two concentric circles, only the "doughnut" shape between
the two circles is considered inside, regardless of the directions in which the
circles are drawn.
/**
* Rotation de la page
*
* @param float $x - la coordonnée X du point de rotation
* @param float $y - la coordonnée X du point de rotation
* @param float $angle - angle de rotation
* @return Zend_Pdf_Page
*/
public function rotate($x, $y, $angle);
/**
* Mise à l'échelle
*
* @param float $xScale - X dimention scale factor
* @param float $yScale - Y dimention scale factor
* @return Zend_Pdf_Page
*/
public function scale($xScale, $yScale);
/**
* Décalage du système de coordonnées
*
* @param float $xShift - coordonnées X du décalage
* @param float $yShift - coordonnées Y du décalage
* @return Zend_Pdf_Page
*/
public function translate($xShift, $yShift);
1108
Zend_Pdf
/**
* Mise en biais du système de coordonnées
*
* @param float $x - the X co-ordinate of axis skew point
* @param float $y - the Y co-ordinate of axis skew point
* @param float $xAngle - X axis skew angle
* @param float $yAngle - Y axis skew angle
* @return Zend_Pdf_Page
*/
public function skew($x, $y, $xAngle, $yAngle);
/**
* Sauvegarde l'état graphique de la page.
* Cela prend un instantané des styles courants, des zones de dessins
* et de toutes les rotations/translations/changements de taille appliqués.
*
* @return Zend_Pdf_Page
*/
public function saveGS();
/**
* Restaure le dernier état graphique sauvegarder avec saveGS().
*
* @return Zend_Pdf_Page
*/
public function restoreGS();
/**
* Découpe la zone courante avec un rectangle.
*
* @param float $x1
* @param float $y1
* @param float $x2
* @param float $y2
* @return Zend_Pdf_Page
*/
public function clipRectangle($x1, $y1, $x2, $y2);
/**
1109
Zend_Pdf
/**
* Découpe la zone courante avec un cercle.
*
* @param float $x
* @param float $y
* @param float $radius
* @param float $startAngle
* @param float $endAngle
* @return Zend_Pdf_Page
*/
public function clipCircle(
$x, $y, $radius, $startAngle = null, $endAngle = null);
/**
* Découpe la zone courante avec une ellipse.
*
* Signatures des méthodes:
* drawEllipse($x1, $y1, $x2, $y2);
* drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle);
*
* @todo s'occuper des cas spéciaux avec $x2-$x1 == 0 ou $y2-$y1 == 0
*
* @param float $x1
* @param float $y1
* @param float $x2
* @param float $y2
* @param float $startAngle
* @param float $endAngle
* @return Zend_Pdf_Page
*/
public function clipEllipse(
$x1, $y1, $x2, $y2, $startAngle = null, $endAngle = null);
5.14. Styles
La classe Zend_Pdf_Style fournit les fonctionnalités de style.
Les styles peuvent être utilisés pour stocker des paramètre d'état graphique et de les appliquer
à une page PDF en une seule opération :
/**
* Choisit le style à utiliser pour les futures opérations
* de dessin sur cette page
*
* @param Zend_Pdf_Style $style
* @return Zend_Pdf_Page
*/
1110
Zend_Pdf
/**
* Renvoie le style appliqué à la page.
*
* @return Zend_Pdf_Style|null
*/
public function getStyle();
/**
* Choisit la couleur de ligne.
*
* @param Zend_Pdf_Color $color
* @return Zend_Pdf_Page
*/
public function setLineColor(Zend_Pdf_Color $color);
/**
* Récupère la couleur de ligne.
*
* @return Zend_Pdf_Color|null
* @return Zend_Pdf_Page
*/
public function getLineColor();
/**
* Choisit l'épaisseur de ligne.
*
* @param float $width
* @return Zend_Pdf_Page
*/
public function setLineWidth($width);
/**
* Récupère l'épaisseur de ligne.
*
* @return float
* @return Zend_Pdf_Page
*/
public function getLineWidth($width);
/**
* Choisit le style de tiret
*
* @param array $pattern
* @param float $phase
* @return Zend_Pdf_Page
*/
public function setLineDashingPattern($pattern, $phase = 0);
/**
* Récupère le style de tiret
*
1111
Zend_Pdf
* @return array
*/
public function getLineDashingPattern();
/**
* Récupère le modèle de tiret
*
* @return float
*/
public function getLineDashingPhase();
/**
* Choisit la couleur de remplissage.
*
* @param Zend_Pdf_Color $color
* @return Zend_Pdf_Page
*/
public function setFillColor(Zend_Pdf_Color $color);
/**
* Récupère la couleur de remplissage.
*
* @return Zend_Pdf_Color|null
*/
public function getFillColor();
/**
* Choisit la police.
*
* @param Zend_Pdf_Font $font
* @param Zend_Pdf_Resource_Font $font
* @param float $fontSize
*/
public function setFont(Zend_Pdf_Resource_Font $font, $fontSize);
/**
* Modifie la taille de police.
*
* @param float $fontSize
* @return Zend_Pdf_Page
*/
public function setFontSize($fontSize);
/**
* Récupère la police courante
*
* @return Zend_Pdf_Resource_Font $font
*/
public function getFont();
/**
* Récupère la taille de la police
*
* @return float $fontSize
*/
1112
Zend_Pdf
5.15. Transparence
Le module Zend_Pdf supporte la gestion de la transparence.
/**
* Règle la transparence
*
* $alpha == 0 - transparent
* $alpha == 1 - opaque
*
* Transparency modes, supported by PDF:
* Normal (default), Multiply, Screen, Overlay, Darken,
* Lighten, ColorDodge, ColorBurn, HardLight,
* SoftLight, Difference, Exclusion
*
* @param float $alpha
* @param string $mode
* @throws Zend_Pdf_Exception
* @return Zend_Pdf_Page
*/
public function setAlpha($alpha, $mode = 'Normal');
6. Interactive Features
6.1. Destinations
A destination defines a particular view of a document, consisting of the following items:
Destinations may be associated with outline items (Document Outline (bookmarks)), annotations
(Annotations), or actions (Actions). In each case, the destination specifies the view of the
document to be presented when the outline item or annotation is opened or the action is
performed. In addition, the optional document open action can be specified.
6.1.1.1. Zend_Pdf_Destination_Zoom
Display the specified page, with the coordinates (left, top) positioned at the upper-left corner of
the window and the contents of the page magnified by the factor zoom.
1113
Zend_Pdf
Where:
NULL, specified for $left, $top or $zoom parameter means "current viewer application value".
• FloatgetLeftEdge();
• setLeftEdge(float $left);
• FloatgetTopEdge();
• setTopEdge(float $top);
• FloatgetZoomFactor();
• setZoomFactor(float $zoom);
6.1.1.2. Zend_Pdf_Destination_Fit
Display the specified page, with the coordinates (left, top) positioned at the upper-left corner of the
window and the contents of the page magnified by the factor zoom. Display the specified page,
with its contents magnified just enough to fit the entire page within the window both horizontally
and vertically. If the required horizontal and vertical magnification factors are different, use the
smaller of the two, centering the page within the window in the other dimension.
6.1.1.3. Zend_Pdf_Destination_FitHorizontally
Display the specified page, with the vertical coordinate top positioned at the top edge of the
window and the contents of the page magnified just enough to fit the entire width of the page
within the window.
Where:
1114
Zend_Pdf
• FloatgetTopEdge();
• setTopEdge(float $top);
6.1.1.4. Zend_Pdf_Destination_FitVertically
Display the specified page, with the horizontal coordinate left positioned at the left edge of the
window and the contents of the page magnified just enough to fit the entire height of the page
within the window.
Where:
• FloatgetLeftEdge();
• setLeftEdge(float $left);
6.1.1.5. Zend_Pdf_Destination_FitRectangle
Display the specified page, with its contents magnified just enough to fit the rectangle specified
by the coordinates left, bottom, right, and top entirely within the window both horizontally and
vertically. If the required horizontal and vertical magnification factors are different, use the smaller
of the two, centering the rectangle within the window in the other dimension.
Where:
• FloatgetLeftEdge();
• setLeftEdge(float $left);
• FloatgetBottomEdge();
1115
Zend_Pdf
• setBottomEdge(float $bottom);
• FloatgetRightEdge();
• setRightEdge(float $right);
• FloatgetTopEdge();
• setTopEdge(float $top);
6.1.1.6. Zend_Pdf_Destination_FitBoundingBox
Display the specified page, with its contents magnified just enough to fit its bounding box
entirely within the window both horizontally and vertically. If the required horizontal and vertical
magnification factors are different, use the smaller of the two, centering the bounding box within
the window in the other dimension.
6.1.1.7. Zend_Pdf_Destination_FitBoundingBoxHorizontally
Display the specified page, with the vertical coordinate top positioned at the top edge of the
window and the contents of the page magnified just enough to fit the entire width of its bounding
box within the window.
Where
• FloatgetTopEdge();
• setTopEdge(float $top);
6.1.1.8. Zend_Pdf_Destination_FitBoundingBoxVertically
Display the specified page, with the horizontal coordinate left positioned at the left edge of the
window and the contents of the page magnified just enough to fit the entire height of its bounding
box within the window.
1116
Zend_Pdf
Where
• FloatgetLeftEdge();
• setLeftEdge(float $left);
6.1.1.9. Zend_Pdf_Destination_Named
In addition to this, PDF document may contain a dictionary of such destinations which
may be used to reference from outside the PDF (e.g. 'http://www.mycompany.com/
document.pdf#chapter3').
StringgetName();
Each destination object (including named destinations) can be resolved using the
resolveDestination($destination) method. It returns corresponding Zend_Pdf_Page
object, if destination target is found, or NULL otherwise.
1117
Zend_Pdf
$pdf->pages[] = $page1;
$pdf->pages[] = $page2;
$destination1 = Zend_Pdf_Destination_Fit::create($page2);
$destination2 = Zend_Pdf_Destination_Fit::create($page3);
$pdf->setNamedDestination('Page2', $destination1);
$pdf->setNamedDestination('Page3', $destination2);
// Returns $destination2
$destination = $pdf->getNamedDestination('Page3');
// Returns $destination1
$pdf->resolveDestination(Zend_Pdf_Destination_Named::create('Page2'));
6.2. Actions
Instead of simply jumping to a destination in the document, an annotation or outline item can
specify an action for the viewer application to perform, such as launching an application, playing
a sound, or changing an annotation's appearance state.
The following action types are recognized while loading PDF document:
1118
Zend_Pdf
1119
Zend_Pdf
It's an array of Zend_Pdf_Action objects, which also may have their sub-actions.
$pdf->pages[] = $page1;
$pdf->pages[] = $page2;
$action1 = Zend_Pdf_Action_GoTo::create(
Zend_Pdf_Destination_Fit::create($page2));
$action2 = Zend_Pdf_Action_GoTo::create(
Zend_Pdf_Destination_Fit::create($page3));
$action3 = Zend_Pdf_Action_GoTo::create(
Zend_Pdf_Destination_Named::create('Chapter1'));
$action4 = Zend_Pdf_Action_GoTo::create(
Zend_Pdf_Destination_Named::create('Chapter5'));
$action2->next[] = $action3;
$action2->next[] = $action4;
$action1->next[] = $action2;
1120
Zend_Pdf
close individual items by clicking them with the mouse. When an item is open, its immediate
children in the hierarchy become visible on the screen; each child may in turn be open or closed,
selectively revealing or hiding further parts of the hierarchy. When an item is closed, all of its
descendants in the hierarchy are hidden. Clicking the text of any visible item activates the item,
causing the viewer application to jump to a destination or trigger an action associated with the
item.
$pdf = Zend_Pdf::load($path);
$pdf->save($path, true);
• setOptions(array $options) - set outline options. The following options are recognized:
'title', 'open', 'color', 'italic', 'bold', and 'target'.
1121
Zend_Pdf
• Zend_Pdf_Outline::create(array $options)
Each outline object may have child outline items listed in Zend_Pdf_Outline::
$childOutlines public property. It's an array of Zend_Pdf_Outline objects, so outlines are
organized in a tree.
$pdf = Zend_Pdf::load($path);
$pdf->save($path, true);
All outline items with unresolved destinations (or destinations of GoTo actions)
are updated while document saving by setting their targets to NULL. So document
will not be corrupted by removing pages referenced by outlines.
6.4. Annotations
An annotation associates an object such as a note, sound, or movie with a location on a page of a
PDF document, or provides a way to interact with the user by means of the mouse and keyboard.
1122
Zend_Pdf
A link annotation represents either a hypertext link to a destination elsewhere in the document
or an action to be performed.
A text annotation represents a "sticky note" attached to a point in the PDF document.
• setLeft(float $left)
• float getLeft()
• setRight(float $right)
• float getRight()
• setTop(float $top)
• float getTop()
• setBottom(float $bottom)
• float getBottom()
• setText(string $text)
• string getText()
Text annotation property is a text to be displayed for the annotation or, if this type of annotation
does not display text, an alternate description of the annotation's contents in human-readable
form.
• setDestination(Zend_Pdf_Target|string $target)
• Zend_Pdf_Target getDestination()
1123
Zend_Pdf
Historiquement ces informations sont stockées dans une structure spéciale Info. Cette structure
est disponible en lecture/écriture sous la forme d'un tableau associatif en utilisant la propriété
publique properties des objets Zend_Pdf :
$pdf = Zend_Pdf::load($pdfPath);
Les clés suivantes sont définies par la norme PDF v1.4 (Acrobat 5) :
• Creator - string, optionnel, si le document a été converti en PDF à partir d'un autre format, le
nom de l'application (par exemple, Adobe FrameMaker®) qui a créé le document original à
partir duquel il a été converti.
• Producer - string, optionnel, si le document a été converti en PDF à partir d'un autre format,
le nom de l'application (par exemple, Acrobat Distiller) qui l'a converti en PDF.
• CreationDate - string, optionnel, la date et l'heure auxquelles le document a été créé sous la
forme suivante : "D:YYYYMMDDHHmmSSOHH'mm'", où :
• MM est le mois.
• O est la différence de l'heure locale par rapport au temps universel (UT), dénoté par un des
caractères +, de #, ou de Z (voir ci-dessous).
• HH suivi par ' est la valeur absolue du décalage par rapport à l'UT en heures (00–23).
• mm suivi par ' est la valeur absolue du décalage par rapport à l'UT en minutes (00–59).
Le caractère apostrophe (') après "HH" et "mm" est un élément de la syntaxe. Chaque
champs après l'année est optionnel. (Le préfixe "D:", bien que lui aussi optionnel, est fortement
recommandé.) Les valeurs par défaut pour "MM" et "DD" sont à "01" ; tous les autres champs
numériques ont par défaut des valeurs à zéro. Un signe plus (+) en tant que valeur pour le
champs "0" signifie que l'heure locale est après l'UT, un signe moins (-) que l'heure locale est
avant l'UT, et la lettre "Z" que l'heure locale est égale à l'UT. Si aucune information concernant
1124
Zend_Pdf
l'UT n'est spécifiée, la différence par rapport à l'UT est considérée inconnue. Que le décalage
horaire soit connu ou non, le reste de la date devrait être exprimée en heure locale.
Par exemple la date "23 décembre 1998 à 19:52 (heure locale U.S. Pacifique)" est représentée
par la chaîne "D:199812231952#08'00'".
• ModDate - string, optionnel, la date et l'heure auxquelles le document a été le plus récemment
modifié, sous la même forme que CreationDate.
• Trapped - boolean, optionnel, indique si le document à été modifié pour inclure une information
de "trapping".
• true - Le document a été entièrement "trappé" ; aucun autre "trapping" n'est nécessaire.
• false - Le document n'a pas encore été "trappé" ; tout "trapping" reste encore à réaliser.
• null - Soit il est impossible de savoir si le document a été "trappé", soit il a été partiellement
"trappé" ; certains "trapping" additionnels sont nécessaires.
Depuis la version v1.6 de la norme PDF, les métadonnées peuvent être stockées dans un
document XML spécial attaché au document PDF (XMP - eXtensible Metadata Platform).
Ce document XML peut être récupéré et attaché au document PDF avec les méthodes
Zend_Pdf::getMetadata() et Zend_Pdf::setMetadata($metadata) :
$pdf = Zend_Pdf::load($pdfPath);
$metadata = $pdf->getMetadata();
$metadataDOM = new DOMDocument();
$metadataDOM->loadXML($metadata);
$titleNode = $xpath->query('/rdf:RDF/rdf:Description/pdf:Title')
->item(0);
$title = $titleNode->nodeValue;
...
Les propriétés communes du document sont dupliquées dans la structure Info et dans le
document de métadonnées (s'il est présent). Il est de la responsabilité de l'utilisateur de
l'application de les maintenir synchronisées.
Il y a aussi un fichier test.pdf, qui peut être utilisé pour réaliser des tests.
1125
$page2->drawPolygon($x, $y,
Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE,
Zend_Pdf_Page::FILL_METHOD_EVEN_ODD);
// Dessine un rectangle
$page2->setFillColor(new Zend_Pdf_Color_GrayScale(0.8))
->setLineColor(new Zend_Pdf_Color_GrayScale(0.2))
->setLineDashingPattern(array(3, 2, 3, 4), 1.6)
->drawRectangle(0, 50, 340, 0);
// Dessine un cercle
$page2->setLineDashingPattern(Zend_Pdf_Page::LINE_DASHING_SOLID)
->setFillColor(new Zend_Pdf_Color_Rgb(1, 0, 0))
->drawCircle(25, 25, 25);
$page2->restoreGS();
//--------------------------------------------------------------------------
if (isset($argv[2])) {
$pdf->save($argv[2]);
} else {
$pdf->save($argv[1], true ); /* met à jour */
}
1126
Zend_ProgressBar
1. Zend_ProgressBar
1.1. Introduction
Zend_ProgressBar est un composant pour créer et mettre à jour les barres de progression
dans différents environnements. Il consiste en un backend unique, qui affiche la progression au
travers de l'un des multiples adaptateurs. A chaque mise à jour, il prend un chemin absolu et
un message d'état, et appelle ensuite l'adaptateur avec certaines valeurs précalculées comme
le pourcentage et le temps restant estimé.
while (!feof($fp)) {
// Faire quelque chose
$progressBar->update($currentByteCount);
}
$progressBar->finish();
Dans un premier temps, une instance de Zend_ProgressBar, avec une valeur minimum de
0, une valeur maximum correspondant à la taille totale du fichier et un adaptateur spécifique.
Ensuite on travaille avec le fichier et à chaque boucle la barre de progression est mise à jour
avec le nombre d'octets courant. A la fin de la boucle, le statut de la barre de progression est
réglé à terminé.
Zend_ProgressBar possède aussi une méthode refresh() qui recalcule le temps restant
estimé et met à jour l'adaptateur. Ceci est pratique quand il n'y a aucune donnée à mettre à jour
mais que vous souhaitez que la barre de progression soit mise à jour.
1.3.1. Zend_ProgressBar_Adapter_Console
Zend_ProgressBar_Adapter_Console est un adaptateur de type texte pour les terminaux.
Il peut automatiquement détecter la largeur du terminal mais supporte aussi des largeurs
1127
Zend_ProgressBar
personnalisées. Vous pouvez définir quels éléments seront affichés avec la barre de progression
et personnaliser leur ordre. Vous pouvez aussi définir le style de la barre de progression elle-
même.
shell_exec est nécessaire pour que ceci fonctionne sur les systèmes de type
*nix. Sur Windows, il y a toujours un terminal fixe de 80 caractères de large, donc
la reconnaissance automatique n'est pas nécessaire.
Vous pouvez paramétrer les options de l'adaptateur soit via les méthodes set* soit en
fournissant un tableau ("array") ou une instance Zend_Config en tant que premier paramètre
du constructeur. Les options disponibles sont :
• outputStream : un flux de sortie différent, si vous ne souhaitez pas utiliser STDOUT. Peut
être n'importe quel autre flux come php://stderr ou un chemin vers un fichier.
• elements : soit NULL par défaut ou un tableau avec au moins l'une des constantes de
Zend_Console_ProgressBar suivantes comme valeur :
• ELEMENT_ETA : le calcul automatique du temps restant estimé (NDT. : "Estimated Time for
Accomplishment"). Cet élément est affiché pour la première fois qu'après cinq secondes,
car durant ce temps, il n'est pas possible de calculer des résultats précis.
1.3.2. Zend_ProgressBar_Adapter_JsPush
Zend_ProgressBar_Adapter_JsPush est un adaptateur qui vous permet de mettre à jour
une barre de progression dans un navigateur au travers de Javascript. Ceci veut dire qu'une
seconde connexion n'est pas nécessaire pour recueillir le statut du processus courant, mais que
le processus lui-même envoie son statut directement au navigateur.
Vous pouvez paramétrer les options de l'adaptateur soit avec les méthodes set* soit en
fournissant un tableau ("array") ou une instance de Zend_Config contenant les options en tant
que premier paramètre du constructeur. Les options disponibles sont :
1128
Zend_ProgressBar
• updateMethodName : la méthode Javascript qui sera appelée à chaque mise à jour. La valeur
par défaut est Zend_ProgressBar_Update.
• finishMethodName : la méthode Javascript qui sera appelée lorsque le statut terminé sera
atteint. La valeur par défaut est NULL, qui veut dire que rien n'est fait.
L'utilisation de cet adaptateur est assez simple. Premièrement vous créez une barre de
progression pour le navigateur, soit avec Javascript ou auparavant en pur HTML. Ensuite vous
définissez une méthode de mise à jour et optionnellement une méthode de finalisation en
Javascript, les deux acceptant un objet JSON en tant qu'unique argument. Ensuite vous appelez
une page Web avec un processus chronophage dans une balise iframe ou object masqué.
Pendant que le processus tourne, l'adaptateur appelle la méthode de mise à jour à chaque mise
à jour avec un objet JSON, contenant le paramètres suivants :
1129
Zend_ProgressBar
Cet exemple illustre un paramétrage basique du HTML, CSS et JavaScript pour l'adaptateur
JsPush :
<div id="zend-progressbar-container">
<div id="zend-progressbar-done"></div>
</div>
#long-running-process {
position: absolute;
left: -100px;
top: -100px;
width: 1px;
height: 1px;
}
#zend-progressbar-container {
width: 100px;
height: 30px;
#zend-progressbar-done {
width: 0;
height: 30px;
background-color: #000000;
}
function Zend_ProgressBar_Update(data)
{
document.getElementById('zend-progressbar-done').style.width = data.percent + '%';
}
Ceci créera un simple conteneur avec une bordure noire et un bloc qui indique le niveau
du processus courant. Vous ne devez pas masquer l'iframe ou l'object par display:
none;, car dans ce cas les navigateurs comme Safari 2 ne chargeront pas le contenu réel.
Plutôt que de créer votre barre de progression personnalisée, vous pouvez utiliser une de
celles disponibles dans les librairies JavaScript comme Dojo, jQuery etc. Par exemple, il
existe :
• Dojo : http://dojotoolkit.org/book/dojo-book-0-9/part-2-dijit/user-assistance-and-feedback/
progress-bar
• jQuery : http://t.wits.sg/2008/06/20/jquery-progress-bar-11/
• MooTools : http://davidwalsh.name/dw-content/progress-bar.php
• Prototype : http://livepipe.net/control/progressbar
1130
Zend_ProgressBar
1.3.3. Zend_ProgressBar_Adapter_JsPull
Zend_ProgressBar_Adapter_JsPull est l'opposé de jsPush, car il requiert de venir
récupérer les nouvelles mises à jour, plutôt que d'envoyer les mises à jour vers le
navigateur. Généralement, vous devriez utiliser l'adaptateur avec l'option de persistance de
Zend_ProgressBar. Lors de l'appel, l'adaptateur envoie une chaîne JSON vers le navigateur,
qui est comparable à la chaîne JSON envoyée par l'adaptateur jsPush. La seule différence est
qu'il contient un paramètre supplémentaire "finished", qui vaut FALSE quand update() est
appelée ou TRUE quand finish() est appelée.
Vous pouvez paramétrer les options de l'adaptateur soit avec les méthodes set* soit en
fournissant un tableau ("array") ou une instance de Zend_Config contenant les options en tant
que premier paramètre du constructeur. Les options disponibles sont :
• exitAfterSend : sort de la requête courante après que les données aient été envoyées au
navigateur. Vaut TRUE par défaut.
1131
Zend_Queue
1. Introduction
Zend_Queue provides a factory function to create specific queue client objects.
A message queue is a method for distributed processing. For example, a Job Broker application
may accept multiple applications for jobs from a variety of sources.
You could create a queue "/queue/applications" that would have a sender and a receiver.
The sender would be any available source that could connect to your message service or
indirectly to an application (web) that could connect to the message service.
<resume>
<name>John Smith</name>
<location>
<city>San Francisco</city>
<state>California</state>
<zip>00001</zip>
</location>
<skills>
<programming>PHP</programming>
<programming>Perl</programming>
</skills>
</resume>
The recipient or consumer of the queue would pick up the message and process the resume.
There are many messaging patterns that can be applied to queues to abstract the flow of
control from the code and provide metrics, transformations, and monitoring of messages queues.
A good book on messaging patterns is Enterprise Integration Patterns: Designing, Building,
and Deploying Messaging Solutions (Addison-Wesley Signature Series) (ISBN-10 0321127420;
ISBN-13 978-0321127426).
2. Example usage
The below example of Zend_Queue shows a variety of features, including queue creation, queue
retrieval, message retrieval, message deletion, and sending messages.
1132
Zend_Queue
3. Framework
The Zend_Queue is a proxy that hides the details of the queue services. The
queue services are represented by Zend_Queue_Adapter_<service>. For example,
Zend_Queue_Adapter_Db is a queue that will use database tables to store and retrieve
messages.
$options = array(
'name' => 'queue1',
'driverOptions' => array(
'host' => '127.0.0.1',
'port' => '3306',
'username' => 'queue',
'password' => 'queue',
'dbname' => 'queue',
'type' => 'pdo_mysql'
)
);
The Zend_Queue constructor will create a Zend_Queue_Adapter_Db and initialize the adapter
with the configuration settings.
The accepted configuration settings for each adapter are provided in the adapter notes.
$messages = $queue->receive(5);
1133
Zend_Queue
3.1. Introduction
Zend_Queue is a proxy class that represents an adapter.
The send(), count($queue), and receive() methods are employed by each adapter to
interact with queues.
send() is not available in all adapters; the Zend_Queue_Adapter_Null does not support
send().
4. Adapters
Zend_Queue supports all queues implementing the interface
Zend_Queue_Adapter_AdapterInterface. The following Message Queue services are
supported:
• Apache ActiveMQ.
1134
Zend_Queue
Limitations
Message transaction handling is not supported.
• $options['name'] = '/temp/queue1';
This is the name of the queue that you wish to start using. (Required)
• $options['driverOptions']['host'] = 'host.domain.tld';
$options['driverOptions']['host'] = '127.0.0.1';
• $options['driverOptions']['port'] = 61613;
• $options['driverOptions']['username'] = 'username';
Optional for some messaging servers. Read the manual for your messaging server.
• $options['driverOptions']['password'] = 'password';
Optional for some messaging servers. Read the manual for your messaging server.
• $options['driverOptions']['timeout_sec'] = 2;
$options['driverOptions']['timeout_usec'] = 0;
This is the amount of time that Zend_Queue_Adapter_Activemq will wait for read activity
on a socket before returning no messages.
4.1.2. Db - Zend_Queue_Adapter_Db
Driver options are checked for a few required options such as type, host, username, password,
and dbname. You may pass along additional parameters for Zend_DB::factory() as
parameters in $options['driverOptions']. An example of an additional option not listed
here, but could be passed would be port.
1135
Zend_Queue
$options = array(
'driverOptions' => array(
'host' => 'db1.domain.tld',
'username' => 'my_username',
'password' => 'my_password',
'dbname' => 'messaging',
'type' => 'pdo_mysql',
'port' => 3306, // optional parameter.
),
'options' => array(
// use Zend_Db_Select for update, not all databases can support this
// feature.
Zend_Db_Select::FOR_UPDATE => true
)
);
• $options['name'] = 'queue1';
This is the name of the queue that you wish to start using. (Required)
• $options['driverOptions']['type'] = 'Pdo';
type is the adapter you wish to have Zend_Db::factory() use. This is the first parameter
for the Zend_Db::factory() class method call.
• $options['driverOptions']['host'] = 'host.domain.tld';
$options['driverOptions']['host'] = '127.0.0.1';
• $options['driverOptions']['username'] = 'username';
• $options['driverOptions']['password'] = 'password';
• $options['driverOptions']['dbname'] = 'dbname';
The database name that you have created the required tables for. See the notes section below.
This is the name of the queue that you wish to start using. (Required)
• $options['driverOptions']['host'] = 'host.domain.tld';
$options['driverOptions']['host'] = '127.0.0.1;'
• $options['driverOptions']['port'] = 22201;
1136
Zend_Queue
The hostname and port corresponding to the Zend Platform Job Queue daemon you will use.
(Required)
• $options['daemonOptions']['password'] = '1234';
The password required for accessing the Zend Platform Job Queue daemon. (Required)
This is the name of the queue that you wish to start using. (Required)
While Apache's ActiveMQ will support multiple subscriptions, the Zend_Queue does not. You
must create a new Zend_Queue object for each individual subscription.
• /queue/
• /topic/
• /temp-queue/
• /temp-topic/
4.2.2. Zend_Db
The database CREATE TABLE ( ... ) SQL statement can be found in Zend/Queue/Adapter/
Db/queue.sql.
1137
Zend_Queue
4.2.3. MemcacheQ
Memcache can be downloaded from http://www.danga.com/memcached/.
• deleteMessage() - Messages are deleted upon reception from the queue. Calling this
function would have no effect. Calling this function will throw an error.
• count() or count($adapter) - MemcacheQ does not support a method for counting the
number of items in a queue. Calling this function will throw an error.
The following is a list of methods where this adapter's behavior diverges from the standard
offerings:
• create() - Zend Platform does not have the concept of discrete queues; instead, it allows
administrators to provide scripts for processing jobs. Since adding new scripts is restricted to
the administration interface, this method simply throws an exception indicating the action is
forbidden.
• isExists() - Just like create(), since Job Queue does not have a notion of named queues,
this method throws an exception when invoked.
• delete() - similar to create(), deletion of JQ scripts is not possible except via the admin
interface; this method raises an exception.
• getQueues() - Zend Platform does not allow introspection into the attached job handling
scripts via the API. This method throws an exception.
• count() - returns the total number of jobs currently active in the Job Queue.
• send() - this method is perhaps the one method that diverges most from other adapters. The
$message argument may be one of three possible types, and will operate differently based
on the value passed:
• string - the name of a script registered with Job Queue to invoke. If passed in this way, no
arguments are provided to the script.
• array - an array of values with which to configure a ZendApi_Job object. These may include
the following:
• priority - the job priority to use when registering with the queue.
• predecessor - the ID of a job on which this one depends, and which must be executed
before this one may begin.
• preserved - whether or not to retain the job within the Job Queue history. By default,
off; pass a TRUE value to retain it.
1138
Zend_Queue
• user_variables - an associative array of all variables you wish to have in scope during
job execution (similar to named arguments).
• interval - how often, in seconds, the job should run. By default, this is set to 0, indicating
it should run once, and once only.
• end_time - an expiry time, past which the job should not run. If the job was set to run
only once, and end_time has passed, then the job will not be executed. If the job was
set to run on an interval, it will not execute again once end_time has passed.
• application_id - the application identifier of the job. By default, this is NULL, indicating
that one will be automatically assigned by the queue, if the queue was assigned an
application ID.
As noted, only the script argument is required; all others are simply available to allow
passing more fine-grained detail on how and when to run the job.
• ZendApi_Job - finally, you may simply pass a ZendApi_Job instance, and it will be passed
along to Platform's Job Queue.
• receive() - retrieves a list of active jobs from Job Queue. Each job in the returned set will
be an instance of Zend_Queue_Message_PlatformJob.
• deleteMessage() - since this adapter only works with Job Queue, this method expects the
provided $message to be a Zend_Queue_Message_PlatformJob instance, and will throw
an exception otherwise.
5. Customizing Zend_Queue
5.1. Creating your own adapter
Zend_Queue will accept any adapter that implements
Zend_Queue_Adapter_AdapterAbstract. You can create your own adapter by extending
one of the existing adapters, or the abstract class Zend_Queue_Adapter_AdapterAbstract.
I suggest reviewing Zend_Queue_Adapter_Array as this adapter is the easiest to
conceptualize.
1139
Zend_Queue
* this is more likely to produce the wanted rows than the existing code.
*
* However, not all databases have SELECT ... FOR UPDATE as a feature.
*
* Note: this was later converted to be an option for Zend_Queue_Adapter_Db
*
* This code still serves as a good example.
*/
}
$options = array(
'name' => 'queue1',
'driverOptions' => array(
'host' => '127.0.0.1',
'port' => '3306',
'username' => 'queue',
'password' => 'queue',
'dbname' => 'queue',
'type' => 'pdo_mysql'
)
);
or
$options = array(
'name' => 'queue1',
'namespace' => 'Custom',
'driverOptions' => array(
'host' => '127.0.0.1',
'port' => '3306',
'username' => 'queue',
'password' => 'queue',
'dbname' => 'queue',
'type' => 'pdo_mysql'
)
);
$queue = new Zend_Queue('DbForUpdate', $config); // loads Custom_DbForUpdate
class Zend_Queue_Message
{
protected $_data = array();
}
1140
Zend_Queue
You can extend the existing messaging class. See the example code in tests/Zend/Queue/
Custom/Message.php.
6. Stomp
Zend_Queue_Stomp provides a basic client to communicate with Stomp compatible servers.
Some servers, such as Apache ActiveMQ and RabbitMQ, will allow you to communicate by other
methods, such as HTTP, and XMPP.
The Stomp protocol provides StompConnect which supports any Java Message Service (JMS)
provider. Stomp is supported by Apache ActiveMQ, RabbitMQ, stompserver, and Gozirra.
1141
Zend_Reflection
1. Introduction
Zend_Reflection est un ensemble de fonctionnalités écrites par dessus l'API Reflection de
PHP, et propose un ensemble de nouvelles fonctionnalités :
• Les blocs de commentaires PHPDoc possèdent aussi une classe de réflexion. Ceci permet de
récupérer un bloc précis de documentation, notamment son nom, sa valeur et sa description,
longue ou courte.
• Les fichiers aussi possèdent leur propre classe de réflexion. Ceci permet l'introspection de
fichiers PHP afin de déterminer les classes et fonctions écrites dans un fichier.
2. Zend_Reflection Exemples
Exemple 609. Effectuer la réflexion sur un fichier
$r = new Zend_Reflection_File($filename);
printf(
"===> The %s file\n".
" has %d lines\n",
$r->getFileName(),
$r->getEndLine()
);
$classes = $r->getClasses();
echo " It has " . count($classes) . ":\n";
foreach ($classes as $class) {
echo " " . $class->getName() . "\n";
}
$functions = $r->getFunctions();
echo " It has " . count($functions) . ":\n";
foreach ($functions as $function) {
echo " " . $function->getName() . "\n";
}
1142
Zend_Reflection
$r = new Zend_Reflection_Class($class);
printf(
"The class level docblock has the short description: %s\n".
"The class level docblock has the long description:\n%s\n",
$r->getDocblock()->getShortDescription(),
$r->getDocblock()->getLongDescription(),
);
printf(
"The method '%s' has a return type of %s",
$r->getName(),
$r->getReturn()
);
printf(
"The short description: %s\n".
"The long description:\n%s\n",
$r->getDocblock()->getShortDescription(),
$r->getDocblock()->getLongDescription(),
);
3. Réference de Zend_Reflection
Les classes de Zend_Reflection reprennent l'API de la Reflection PHP - mais avec une
différence importante : la Reflection PHP ne propose pas de manière d'introspecter les tags de
documentation PHPDoc, ni les types des variables paramètres ou encore les types de retour
des fonctions.
1143
Zend_Reflection
Zend_Reflection analyse les commentaires PHPDoc pour déterminer les types des variables
passées en paramètres ou de retour. Plus spécialement, les annotations @param et @return
sont utilisées, même s'il reste possible d'analyser les autres blocs de commentaire, ainsi que
leurs descriptions respectives.
Zend_Reflection_File est une nouvelle classe qui permet d'introspecter les fichiers PHP à
la recherche de classes, fonctions ou encore code global PHP contenu à l'intérieur.
Enfin, la plupart des méthodes qui retournent des objets réflexion acceptent un second paramètre
permettant de spécifier la classe qui sera instanciée pour créer de tels objets.
3.1. Zend_Reflection_Docblock
Zend_Reflection_Docblock est le coeur de la valeur ajoutée par Zend_Reflection par
rapport à la Reflection PHP. Voici les méthodes proposées :
• getTags($filter) : Récupère tous les tags qui correspondent au filtre $filter. Le type
de retour est un tableau d'objets Zend_Reflection_Docblock_Tag.
3.2. Zend_Reflection_Docblock_Tag
Zend_Reflection_Docblock_Tag propose la réflexion pour un tag individuel. La plupart des
tags se composent d'un nom et d'un description. Dans le cas de certains tags spéciaux, la classe
propose une méthode de fabrique qui retourne la bonne instance.
3.3. Zend_Reflection_Docblock_Tag_Param
Zend_Reflection_Docblock_Tag_Param est une version spéciale de
Zend_Reflection_Docblock_Tag. La description du tag @param consiste en un
1144
Zend_Reflection
type, un nom de variable et une description. Elle ajoute les méthodes suivantes à
Zend_Reflection_Docblock_Tag :
3.4. Zend_Reflection_Docblock_Tag_Return
Comme Zend_Reflection_Docblock_Tag_Param,
Zend_Reflection_Docblock_Tag_Return est une version spéciale de
Zend_Reflection_Docblock_Tag. Le tag @return consiste en un type de retour et une
description. Elle ajoute les méthodes suivantes à Zend_Reflection_Docblock_Tag :
3.5. Zend_Reflection_File
Zend_Reflection_File propose l'introspection de fichiers PHP. Grâce à cela, vous pouvez
déterminer les classes, fonctions ou le code pur PHP contenus dans un fichier PHP donné. Voici
les méthodes proposées :
3.6. Zend_Reflection_Class
Zend_Reflection_Class étend ReflectionClass, et propose son API. Elle ajoute
juste une méthode, getDeclaringFile(), qui peut être utilisée pour créer un objet
Zend_Reflection_File.
Aussi, les méthodes suivantes proposent un argument supplémentaire pour spécifier sa propre
classe de réflexion:
• getDeclaringFile($reflectionClass = 'Zend_Reflection_File')
• getDocblock($reflectionClass = 'Zend_Reflection_Docblock')
1145
Zend_Reflection
• getInterfaces($reflectionClass = 'Zend_Reflection_Class')
• getMethod($reflectionClass = 'Zend_Reflection_Method')
• getParentClass($reflectionClass = 'Zend_Reflection_Class')
3.7. Zend_Reflection_Extension
Zend_Reflection_Extension étend ReflectionExtension et propose son API. Elle
surcharge les méthodes suivantes afin d'ajouter un paramètre permettant de spécifier sa propre
classe de réflexion :
3.8. Zend_Reflection_Function
Zend_Reflection_Function ajoute une méthode pour retrouver le type de retour de la
fonction introspéctée, et surcharge d'autres méthodes pour proposer de passer en paramètre
une classe de réflexion à utiliser.
3.9. Zend_Reflection_Method
Zend_Reflection_Method reprend l'API de Zend_Reflection_Function et surcharge la
méthode suivante:
3.10. Zend_Reflection_Parameter
Zend_Reflection_Parameter ajoute une méthode pour retrouver le type d'un paramètre, et
aussi surcharge certaines méthodes en rajoutant un paramètre permettant de spécifier sa propre
classe de réflexion.
1146
Zend_Reflection
• getDeclaringFunction($reflectionClass = 'Zend_Reflection_Function') :
retourne un objet réflexion représentant la fonction passée comme paramètre (si disponible).
3.11. Zend_Reflection_Property
Zend_Reflection_Property surcharge une seule méthode afin de pouvoir spécifier le type
de classe de retour :
1147
Zend_Registry
1. Utiliser le registre
Un registre est un conteneur pour stocker des objets et des valeurs dans l'espace d'application.
En stockant la valeur dans le registre, le même objet est toujours disponible partout dans votre
application. Ce mécanisme est une alternative à l'utilisation du stockage global.
L'utilisation typique des registres dans Zend Framework est par des méthodes statiques dans
la classe Zend_Registry. Alternativement, la classe est un tableau, donc vous pouvez avoir
accès aux éléments stockés avec une interface semblable au tableau.
Zend_Registry::set('index', $value);
La valeur peut être un objet, un tableau, ou un scalaire. Vous pouvez changer la valeur stockée
dans une entrée spécifique du registre en utilisant set() pour enregistrer une nouvelle valeur.
L'index peut être un scalaire (NULL, chaîne, ou nombre), comme un tableau ordinaire.
$value = Zend_Registry::get('index');
La méthode getInstance() retourne l'objet registre en entier. Un objet registre est itératif,
rendant les valeurs stockées facilement accessibles.
$registry = Zend_Registry::getInstance();
1148
Zend_Registry
L'instance du registre à laquelle vous avez accès par les méthodes statiques est simplement
une instance et il est plus commode qu'il soit stocké statiquement, afin d'y avoir accès partout
dans votre application.
Utiliser le constructeur traditionnel new pour créer une instance du registre. Ceci vous donne la
possibilité d'initialiser les entrées du registre avec un tableau associatif.
Après avoir construit cette instance, vous pouvez l'utiliser avec les fonctions habituelles de
tableau, ou vous pouvez rendre cette instance statique en utilisant la méthode setInstance().
Zend_Registry::setInstance($registry);
$registry = Zend_Registry::getInstance();
$registry['index'] = $value;
var_dump( $registry['index'] );
1149
Zend_Registry
var_dump($registry->index);
if (Zend_Registry::isRegistered($index)) {
$value = Zend_Registry::get($index);
}
Pour savoir si un index particulier du registre de type tableau a une valeur, il faut utiliser la
fonction isset() comme vous le feriez avec un tableau ordinaire.
$registry = Zend_Registry::getInstance();
1150
Zend_Registry
Zend_Registry::setClassName('Mon_Registre');
Zend_Registry::set('index', $value);
L'enregistrement lève une Zend_Exception si vous essayez d'affecter un nom de classe après
que le registre ait eu un premier accès. Il est recommandé de spécifier le nom de classe pour
votre registre statique dans le fichier de lancement de votre application.
Vous pourriez utiliser cette méthode, par exemple, si vous voulez utiliser setInstance() ou
setClassName() après que l'objet de registre statique a été initialisé. Décharger l'instance
statique vous permet d'utiliser ces méthodes même après instanciation de l'objet registre. Utiliser
Zend_Registry de cette manière n'est pas recommandé dans les applications.
Zend_Registry::set('index', $value);
Zend_Registry::_unsetInstance();
// Changer la classe
Zend_Registry::setClassName('Mon_Registre');
Zend_Registry::set('index', $value);
1151
Zend_Rest
1. Introduction
Les services Web REST utilisent des formats XML spécifiques. Ces normes ad-hoc signifient que
la façon d'accéder à un service Web REST est différente pour chaque service. Les services Web
REST utilise typiquement les paramètres URL (données GET) ou les informations du chemin
pour demander des données, et les données POST pour envoyer des données.
Zend Framework fournit les possibilités de client et de serveur, qui, une fois utilisées ensemble
tiennent compte de beaucoup plus d'expérience d'interface "locale" par l'intermédiaire de l'accès
aux propriétés d'objet virtuel. Le composant serveur comporte l'exposition automatique des
fonctions et des classes employant un format compréhensible et simple de XML. En accédant à
ces services via le client, il est possible de rechercher facilement les données retournées lors de
l'appel à distance. Si vous souhaitez employer le client avec un service non basé sur le service
Zend_Rest_Server, il fournira un accès plus facile aux données.
2. Zend_Rest_Client
2.1. Introduction
Utiliser le Zend_Rest_Client est très semblable à l'utilisation des objets de SoapClient
(SOAP web service extension). Vous pouvez simplement appeler les procédures de service de
REST comme méthodes de Zend_Rest_Client. Vous devez indiquer l'adresse complète du
service dans le constructeur de Zend_Rest_Client.
/**
* Connexion au serveur framework.zend.com server
*/
require_once 'Zend/Rest/Client.php';
$client->sayHello('Davey', 'Day');
1152
Zend_Rest
echo $client->get();
2.2. Réponses
Toutes les demandes faites en utilisant Zend_Rest_Client retourne un objet
Zend_Rest_Client_Response. Cet objet a beaucoup de propriétés qui facilitent l'accès aux
résultats.
Quand le service est basé sur Zend_Rest_Server, Zend_Rest_Client peut faire plusieurs
suppositions au sujet de la réponse, y compris le statut de réponse (succès ou échec) et le type
de retour.
if ($result->isSuccess()) {
echo $result; // "Hello Davey, Good Day"
}
Dans l'exemple ci-dessus, vous pouvez voir que nous utilisons le résultat de la demande
comme un objet, pour appeler isSuccess(), et puis grâce à __toString(), nous pouvons
simplement faire echo sur l'objet pour récupérer le résultat. Zend_Rest_Client_Response
vous permettra de afficher n'importe quelle valeur scalaire. Pour les types complexes, vous
pouvez employer la notation type objet ou type tableau.
1153
Zend_Rest
Ici nous accédons aux propriétés firstname et lastname.Bien que ce ne soient pas les
éléments supérieurs, elles sont automatiquement retournées quand on accède par le nom.
Éléments multiples
1154
Zend_Rest
$client->arg('value1');
$client->arg2('value2');
$client->get();
// or
$client->arg('value1')->arg2('value2')->get();
Les deux méthodes dans l'exemple ci-dessus, auront comme conséquence l'obtention des
arguments suivants : ?method=arg&arg1=value1&arg=value1&arg2=value2
Sévérité de Zend_Rest_Client
Tout service REST qui est strict au sujet des arguments qu'il reçoit échouera
probablement en utilisant Zend_Rest_Client, en raison du comportement
décrit ci-dessus. Ce n'est pas une pratique courante et ne devrait pas poser des
problèmes.
3. Zend_Rest_Server
3.1. Introduction
Zend_Rest_Server est prévu comme un serveur supportant l'ensemble des fonctionnalités d'un
serveur REST.
1155
Zend_Rest
/**
* Dit Bonjour
*
* @param string $qui
* @param string $quand
* @return string
*/
function ditBonjour($qui, $quand)
{
return "Bonjour $qui, bonne $quand";
}
Index numérique
?method=ditBonjour&qui=Davey&quand=journée
or:
?method=ditBonjour&arg1=Davey&arg2=journée
1156
Zend_Rest
/**
* Dit Bonjour
*
* @param string $qui
* @param string $quand
* @return array
*/
function ditBonjour($qui, $quand)
{
return array('msg' => "Une erreur est apparue", 'status' => false);
}
/**
* Dit Bonjour
*
* @param string $who
* @param string $when
* @return SimpleXMLElement
*/
function ditBonjour($qui, $quand)
{
$xml ='<?xml version="1.0" encoding="ISO-8859-1"?>
<mysite>
<value>Salut $qui! J\'espère que tu passes une bonne $when</value>
<constant>200</constant>
</mysite>';
$xml = simplexml_load_string($xml);
return $xml;
}
$server->handle();
1157
Zend_Search_Lucene
1. Vue d'ensemble
1.1. Introduction
Zend_Search_Lucene est un moteur de recherche de contenus principalement textuels écrit
entièrement en PHP 5. Comme il stocke ses index sur le système de fichiers et qu'il ne requiert
pas de base de données, il peut offrir des fonctionnalités de recherche à presque n'importe quel
site écrit en PHP. Zend_Search_Lucene dispose des caractéristiques suivantes :
• Plusieurs puissants types de requêtes : phrase, booléen, joker (wildcard), proximité, intervalle
et bien d'autres.
A partir de Zend Framework 1.5, tout index créé en utilisant une version
antérieure à la 2.1 et automatiquement mis à niveau au format Lucene 2.1
après la mise à jour de Zend_Search_Lucene et ne sera pas compatible avec
les implémentations de Zend_Search_Lucene incluses dans Zend Framework
1.0.x.
Il est important de noter que n'importe quelle information peut être ajoutée à l'index. Des
informations propres à l'application ou des métadonnées peuvent être stockées dans le
document, puis récupérées durant la recherche.
Il est de la responsabilité de votre application de gérer l'indexation. Cela signifie que les données
peuvent être indexées depuis n'importe quelle source accessible par votre application. Par
exemple, elles peuvent provenir du système de fichier, d'une base de données, d'un formulaire
HTML, etc.
1158
Zend_Search_Lucene
// Le champ n'est pas "tokenizé", mais il est indexé et stocké dans l'index.
// Les champs stockés peuvent être récupéré depuis l'index.
$doc->addField(Zend_Search_Lucene_Field::Keyword('doctype',
'autogenerated'));
L'encodage peut différer par document, voire par champ au sein d'un même document :
Si le paramètre d'encodage est omis, la locale courante est alors utilisée pour le déterminer à
l'exécution. Par exemple :
setlocale(LC_ALL, 'de_DE.iso-8859-1');
...
$doc->addField(Zend_Search_Lucene_Field::UnStored('contents', $contents));
Les champs sont toujours stockés et retournés depuis l'index en UTF-8. Toute conversion
requise vers UTF-8 est effectuée automatiquement.
Les analyseurs de texte (voir plus bas) peuvent également convertir du texte vers d'autres
encodages. Actuellement, l'analyseur par défaut convertit le texte au format "ASCII/TRANSLIT".
Soyez prudent, cependant; cette conversion peut déprendre de la locale.
Le nom des champs est défini par vous dans la méthode addField().
Java Lucene utilise le champ "contents" comme champ de recherche par défaut.
Zend_Search_Lucene cherche par défaut dans tous les champs. Cela dit, ce comportement
est configurable. Consultez le chapitre "Champ de recherche par défaut" pour plus de détails.
1159
Zend_Search_Lucene
• Les champs UnIndexed (non-indexé) ne peuvent pas être utilisés dans la recherche. En
revanche, ils peuvent être retournés dans les résultats. Des timestamps de base de données,
des clés primaires, des chemins de fichiers et d'autres identifiants externes sont autant de
bons exemples d'utilisation des champs de type UnIndexed.
• Les champs Binary (binaire) ne sont ni "tokenizés", ni indexés, mais ils sont stockés dans
le but d'être retournés dans les résultats de recherche. Ils peuvent être utilisés pour stocker
n'importe quelle donnée encodée en chaîne binaire, telle qu'une icône par exemple.
• Les champs Text (texte) sont stockés, indexés et "tokenizés". Les champs de type Text sont
appropriés pour stocker des informations telles que sujets et titres sur lesquels on veut pouvoir
effectuer des recherches, mais également les utiliser dans l'affichage des résultats.
• Les champs UnStored sont "tokenizés" et indexés, mais pas stockés dans l'index. Il est
recommandé d'utiliser ce type de champ pour indexer les textes conséquents. Stocker
des données implique la création d'index plus volumineux sur le disque. Donc si vous
disposez de données sur lesquelles vous voulez uniquement effectuer des recherches sans
nécessairement afficher ces données dans les résultats, utilisez un champ de type UnStored.
Le type UnStored est pratique lorsque vous utilisez un index Zend_Search_Lucene en
combinaison avec une base de données relationnelle. Vous pouvez indexer des gros champs
de données dans des champs de type UnStored et les extraire de la base de données
relationnelle en utilisant un champ séparé en tant qu'identifiant.
$doc = Zend_Search_Lucene_Document_Html::loadHTMLFile($filename);
$index->addDocument($doc);
...
$doc = Zend_Search_Lucene_Document_Html::loadHTML($htmlString);
$index->addDocument($doc);
1160
Zend_Search_Lucene
HTML, ainsi il n'est pas nécessaire que le HTML soit bien formé ou au format XHTML. Par contre,
ces méthodes prennent en compte l'encodage spécifié dans la balise méta "http-equiv".
Le champ "title" correspond au contenu de la balise /html/head/title. Il est stocké dans l'index,
"tokenizé" et disponible pour la recherche.
The third parameter of loadHTML() and loadHTMLFile() methods optionally specifies source
HTML document encoding. It's used if encoding is not specified using Content-type HTTP-EQUIV
meta tag.
Les autres métadonnées génèrent des champs additionnels dans le document. Le champ
"name" prend sa valeur dans l'attribut "name" de la métadonnées. Le champ "value" prend sa
valeur dans l'attribut "content" de la métadonnées. Ces deux champs sont "tokenizés", indexés
et stockés. Ainsi les documents peuvent être cherchés à travers leurs métadonnées (p. ex. par
mots-clés).
Les documents analysés peuvent être enrichis par le programmeur avec d'autres champs :
$doc = Zend_Search_Lucene_Document_Html::loadHTML($htmlString);
$doc->addField(Zend_Search_Lucene_Field::UnIndexed('created',
time()));
$doc->addField(Zend_Search_Lucene_Field::UnIndexed('updated',
time()));
$doc->addField(Zend_Search_Lucene_Field::Text('annotation',
'Document annotation text'));
$index->addDocument($doc);
Les liens des documents ne sont pas inclus dans le document généré, mais ils peuvent être
récupérés avec les méthodes Zend_Search_Lucene_Document_Html::getLinks() et
Zend_Search_Lucene_Document_Html::getHeaderLinks() :
$doc = Zend_Search_Lucene_Document_Html::loadHTML($htmlString);
$linksArray = $doc->getLinks();
$headerLinksArray = $doc->getHeaderLinks();
La méthode Zend_Search_Lucene_Document_Html::getExcludeNoFollowLinks()
retourne la valeur courante du flag "Exclude nofollow links".
1161
Zend_Search_Lucene
$doc = Zend_Search_Lucene_Document_Docx::loadDocxFile($filename);
$index->addDocument($doc);
Le champ "body" contient le véritable contenu du document Word 2007. Il n'inclut que le texte
normal. Les commentaires et révisions ne sont pas inclus.
Les documents parsés peuvent être étendus par le programmeur avec d'autres champs :
$doc = Zend_Search_Lucene_Document_Docx::loadDocxFile($filename);
$doc->addField(Zend_Search_Lucene_Field::UnIndexed(
'indexTime',
time())
);
$doc->addField(Zend_Search_Lucene_Field::Text(
'annotation',
'Document annotation text')
);
$index->addDocument($doc);
1162
Zend_Search_Lucene
$doc = Zend_Search_Lucene_Document_Pptx::loadPptxFile($filename);
$index->addDocument($doc);
Le champ "body" contient le véritable contenu de toutes les slides, ainsi que les notes dans le
document Powerpoint 2007.
Les documents analysés peuvent être étendus par le programmeur avec d'autres champs :
$doc = Zend_Search_Lucene_Document_Pptx::loadPptxFile($filename);
$doc->addField(Zend_Search_Lucene_Field::UnIndexed(
'indexTime',
time()));
$doc->addField(Zend_Search_Lucene_Field::Text(
'annotation',
1163
Zend_Search_Lucene
$doc = Zend_Search_Lucene_Document_Xlsx::loadXlsxFile($filename);
$index->addDocument($doc);
Le champ "body" contient le véritable contenu de toutes les cellules de toutes les feuilles de
calcul du document Excel 2007.
Les documents analysés peuvent être étendus par le programmeur avec d'autres champs :
$doc = Zend_Search_Lucene_Document_Xlsx::loadXlsxFile($filename);
$doc->addField(Zend_Search_Lucene_Field::UnIndexed(
'indexTime',
1164
Zend_Search_Lucene
time()));
$doc->addField(Zend_Search_Lucene_Field::Text(
'annotation',
'Document annotation text'));
$index->addDocument($doc);
Le listing ci-dessous donne un exemple d'indexation d'un fichier en utilisant l'API d'indexation
de Zend_Search_Lucene :
// Création de l'index
$index = Zend_Search_Lucene::create('/data/my-index');
// Stockage de l'URL du document afin de pouvoir l'identifier dans les résultats de recherch
$doc->addField(Zend_Search_Lucene_Field::Text('url', $docUrl));
1165
Zend_Search_Lucene
$removePath = ...;
$hits = $index->find('path:' . $removePath);
foreach ($hits as $hit) {
$index->delete($hit->id);
}
$indexSize = $index->count();
$documents = $index->numDocs();
L'optimisation d'index retire les documents supprimés et resserre les identifiants de documents
dans un intervalle plus petit. Ainsi, un identifiant interne de document peut être modifié durant
l'optimisation de l'index.
Les fichiers de segment d'index Lucene ne peuvent pas être mis à jour conceptuellement. Une
mise à jour de segment requiert une réorganisation complète de tous les segments. Consultez
les formats de fichiers d'index pour plus de détails (http://lucene.apache.org/java/2_3_0/
1
fileformats.html) Les nouveaux documents sont ajoutés à l'index en créant de nouveaux
segments.
1166
Zend_Search_Lucene
// Optimisation de l'index.
$index->optimize();
L'optimisation automatique de l'index est lancée pour garder les index dans un état cohérent.
L'optimisation automatique est un processus itératif géré par plusieurs options d'index. Il s'agit
de fusionner les très petits segments pour obtenir de plus gros segments, puis de fusionner ces
segments obtenus vers des segments encore plus gros et ainsi de suite.
L'option MergeFactor constitue une bonne estimation pour le nombre moyen de segments
fusionnés par une passe d'auto-optimisation. Des valeurs trop grandes produisent un nombre
trop important de segments car ils ne sont pas fusionnés. Cela peut causer l'erreur "failed to
open stream: Too many open files". Cette limitation est dépendante du système.
1167
Zend_Search_Lucene
Lucene Java et Luke (Lucene Index Toolbox - http://www.getopt.org/luke/) peuvent aussi être
utilisés pour optimiser un index. La dernière version de Luke (v0.8) est basée sur Lucene v2.3 et
est compatible avec l'implémentation courante du composant Zend_Search_Lucene (ZF 1.6).
Les versions précédentes de Zend_Search_Lucene nécessitent d'autres versions des outils
de Java Lucene :
2.6. Permissions
Par défaut, les fichiers d'index sont disponibles en lecture et écriture par tout le monde.
2.7. Limitations
2.7.1. Taille de l'index
La taille de l'index est limité à 2GB sur les plate-formes 32 bits.
Selon la documentation PHP, "flock() ne fonctionnera pas sur NFS et plusieurs autres
systèmes de fichiers en réseaux".
3. Searching an Index
3.1. Building Queries
There are two ways to search the index. The first method uses query parser to construct a
query from a string. The second is to programmatically create your own queries through the
Zend_Search_Lucene API.
Before choosing to use the provided query parser, please consider the following:
1168
Zend_Search_Lucene
1. If you are programmatically creating a query string and then parsing it with the query parser
then you should consider building your queries directly with the query API. Generally speaking,
the query parser is designed for human-entered text, not for program-generated text.
2. Untokenized fields are best added directly to queries and not through the query parser. If a
field's values are generated programmatically by the application, then the query clauses for
this field should also be constructed programmatically. An analyzer, which the query parser
uses, is designed to convert human-entered text to terms. Program-generated values, like
dates, keywords, etc., should be added with the query API.
3. In a query form, fields that are general text should use the query parser. All others, such as
date ranges, keywords, etc., are better added directly through the query API. A field with a
limited set of values that can be specified with a pull-down menu should not be added to a
query string that is subsequently parsed but instead should be added as a TermQuery clause.
4. Boolean queries allow the programmer to logically combine two or more queries into new one.
Thus it's the best way to add additional criteria to a search defined by a query string.
Both ways use the same API method to search through the index:
$index = Zend_Search_Lucene::open('/data/my_index');
$index->find($query);
It is important to note that the query parser uses the standard analyzer to tokenize separate parts
of query string. Thus all transformations which are applied to indexed text are also applied to
query strings.
The standard analyzer may transform the query string to lower case for case-insensitivity, remove
stop-words, and stem among other transformations.
The API method doesn't transform or filter input terms in any way. It's therefore more suitable
for computer generated or untokenized fields.
This query object may be used in query construction API methods to combine user entered
queries with programmatically generated queries.
Actually, in some cases it's the only way to search for values within untokenized fields:
$userQuery = Zend_Search_Lucene_Search_QueryParser::parse($queryStr);
1169
Zend_Search_Lucene
$hits = $index->find($query);
$userQuery = Zend_Search_Lucene_Search_QueryParser::parse($queryStr,
'iso-8859-5');
It's also possible to specify the default query string encoding with
Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding() method:
Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding('iso-8859-5');
...
$userQuery = Zend_Search_Lucene_Search_QueryParser::parse($queryStr);
$index = Zend_Search_Lucene::open('/data/my_index');
$hits = $index->find($query);
$index = Zend_Search_Lucene::open('/data/my_index');
$hits = $index->find($query);
foreach ($hits as $hit) {
1170
Zend_Search_Lucene
// same as getFieldValue()
echo $document->title;
}
The fields available from the Zend_Search_Lucene_Document object are determined at the
time of indexing. The document fields are either indexed, or index and stored, in the document
by the indexing application (e.g. LuceneIndexCreation.jar).
Note that the document identity ('path' in our example) is also stored in the index and must be
retrieved from it.
$currentResultSetLimit = Zend_Search_Lucene::getResultSetLimit();
Zend_Search_Lucene::setResultSetLimit($newLimit);
Roughly speaking, search hits that contain the searched term or phrase more frequently will have
a higher score.
A hit's score can be retrieved by accessing the score property of the hit:
$hits = $index->find($query);
2
Returned hits are still ordered by score or by the specified order, if given.
1171
Zend_Search_Lucene
A name of stored field by which to sort result should be passed as the $sortField parameter.
$sortType may be omitted or take the following enumerated values: SORT_REGULAR (compare
items normally- default value), SORT_NUMERIC (compare items numerically), SORT_STRING
(compare items as strings).
$sortOrder may be omitted or take the following enumerated values: SORT_ASC (sort in
ascending order- default value), SORT_DESC (sort in descending order).
Examples:
Please use caution when using a non-default search order; the query needs to retrieve
documents completely from an index, which may dramatically reduce search performance.
/**
* Highlight text with specified color
*
* @param string|array $words
* @param string $colour
* @return string
*/
public function highlight($words, $colour = '#66ffff');
/**
* Highlight text using specified View helper or callback function.
*
* @param string|array $words Words to highlight. Words could be organized
using the array or string.
1172
Zend_Search_Lucene
E.g. if current analyzer is case insensitive and we request to highlight 'text' word,
then 'text', 'Text', 'TEXT' and other case combinations will be highlighted.
On the other hand, if word is skipped by current analyzer (e.g. if short words filter
is applied to the analyzer), then nothing will be highlighted.
$query = Zend_Search_Lucene_Search_QueryParser::parse($queryStr);
$highlightedHTML = $query->highlightMatches($sourceHTML);
Optional second parameter is a default HTML document encoding. It's used if encoding is not
specified using Content-type HTTP-EQUIV meta tag.
interface Zend_Search_Lucene_Search_Highlighter_Interface
{
3
The first is an HTML fragment for highlighting and others are callback behavior dependent. Returned value is a highlighted HTML
fragment.
4
In both cases returned HTML is automatically transformed into valid XHTML.
1173
Zend_Search_Lucene
/**
* Set document for highlighting.
*
* @param Zend_Search_Lucene_Document_Html $document
*/
public function setDocument(Zend_Search_Lucene_Document_Html $document);
/**
* Get document for highlighting.
*
* @return Zend_Search_Lucene_Document_Html $document
*/
public function getDocument();
/**
* Highlight specified words (method is invoked once per subquery)
*
* @param string|array $words Words to highlight. They could be
organized using the array or string.
*/
public function highlight($words);
}
Actually, default highlighter does this walking through predefined color table. So you can
implement your own highlighter or just extend the default and redefine color table.
Zend_Search_Lucene_Search_Query->htmlFragmentHighlightMatches() has
similar behavior. The only difference is that it takes as an input and returns HTML fragment
without <>HTML>, <HEAD>, <BODY> tags. Nevertheless, fragment is automatically transformed
to valid XHTML.
4. Langage de requêtes
Java Lucene et Zend_Search_Lucene fournissent des langages de requêtes plutôt puissants.
Ces langages sont pratiquement pareils, exceptées les quelques différences ci-dessous.
La syntaxe complète du langage de requêtes Java Lucene peut être trouvée ici.
4.1. Termes
Une requête est décomposée en termes et opérateurs. Il y a 3 types de termes : le termes
simples, les phrases et les sous-requêtes.
Une phrase est un groupe de mots inclus dans des double guillemets, tel que "hello dolly".
1174
Zend_Search_Lucene
Une sous-requête est une requête incluse dans des parenthèses, tel que "(hello dolly)".
De multiples termes peuvent être combinés ensemble avec des opérateurs booléens pour former
des requêtes complexes (voyez ci-dessous).
4.2. Champs
Lucene supporte les champs de données. Lorsque vous effectuez une recherche, vous pouvez
soit spécifier un champ, soit utiliser le champ par défaut. Le nom du champ dépend des données
indexées et le champ par défaut est défini par les paramètres courants.
La première différence et la plus significative avec Java Lucene est que par défaut les termes
sont cherchés dans tous les champs.
$defaultSearchField = Zend_Search_Lucene::getDefaultSearchField();
...
Zend_Search_Lucene::setDefaultSearchField('contents');
La valeur NULL indique que la recherche est effectuée dans tous les champs. C'est le
paramétrage par défaut
Vous pouvez chercher dans des champs spécifiques en tapant le nom du champ suivi de ":",
suivi du terme que vous cherchez.
Par exemple, prenons un index Lucene contenant deux champs -title et text- avec text comme
champ par défaut. Si vous voulez trouver le document ayant pour titre "The Right Way" qui
contient le text "don't go this way", vous pouvez entrer :
or
"text" étant le champ par défaut, l'indicateur de champ n'est pas requis.
Note: Le champ n'est valable que pour le terme, la phrase ou la sous-requête qu'il précède
directement, ainsi la requête
title:Do it right
ne trouvera que "Do" dans le champ 'title'. Elle trouvera "it" et "right" dans le champ par défaut
(si le champ par défaut est défini) ou dans tous les champs indexés (si le champ par défaut est
défini à NULL).
Pour effectuez une recherche avec joker sur un seul caractère, utilisez le symbole "?".
1175
Zend_Search_Lucene
Pour effectuez une recherche avec joker sur plusieurs caractères, utilisez le symbole "*".
La recherche avec un joker sur un seul caractère va faire correspondre le terme avec le "?"
remplacé par n'importe quel autre caractère unique. Par exemple, pour trouver "text" ou "test"
vous pouvez utiliser la recherche :
te?t
Multiple character wildcard searches look for 0 or more characters when matching strings against
terms. For example, to search for test, tests or tester, you can use the search:
test*
You can use "?", "*" or both at any place of the term:
*wr?t*
Starting from ZF 1.7.7 wildcard patterns need some non-wildcard prefix. Default prefix length is
5
3 (like in Java Lucene). So "*", "te?t", "*wr?t*" terms will cause an exception .
"~" modifier can be used to specify proximity search for phrases or fuzzy search for individual
terms.
mod_date:[20020101 TO 20030101]
This will find documents whose mod_date fields have values between 20020101 and 20030101,
inclusive. Note that Range Queries are not reserved for date fields. You could also use range
queries with non-date fields:
title:{Aida TO Carmen}
This will find all documents whose titles would be sorted between Aida and Carmen, but not
including Aida and Carmen.
Inclusive range queries are denoted by square brackets. Exclusive range queries are denoted
by curly brackets.
5
Please note, that it's not a Zend_Search_Lucene_Search_QueryParserException, but a
Zend_Search_Lucene_Exception. It's thrown during query rewrite (execution) operation.
1176
Zend_Search_Lucene
If field is not specified then Zend_Search_Lucene searches for specified interval through all
fields by default.
{Aida TO Carmen}
roam~
This search will find terms like foam and roams. Additional (optional) parameter can specify the
required similarity. The value is between 0 and 1, with a value closer to 1 only terms with a higher
similarity will be matched. For example:
roam~0.8
So Zend_Search_Lucene sets a limit of matching terms per query (subquery). This limit
can be retrieved and set using Zend_Search_Lucene::getTermsPerQueryLimit()/
Zend_Search_Lucene::setTermsPerQueryLimit($limit) methods.
"Zend Framework"~10
Boosting allows you to control the relevance of a document by boosting individual terms. For
example, if you are searching for
PHP framework
and you want the term "PHP" to be more relevant boost it using the ^ symbol along with the boost
factor next to the term. You would type:
1177
Zend_Search_Lucene
PHP^4 framework
This will make documents with the term PHP appear more relevant. You can also boost phrase
terms and subqueries as in the example:
By default, the boost factor is 1. Although the boost factor must be positive, it may be less than
1 (e.g. 0.2).
AND, OR, and NOT operators and "+", "-" defines two different styles to construct boolean
queries. Unlike Java Lucene, Zend_Search_Lucene doesn't allow these two styles to be mixed.
If the AND/OR/NOT style is used, then an AND or OR operator must be present between all
query terms. Each term may also be preceded by NOT operator. The AND operator has higher
precedence than the OR operator. This differs from Java Lucene behavior.
4.10.1. AND
The AND operator means that all terms in the "AND group" must match some part of the searched
field(s).
To search for documents that contain "PHP framework" and "Zend Framework" use the query:
4.10.2. OR
The OR operator divides the query into several optional terms.
To search for documents that contain "PHP framework" or "Zend Framework" use the query:
4.10.3. NOT
The NOT operator excludes documents that contain the term after NOT. But an "AND group"
which contains only terms with the NOT operator gives an empty result set instead of a full set
of indexed documents.
To search for documents that contain "PHP framework" but not "Zend Framework" use the query:
1178
Zend_Search_Lucene
4.10.5. +
The "+" or required operator stipulates that the term after the "+" symbol must match the
document.
To search for documents that must contain "Zend" and may contain "Framework" use the query:
+Zend Framework
4.10.6. -
The "-" or prohibit operator excludes documents that match the term after the "-" symbol.
To search for documents that contain "PHP framework" but not "Zend Framework" use the query:
4.10.7. No Operator
If no operator is used, then the search behavior is defined by the "default boolean operator".
That implies each term is optional by default. It may or may not be present within document, but
documents with this term will receive a higher score.
To search for documents that requires "PHP framework" and may contain "Zend Framework"
use the query:
4.11. Grouping
Java Lucene and Zend_Search_Lucene support using parentheses to group clauses to form
sub queries. This can be useful if you want to control the precedence of boolean logic operators
for a query or mix different boolean query styles:
To search for a title that contains both the word "return" and the phrase "pink panther" use the
query:
1179
Zend_Search_Lucene
+ - && || ! ( ) { } [ ] ^ " ~ * ? : \
For other instances of these characters use the \ before each special character you'd like to
escape. For example to search for (1+1):2 use the query:
\(1\+1\)\:2
Les requêtes utilisateur peuvent être combinées avec les requêtes créées à travers l'API de
requêtes. Utilisez simplement le parseur de requêtes pour construire une requête à partir d'une
chaîne :
$query = Zend_Search_Lucene_Search_QueryParser::parse($queryString);
try {
$query = Zend_Search_Lucene_Search_QueryParser::parse($queryString);
} catch (Zend_Search_Lucene_Search_QueryParserException $e) {
echo "Query syntax error: " . $e->getMessage() . "\n";
}
La même technique devrait être utilisée pour la méthode find() d'un objet
Zend_Search_Lucene.
1180
Zend_Search_Lucene
Zend_Search_Lucene_Search_QueryParser::dontSuppressQueryParsingExceptions().
Les méthodes
Zend_Search_Lucene_Search_QueryParser::suppressQueryParsingExceptions()
et
Zend_Search_Lucene_Search_QueryParser::queryParsingExceptionsSuppressed()
sont également destinées à gérer le comportement de gestion des exceptions.
word1
ou
L'argument field est optionnel. Zend_Search_Lucene cherche dans tous les champs indexés
si aucun champ n'a été spécifié :
Chaque terme dans une collection peut être défini comme requis, interdit, ou aucun des deux.
• requis signifie que les documents ne correspondant pas au terme ne correspondront pas à
la requête;
• interdit signifie que les documents correspondant au terme ne correspondront pas à la requête;
• aucun des deux, dans ce cas les documents n'ont pas l'interdiction, ni l'obligation de
correspondre au terme. Cela dit, un document devra correspondre au moins à l'un des termes
pour correspondre à la requête.
Si des termes optionnels sont ajoutés à une requête possédant des termes requis, les deux
requêtes auront les mêmes résultats, mais les termes optionnels pourraient influer sur le score
des documents retournés.
Les deux méthodes de recherche peuvent être utilisées pour les requêtes multi-termes.
1181
Zend_Search_Lucene
• Le préfixe 'field:' est utilisé pour indiqué un champ sur lequel on veut chercher. S'il est omis,
la recherche se fera sur tous les champs.
or
Il est également possible de spécifier des listes de termes dans le contructeur de la requête
multi-termes :
• NULL est utilisé pour définir un terme qui n'est ni requis, ni interdit.
Chaque sous-requête dans une collection peut être définie comme requis, interdit, ou optionnel.
• optionel, dans ce cas les documents n'ont pas l'interdiction, ni l'obligation de correspondre à la
sous-requête. Cela dit, un document devra correspondre au moins à l'une des sous-requêtes
pour correspondre à la requête.
Si des sous-requêtes optionnelles sont ajoutées à une requête possédant des sous-requêtes
requises, les deux requêtes auront les mêmes résultats, mais les sous-requêtes optionnelles
pourraient influer sur le score des documents retournés.
1182
Zend_Search_Lucene
Les deux méthodes de recherche peuvent être utilisées pour les requêtes booléenes.
• Le préfixe 'field:' est utilisé pour indiqué un champ sur lequel on veut chercher. S'il est omis,
la recherche se fera sur tous les champs.
or
Il est également possible de spécifier des listes de sous-requêtes dans le constructeur d'une
requêtes booléene :
...
$subqueries = array($subquery1, $subquery2, $subquery3);
$signs = array(true, null, false);
$query = new Zend_Search_Lucene_Search_Query_Boolean($subqueries, $signs);
$hits = $index->find($query);
• NULL est utilisé pour définir une sous-requête qui n'est ni requise, ni interdite.
Chaque requête qui utilise des opérateurs booléens peut être réécrite en utilisant les notations
de signes et construites à l'aide de l'API. Par exemple :
est équivalent à
1183
Zend_Search_Lucene
field1:test*
ou
L'argument field est optionnel. Zend_Search_Lucene cherche dans tous les champs indexés
si aucun champ n'a été spécifié :
field1:test~
Cette requête va correspondre à des documents contenant 'test', 'text', 'best' ou d'autres mots
similaires.
ou
Un indice de similarité (optional similarity) peut être spécifié après le signe "~".
1184
Zend_Search_Lucene
field1:test~0.4
ou
L'argument field est optionnel. Zend_Search_Lucene cherche dans tous les champs indexés
si aucun champ n'a été spécifié :
Les phrases peuvent aussi contenir des trous ou des termes aux mêmes places; elles peuvent
être générées par l'analyseur dans différents buts. Par exemple, un terme peut être dupliqué
pour augmenter son poids, ou plusieurs synonymes peuvent être placés sur une seule et unique
position.
Une requête de phrase peut être construite en une seule étape à l'aide
du constructeur ou étape par étape avec des appels à la méthode
Zend_Search_Lucene_Search_Query_Phrase::addTerm().
Zend_Search_Lucene_Search_Query_Phrase(
[array $terms[, array $offsets[, string $field]]]
1185
Zend_Search_Lucene
);
Le paramètre $terms est un tableau de chaînes de caractères qui contient une collection de
termes pour une phrase. S'il est omis ou égal à NULL, une requête vide sera construite.
Le paramètre $offsets est un tableau d'entiers qui contient les positions des termes dans la
phrase. S'il est omis ou égale à NULL, les positions des termes seront implicitement séquentielles
sans trou.
Le paramètre $field est un chaîne de caractères qui indique le champ dans lequel chercher.
S'il est omis ou égal à NULL, la recherche se fera dans le champ par défaut.
Ainsi :
$query =
new Zend_Search_Lucene_Search_Query_Phrase(array('zend', 'framework'));
va chercher la phrase 'zend ????? download' et correspondra à 'zend platform download', 'zend
studio download', 'zend core download', 'zend framework download', etc.
Zend_Search_Lucene_Search_Query_Phrase::addTerm(
Zend_Search_Lucene_Index_Term $term[, integer $position]
);
Le paramètre $term décrit le prochain terme dans la phrase. Il doit indiquer le même champ
que les termes précédents, sinon une exception sera levée.
Ainsi :
1186
Zend_Search_Lucene
va chercher la phrase 'zend ????? download' et correspondra à 'zend platform download', 'zend
studio download', 'zend core download', 'zend framework download', etc.
Le 'slop factor' établit le nombre d'autres mots autorisés entre les mots spécifiés dans la requête
de phrase. S'il est défini à zéro, la requête correspondante sera une recherche d'une phrase
exacte. Pour des valeurs supérieures à zéro, cela fonctionne comme les opérateurs WITHIN
ou NEAR.
Le 'slop factor' est en fait une distance d'édition, où les éditions consistent à déplacer les
termes dans la phrase recherchée. Par exemple, inverser l'ordre de deux mots requiert deux
déplacements (le premier déplacement positionne les mots l'un sur l'autre), donc pour permettre
le réarrangement de phrase, le 'slop factor' doit être d'au moins deux.
Les correspondances les plus exactes possèdent un meilleur score que celles ayant eu recours
au 'slop factor'; ainsi les résultats de recherche sont classés par exactitude. Le 'slop factor' est
à zéro par défaut, requérant des correspondances exactes.
mod_date:[20020101 TO 20030101]
title:{Aida TO Carmen}
ou
1187
Zend_Search_Lucene
L'argument field est optionnel. Zend_Search_Lucene cherche dans tous les champs indexés
si aucun champ n'a été spécifié :
L'une ou l'autre (mais pas les deux) des bornes peut être définie à NULL. Dans ce cas,
Zend_Search_Lucene cherche depuis le début ou jusqu'à la fin du dictionnaire pour le(s)
champs spécifié(s) :
6. Jeu de caractères
6.1. Support UTF-8 et caractères sur un octet
Zend_Search_Lucene utilise l'UTF-8 en interne. Les fichiers d'index stockent des données
Unicode dans le "format UTF-8 modifié" de Java. Zend_Search_Lucene supporte totalement
6
ce format, à une exception près.
L'encodage des caractères d'entrée peut être spécifié grâce à l'API de Zend_Search_Lucene.
Les données seront converties automatiquement en UTF-8.
ctype_alpha() n'est pas compatible UTF-8, donc l'analyseur convertit le texte vers "ASCII//
TRANSLIT" avant l'indexation. Le même processus est utilisé de manière transparente lors du
7
requêtage.
L'analyseur par défaut isole les chiffres. Utilisez le parseur "Num" si vous voulez
que les chiffres soient considérés comme des mots.
6
Zend_Search_Lucene supporte seulement les caractères Basic Multilingual Plane (BMP) (de 0x0000 à 0xFFFF) et ne supporte pas
les "caractères supplémentaires" (caractères dont les codes sont supérieurs à 0xFFFF).
Java 2 représente ces caractères comme une paire de char (16-bit), le premier depuis l'échelle haute (0xD800-0xDBFF), le second pour
l'échelle basse (0xDC00-0xDFFF). Ils sont alors encodés comme des caractères UTF-8 standards sur six octets. La représentation
UTF-8 standard utilise elle 4 octets pour les caractères supplémentaires.
7
La conversion vers 'ASCII//TRANSLIT' peut dépendre de la locale courante ou de l'OS.
1188
Zend_Search_Lucene
Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num,
Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8_CaseInsensitive,
Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive.
N'importe quel analyseur peut être activé avec un code comme celui-ci:
Zend_Search_Lucene_Analysis_Analyzer::setDefault(
new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8());
Les analyseurs UTF-8 ont été améliorés dans Zend Framework 1.5. Les
anciennes versions considéraient les caractères non-ASCII comme des lettres.
Les nouveaux analyseurs sont quant à eux plus précis sur ce point.
Ceci pourrait vous obliger à reconstruire votre index afin que vos données et
vos requêtes de recherche soient au même format. Des résultats faux peuvent
apparaître sinon.
Tous ces analyseurs nécessitent que la libraire PCRE (Perl-compatible regular expressions) soit
compilée avec le support d'UTF-8. Ce support d'UTF-8 pour PCRE est activé par défaut dans les
sources des libraires PCRE livrées avec PHP, mais si vous avez utilisé des librairies partagées
pour la compilation de PHP, alors le support d'UTF-8 peut dépendre de l'OS.
Utilisez ce code pour vérifier si le support d'UTF-8 est assuré pour PCRE :
if (@preg_match('/\pL/u', 'a') == 1) {
echo "support UTF-8 pour PCRE activé.\n";
} else {
echo "support UTF-8 pour PCRE désactivé.\n";
}
Les analyseurs UTF-8 insensibles à la casse ont aussi besoin de l'extension mbstring pour être
activés.
Si vous voulez les analyseurs UTF-8 insensibles à la casse, mais que vous n'avez pas mbstring,
normalisez alors vos données avant de les indexer ainsi que vos requêtes avant vos recherches,
ceci en les tranformant en casse minuscule :
// Indexation
setlocale(LC_CTYPE, 'de_DE.iso-8859-1');
...
Zend_Search_Lucene_Analysis_Analyzer::setDefault(
new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8());
...
$doc->addField(Zend_Search_Lucene_Field::UnStored(
'contents', strtolower($contents))
);
1189
Zend_Search_Lucene
'title', strtolower($title))
);
// Recherche
setlocale(LC_CTYPE, 'de_DE.iso-8859-1');
...
Zend_Search_Lucene_Analysis_Analyzer::setDefault(
new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8());
...
$hits = $index->find(strtolower($query));
7. Extensibilité
7.1. Analyse de texte
La classe Zend_Search_Lucene_Analysis_Analyzer est utilisé par l'indexeur afin de
transformer en segments les champs texte du document.
Vous pouvez assigner votre propre analyseur de texte ou choisir parmi un ensemble
d'analyseurs prédéfinis : Zend_Search_Lucene_Analysis_Analyzer_Common_Text
et Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive (par
défaut). Tout deux interprètent les segments comme des séquences de
lettres. Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive
convertit tous les segments en minuscule.
Zend_Search_Lucene_Analysis_Analyzer::setDefault(
new Zend_Search_Lucene_Analysis_Analyzer_Common_Text());
...
$index->addDocument($doc);
La méthode nextToken() doit appeler la méthode normalize() sur chacun des segments.
Ce qui vous permet d'utiliser des filtres de segments avec votre analyseur.
Voici l'exemple d'analyseur personnalisé, qui accepte des mots contenant des chiffres comme
terme :
1190
Zend_Search_Lucene
/**
Exemple 633. Analyseur de texte personnalisé
* Voici un analyseur de texte qui traite les mots contenant des chiffres comme
* un seul terme
*/
class My_Analyzer extends Zend_Search_Lucene_Analysis_Analyzer_Common
{
private $_position;
/**
* Remet à Zéro le flux de segments
*/
public function reset()
{
$this->_position = 0;
}
/**
* API du flux de segmentation
* Récupère le segment suivant
* Retourne null à la fin du flux
*
* @return Zend_Search_Lucene_Analysis_Token|null
*/
public function nextToken()
{
if ($this->_input === null) {
return null;
}
while ($this->_position < strlen($this->_input)) {
// Saute les espaces
while ($this->_position < strlen($this->_input) &&
!ctype_alnum( $this->_input[$this->_position] )) {
$this->_position++;
}
$termStartPosition = $this->_position;
// lit le segment
while ($this->_position < strlen($this->_input) &&
ctype_alnum( $this->_input[$this->_position] )) {
$this->_position++;
}
// Segment vide, fin de flux.
if ($this->_position == $termStartPosition) {
return null;
}
$token = new Zend_Search_Lucene_Analysis_Token(
substr($this->_input,
$termStartPosition,
$this->_position -
$termStartPosition),
$termStartPosition,
$this->_position);
$token = $this->normalize($token);
if ($token !== null) {
return $token;
}
// Continue si le segment est sauté
}
return null;
}
}
Zend_Search_Lucene_Analysis_Analyzer::setDefault(
new My_Analyzer());
1191
Zend_Search_Lucene
Chaque filtre personnalisé doit implémenter la méthode normalize() qui devrait transformer
le segment en entrée ou signaler que le segment courant doit être sauté.
• Zend_Search_Lucene_Analysis_TokenFilter_LowerCase
• Zend_Search_Lucene_Analysis_TokenFilter_ShortWords
• Zend_Search_Lucene_Analysis_TokenFilter_StopWords
Le filtre LowerCase filtre est déjà utilisé par défaut par l'analyseur
Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive.
Les filtres ShortWords et StopWords peuvent être utilisés avec des analyseurs prédéfinis ou
personnalisés comme ceci :
Ce fichier doit être un simple fichier texte avec un mot par ligne. Le caractère '#' transforme la
ligne en commentaire.
Le constructeur de la classe
Zend_Search_Lucene_Analysis_TokenFilter_ShortWords a un argument optionnel. Il
s'agit de la longueur maximum de mot, elle est définie par défaut à 2.
1192
Zend_Search_Lucene
La correspondance au sein de champs plus long est moins précise, ainsi l'implémentation de
cette méthode retourne généralement de plus petites valeurs quand numTokens est important,
et de plus grandes valeurs lorsque numTokens est petit.
coord(q,d) - Zend_Search_Lucene_Search_Similarity::coord($overlap,
$maxOverlap) - un facteur de score basé sur la fraction de tout les termes de la recherche
que le document contient.
La présence d'une grande partie des termes de la requête indique une meilleure correspondance
avec la requête, ainsi les implémentations de cette méthode retourne habituellement de plus
grandes valeurs lorsque le ration entre ces paramètres est grand que lorsque le ratio entre elle
est petit.
Ceci n'affecte pas le classement, mais tente plutôt de faire des scores à partir de différentes
requêtes comparables entre elles.
1193
Zend_Search_Lucene
1194
Zend_Search_Lucene
1195
Zend_Search_Lucene
* de fichier.
* La nouvelle position, calculé en octets depuis le début du fichier,
* est obtenu en ajoutant l'offset à la position spécifiée par $whence,
* dont les valeurs sont définit comme suit :
* SEEK_SET - Définit la position comme égale aux octets de l'offset.
* SEEK_CUR - Définit la position à la position courante plus l'offset.
* SEEK_END - Définit la position à la fin du fichier plus l'offset.
*(Pour déplacer à une position avant la fin du fichier, vous devrez passer
* une valeur négative à l'offset.)
* En cas de succès, retourne 0; sinon, retourne -1
*
* @param integer $offset
* @param integer $whence
* @return integer
*/
public function seek($offset, $whence=SEEK_SET) {
...
}
/**
* Lit $length octets dans le fichier et avance le pointeur de fichier.
*
* @param integer $length
* @return string
*/
protected function _fread($length=1) {
...
}
}
• Les fichiers *.cfs contiennent les segments de l'index. Notez qu'un index optimisé a toujours
un seul segment.
• Les fichiers effaçables sont des fichiers qui ne sont plus utilisés par l'index, mais qui n'ont
pas pu être supprimés.
/**
8
La version du format de fichier supporté actuellement est la 2.3 (depuis Zend Framework 1.6).
1196
Zend_Search_Lucene
* Index creation:
*/
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.document.*;
import java.io.*
...
...
indexWriter.addDocument(doc);
9. Avancé
9.1. Depuis Zend Framework 1.6, gestion des transformations de
format d'index
Zend_Search_Lucene fonctionne avec les index Lucene Java 1.4-1.9, 2.1 et 2.3.
Les modifications de l'index n'arrivent que si une mise à jour de l'index est faite. Ceci arrive
lorsqu'un nouveau document est ajouté à l'index, ou lors de l'optimisation manuelle de l'index
par $index->optimize().
Dans un tel cas, Zend_Search_Lucene peut convertir l'index vers une version plus haute. Ceci
arrive toujours pour le format Zend_Search_Lucene::FORMAT_PRE_2_1, alors transformé
en format 2.1.
1197
Zend_Search_Lucene
Les conversions vers des versions antérieure de formats ne sont pas supportées.
Important!
Les formats ne peuvent pas être convertis vers des versions antérieures. Gardez
une copie de sauvegarde si ceci s'avère nécessaire, car après une conversion il
ne sera plus possible de faire marche arrière.
Il stocke les documents ajoutés en mémoire et les vide dans un segment sur le disque en fonction
du paramètre MaxBufferedDocs.
Si la limite MaxBufferedDocs n'est pas atteinte, alors il y aura des documents non
sauvegardés qui seront sauvés comme nouveau segment lors de la destruction de l'objet. La
procédure d'optimisation automatique est lancée si nécessaire, cela dépend des paramètres
MaxBufferedDocs, MaxMergeDocs et MergeFactor.
Les propriétés statiques d'un objet sont détruites après la dernière ligne de code exécutée.
class Searcher {
private static $_index;
Searcher::initIndex();
Un problème potentiel peut être les exceptions. Celles envoyées dans un destructeur d'un objet
statique n'ont pas de contexte, car le destructeur est appelé après la fin d'exécution du script.
Vous pouvez alors voir une "Fatal error: Exception thrown without a stack frame in Unknown on
line 0" au lieu de l'exception décrivant réellement le contexte.
class Searcher {
private static $_index;
1198
Zend_Search_Lucene
self::$_index = Zend_Search_Lucene::open('path/to/index');
}
...
Searcher::initIndex();
...
Cependant, il est préférable de ne pas utiliser les noms 'id' et 'score' afin d'éviter toute ambiguïté
dans les propriétés de QueryHit.
$hits = $index->find($query);
1199
Zend_Search_Lucene
Chaque segment d'index et une portion de données entièrement indépendante. Ainsi plus un
index contient de segments, plus il sera gourmand en mémoire et en temps de calcul lors de
la recherche.
$index = Zend_Search_Lucene::open($indexPath);
$index->optimize();
L'optimisation d'index fonctionne avec des "data streams" et ne consomme pas pas une grande
quantité de mémoire, mais nécessite des ressources de processeur et du temps.
Par nature, les segments d'index de Lucene ne peuvent pas être mis à jour (l'opération de mise à
jour nécessite une réécriture complète du segment). Ainsi, l'ajout de nouveau(x) document(s) à
un index implique toujours la génération d'un nouveau segment. De fait, cela dégrade la qualité
de l'index.
Une optimisation automatique d'un index est effectuée après chaque génération de segment et
consiste en la fusion des segments partiels.
MaxBufferedDocs n'a aucun effet si vous n'indexez qu'un seul document par exécution de script.
En revanche, il est très important pour les indexations massives ("batch indexing"). Plus sa
valeur est élevée, meilleures seront les performances d'indexation, mais plus la consommation
de mémoire sera importante.
1200
Zend_Search_Lucene
Une bonne méthode pour trouver une valeur correcte consiste à effectuer plusieurs tests avec
9
les documents les plus volumineux que vous vous attendez à voir figurer dans l'index. . Une
bonne pratique consiste à ne pas utiliser plus de la moitié de la mémoire allouée.
Diminuer la valeur du paramètre MaxMergeDocs peut aussi améliorer les performances lors de
l'indexation en masse ("batch indexing"). L'auto-optimisation est un processus itératif et utilise
une technique ascendante. Les petits segments sont fusionnés vers de plus gros segments qui
sont eux-mêmes fusionnés vers des segments encore plus gros, etc. L'optimisation complète
est achevée lorsqu'il ne reste qu'un seul gros segment.
En général, l'optimisation d'index en arrière-plan devrait être effectuée pour les modes
d'indexation interactifs et la valeur de MaxMergeDocs ne devrait pas être trop faible pour les
indexations de masse ("batch indexing").
2. Plus grand que MaxBufferedDocs, mais pas plus grand que MaxBufferedDocs*MergeFactor.
4. ...
La formule ci-dessous donne une bonne approximation du nombre de segments dans un index :
MaxBufferedDocs est déterminé par la mémoire allouée. Cela permet pour le facteur de fusion
(MergeFactor) approprié d'avoir un nombre raisonnable de segments.
9
memory_get_usage() et memory_get_peak_usage() peuvent être utilisées pour contrôler l'utilisation de la mémoire.
10
Zend_Search_Lucene maintient chaque segment ouvert pour améliorer les performances de recherche.
1201
Zend_Search_Lucene
L'optimisation du paramètre MergeFactor est plus efficace pour les performances de l'indexation
de masse (batch indexing) que MaxMergeDocs. Mais cette méthode manque un peu de
finesse. Le mieux est d'utiliser l'estimation ci-dessus pour optimiser MergeFactor, puis de jouer
avec MaxMergeDocs pour obtenir les meilleures performances d'indexation de masse (batch
indexing).
L'objet qui représente l'index est automatiquement fermé lorsque lui, ainsi que tous les objets de
résultats de requête qui lui sont associés, sont hors de portée du script principal.
Si l'objet d'index est stocké dans une variable globale, il ne sera fermé qu'à la fin de l'exécution
11
du script .
Cela n'empêche pas la fermeture normale du processus de l'index, mais cela peut empêcher un
diagnostic d'erreur précis si une erreur survient durant la fermeture.
$index = Zend_Search_Lucene::open($indexPath);
...
unset($index);
Le second est d'effectuer une opération de commit avant la fin du script exécution :
$index = Zend_Search_Lucene::open($indexPath);
$index->commit();
Cette possibilité est également décrite dans la section "Avancé - Utiliser les propriétés statiques
de l'index".
1202
Zend_Search_Lucene
...
Il utilise la mémoire pour mettre en cache certaines informations et optimiser la recherche, ainsi
que les performances de l'indexation.
1203
Zend_Search_Lucene
L'indexation utilise la même quantité de mémoire que la recherche, plus de la mémoire pour
mettre les documents en tampon. La quantité de mémoire peut être gérée par le paramètre
MaxBufferedDocs.
10.6. Encodage
Zend_Search_Lucene travaille avec des chaînes en UTF-8 en interne. Ainsi toutes les chaînes
de caractères retournée par Zend_Search_Lucene sont encodées en UTF-8.
Vous ne devriez pas être concernés par l'encodage si vous travaillez avec des chaîne purement
ASCII, mais vous devez être prudent si ce n'est pas le cas.
Un mauvais encodage peut causer des notices (erreur) durant la conversation d'encodage, voire
la perte de données.
C'est le meilleur moyen d'éviter toute ambiguïté dans les encodages utilisés.
Si le paramètre optionnel de l'encodage est omis, la locale courante est utilisée. La locale
courante peut contenir des données d'encodage en plus des spécification de langue :
setlocale(LC_ALL, 'fr_FR');
...
setlocale(LC_ALL, 'de_DE.iso-8859-1');
...
setlocale(LC_ALL, 'ru_RU.UTF-8');
...
La même approche est utilisée pour définir l'encodage des chaînes de requête.
Si l'encodage n'est pas spécifié, la locale courante est utilisée pour le déterminer.
L'encodage peut être passée comme paramètre optionnel si la requête est analysée
explicitement avant la recherche :
$query =
Zend_Search_Lucene_Search_QueryParser::parse($queryStr, 'iso-8859-5');
$hits = $index->find($query);
...
1204
Zend_Search_Lucene
L'encodage par défaut peut également être spécifié avec la méthode setDefaultEncoding()
:
Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding('iso-8859-1');
$hits = $index->find($queryStr);
...
chaîne vide sous-entend "locale courante".
Si l'encodage correct est spécifié, il pourra être correctement interprété par l'analyseur. Le
comportement dépend de quel analyseur est utilisé. Consultez la section sur les Jeu de
caractères pour plus de détails.
Les index ne devrait pas être utilisés pour du stockage de données. Ils ne fournissent pas
de fonctionnalités de backup/restauration partiel, journalisation, logs, transactions et beaucoup
d'autres fonctionnalités assimilées aux systèmes de gestion de bases de données.
Cependant, Zend_Search_Lucene tente de garder ses index dans un état constant en tout
temps.
Le backup et la restauration d'un index devrait être effectué en copiant le contenu du répertoire
de l'index.
Si pour une raison quelconque, un index devait être corrompu, il devrait être restauré ou
complètement reconstruit.
C'est donc une bonne idée de faire des backups des gros index et de stocker les logs de
modifications pour pouvoir effectuer des restaurations manuelles et des opérations de "roll-
forward" si nécessaire. Cette pratique réduit considérablement le temps de restauration.
1205
Zend_Serializer
1. Introduction
Zend_Serializer utilise une interface basée sur des adaptateurs afin de générer des
représentations stockables de types php et inversement.
$serializer = Zend_Serializer::factory('PhpSerialize');
// $serializer est instance de Zend_Serializer_Adapter_AdapterInterface,
// précisémment Zend_Serializer_Adapter_PhpSerialize
try {
$serialized = $serializer->serialize($data);
// $serialized est une chaine
$unserialized = $serializer->unserialize($serialized);
// ici $data == $unserialized
} catch (Zend_Serializer_Exception $e) {
echo $e;
}
La méhode serialize génère une chaine. Pour regénérer la donnée utilisez la méthode
unserialize.
try {
$serialized = $serializer->serialize($data, array('comment' => 'change comment'));
$unserialized = $serializer->unserialize($serialized, array(/* options pour unserialize
} catch (Zend_Serializer_Exception $e) {
echo $e;
}
Les options passées à factory sont valides pour l'objet crée. Vous pouvez alors changer ces
options grâce à la méthode setOption(s). Pour changer des options pour un seul appel,
passez celles-ci en deuxième arguement des méthodes serialize ou unserialize.
1206
Zend_Serializer
Zend_Serializer::setDefaultAdapter('PhpSerialize', $options);
// ou
$serializer = Zend_Serializer::factory('PhpSerialize', $options);
Zend_Serializer::setDefaultAdapter($serializer);
try {
$serialized = Zend_Serializer::serialize($data, $options);
$unserialized = Zend_Serializer::unserialize($serialized, $options);
} catch (Zend_Serializer_Exception $e) {
echo $e;
}
2. Zend_Serializer_Adapter
Les adaptateurs Zend_Serializer servent à changer les méthodes de sérialisation
facilement.
Chaque adaptateurs possède ses propres atouts et inconvénients. Dans certains cas, certains
types PHP (objets) ne peuvent être représentés sous forme de chaines. Dans ce cas, ces types
seront convertis vers un type sérialisable (par exemple, les objets seront convertis en tableaux).
Ci ceci échoue, une exception Zend_Serializer_Exception sera alors envoyée.
2.1. Zend_Serializer_Adapter_PhpSerialize
Cet adaptateur utilise les fonctions PHP un/serialize et constitue un bon choix d'adaptateur
par défaut.
2.2. Zend_Serializer_Adapter_Igbinary
Igbinary est un logiciel Open Source crée par Sulake Dynamoid Oy. C'est un remplaçant du
sérialiseur utiliser par PHP. Au lieu d'utiliser une représentation textuelle (couteuse en temps et
en poids), igbinary représente les structures de données PHP dans un format binaire compact.
Les gains sont importants lorsqu'un système de stockage comme memcache est utilisé pour les
données sérialisées.
2.3. Zend_Serializer_Adapter_Wddx
WDDX (Web Distributed Data eXchange) est à la fois un langage de programmation, une
plateforme et un mecanisme de transport de données entre différents environnements.
Cet adaptateur utilise simplement les fonctions PHP wddx_*(). Veuillez lire le manuel PHP afin
de vérifier la disponibilité et l'installation de ces fonctions.
Aussi, l'extension PHP SimpleXML est utilisée pour vérifier si une valeur NULL retournée par
wddx_unserialize() est basée sur une donnée sérialisée NULL ou au contraire des données
non valides
1207
Zend_Serializer
2.4. Zend_Serializer_Adapter_Json
L'adaptateur JSON acréer un pont vers Zend_Json et/ou ext/json (l'extension json de PHP).
Pour plus d'informations, lisez le manuel de Zend_Json.
2.5. Zend_Serializer_Adapter_Amf 0 et 3
Les adaptateurs AMF, Zend_Serializer_Adapter_Amf0 et
Zend_Serializer_Adapter_Amf3, sont un pont vers le sérialiseur du composant Zend_Amf.
Veuillez lire la documentation de Zend_Amf documentation pour plus d'informations.
2.6. Zend_Serializer_Adapter_PythonPickle
Cet adaptateur convertit des types PHP vers une chaine Python Pickle Grâce à lui, vous pouvez
lire en Python des données sérialisées de PHP et inversement.
1208
Zend_Serializer
2.7. Zend_Serializer_Adapter_PhpCode
Cet adaptateur génère une chaine représentant du code analysable par PHP via var_export().A
la désérialisation, les données seront exécutées par eval.
Utilisation de eval()
L'adaptateur PhpCode utilise eval() pour désérialiser. Ceci mène à des
problèmes de performance et de sécurité, un nouveau processus sera crée.
Typiquement, vous devriez utiliser l'adaptateur PhpSerialize à moins que
vous ne vouliez que les données sérialisées ne soient analysables à l'oeil
humain.
1209
Zend_Server
1. Introduction
La famille de classes Zend_Server fournit des fonctionnalités pour les différentes classes
serveur, notamment Zend_XmlRpc_Server, Zend_Rest_Server, Zend_Json_Server et
Zend_Soap_Wsdl. Zend_Server_Interface fournit une interface qui imite la classe
SoapServer de PHP5; toutes les classes serveur devraient implémenter cette interface de
manière à fournir une API serveur standard.
2. Zend_Server_Reflection
2.1. Introduction
Zend_Server_Reflection fournit un mécanisme standard pour exécuter l'introspection de
fonction et de classe pour utiliser avec des classes serveur. Il est basé sur l'API de réflexion de
PHP 5, et l'améliore pour fournir des méthodes de recherche des types et des descriptions de
paramètre et de valeur de retour, une liste complète des prototypes de fonction et de méthode
(c.-à-d., toutes les combinaisons d'appels valides possibles), et des descriptions de fonction/
méthode.
Typiquement, cette fonctionnalité sera seulement utilisée par les développeurs des classes
serveur pour le framework.
2.2. Utilisation
L'utilisation de base est simple :
$class = Zend_Server_Reflection::reflectClass('Ma_Classe');
$function = Zend_Server_Reflection::reflectFunction('ma_fonction');
1210
Zend_Server
1211
Zend_Service
1. Introduction
Zend_Service est une classe abstraite qui sert de fondation à l'implémentation des services
Web, comme SOAP ou REST.
Zend_Service offre un support pour tout service Web basé sur REST à travers
Zend_Rest_Client.
• Akismet
• Amazon
• Audioscrobbler
• Del.icio.us
• Flickr
• Simpy
• SlideShare
• StrikeIron
• Yahoo!
2. Zend_Service_Akismet
2.1. Introduction
Zend_Service_Akismet provides a client for the Akismet API. The Akismet service is used to
determine if incoming data is potentially spam. It also exposes methods for submitting data as
known spam or as false positives (ham). It was originally intended to help categorize and identify
spam for Wordpress, but it can be used for any type of data.
Akismet requires an API key for usage. You can get one by signing up for a WordPress.com
account. You do not need to activate a blog. Simply acquiring the account will provide you with
the API key.
Akismet requires that all requests contain a URL to the resource for which data is being filtered.
Because of Akismet's origins in WordPress, this resource is called the blog URL. This value
should be passed as the second argument to the constructor, but may be reset at any time using
the setBlogUrl() method, or overridden by specifying a 'blog' key in the various method calls.
1212
Zend_Service
If called with no arguments, verifyKey() uses the API key provided to the constructor.
• user_ip, the IP address of the user submitting the data (not your IP address, but that of a
user on your site).
• user_agent, the reported UserAgent string (browser and version) of the user submitting the
data.
• blog, the fully qualified URL to the resource or application. If not specified, the URL provided
to the constructor will be used.
• referrer, the content of the HTTP_REFERER header at the time of submission. (Note
spelling; it does not follow the header name.)
• permalink, the permalink location, if any, of the entry the data was submitted to.
• comment_type, the type of data provided. Values specified in the API include 'comment',
'trackback', 'pingback', and an empty string (''), but it may be any value.
• comment_author_url, the URL or home page of the person submitting the data.
You may also submit any other environmental variables you feel might be a factor in determining
if data is spam. Akismet suggests the contents of the entire $_SERVER array.
1213
Zend_Service
The isSpam() method will return either TRUE or FALSE, or throw an exception if the API key
is invalid.
$data = array(
'user_ip' => '111.222.111.222',
'user_agent' => 'Mozilla/5.0 ' . (Windows; U; Windows NT ' .
'5.2; en-GB; rv:1.8.1) Gecko/20061010 ' .
'Firefox/2.0',
'comment_type' => 'contact',
'comment_author' => 'John Doe',
'comment_author_email' => 'nospam@myhaus.net',
'comment_content' => "I'm not a spammer, honest!"
);
if ($akismet->isSpam($data)) {
echo "Sorry, but we think you're a spammer.";
} else {
echo "Welcome to our site!";
}
$data = array(
'user_ip' => '111.222.111.222',
'user_agent' => 'Mozilla/5.0 (Windows; U; Windows NT 5.2;' .
'en-GB; rv:1.8.1) Gecko/20061010 Firefox/2.0',
'comment_type' => 'contact',
'comment_author' => 'John Doe',
'comment_author_email' => 'nospam@myhaus.net',
'comment_content' => "I'm not a spammer, honest!"
);
$akismet->submitSpam($data));
1214
Zend_Service
$data = array(
'user_ip' => '111.222.111.222',
'user_agent' => 'Mozilla/5.0 (Windows; U; Windows NT 5.2;' .
'en-GB; rv:1.8.1) Gecko/20061010 Firefox/2.0',
'comment_type' => 'contact',
'comment_author' => 'John Doe',
'comment_author_email' => 'nospam@myhaus.net',
'comment_content' => "I'm not a spammer, honest!"
);
$akismet->submitHam($data));
• getBlogUrl() and setBlogUrl() allow you to retrieve and modify the blog URL used in
requests.
• getApiKey() and setApiKey() allow you to retrieve and modify the API key used in
requests.
• getCharset() and setCharset() allow you to retrieve and modify the character set used
to make the request.
• getPort() and setPort() allow you to retrieve and modify the TCP port used to make the
request.
• getUserAgent() and setUserAgent() allow you to retrieve and modify the HTTP user
agent used to make the request. Note: this is not the user_agent used in data submitted to the
service, but rather the value provided in the HTTP User-Agent header when making a request
to the service.
The value used to set the user agent should be of the form some user agent/
version | Akismet/version. The default is Zend Framework/ZF-VERSION |
Akismet/1.11, where ZF-VERSION is the current Zend Framework version as stored in the
Zend_Framework::VERSION constant.
3. Zend_Service_Amazon
3.1. Introduction
Zend_Service_Amazon est une API simple pour utiliser les Web services d'Amazon.
Zend_Service_Amazon a deux APIs : une plutôt traditionnelle qui suit la propre API d'Amazon,
et un "Query API" simplifiée pour construire facilement des requêtes de recherche, même
compliquées.
• Le stockage de données informatives, comme des images, des descriptions, le prix et plus
1215
Zend_Service
Pour pouvoir utiliser Zend_Service_Amazon, vous devez avant tout avoir une clé "developer
API" Amazon ainsi que votre clé secrète. Pour obtenir une telle clé et pour plus d'informations,
vous pouvez visitez le site Web Amazon Web Services. A partir du 15 août 2009, vous ne pourrez
utiliser l'API Amazon à travers Zend_Service_Amazon, quand spécifiant la clé secrète.
Attention
Votre clé "developer API" et votre clé secret sont liées à votre identité Amazon,
donc faites en sorte de les conserver privées.
Dans cet exemple, nous recherchons les livres sur PHP disponibles chez Amazon et boucler
sur les résultats pour les afficher.
Ici nous cherchons aussi les livres sur PHP disponibles chez Amazon, mais en utilisant l'API
de requête, qui ressemble au modèle de conception Interface Fluide.
1216
Zend_Service
Codes de pays
Les codes de pays valides sont CA, DE, FR, JP, UK, et US.
Exemple 641. Rechercher une produit Amazon spécifique avec son ASIN
La méthode itemLookup() accepte aussi un second paramètre optionnel pour gérer les
options de recherche. Pour les détails complets et une liste des options disponibles, visitez la
documentation Amazon correspondante. .
Pour récupérer les informations d'images pour vos résultats de recherche, vous
devez définir l'option ResponseGroup à Medium ou Large.
L'option ResponseGroup est utilisée pour contrôler les informations spécifiques qui sont
retournées dans la réponse.
1217
Zend_Service
La méthode itemSearch() accepte un seul tableau en paramètre pour gérer les options de
recherche. Pour plus de détails et une liste des options disponibles, visitez la documentation
Amazon correspondante
Dans cet exemple, l'API de requêtes alternative est utilisée comme une interface fluide pour
spécifier les options et leurs valeurs respectives :
Pour plus d'information sur les options disponibles, vous pouvez vous référer à la
documentation spécifique.
• Zend_Service_Amazon_Item
• Zend_Service_Amazon_Image
• Zend_Service_Amazon_ResultSet
• Zend_Service_Amazon_OfferSet
• Zend_Service_Amazon_Offer
• Zend_Service_Amazon_SimilarProduct
• Zend_Service_Amazon_Accessories
1218
Zend_Service
• Zend_Service_Amazon_CustomerReview
• Zend_Service_Amazon_EditorialReview
• Zend_Service_Amazon_ListMania
3.6.1. Zend_Service_Amazon_Item
Zend_Service_Amazon_Item est le type de classe utilisé pour représenter un produit Amazon
retourné par le service Web. Elle récupère tous les attributs des articles, incluant le titre, la
description, les revues, etc.
3.6.1.1. Zend_Service_Amazon_Item::asXML()
string asXML();
3.6.1.2. Propriétés
1219
Zend_Service
3.6.2. Zend_Service_Amazon_Image
Zend_Service_Amazon_Image représente une image distante pour un produit.
3.6.2.1. Propriétés
3.6.3. Zend_Service_Amazon_ResultSet
Des objets Zend_Service_Amazon_ResultSet sont retournés par
Zend_Service_Amazon::itemSearch() et vous permettent de gérer facilement les différents
résultats retournés.
SeekableIterator
3.6.3.1. Zend_Service_Amazon_ResultSet::totalResults()
int totalResults();
1220
Zend_Service
3.6.4. Zend_Service_Amazon_OfferSet
Chaque résultat retourné par Zend_Service_Amazon::itemSearch() et
Zend_Service_Amazon::itemLookup() contient un objet Zend_Service_Amazon_OfferSet
au travers duquel il est possible de récupérer les informations de prix de l'article.
3.6.4.1. Propriétés
3.6.5. Zend_Service_Amazon_Offer
Chaque offre pour un article est retourné sous la forme d'un objet
Zend_Service_Amazon_Offer.
1221
Zend_Service
3.6.6. Zend_Service_Amazon_SimilarProduct
Lors de la recherche d'articles, Amazon retourne aussi une liste de produits similaires
qui pourraient intéresser le visiteur. Chacun d'entre eux est retourné dans un objet
Zend_Service_Amazon_SimilarProduct.
Chaque objet contient l'information qui vous permet de faire les requêtes suivantes pour obtenir
les informations complètes sur un article.
3.6.6.1. Propriétés
3.6.7. Zend_Service_Amazon_Accessories
Les accessoires pour un article retourné sont représentés comme un objet
Zend_Service_Amazon_Accessories.
3.6.7.1. Propriétés
1222
Zend_Service
3.6.8. Zend_Service_Amazon_CustomerReview
3.6.8.1. Propriétés
3.6.9. Zend_Service_Amazon_EditorialReview
3.6.9.1. Propriétés
3.6.10. Zend_Service_Amazon_Listmania
3.6.10.1. Propriétés
1223
Zend_Service
4. Zend_Service_Amazon_Ec2
4.1. Introduction
Zend_Service_Amazon_Ec2 provides an interface to Amazon Elastic Clound Computing
(EC2).
Zend_Service_Amazon_Ec2_Ebs::setKeys('aws_key','aws_secret_key');
To set the region that you are working in you can call the setRegion to set which Amazon Ec2
Region you are working in. Currently there is only two region available us-east-1 and eu-west-1.
If an invalid value is passed it will throw an exception stating that.
Zend_Service_Amazon_Ec2_Ebs::setRegion('us-east-1');
5. Zend_Service_Amazon_Ec2: Instances
5.1. Instance Types
Amazon EC2 instances are grouped into two families: standard and High-CPU. Standard
instances have memory to CPU ratios suitable for most general purpose applications; High-CPU
instances have proportionally more CPU resources than memory (RAM) and are well suited
for compute-intensive applications. When selecting instance types, you might want to use less
powerful instance types for your web server instances and more powerful instance types for your
database instances. Additionally, you might want to run CPU instance types for CPU-intensive
data processing tasks.
One of the advantages of EC2 is that you pay by the instance hour, which makes it convenient
and inexpensive to test the performance of your application on different instance families and
types. One good way to determine the most appropriate instance family and instance type is to
launch test instances and benchmark your application.
1224
Zend_Service
Instance Types
The instance types are defined as constants in the code. Column eight in the
table is the defined constant name
1225
Zend_Service
run will launch a specified number of EC2 Instances. run takes an array of parameters to
start, below is a table containing the valid values.
1226
Zend_Service
run will return information about each instance that is starting up.
This operation is asynchronous; it only queues a request to reboot the specified instance(s).
The operation will succeed if the instances are valid and belong to the user. Requests to
reboot terminated instances are ignored.
terminate shuts down one or more instances. This operation is idempotent; if you
terminate an instance more than once, each call will succeed.
Terminated Instances
1227
Zend_Service
If you specify one or more instance IDs, Amazon EC2 returns information for those instances.
If you do not specify instance IDs, Amazon EC2 returns information for all relevant instances.
If you specify an invalid instance ID, a fault is returned. If you specify an instance that you
do not own, it will not be included in the returned results.
Terminated Instances
Recently terminated instances might appear in the returned results. This interval
is usually less than one hour. If you do not want terminated instances to be
returned, pass in a second variable of boolean TRUE to describe and the
terminated instances will be ignored.
describeByImageId is functionally the same as describe but it will only return the
instances that are using the provided imageId.
Terminated Instances
Recently terminated instances might appear in the returned results. This interval
is usually less than one hour. If you do not want terminated instances to be
returned, pass in a second variable of boolean TRUE to describe and the
terminated instances will be ignored.
Instance console output is buffered and posted shortly after instance boot, reboot, and
termination. Amazon EC2 preserves the most recent 64 KB output which will be available
for at least one hour after the most recent post.
consoleOutput returns an array containing the instanceId, timestamp from the last
output and the output from the console.
1228
Zend_Service
confirmProduct returns TRUE if the specified product code is attached to the specified
instance. The operation returns FALSE if the product code is not attached to the instance.
The confirmProduct operation can only be executed by the owner of the AMI. This feature
is useful when an AMI owner is providing support and wants to verify whether a user's
instance is eligible.
monitor returns the list of instances and their current state of the CloudWatch Monitoring.
If the instance does not currently have Monitoring enabled it will be turned on.
monitor returns the list of instances and their current state of the CloudWatch Monitoring.
If the instance currently has Monitoring enabled it will be turned off.
• Remote Desktop—To access Windows instances, you use Remote Desktop instead of SSH.
• Administrative Password—To access Windows instances the first time, you must obtain the
administrative password using the ec2-get-password command.
• Simplified Bundling—To bundle a Windows instance, you use a single command that shuts
down the instance, saves it as an AMI, and restarts it.
As part of this service, Amazon EC2 instances can now run Microsoft Windows Server 2003.
Our base Windows image provides you with most of the common functionality associated with
Windows. However, if you require more than two concurrent Windows users or need to leverage
applications that require LDAP, Kerberos, RADIUS, or other credential services, you must use
Windows with Authentication Services. For example, Microsoft Exchange Server and Microsoft
SharePoint Server require Windows with Authentication Services.
1229
Zend_Service
The Windows public AMIs that Amazon provides are unmodified versions of Windows with the
following two exceptions: we added drivers to improve the networking and disk I/O performance
and we created the Amazon EC2 configuration service. The Amazon EC2 configuration service
performs the following functions:
• Randomly sets the Administrator password on initial launch, encrypts the password with the
user's SSH key, and reports it to the console. This operation happens upon initial AMI launch.
If you change the password, AMIs that are created from this instance use the new password.
• Configures the computer name to the internal DNS name. To determine the internal DNS
name, see Using Instance Addressing.
• Sends the last three system and application errors from the event log to the console. This
helps developers to identify problems that caused an instance to crash or network connectivity
to be lost.
1230
Zend_Service
Amazon EC2 Reserved Instances are based on instance type and location (region and
Availability Zone) for a specified period of time (e.g., 1 year or 3 years) and are only available
for Linux or UNIX instances.
Amazon EC2 applies the two m1.small Reserved Instances to two of the instances in Availability
Zone us-east-1a. Amazon EC2 doesn't apply the two c1.medium Reserved Instances because
the c1.medium instances are in a different Availability Zone and does not apply the m1.xlarge
Reserved Instances because there are no running m1.xlarge instances.
1231
Zend_Service
purchaseOffering() Purchases a Reserved Instance for use with your account. With
Amazon EC2 Reserved Instances, you purchase the right to launch Amazon EC2 instances
for a period of time (without getting insufficient capacity errors) and pay a lower usage rate
for the actual time used.
listMetrics() returns a list of up to 500 valid metrics for which there is recorded data
available to a you and a NextToken string that can be used to query for the next set of results.
1232
Zend_Service
getMetricStatistics() only requires two parameters but it also has four additional
parameters that are optional.
• Required:
• MeasureName The measure name that corresponds to the measure for the gathered
metric. Valid EC2 Values are CPUUtilization, NetworkIn, NetworkOut, DiskWriteOps
DiskReadBytes, DiskReadOps, DiskWriteBytes. Valid Elastic Load Balancing Metrics are
Latency, RequestCount, HealthyHostCount UnHealthyHostCount. For more information
click here
• Statistics The statistics to be returned for the given metric. Valid values are Average,
Maximum, Minimum, Samples, Sum. You can specify this as a string or as an array of
values. If you don't specify one it will default to Average instead of failing out. If you specify
an incorrect option it will just skip it. For more information click here
• Optional:
• Dimensions Amazon CloudWatch allows you to specify one Dimension to further filter
metric data on. If you don't specify a dimension, the service returns the aggregate of all
the measures with the given measure name and time range.
• Unit The standard unit of Measurement for a given Measure. Valid Values: Seconds,
Percent, Bytes, Bits, Count, Bytes/Second, Bits/Second, Count/Second, and None.
Constraints: When using count/second as the unit, you should use Sum as the statistic
instead of Average. Otherwise, the sample returns as equal to the number of requests
instead of the number of 60-second intervals. This will cause the Average to always equals
one when the unit is count/second.
• StartTime The timestamp of the first datapoint to return, inclusive. For example,
2008-02-26T19:00:00+00:00. We round your value down to the nearest minute. You can
set your start time for more than two weeks in the past. However, you will only get data
for the past two weeks. (in ISO 8601 format). Constraints: Must be before EndTime.
• EndTime The timestamp to use for determining the last datapoint to return. This is the
last datapoint to fetch, exclusive. For example, 2008-02-26T20:00:00+00:00 (in ISO 8601
format).
1233
Zend_Service
register Each AMI is associated with an unique ID which is provided by the Amazon EC2
service through the RegisterImage operation. During registration, Amazon EC2 retrieves the
specified image manifest from Amazon S3 and verifies that the image is owned by the user
registering the image.
deregister, Deregisters an AMI. Once deregistered, instances of the AMI can no longer
be launched.
describe Returns information about AMIs, AKIs, and ARIs available to the user.
Information returned includes image type, product codes, architecture, and kernel and RAM
disk IDs. Images available to the user include public images available for any user to launch,
private images owned by the user making the request, and private images owned by other
users for which the user has explicit launch permissions.
Name Description
public The owner of the AMI granted launch
permissions for the AMI to the all group.
All users have launch permissions for these
AMIs.
explicit The owner of the AMI granted launch
permissions to a specific user.
1234
Zend_Service
Name Description
implicit A user has implicit launch permissions for all
AMIs he or she owns.
The list of AMIs returned can be modified by specifying AMI IDs, AMI owners, or users with
launch permissions. If no options are specified, Amazon EC2 returns all AMIs for which the
user has launch permissions.
If you specify one or more AMI IDs, only AMIs that have the specified IDs are returned. If
you specify an invalid AMI ID, a fault is returned. If you specify an AMI ID for which you do
not have access, it will not be included in the returned results.
If you specify one or more AMI owners, only AMIs from the specified owners and for which
you have access are returned. The results can include the account IDs of the specified
owners, amazon for AMIs owned by Amazon or self for AMIs that you own.
If you specify a list of executable users, only users that have launch permissions for the
AMIs are returned. You can specify account IDs (if you own the AMI(s)), self for AMIs for
which you own or have explicit permissions, or all for public AMIs.
describe returns an array for all the images that match the critera that was passed
in. The array contains the imageId, imageLocation, imageState, imageOwnerId, isPublic,
architecture, imageType, kernelId, ramdiskId and platform.
Name Description
launchPermission Controls who has permission to launch the
AMI. Launch permissions can be granted to
specific users by adding userIds.
1235
Zend_Service
Name Description
productCodes Associates a product code with AMIs. This
allows developers to charge users for using
AMIs. The user must be signed up for the
product before they can launch the AMI. This
is a write once attribute; after it is set, it
cannot be changed or removed.
resetAttribute will reset the attribute of an AMI to its default value. The productCodes
attribute cannot be reset.
describeAttribute returns an array with the value of the attribute that was requested.
You can create up to twenty Amazon EBS volumes of any size (from one GiB up to one TiB).
Each Amazon EBS volume can be attached to any Amazon EC2 instance in the same Availability
Zone or can be left unattached.
1236
Zend_Service
Amazon EBS provides the ability to create snapshots of your Amazon EBS volumes to Amazon
S3. You can use these snapshots as the starting point for new Amazon EBS volumes and can
protect your data for long term durability.
Creating a brand new EBS Volume requires the size and which zone you want the EBS
Volume to be in.
createNewVolume will return an array containing information about the new Volume which
includes the volumeId, size, zone, status and createTime.
Creating an EBS Volume from a snapshot requires the snapshot_id and which zone you
want the EBS Volume to be in.
Creating a Snapshot of an EBS Volume requires the volumeId of the EBS Volume.
createSnapshot will return an array containing information about the new Volume
Snapshot which includes the snapshotId, volumeId, status, startTime and progress.
describeVolume will return an array with information about each Volume which includes
the volumeId, size, status and createTime. If the volume is attached to an instance, an
addition value of attachmentSet will be returned. The attachment set contains information
about the instance that the EBS Volume is attached to, which includes volumeId, instanceId,
device, status and attachTime.
1237
Zend_Service
To return a list of EBS Volumes currently attached to a running instance you can call
this method. It will only return EBS Volumes attached to the instance with the passed in
instanceId.
describeSnapshot will return an array containing information about each EBS Volume
Snapshot which includes the snapshotId, volumeId, status, startTime and progress.
attachVolume will attach an EBS Volume to a running Instance. To attach a volume you
need to specify the volumeId, the instanceId and the device (ex: /dev/sdh).
attachVolume will return an array with information about the attach status which contains
volumeId, instanceId, device, status and attachTime
detachVolume returns an array containing status information about the EBS Volume which
includes volumeId, instanceId, device, status and attachTime.
Forced Detach
You should only force a detach if the previous detachment attempt did not
occur cleanly (logging into an instance, unmounting the volume, and detaching
1238
Zend_Service
normally). This option can lead to data loss or a corrupted file system. Use this
option only as a last resort to detach a volume from a failed instance. The instance
will not have an opportunity to flush file system caches or file system meta data.
If you use this option, you must perform file system check and repair procedures.
If you use dynamic DNS to map an existing DNS name to a new instance's public IP address, it
might take up to 24 hours for the IP address to propagate through the Internet. As a result, new
instances might not receive traffic while terminated instances continue to receive requests.
To solve this problem, Amazon EC2 provides elastic IP addresses. Elastic IP addresses are
static IP addresses designed for dynamic cloud computing. Elastic IP addresses are associated
with your account, not specific instances. Any elastic IP addresses that you associate with your
account remain associated with your account until you explicitly release them. Unlike traditional
static IP addresses, however, elastic IP addresses allow you to mask instance or Availability
Zone failures by rapidly remapping your public IP addresses to any instance in your account.
1239
Zend_Service
describe has an optional paramater to describe all of your allocated Elastic IP addresses
or just some of your allocated addresses.
describe returns an array that contains information on each Elastic IP Address which
contains the publicIp and the instanceId if it is assocated.
// describe a subset
$ips = $ec2_eip->describe(array('ip1', 'ip2', 'ip3'));
create, creates a new 2048 bit RSA key pair and returns a unique ID that can be used to
reference this key pair when launching new instances.
create returns an array which contains the keyName, keyFingerprint and keyMaterial.
1240
Zend_Service
delete, will delete the key pair. This will only prevent it from being used with new instances.
Instances currently running with the keypair will still allow you to access them.
describe returns information about key pairs available to you. If you specify key pairs,
information about those key pairs is returned. Otherwise, information for all registered key
pairs is returned.
Each Amazon EC2 region is designed to be completely isolated from the other Amazon EC2
regions. This achieves the greatest possible failure independence and stability, and it makes the
locality of each EC2 resource unambiguous.
describe is used to find out which regions your account has access to.
describe will return an array containing information about which regions are available.
Each array will contain regionName and regionUrl.
foreach($regions as $region) {
print $region['regionName'] . ' -- ' . $region['regionUrl'] . '<br />';
}
1241
Zend_Service
describe is used to find out which what the status is of each availability zone.
describe will return an array containing information about which zones are available. Each
array will contain zoneName and zoneState.
foreach($zones as $zone) {
print $zone['zoneName'] . ' -- ' . $zone['zoneState'] . '<br />';
}
You can modify rules for a group at any time. The new rules are automatically enforced for all
running instances and instances launched in the future.
create a new security group. Every instance is launched in a security group. If no security
group is specified during launch, the instances are launched in the default security group.
Instances within the same security group have unrestricted network access to each other.
Instances will reject network access attempts from other instances in a different security
group.
1242
Zend_Service
If you specify security group names, information about those security groups is returned.
Otherwise, information for all security groups is returned. If you specify a group that does
not exist, a fault is returned.
describe will return an array containing information about security groups which includes
the ownerId, groupName, groupDescription and an array containing all the rules for that
security group.
delete will remove the security group. If you attempt to delete a security group that contains
instances, a fault is returned. If you attempt to delete a security group that is referenced
by another security group, a fault is returned. For example, if security group B has a rule
that allows access from security group A, security group A cannot be deleted until the allow
rule is removed.
Permissions are specified by the IP protocol (TCP, UDP or ICMP), the source of the request
(by IP range or an Amazon EC2 user-group pair), the source and destination port ranges
(for TCP and UDP), and the ICMP codes and types (for ICMP). When authorizing ICMP, -1
can be used as a wildcard in the type and code fields.
Permission changes are propagated to instances within the security group as quickly as
possible. However, depending on the number of instances, a small delay might occur.
1243
Zend_Service
Permission changes are propagated to instances within the security group as quickly as
possible. However, depending on the number of instances, a small delay might occur.
Permissions are specified by the IP protocol (TCP, UDP or ICMP), the source of the request
(by IP range or an Amazon EC2 user-group pair), the source and destination port ranges
(for TCP and UDP), and the ICMP codes and types (for ICMP). When authorizing ICMP, -1
can be used as a wildcard in the type and code fields.
Permission changes are propagated to instances within the security group as quickly as
possible. However, depending on the number of instances, a small delay might occur.
Permission changes are propagated to instances within the security group as quickly as
possible. However, depending on the number of instances, a small delay might occur.
1244
Zend_Service
15. Zend_Service_Amazon_S3
15.1. Introduction
Amazon S3 provides a simple web services interface that can be used to store and retrieve any
amount of data, at any time, from anywhere on the web. It gives any developer access to the
same highly scalable, reliable, fast, inexpensive data storage infrastructure that Amazon uses
to run its own global network of web sites. The service aims to maximize benefits of scale and
to pass those benefits on to developers.
After registering, you will receive an application key and a secret key. You will need both to
access the S3 service.
15.4. Features
Zend_Service_Amazon_S3 provides the following functionality:
• A single point for configuring your amazon.s3 authentication credentials that can be used
across the amazon.s3 namespaces.
• A proxy object that is more convenient to use than an HTTP client alone, mostly removing the
need to manually construct HTTP POST requests to access the REST service.
• A response wrapper that parses each response body and throws an exception if an error
occurred, alleviating the need to repeatedly check the success of many commands.
The following example demonstrates creating a bucket, storing and retrieving the data.
1245
Zend_Service
require_once 'Zend/Service/Amazon/S3.php';
$s3->createBucket("my-own-bucket");
$s3->putObject("my-own-bucket/myobject", "somedata");
echo $s3->getObject("my-own-bucket/myobject");
require_once 'Zend/Service/Amazon/S3.php';
Zend_Service_Amazon_S3::setKeys($my_aws_key, $my_aws_secret_key);
$s3 = new Zend_Service_Amazon_S3();
Bucket name can contain lowercase letters, digits, periods (.), underscores (_), and dashes
(-). No other symbols allowed. Bucket name should start with letter or digit, and be 3 to 255
characters long. Names looking like an IP address (e.g. "192.168.16.255") are not allowed.
• removeBucket() removes the bucket from the system. The bucket should be empty to be
removed.
require_once 'Zend/Service/Amazon/S3.php';
$s3->cleanBucket("my-own-bucket");
$s3->removeBucket("my-own-bucket");
• getBuckets() returns the list of the names of all buckets belonging to the user.
1246
Zend_Service
require_once 'Zend/Service/Amazon/S3.php';
$list = $s3->getBuckets();
foreach($list as $bucket) {
echo "I have bucket $bucket\n";
}
The object are contained in buckets. Object is identified by name, which can be any utf-8 string.
It is common to use hierarchical names (such as Pictures/Myself/CodingInPHP.jpg) to
organise object names. Object name is prefixed with bucket name when using object functions,
so for object "mydata" in bucket "my-own-bucket" the name would be my-own-bucket/
mydata.
Objects can be replaced (by rewriting new data with the same key) or deleted, but not modified,
appended, etc. Object is always stored whole.
By default, all objects are private and can be accessed only by their owner. However, it is possible
to specify object with public access, in which case it will be available through the URL: http://
s3.amazonaws.com/[bucket-name]/[object-name].
Optional $meta parameter is the array of metadata, which currently supports the following
parameters as keys:
S3_CONTENT_TYPE_HEADER MIME content type of the data. If not specified, the type will be
guessed according to the file extension of the object name.
1247
Zend_Service
require_once 'Zend/Service/Amazon/S3.php';
$s3->putObject("my-own-bucket/Pictures/Me.png", file_get_con
array(Zend_Service_Amazon_S3::S3_ACL_HEADER =>
Zend_Service_Amazon_S3::S3_ACL_PUBLIC_READ));
// or:
$s3->putFile("me.png", "my-own-bucket/Pictures/Me.png",
array(Zend_Service_Amazon_S3::S3_ACL_HEADER =>
Zend_Service_Amazon_S3::S3_ACL_PUBLIC_READ));
echo "Go to http://s3.amazonaws.com/my-own-bucket/Pictures/M
• getInfo($object) retrieves the metadata information about the object. The function will
return array with metadata information. Some of the useful keys are:
etag The ETag of the data, which is the MD5 hash of the data, surrounded by quotes (").
The function will return FALSE if the key does not correspond to any existing object.
• getObjectsByBucket($bucket) returns the list of the object keys, contained in the bucket.
require_once 'Zend/Service/Amazon/S3.php';
$list = $s3->getObjectsByBucket("my-own-bucket");
foreach($list as $name) {
echo "I have $name key:\n";
$data = $s3->getObject("my-own-bucket/$name");
echo "with data: $data\n";
}
1248
Zend_Service
• putFile($path, $object, $meta) puts the content of the file in $path into the object
named $object.
The optional $meta argument is the same as for putObject. If the content type is omitted,
it will be guessed basing on the source file name.
$response = $amazon->getObjectStream("mybycket/zftest");
// copy file
copy($response->getStreamName(), "my/downloads/file");
// use stream
$fp = fopen("my/downloads/file2", "w");
stream_copy_to_stream($response->getStream(), $fp);
Second parameter for getObjectStream() is optional and specifies target file to write the
data. If not specified, temporary file is used, which will be deleted after the respons eobject is
destroyed.
To send object using streaming, use putFileStream() which has the same signature as
putFile() but will use streaming and not read the file into memory.
Also, you can pass stream resource to putObject() method data parameter, in which case
the data will be read from the stream when sending the request to the server.
require_once 'Zend/Service/Amazon/S3.php';
$s3->registerStreamWrapper("s3");
mkdir("s3://my-own-bucket");
file_put_contents("s3://my-own-bucket/testdata", "mydata");
echo file_get_contents("s3://my-own-bucket/testdata");
Directory operations (mkdir, rmdir, opendir, etc.) will operate on buckets and thus their
arguments should be of the form of s3://bucketname. File operations operate on objects.
Object creation, reading, writing, deletion, stat and directory listing is supported.
1249
Zend_Service
16. Zend_Service_Amazon_Sqs
16.1. Introduction
Amazon Simple Queue Service (Amazon SQS) offers a reliable, highly scalable, hosted queue
for storing messages as they travel between computers. By using Amazon SQS, developers can
simply move data between distributed components of their applications that perform different
tasks, without losing messages or requiring each component to be always available. Amazon
SQS makes it easy to build an automated workflow, working in close conjunction with the Amazon
Elastic Compute Cloud (Amazon EC2) and the other AWS infrastructure web services.
After registering, you will receive an application key and a secret key. You will need both to
access the SQS service.
16.4. Features
Zend_Service_Amazon_Sqs provides the following functionality:
• A single point for configuring your amazon.sqs authentication credentials that can be used
across the amazon.sqs namespaces.
• A proxy object that is more convenient to use than an HTTP client alone, mostly removing the
need to manually construct HTTP POST requests to access the REST service.
• A response wrapper that parses each response body and throws an exception if an error
occurred, alleviating the need to repeatedly check the success of many commands.
The following example demonstrates creating a queue, storing and retrieving messages.
1250
Zend_Service
$queue_url = $sqs->create('test');
Since the Zend_Service_Amazon_Sqs service requires authentication, you should pass your
credentials (AWS key and secret key) to the constructor. If you only use one account, you can
set default credentials for the service:
Zend_Service_Amazon_Sqs::setKeys($my_aws_key, $my_aws_secret_key);
$sqs = new Zend_Service_Amazon_Sqs();
Queue names can contain lowercase letters, digits, periods (.), underscores (_), and dashes (-).
No other symbols allowed. Queue names can be a maximum of 80 characters.
• getQueues() returns the list of the names of all queues belonging to the user.
1251
Zend_Service
• sent($queue_url, $message) send the $message to the $queue_url SQS queue URL.
if ($sqs->deleteMessage($queue_url, $message['handle'])) {
echo "Message deleted";
}
else {
echo "Message not deleted";
}
}
17. Zend_Service_Audioscrobbler
17.1. Introduction
Zend_Service_Audioscrobbler is a simple API for using the Audioscrobbler REST
Web Service. The Audioscrobbler Web Service provides access to its database
of Users, Artists, Albums, Tracks, Tags, Groups, and Forums. The methods of
the Zend_Service_Audioscrobbler class begin with one of these terms. The
syntax and namespaces of the Audioscrobbler Web Service are mirrored in
Zend_Service_Audioscrobbler. For more information about the Audioscrobbler REST Web
Service, please visit the Audioscrobbler Web Service site.
1252
Zend_Service
17.2. Users
In order to retrieve information for a specific user, the setUser() method is first used to select
the user for which data are to be retrieved. Zend_Service_Audioscrobbler provides several
methods for retrieving data specific to a single user:
• userGetFriends(): Returns a SimpleXML object containing the user names of the current
user's friends.
1253
Zend_Service
$previousWeeklyArtists = $as->userGetPreviousWeeklyArtistChart();
17.3. Artists
Zend_Service_Audioscrobbler provides several methods for retrieving data about a
specific artist, specified via the setArtist() method:
1254
Zend_Service
17.4. Tracks
Zend_Service_Audioscrobbler provides two methods for retrieving data specific to a single
track, specified via the setTrack() method:
• trackGetTopFans(): Returns a SimpleXML object containing a list of Users who listen most
to the current Track.
17.5. Tags
Zend_Service_Audioscrobbler provides several methods for retrieving data specific to a
single tag, specified via the setTag() method:
17.6. Groups
Zend_Service_Audioscrobbler provides several methods for retrieving data specific to a
single group, specified via the setGroup() method:
1255
Zend_Service
17.7. Forums
Zend_Service_Audioscrobbler provides a method for retrieving data specific to a single
forum, specified via the setForum() method:
18. Zend_Service_Delicious
18.1. Introduction
Zend_Service_Delicious est une API pour accéder aux Web services XML et JSON de
del.icio.us. Ce composant vous donne, si vous avez les droits, un accès en lecture-écriture à
vos entrées sur del.icio.us. Il permet également un accès en lecture seule aux données de tous
les utilisateurs.
1256
Zend_Service
/**
* Récupère les entrées correspondants aux arguments. Si la date ou
* l'url n'est pas précisée, la date la plus récente
* sera prise en compte.
*
* @param string $tag Optionnel pour filtrer par tag
* @param Zend_Date $dt Optionnel pour filtrer par date
* @param string $url Optionnel pour filtrer par url
* @return Zend_Service_Delicious_PostList
*/
public function getPosts($tag = null, $dt = null, $url = null);
/**
* Récupère les dernières entrées
*
* @param string $tag Optionnel pour filtrer par tag
* @param string $count Nombre maximum d'entrées à récupérer
* (15 par défaut)
* @return Zend_Service_Delicious_PostList
*/
public function getRecentPosts($tag = null, $count = 15);
/**
* Récupère toutes les entrées
*
* @param string $tag Optionnel pour filtrer par tag
* @return Zend_Service_Delicious_PostList
*/
public function getAllPosts($tag = null);
18.3. Zend_Service_Delicious_PostList
Des instances de cette classe sont retournées par les méthodes
getPosts(), getAllPosts(),getRecentPosts(), et getUserPosts() de
Zend_Service_Delicious.
Pour faciliter l'accès au données cette classe implémente les interfaces Countable, Iterator,
etArrayAccess.
1257
Zend_Service
Les objets d'entrées ont deux capacités de filtrage incorporées. Les entrées peuvent être filtrées
par étiquette et URL.
Les entrées peuvent être filtrées par une (des) étiquette(s) spécifique(s) en utilisant
withTags(). Par confort, withTag() est aussi fourni quand il est nécessaire 'e ne
spécifier qu'une seule étiquette
Les entrées peuvent être filtrées par URL correspondant à une expression régulière
spécifiée en utilisant la méthode withUrl() :
// change le titre
$posts[0]->setTitle('Nouveau Titre');
// sauvegarde le changement
$posts[0]->save();
1258
Zend_Service
$posts[0]->setTitle('Nouveau Titre')
->setNotes('Nouvelle note')
->save();
1259
Zend_Service
// ajoute un groupe
$delicious->addBundle('newBundle', array('tag1', 'tag2'));
1260
Zend_Service
Quand vous effectuez plus d'une requête avec Zend_Service_Delicious vous pouvez
accélérez vos requêtes en configurant votre client HTTP pour qu'il ne ferme pas les connexions.
Exemple 726. Configurer votre client HTTP pour qu'il ne ferme pas les connexions
Zend_Rest_Client::getHttpClient()->setConfig(array(
'keepalive' => true
));
1261
Zend_Service
19. Zend_Service_DeveloperGarden
19.1. Introduction to DeveloperGarden
DeveloperGarden is the name for the "Open Development services" of the German Telekom.
The "Open Development services" are a set of SOAP API Services.
• IpLocation: Locale the given IP and returns geo coordinates. Works only with IPs allocated in
the network of the german telekom.
• LocalSearch: Allows you to search with options nearby or around a given geo coordinate or
city.
• SmsValidation: You can validate a number to use it with SendSms for also supply a back
channel.
• ConferenceCall: You can configure a whole conference room with participants for an adhoc
conference or you can also schedule your conference.
• production: In Production environement you have to pay for calls, sms and other payable
services.
• sandbox: In the Sandbox mode you can use the same features with some limitations like in
the production just without to pay for them. Normally during development you can test your
application.
• mock: The Mock environment allows you to build your application and have results but you
don not initiate any action on the API side.
For every environment and service are some special features (options) available for testing.
Please look here for details.
1262
Zend_Service
require_once 'Zend/Service/DeveloperGarden/SendSms.php';
$config = array(
'username' => 'yourUsername',
'password' => 'yourPassword',
'environment' => Zend_Service_DeveloperGarden_SendSms::ENV_PRODUCTION,
);
$service = new Zend_Service_DeveloperGarden_SendSms($config);
19.2. BaseUserService
The class can be used to set and get quota values for the services and to fetch account details.
The getAccountBalance() method is there to fetch an array of account id's with the current
balance status (credits).
You get a Result object that contains all information that you need, optional you can pass to
the QuotaInformation method the environment constant to fetch the quota for the specific
environment.
• getConfernceCallQuotaInformation()
• getIPLocationQuotaInformation()
• getLocalSearchQuotaInformation()
1263
Zend_Service
• getSmsQuotaInformation()
• getVoiceCallQuotaInformation()
• changeConferenceCallQuotaPool()
• changeIPLocationQuotaPool()
• changeLocalSearchQuotaPool()
• changeSmsQuotaPool()
• changeVoiceCallQuotaPool()
19.3. IP Location
This service allows you to retrieve location information for a given IP address.
1264
Zend_Service
• An SMS or Flash SMS in the production environment must not be longer than 765 characters
and must not be sent to more than 10 recipients.
• An SMS or Flash SMS in the sandbox environment is shortened and enhanced by a note in
the DeveloperGarden. The maximum length of the message sent is 160 characters.
• In the sandbox environment, a maximum of 10 SMS can be sent per day.
• If an SMS or Flash SMS is longer than 160 characters, one message is charged for each 153
characters (quota and credit).
• The sender can exist of a maximum of 11 characters. Permitted characters are letters and
numbers.
• The specification of a phone number as the sender is only permitted if the phone number has
been validated. (See: SMS Validation)
After you get your keyword, you have to use the validate() to validate your number with the
keyword against the service.
With the method getValidatedNumbers(), you will get a list of all already validated numbers
and the status of each.
1265
Zend_Service
• The call is open until one of the participants hangs up or the expire mechanism intercepts.
If the call is initiated, you can ask the result object for the session ID and use this session ID for
an additional call to the callStatus or tearDownCall() methods. The second parameter on
the callStatus() method call extends the expiration for this call.
Exemple 737. Call two numbers, ask for status, and cancel
$sessionId = $newCall->getSessionId();
$service->tearDownCall($sessionId);
1266
Zend_Service
19.8. ConferenceCall
Conference Call allows you to setup and start a phone conference.
1267
Zend_Service
$client->newParticipant($conference->getConferenceId(), $part1);
// add a second, third ... participant
$client->commitConference($conference->getConferenceId());
First of all, you can setup the internal SoapClient (PHP) caching values.
Zend_Service_DeveloperGarden_SecurityTokenServer_Cache::setWsdlCache([PHP CONSTANT]);
If you want also to cache the result for calls to the SecuritTokenServer you can setup a
Zend_Cache instance and pass it to the setCache().
1268
Zend_Service
20. Zend_Service_Flickr
20.1. Introduction
Zend_Service_Flickr est une API simple pour utiliser le service Web REST de Flick. Pour
pouvoir utiliser les services Web Flickr, vous devez avoir une clé d'utilisation de l'API. Pour
obtenir une telle clé, et pour plus d'information sur le service Web REST de Flickr, veuillez vous
référez à la documentation de l'API Flickr.
Dans l'exemple suivant, nous allons utiliser la méthode tagSearch() pour rechercher des
photos ayant "php" dans les tags.
$results = $flickr->tagSearch("php");
Paramètres optionnels
• userSearch(): Accepte une chaîne de caractère de balise délimitée par des espaces, et
un tableau d'options en second paramètre optionnel. Elle retourne un jeu de photos sous la
forme d'un objet Zend_Service_Flickr_ResultSet.
Exemple 742. Trouver les photos publiques d'un utilisateur Flickr par son adresse
émail
Dans cet exemple, nous havons une adresse émail d'un utilisateur Flickr, et nous
recherchons les photos publiques des utilisateurs en utilisant la méthode userSearch() :
$results = $flickr->userSearch($userEmail);
1269
Zend_Service
Exemple 743. Récupération les photos du pool d'un groupe grâce à son ID
$results = $flickr->groupPoolGetPhotos($groupId);
Paramètre optionnel
groupPoolGetPhotos() accepte un second paramètre optionnel sous la
forme d'un tableau d'options.
Une fois que vous avez l'identifiant de l'image Flickr, il est simple de retrouver les
informations qui lui sont associées :
$image = $flickr->getImageDetails($imageId);
• Zend_Service_Flickr_ResultSet
• Zend_Service_Flickr_Result
• Zend_Service_Flickr_Image
20.5.1. Zend_Service_Flickr_ResultSet
Représente le jeu de résultats d'une recherche sur Flickr.
1270
Zend_Service
20.5.1.1. Propriétés
20.5.1.2. Zend_Service_Flickr_ResultSet::totalResults()
int totalResults();
20.5.2. Zend_Service_Flickr_Result
20.5.2.1. Propriétés
1271
Zend_Service
20.5.3. Zend_Service_Flickr_Image
Représente une image retournée pour une recherche Flickr.
20.5.3.1. Propriétés
21. Zend_Service_LiveDocx
21.1. Introduction to LiveDocx
LiveDocx is a SOAP service that allows developers to generate word processing documents by
combining structured data from PHP with a template, created in a word processor. The resulting
document can be saved as a PDF, DOCX, DOC, HTML or RTF file. LiveDocx implements mail-
merge in PHP.
1272
Zend_Service
In addition to this section of the manual, if you are interested in learning more about
Zend_Service_LiveDocx and the backend SOAP service LiveDocx, please take a look at the
following resources:
• LiveDocx WSDL.
The term template is used to refer to the input file, created in a word processor, containing
formatting and text fields. You can download an example template, stored as a DOCX file. The
term document is used to refer to the output file that contains the template file, populated with
data - i.e. the finished document. You can download an example document, stored as a PDF file.
The resulting document can be saved in any of the following file formats:
1273
Zend_Service
The resulting document can be saved in any of the following graphical file formats:
21.2. Zend_Service_LiveDocx_MailMerge
Zend_Service_LiveDocx_MailMerge is the mail-merge object in the
Zend_Service_LiveDocx family.
1274
Zend_Service
A template, created in a word processing application, such as Microsoft Word, is loaded into
LiveDocx. Data is then inserted into the template and the resulting document is saved to any
supported format.
Start off by launching Microsoft Word and creating a new document. Next, open up the Field
dialog box. This looks as follows:
Using this dialog, you can insert the required merge fields into your document. Below is a
screenshot of a license agreement in Microsoft Word 2007. The merge fields are marked as
{ MERGEFIELD FieldName }:
1275
Zend_Service
In the next step, we are going to populate the merge fields with textual data from PHP.
To populate the merge fields in the above cropped screenshot of the template in Microsoft Word,
all we have to code is as follows:
$phpLiveDocx->setUsername('myUsername')
->setPassword('myPassword');
$phpLiveDocx->setLocalTemplate('template.docx');
1276
Zend_Service
$phpLiveDocx->createDocument();
$document = $phpLiveDocx->retrieveDocument('pdf');
file_put_contents('document.pdf', $document);
The resulting document is written to disk in the file document.pdf. This file can now be post-
processed, sent via e-mail or simply displayed, as is illustrated below in Document Viewer 2.26.1
on Ubuntu 9.04:
In addition to text fields, it is also possible specify regions of a document, which should be
repeated.
1277
Zend_Service
For example, in a telephone bill it is necessary to print out a list of all connections, including
the destination number, duration and cost of each call. This repeating row functionality can be
achieved with so called blocks.
Blocks are simply regions of a document, which are repeated when createDocument() is
called. In a block any number of block fields can be specified.
Blocks consist of two consecutive document targets with a unique name. The following
screenshot illustrates these targets and their names in red:
(view larger).
For example:
blockStart_block1
blockEnd_block1
The content of a block is repeated, until all data assigned in the block fields has been injected
into the template. The data for block fields is specified in PHP as a multi-assoc array.
The following screenshot of a template in Microsoft Word 2007 shows how block fields are used:
1278
Zend_Service
$phpLiveDocx->setUsername('myUsername')
->setPassword('myPassword');
$phpLiveDocx->setLocalTemplate('template.doc');
$billConnections = array(
array(
'connection_number' => '+49 421 335 912',
'connection_duration' => '00:00:07',
'fee' => '€ 0.03',
),
array(
'connection_number' => '+49 421 335 913',
'connection_duration' => '00:00:07',
'fee' => '€ 0.03',
),
array(
'connection_number' => '+49 421 335 914',
'connection_duration' => '00:00:07',
'fee' => '€ 0.03',
),
1279
Zend_Service
array(
'connection_number' => '+49 421 335 916',
'connection_duration' => '00:00:07',
'fee' => '€ 0.03',
),
);
$phpLiveDocx->assign('connection', $billConnections);
$phpLiveDocx->createDocument();
$document = $phpLiveDocx->retrieveDocument('pdf');
file_put_contents('document.pdf', $document);
The data, which is specified in the array $billConnections is repeated in the template in the
block connection. The keys of the array (connection_number, connection_duration and
fee) are the block field names - their data is inserted, one row per iteration.
The resulting document is written to disk in the file document.pdf. This file can now be post-
processed, sent via e-mail or simply displayed, as is illustrated below in Document Viewer 2.26.1
on Ubuntu 9.04:
1280
Zend_Service
You can download the DOC template file and the resulting PDF document.
$fromPage is the lower-bound page number of the page range that should be returned as an
image and $toPage the upper-bound page number. $zoomFactor is the size of the images, as
a percent, relative to the original page size. The range of this parameter is 10 to 400. $format
is the format of the images returned by this method. The supported formats can be obtained by
calling getImageFormats().
$phpLiveDocx->setUsername('myUsername')
->setPassword('myPassword');
$phpLiveDocx->setLocalTemplate('template.docx');
$phpLiveDocx->createDocument();
This produces two files (documentPage1.png and documentPage2.png) and writes them to
disk in the same directory as the executable PHP file.
1281
Zend_Service
1282
Zend_Service
In the case that a template is stored locally, it must be transfered from the client to the server on
every request. If the content of the template rarely changes, this approach is inefficient. Similarly,
if the template is several megabytes in size, it may take considerable time to transfer it to the
server. Local template are useful in situations in which the content of the template is constantly
changing.
$phpLiveDocx->setUsername('myUsername')
->setPassword('myPassword');
$phpLiveDocx->setLocalTemplate('./template.docx');
1283
Zend_Service
In the case that a template is stored remotely, it is uploaded once to the server and then
simply referenced on all subsequent requests. Obviously, this is much quicker than using a local
template, as the template does not have to be transfered on every request. For speed critical
applications, it is recommended to use the remote template method.
$phpLiveDocx->setUsername('myUsername')
->setPassword('myPassword');
$phpLiveDocx->uploadTemplate('template.docx');
The following code illustrates how to reference the remotely stored template on all subsequent
requests:
$phpLiveDocx->setUsername('myUsername')
->setPassword('myPassword');
$phpLiveDocx->setRemoteTemplate('template.docx');
The following code returns and displays an array of all field names in the specified template.
This functionality is useful, in the case that you create an application, in which an end-user
can update a template.
$phpLiveDocx->setUsername('myUsername')
->setPassword('myPassword');
$templateName = 'template-1-text-field.docx';
$phpLiveDocx->setLocalTemplate($templateName);
$fieldNames = $phpLiveDocx->getFieldNames();
foreach ($fieldNames as $fieldName) {
printf('- %s%s', $fieldName, PHP_EOL);
}
1284
Zend_Service
The following code returns and displays an array of all block field names in the specified
template. This functionality is useful, in the case that you create an application, in which an
end-user can update a template. Before such templates can be populated, it is necessary
to find out the names of the contained block fields.
$phpLiveDocx->setUsername('myUsername')
->setPassword('myPassword');
$templateName = 'template-block-fields.doc';
$phpLiveDocx->setLocalTemplate($templateName);
$blockNames = $phpLiveDocx->getBlockNames();
foreach ($blockNames as $blockName) {
$blockFieldNames = $phpLiveDocx->getBlockFieldNames($blockName);
foreach ($blockFieldNames as $blockFieldName) {
printf('- %s::%s%s', $blockName, $blockFieldName, PHP_EOL);
}
}
The following code returns and displays an array of all fonts installed on the server. You can
use this method to present a list of fonts which may be used in a template. It is important
to inform the end-user about the fonts installed on the server, as only these fonts may be
used in a template. In the case that a template contains fonts, which are not available on
the server, font-substitution will take place. This may lead to undesirable results.
$phpLiveDocx->setUsername('myUsername')
->setPassword('myPassword');
Zend_Debug::dump($phpLiveDocx->getFontNames());
NOTE: As the return value of this method changes very infrequently, it is highly
recommended to use a cache, such as Zend_Cache - this will considerably speed up your
application.
The following code returns and displays an array of all supported template file formats. This
method is particularly useful in the case that a combo list should be displayed that allows
the end-user to select the input format of the documentation generation process.
$phpLiveDocx->setUsername('myUsername')
->setPassword('myPassword');
Zend_Debug::dump($phpLiveDocx->getTemplateFormats());
NOTE: As the return value of this method changes very infrequently, it is highly
recommended to use a cache, such as Zend_Cache - this will considerably speed up your
application.
1285
Zend_Service
The following code returns and displays an array of all supported document file formats. This
method is particularly useful in the case that a combo list should be displayed that allows
the end-user to select the output format of the documentation generation process.
$phpLiveDocx->setUsername('myUsername')
->setPassword('myPassword');
Zend_Debug::dump($phpLiveDocx->getDocumentFormats());
The following code returns and displays an array of all supported image file formats. This
method is particularly useful in the case that a combo list should be displayed that allows
the end-user to select the output format of the documentation generation process.
$phpLiveDocx->setUsername('myUsername')
->setPassword('myPassword');
Zend_Debug::dump($phpLiveDocx->getImageFormats());
NOTE: As the return value of this method changes very infrequently, it is highly
recommended to use a cache, such as Zend_Cache - this will considerably speed up your
application.
22. Zend_Service_Nirvanix
22.1. Introduction
Nirvanix provides an Internet Media File System (IMFS), an Internet storage service that allows
applications to upload, store and organize files and subsequently access them using a standard
Web Services interface. An IMFS is distributed clustered file system, accessed over the Internet,
and optimized for dealing with media files (audio, video, etc). The goal of an IMFS is to provide
massive scalability to deal with the challenges of media storage growth, with guaranteed access
and availability regardless of time and location. Finally, an IMFS gives applications the ability to
access data securely, without the large fixed costs associated with acquiring and maintaining
physical storage assets.
After registering, you will receive a Username, Password, and Application Key. All three are
required to use Zend_Service_Nirvanix.
1286
Zend_Service
Zend_Service_Nirvanix aims to make using the Nirvanix REST service easier but
understanding the service itself is still essential to be successful with Nirvanix.
The Nirvanix API Documentation provides an overview as well as detailed information using
the service. Please familiarize yourself with this document and refer back to it as you use
Zend_Service_Nirvanix.
22.4. Features
Nirvanix's REST service can be used effectively with PHP using the SimpleXML extension
and Zend_Http_Client alone. However, using it this way is somewhat inconvenient due to
repetitive operations like passing the session token on every request and repeatedly checking
the response body for error codes.
• A single point for configuring your Nirvanix authentication credentials that can be used across
the Nirvanix namespaces.
• A proxy object that is more convenient to use than an HTTP client alone, mostly removing the
need to manually construct HTTP POST requests to access the REST service.
• A response wrapper that parses each response body and throws an exception if an error
occurred, alleviating the need to repeatedly check the success of many commands.
echo $imfs->getContents('/foo.txt');
$imfs->unlink('/foo.txt');
The first step to using Zend_Service_Nirvanix is always to authenticate against the service.
This is done by passing your credentials to the Zend_Service_Nirvanix constructor above.
The associative array is passed directly to Nirvanix as POST parameters.
Nirvanix divides its web services into namespaces. Each namespace encapsulates a group
of related operations. After getting an instance of Zend_Service_Nirvanix, call the
getService() method to create a proxy for the namespace you want to use. Above, a proxy
for the IMFS namespace is created.
1287
Zend_Service
After you have a proxy for the namespace you want to use, call methods on it. The proxy will allow
you to use any command available on the REST API. The proxy may also make convenience
methods available, which wrap web service commands. The example above shows using the
IMFS convenience methods to create a new file, retrieve and display that file, and finally delete
the file.
A proxy object may provide convenience methods. These are methods that the
Zend_Service_Nirvanix provides to simplify the use of the Nirvanix web services. In
the previous example, the methods putContents(), getContents(), and unlink() do
not have direct equivalents in the REST API. They are convenience methods provided by
Zend_Service_Nirvanix that abstract more complicated operations on the REST API.
For all other method calls to the proxy object, the proxy will dynamically convert the method call
to the equivalent HTTP POST request to the REST API. It does this by using the method name
as the API command, and an associative array in the first argument as the POST parameters.
Let's say you want to call the REST API method RenameFile, which does not have a convenience
method in Zend_Service_Nirvanix:
Above, a proxy for the IMFS namespace is created. A method, renameFile(), is then called on
the proxy. This method does not exist as a convenience method in the PHP code, so it is trapped
by __call() and converted into a POST request to the REST API where the associative array
is used as the POST parameters.
Notice in the Nirvanix API documentation that sessionToken is required for this method but
we did not give it to the proxy object. It is added automatically for your convenience.
The simplest way to examine a result from the service is to use the built-in PHP functions like
print_r():
1288
Zend_Service
<?php
$auth = array('username' => 'your-username',
'password' => 'your-password',
'appKey' => 'your-app-key');
You can access any property or method of the decorated SimpleXMLElement. In the above
example, $result->BytesUploaded could be used to see the number of bytes received.
Should you want to access the SimpleXMLElement directly, just use $result->getSxml().
The most common response from Nirvanix is success (ResponseCode of zero). It is not
normally necessary to check ResponseCode because any non-zero result will throw a
Zend_Service_Nirvanix_Exception. See the next section on handling errors.
All operations against the REST service result in an XML return payload that contains a
ResponseCode element, such as the following example:
<Response>
<ResponseCode>0</ResponseCode>
</Response>
When the ResponseCode is zero such as in the example above, the operation was successful.
When the operation is not successful, the ResponseCode is non-zero and an ErrorMessage
element should be present.
try {
1289
Zend_Service
$imfs = $nirvanix->getService('IMFS');
$imfs->unlink('/a-nonexistant-path');
In the example above, unlink() is a convenience method that wraps the DeleteFiles
command on the REST API. The filePath parameter required by the DeleteFiles command
contains a path that does not exist. This will result in a Zend_Service_Nirvanix exception
being thrown with the message "Invalid path" and code 70005.
The Nirvanix API Documentation describes the errors associated with each command.
Depending on your needs, you may wrap each command in a try block or wrap many commands
in the same try block for convenience.
23. Zend_Service_ReCaptcha
23.1. Introduction
Zend_Service_ReCaptcha fournit un client pour le Service Web reCAPTCHA. D'après le site
de reCAPTCHA, "reCAPTCHA est un service gratuit de CAPTCHA qui aide à la numérisation
de livres." Chaque reCAPTCHA requière que l'utilisateur saisisse 2 mots, le premier est le
CAPTCHA, et le second est issu de texte scanné que les OCR (Optical Character Recognition)
ne peuvent identifier.
Pour utiliser le service reCAPTCHA, vous devez créer un compte et enregistrer un ou plusieurs
domaines d'utilisation afin de générer une clé publique et une privée.
echo $recaptcha->getHTML();
$result = $recaptcha->verify(
$_POST['recaptcha_challenge_field'],
$_POST['recaptcha_response_field']
);
Une fois que vous possédez le résultat, vérifiez sa validité. Il s'agit d'un objet
Zend_Service_ReCaptcha_Response qui possède une méthode isValid().
1290
Zend_Service
if (!$result->isValid()) {
// Validation échouée
}
In order to use this component you will need an account, and generate public and private keys
for the mailhide API.
// Create an instance of the mailhide component, passing it your public and private keys
// the mail address you want to hide
$mailHide = new Zend_Service_ReCaptcha_Mailhide();
$mailHide->setPublicKey($pubKey);
$mailHide->setPrivateKey($privKey);
$mailHide->setEmail($mail);
// Display it
print($mailHide);
The example above will display "m...@example.com" where "..." has a link that opens up a popup
windows with a ReCaptcha challenge.
The public key, private key and the email address can also be specified in the constructor of the
class. A fourth argument also exists that enables you to set some options for the component.
The available options are listed in the following table:
The configuration options can be set by sending it as the fourth argument to the constructor
or by calling the setOptions($options) which takes an associative array or an instance of
Zend_Config.
1291
Zend_Service
// Create an instance of the mailhide component, passing it your public and private keys
// well the mail address you want to hide
$mailHide = new Zend_Service_ReCaptcha_Mailhide();
$mailHide->setPublicKey($pubKey);
$mailHide->setPrivateKey($privKey);
$mailHide->setOptions(array(
'linkTitle' => 'Click me',
'linkHiddenText' => '+++++',
));
24. Zend_Service_Simpy
24.1. Introduction
Zend_Service_Simpy permet l'interaction avec les services Web du réseau social de partage
de 'marque-pages' (de 'favoris', de 'liens') Simpy.
Pour utiliser Zend_Service_Simpy, vous devez posséder un compte. Pour ceci, voyez le site
Web de Simpy. Pour plus d'informations sur l'API REST de Simpy, voyez sa documentation .
Les sections qui suivent vont vous indiquer les éléments sur lesquels vous pouvez agir en
utilisant l'API de Simpy, via Zend_Service_Simpy.
24.2. Liens
Lors d'interactions avec les liens, ceux-ci sont retournées dans l'ordre descendant par date
d'ajout. Vous pouvez les chercher par titre, auteur, mots-clés, notes ou même via le contenu de
leur page. Vous pouvez chercher selon tous ces critères avec des phrases, des booléens ou
des jokers. Voyez la syntaxe de recherche et les champs de recherche depuis la FAQ de Simpy,
pour plus d'informations.
1292
Zend_Service
Les liens sont représentés de manière unique par leur URL. Ainsi tenter de sauvegarder un lien
ayant le même URL qu'un lien existant va l'écraser.
1293
Zend_Service
/* Sauvegarde un lien */
$simpy->saveLink(
'Zend Framework' // Title
'http://framework.zend.com', // URL
Zend_Service_Simpy_Link::ACCESSTYPE_PUBLIC, // Access Type
'zend, framework, php' // Tags
'Zend Framework home page' // Alternative title
'This site rocks!' // Note
);
/* Effacement du lien */
$simpy->deleteLink('http://framework.zend.com');
24.3. Mots-clés
Les mots-clés sont récupérés dans l'ordre descendant, par le nombre de liens utilisant le mot-clé.
1294
Zend_Service
24.4. Notes
Les notes peuvent être sauvées, récupérées, effacées. Elles possèdent un identifiant numérique
unique.
1295
Zend_Service
/* Cherche les notes avec le mot 'PHP' dans leur titre et pas le mot
'framework' dans leur description */
$noteSet = $simpy->getNotes('+title:PHP -description:framework');
1296
Zend_Service
25. Introduction
Le composant Zend_Service_SlideShare est utilisé dans l'interaction avec les services Web
de slideshare.net, plate-forme servant d'hébergement de diaporamas. Grâce à ce composant,
vous pouvez intégrer des diaporamas de Slideshare, dans votre propre site Web, ou même
uploader des nouveaux diaporamas sur votre compte Slideshare, depuis votre site Web.
Une fois votre compte créé, vous pouvez utiliser Zend_Service_SlideShare en créant une
instance de Zend_Service_SlideShare en lui passant vos identifiants :
1297
Zend_Service
'PASSWORD');
class Zend_Service_SlideShare_SlideShow {
/**
* Récupère l'emplacement du diaporama
*/
public function getLocation() {
return $this->_location;
}
/**
* Récupère la transcription du diaporama
*/
public function getTranscript() {
return $this->_transcript;
}
/**
* Associe un mot-clé au diaporama
*/
public function addTag($tag) {
$this->_tags[] = (string)$tag;
return $this;
}
/**
* Associe des mots-clés au diaporama
*/
public function setTags(Array $tags) {
$this->_tags = $tags;
return $this;
}
/**
* Récupère tous les mots-clés associés au diaporama
*/
public function getTags() {
return $this->_tags;
}
/**
* Règle le nom de fichier du diaporama dans le système
* de fichiers local (pour l'upload d'un nouveau diaporama)
*/
public function setFilename($file) {
$this->_slideShowFilename = (string)$file;
return $this;
}
/**
* Rècupère le nom de fichier du diaporama dans le système
* de fichiers local qui sera uploadé
1298
Zend_Service
*/
public function getFilename() {
return $this->_slideShowFilename;
}
/**
* Récupère l'ID du diaporama
*/
public function getId() {
return $this->_slideShowId;
}
/**
* Récupère le code HTML utilisé pour la projection du diaporama
*/
public function getEmbedCode() {
return $this->_embedCode;
}
/**
* Récupère l'URI de la vignette du diaporama
*/
public function getThumbnailUrl() {
return $this->_thumbnailUrl;
}
/**
* Règle le titre du diaporama
*/
public function setTitle($title) {
$this->_title = (string)$title;
return $this;
}
/**
* Récupère le titre du diaporama
*/
public function getTitle() {
return $this->_title;
}
/**
* Régle la description du diaporama
*/
public function setDescription($desc) {
$this->_description = (string)$desc;
return $this;
}
/**
* Récupère la description du diaporama
*/
public function getDescription() {
return $this->_description;
}
/**
* Récupère le statut (numérique) du diaporama sur le serveur
*/
public function getStatus() {
1299
Zend_Service
return $this->_status;
}
/**
* Récupère la description textuelle du statut du diaporama
* sur le serveur
*/
public function getStatusDescription() {
return $this->_statusDescription;
}
/**
* Récupère le lien permanent du diaporama
*/
public function getPermaLink() {
return $this->_permalink;
}
/**
* Récupère le nombre de diapositives que le diaporama comprend
*/
public function getNumViews() {
return $this->_numViews;
}
}
La classe présentée ci dessus ne montre que les méthodes qui sont sensées
être utilisées par les développeurs. D'autres méthodes internes au composant
existent.
$slideshow = $ss->getSlideShow(123456);
1300
Zend_Service
$starting_offset = 0;
$limit = 10;
$frontendOptions = array(
'lifetime' => 7200,
'automatic_serialization' => true);
$backendOptions = array(
'cache_dir' => '/webtmp/');
$cache = Zend_Cache::factory('Core',
'File',
$frontendOptions,
$backendOptions);
1301
Zend_Service
26. Zend_Service_StrikeIron
Zend_Service_StrikeIron provides a PHP 5 client to StrikeIron web services. See the
following sections:
26.1. Overview
StrikeIron offers hundreds of commercial data services ("Data as a Service") such as Online
Sales Tax, Currency Rates, Stock Quotes, Geocodes, Global Address Verification, Yellow/White
Pages, MapQuest Driving Directions, Dun & Bradstreet Business Credit Checks, and much,
much more.
Each StrikeIron web service shares a standard SOAP (and REST) API, making it easy to
integrate and manage multiple services. StrikeIron also manages customer billing for all services
in a single account, making it perfect for solution providers. Get started with free web services
at http://www.strikeiron.com/sdp.
StrikeIron's services may be used through the PHP 5 SOAP extension alone. However, using
StrikeIron this way does not give an ideal PHP-like interface. The Zend_Service_StrikeIron
component provides a lightweight layer on top of the SOAP extension for working with StrikeIron
services in a more convenient, PHP-like manner.
• A single point for configuring your StrikeIron authentication credentials that can be used across
many StrikeIron services.
1302
Zend_Service
• A standard way of retrieving your StrikeIron subscription information such as license status
and the number of hits remaining to a service.
• The ability to use any StrikeIron service from its WSDL without creating a PHP wrapper class,
and the option of creating a wrapper for a more convenient interface.
After registering, you will receive a StrikeIron username and password. These will be used when
connecting to StrikeIron using Zend_Service_StrikeIron.
You will also need to sign up for StrikeIron's Super Data Pack Web Service.
Both registration steps are free and can be done relatively quickly through the StrikeIron website.
• US Address Verification
The getService() method will return a client for any StrikeIron service by the name of
its PHP wrapper class. In this case, the name SalesUseTaxBasic refers to the wrapper
class Zend_Service_StrikeIron_SalesUseTaxBasic. Wrappers are included for three
services and described in Bundled Services.
The getService() method can also return a client for a StrikeIron service that does not yet
have a PHP wrapper. This is explained in Using Services by WSDL.
1303
Zend_Service
// Get a client for the Sales & Use Tax Basic service
$taxBasic = $strikeIron->getService(array('class' => 'SalesUseTaxBasic'));
In the example above, the getService() method is used to return a client to the Sales & Use
Tax Basic service. The client object is stored in $taxBasic.
The getTaxRateCanada() method is then called on the service. An associative array is used to
supply keyword parameters to the method. This is the way that all StrikeIron methods are called.
The result from getTaxRateCanada() is stored in $rateInfo and has properties like
province and GST.
Many of the StrikeIron services are as simple to use as the example above. See Bundled Services
for detailed information on three StrikeIron services.
The simplest way to examine a result from the service is to use the built-in PHP functions like
print_r():
<?php
$strikeIron = new Zend_Service_StrikeIron(array('username' => 'your-username',
'password' => 'your-password'));
In the output above, we see that the decorator ($rateInfo) wraps an object named
GetTaxRateCanadaResult, the result of the call to getTaxRateCanada().
1304
Zend_Service
This means that $rateInfo has public properties like abbreviation, province, and GST.
These are accessed like $rateInfo->province.
StrikeIron result properties sometimes start with an uppercase letter such as Foo
or Bar where most PHP object properties normally start with a lowercase letter
as in foo or bar. The decorator will automatically do this inflection so you may
read a property Foo as foo.
If you ever need to get the original object or its name out of the decorator, use the respective
methods getDecoratedObject() and getDecoratedObjectName().
An exception will be thrown when such a fault occurs. You should anticipate and catch these
exceptions when making method calls to the service:
try {
It's important to understand the difference between exceptions and normal failed method calls.
Exceptions occur for exceptional conditions, such as the network going down or your subscription
expiring. Failed method calls that are a common occurrence, such as getTaxRateCanada()
not finding the province you supplied, will not result an in exception.
Every time you make a method call to a StrikeIron service, you should check the
response object for validity and also be prepared to catch an exception.
Each StrikeIron client returned by the getService method has the ability to check the
subscription status for that service using the getSubscriptionInfo() method of the client:
1305
Zend_Service
// Get a client for the Sales & Use Tax Basic service
$strikeIron = new Zend_Service_StrikeIron(array('username' => 'your-username',
'password' => 'your-password'));
// Check remaining hits for the Sales & Use Tax Basic service
$subscription = $taxBasic->getSubscriptionInfo();
echo $subscription->remainingHits;
Checking your subscription to a service does not use any remaining hits to the service. Each
time any method call to the service is made, the number of hits remaining will be cached and this
cached value will be returned by getSubscriptionInfo() without connecting to the service
again. To force getSubscriptionInfo() to override its cache and query the subscription
information again, use getSubscriptionInfo(true).
The service contains a getZipCode() method that will retrieve information about a United
States ZIP code or Canadian postal code:
1306
Zend_Service
The service contains a verifyAddressUSA() method that will verify an address in the United
States:
1307
Zend_Service
// valid address?
$valid = ($result->valid == 'VALID');
}
The service contains two methods, getTaxRateUSA() and getTaxRateCanada(), that will
retrieve sales and use tax data for the United States and Canada, respectively.
// Get a client for the Sales & Use Tax Basic service
$taxBasic = $strikeIron->getService(array('class' => 'SalesUseTaxBasic'));
To try a StrikeIron service that does not have a wrapper class available, give the wsdl option
to getService() instead of the class option:
1308
Zend_Service
Using StrikeIron services from the WSDL will require at least some understanding of the WSDL
files. StrikeIron has many resources on its site to help with this. Also, Jan Schneider from the
Horde project has written a small PHP routine that will format a WSDL file into more readable
HTML.
Please note that only the services described in the Bundled Services section are officially
supported.
PHP's SOAPClient has a trace option that causes it to remember the XML exchanged during
the last transaction. Zend_Service_StrikeIron does not enable the trace option by default
but this can easily by changed by specifying the options that will be passed to the SOAPClient
constructor.
To view a SOAP transaction, call the getSoapClient() method to get the SOAPClient
instance and then call the appropriate methods like __getLastRequest() and
__getLastRequest():
$strikeIron =
new Zend_Service_StrikeIron(array('username' => 'your-username',
'password' => 'your-password',
'options' => array('trace' => true)));
// Get a client for the Sales & Use Tax Basic service
$taxBasic = $strikeIron->getService(array('class' => 'SalesUseTaxBasic'));
29. Zend_Service_Technorati
29.1. Introduction
Zend_Service_Technorati provides an easy, intuitive and object-oriented interface for using
the Technorati API. It provides access to all available Technorati API queries and returns the
original XML response as a friendly PHP object.
Technorati is one of the most popular blog search engines. The API interface enables developers
to retrieve information about a specific blog, search blogs matching a single tag or phrase and
get information about a specific author (blogger). For a full list of available queries please see
the Technorati API documentation or the Available Technorati queries section of this document.
1309
Zend_Service
Once you have a valid API key, you're ready to start using Zend_Service_Technorati.
Each query method accepts an array of optional parameters that can be used to refine your query.
1310
Zend_Service
1311
Zend_Service
$author = $result->getAuthor();
echo '<h2>Blogs authored by ' . $author->getFirstName() . " " .
$author->getLastName() . '</h2>';
echo '<ol>';
foreach ($result->getWeblogs() as $weblog) {
echo '<li>' . $weblog->getName() . '</li>';
}
echo "</ol>";
Please read the Zend_Service_Technorati Classes section for further details about response
classes.
There are several reasons that may cause a Zend_Service_Technorati query to fail.
Zend_Service_Technorati validates all parameters for any query request. If a parameter
is invalid or it contains an invalid value, a new Zend_Service_Technorati_Exception
exception is thrown. Additionally, the Technorati API interface could be temporally unavailable,
or it could return a response that is not well formed.
Zend_Service_Technorati::keyInfo() returns a
Zend_Service_Technorati_KeyInfoResult object. For full details please see the API
reference guide.
1312
Zend_Service
• Cosmos
• Search
• Tag
• DailyCounts
• TopTags
• BlogInfo
• BlogPostTags
• GetInfo
Cosmos query lets you see what blogs are linking to a given URL. It returns
a Zend_Service_Technorati_CosmosResultSet object. For full details please see
Zend_Service_Technorati::cosmos() in the API reference guide.
The Search query lets you see what blogs contain a given search string. It returns
a Zend_Service_Technorati_SearchResultSet object. For full details please see
Zend_Service_Technorati::search() in the API reference guide.
1313
Zend_Service
1314
Zend_Service
The BlogInfo query provides information on what blog, if any, is associated with a given URL. It
returns a Zend_Service_Technorati_BlogInfoResult object. For full details please see
Zend_Service_Technorati::blogInfo() in the API reference guide.
The BlogPostTags query provides information on the top tags used by a specific blog. It
returns a Zend_Service_Technorati_TagsResultSet object. For full details please see
Zend_Service_Technorati::blogPostTags() in the API reference guide.
The GetInfo query tells you things that Technorati knows about a member. It returns
a Zend_Service_Technorati_GetInfoResult object. For full details please see
Zend_Service_Technorati::getInfo() in the API reference guide.
1315
Zend_Service
$author = $result->getAuthor();
echo "<h2>Blogs authored by " . $author->getFirstName() . " " .
$author->getLastName() . "</h2>";
echo "<ol>";
foreach ($result->getWeblogs() as $weblog) {
echo "<li>" . $weblog->getName() . "</li>";
}
echo "</ol>";
The KeyInfo query provides information on daily usage of an API key. It returns
a Zend_Service_Technorati_KeyInfoResult object. For full details please see
Zend_Service_Technorati::keyInfo() in the API reference guide.
• Zend_Service_Technorati_ResultSet
• Zend_Service_Technorati_CosmosResultSet
• Zend_Service_Technorati_SearchResultSet
• Zend_Service_Technorati_TagResultSet
• Zend_Service_Technorati_DailyCountsResultSet
• Zend_Service_Technorati_TagsResultSet
• Zend_Service_Technorati_Result
• Zend_Service_Technorati_CosmosResult
• Zend_Service_Technorati_SearchResult
• Zend_Service_Technorati_TagResult
• Zend_Service_Technorati_DailyCountsResult
• Zend_Service_Technorati_TagsResult
• Zend_Service_Technorati_GetInfoResult
• Zend_Service_Technorati_BlogInfoResult
1316
Zend_Service
• Zend_Service_Technorati_KeyInfoResult
Zend_Service_Technorati_GetInfoResult,
Zend_Service_Technorati_BlogInfoResult and
Zend_Service_Technorati_KeyInfoResult represent exceptions to the
above because they don't belong to a result set and they don't
implement any interface. They represent a single response object
and they act as a wrapper for additional Zend_Service_Technorati
objects, such as Zend_Service_Technorati_Author and
Zend_Service_Technorati_Weblog.
29.8.1. Zend_Service_Technorati_ResultSet
Zend_Service_Technorati_ResultSet is the most essential result set. The scope of this
class is to be extended by a query-specific child result set class, and it should never be used
to initialize a standalone object. Each of the specific result sets represents a collection of query-
specific Zend_Service_Technorati_Result objects.
29.8.2. Zend_Service_Technorati_CosmosResultSet
Zend_Service_Technorati_CosmosResultSet represents a Technorati Cosmos query
result set.
Zend_Service_Technorati_CosmosResultSet extends
Zend_Service_Technorati_ResultSet.
29.8.3. Zend_Service_Technorati_SearchResultSet
Zend_Service_Technorati_SearchResultSet represents a Technorati Search query
result set.
1317
Zend_Service
Zend_Service_Technorati_SearchResultSet extends
Zend_Service_Technorati_ResultSet.
29.8.4. Zend_Service_Technorati_TagResultSet
Zend_Service_Technorati_TagResultSet represents a Technorati Tag query result set.
Zend_Service_Technorati_TagResultSet extends
Zend_Service_Technorati_ResultSet.
29.8.5. Zend_Service_Technorati_DailyCountsResultSet
Zend_Service_Technorati_DailyCountsResultSet represents a Technorati
DailyCounts query result set.
Zend_Service_Technorati_DailyCountsResultSet extends
Zend_Service_Technorati_ResultSet.
29.8.6. Zend_Service_Technorati_TagsResultSet
Zend_Service_Technorati_TagsResultSet represents a Technorati TopTags or
BlogPostTags queries result set.
Zend_Service_Technorati_TagsResultSet extends
Zend_Service_Technorati_ResultSet.
29.8.7. Zend_Service_Technorati_Result
Zend_Service_Technorati_Result is the most essential result object. The scope of this
class is to be extended by a query specific child result class, and it should never be used to
initialize a standalone object.
29.8.8. Zend_Service_Technorati_CosmosResult
Zend_Service_Technorati_CosmosResult represents a single Technorati Cosmos query
result object. It is never returned as a standalone object, but it always belongs to a valid
Zend_Service_Technorati_CosmosResultSet object.
Zend_Service_Technorati_CosmosResult extends
Zend_Service_Technorati_Result.
29.8.9. Zend_Service_Technorati_SearchResult
Zend_Service_Technorati_SearchResult represents a single Technorati Search query
result object. It is never returned as a standalone object, but it always belongs to a valid
Zend_Service_Technorati_SearchResultSet object.
Zend_Service_Technorati_SearchResult extends
Zend_Service_Technorati_Result.
1318
Zend_Service
29.8.10. Zend_Service_Technorati_TagResult
Zend_Service_Technorati_TagResult extends
Zend_Service_Technorati_Result.
29.8.11. Zend_Service_Technorati_DailyCountsResult
Zend_Service_Technorati_DailyCountsResult extends
Zend_Service_Technorati_Result.
29.8.12. Zend_Service_Technorati_TagsResult
Zend_Service_Technorati_TagsResult extends
Zend_Service_Technorati_Result.
29.8.13. Zend_Service_Technorati_GetInfoResult
29.8.14. Zend_Service_Technorati_BlogInfoResult
29.8.15. Zend_Service_Technorati_KeyInfoResult
30. Zend_Service_Twitter
30.1. Introduction
Zend_Service_Twitter provides a client for the Twitter REST API.
Zend_Service_Twitter allows you to query the public timeline. If you provide a username
1319
Zend_Service
and password for Twitter, it will allow you to get and update your status, reply to friends, direct
message friends, mark tweets as favorite, and much more.
Zend_Service_Twitter is broken up into subsections so you can easily identify which type
of call is being requested.
• account makes sure that your account credentials are valid, checks your API rate limit, and
ends the current session for the authenticated user.
• status retrieves the public and user timelines and shows, updates, destroys, and retrieves
replies for the authenticated user.
• user retrieves friends and followers for the authenticated user and returns extended
information about a passed user.
• directMessage retrieves the authenticated user's received direct messages, deletes direct
messages, and sends new direct messages.
30.2. Authentication
With the exception of fetching the public timeline, Zend_Service_Twitter requires
authentication to work. Twitter currently uses HTTP Basic Authentication. You can pass in your
username or registered email along with your password for Twitter to login.
The following code sample is how you create the Twitter service, pass in your username
and password, and verify that they are correct.
You can also pass in an array that contains the username and password as the first
argument.
1320
Zend_Service
• friendsTimeline() returns the 20 most recent statuses posted by the authenticating user
and that user's friends.
• since narrows the returned results to just those statuses created after the specified date/
time (up to 24 hours old).
• userTimeline() returns the 20 most recent statuses posted from the authenticating user.
The userTimeline() method accepts an array of optional parameters to modify the query.
1321
Zend_Service
• id specifies the ID or screen name of the user for whom to return the friends_timeline.
• since narrows the returned results to just those statuses created after the specified date/
time (up to 24 hours old).
• count specifies the number of statuses to retrieve. May not be greater than 200.
• show() returns a single status, specified by the id parameter below. The status' author will
be returned inline.
• update() updates the authenticating user's status. This method requires that you pass in the
status update that you want to post to Twitter.
• replies() returns the 20 most recent @replies (status updates prefixed with @username)
for the authenticating user.
The replies() method accepts an array of optional parameters to modify the query.
• since narrows the returned results to just those statuses created after the specified date/
time (up to 24 hours old).
• since_id returns only statuses with an ID greater than (that is, more recent than) the
specified ID.
1322
Zend_Service
The friends() method accepts an array of optional parameters to modify the query.
• id specifies the ID or screen name of the user for whom to return a list of friends.
• since narrows the returned results to just those statuses created after the specified date/
time (up to 24 hours old).
• followers() returns the authenticating user's followers, each with current status inline.
The followers() method accepts an array of optional parameters to modify the query.
• id specifies the ID or screen name of the user for whom to return a list of followers.
• show() returns extended information of a given user, specified by ID or screen name as per
the required id parameter below.
The message() method accepts an array of optional parameters to modify the query.
• since_id returns only direct messages with an ID greater than (that is, more recent than)
the specified ID.
1323
Zend_Service
• since narrows the returned results to just those statuses created after the specified date/
time (up to 24 hours old).
• sent() returns a list of the 20 most recent direct messages sent by the authenticating user.
The sent() method accepts an array of optional parameters to modify the query.
• since_id returns only direct messages with an ID greater than (that is, more recent than)
the specified ID.
• since narrows the returned results to just those statuses created after the specified date/
time (up to 24 hours old).
• new() sends a new direct message to the specified user from the authenticating user.
Requires both the user and text parameters below.
• destroy() destroys the direct message specified in the required id parameter. The
authenticating user must be the recipient of the specified direct message.
• destroy() discontinues friendship with the user specified in the id parameter and the
authenticating user.
1324
Zend_Service
• exists() tests if a friendship exists between the user specified in the id parameter and the
authenticating user.
The favorites() method accepts an array of optional parameters to modify the query.
• id specifies the ID or screen name of the user for whom to request a list of favorite statuses.
• create() favorites the status specified in the id parameter as the authenticating user.
• destroy() un-favorites the status specified in the id parameter as the authenticating user.
1325
Zend_Service
• returnResult specifies whether or not return the user object instead of just TRUE or
FALSE.
• create() blocks the user specified in the id parameter as the authenticating user and
destroys a friendship to the blocked user if one exists. Returns the blocked user in the
requested format when successful.
• destroy() un-blocks the user specified in the id parameter for the authenticating user.
Returns the un-blocked user in the requested format when successful.
• blocking() returns an array of user objects that the authenticating user is blocking.
• page specifies which page ou want to return. A single page contains 20 IDs.
• returnUserIds specifies whether to return an array of numeric user IDs the authenticating
user is blocking instead of an array of user objects.
30.10. Zend_Service_Twitter_Search
30.10.1. Introduction
Zend_Service_Twitter_Search provides a client for the Twitter Search API. The Twitter
Search service is use to search Twitter. Currently, it only returns data in Atom or JSON format,
but a full REST service is in the future, which will support XML responses.
1326
Zend_Service
The search method can accept six different optional URL parameters passed in as an array:
• lang restricts the tweets to a given language. lang must be given by an ISO 639-1 code.
• page specifies the page number to return, up to a maximum of roughly 1500 results (based
on rpp * page).
• since_id returns tweets with status IDs greater than the given ID.
• show_user specifies whether to add ">user<:" to the beginning of the tweet. This is useful for
readers that do not display Atom's author field. The default is "FALSE".
• geocode returns tweets by users located within a given radius of the given latitude/longitude,
where the user's location is taken from their Twitter profile. The parameter value is specified
by "latitude,longitude,radius", where radius units must be specified as either "mi" (miles) or
"km" (kilometers).
The following code sample will return an array with the search results.
1327
Zend_Service
31. Zend_Service_WindowsAzure
31.1. Introduction
Windows Azure is the name for Microsoft’s Software + Services platform, an operating system in
the cloud providing services for hosting, management, scalable storage with support for simple
blobs, tables, and queues, as well as a management infrastructure for provisioning and geo-
distribution of cloud-based services, and a development platform for the Azure Services layer.
• You can develop your application using Zend_Service_WindowsAzure and the Windows
Azure SDK, which provides a local developent environment of the services provided by
Windows Azure's cloud infrastructure.
The first case requires you to install the Windows Azure SDK on your development machine. It
is currently only available for Windows environments; progress is being made on a Java-based
version of the SDK which can run on any platform.
31.4. Features
Zend_Service_WindowsAzure provides the following functionality:
• PHP classes for Windows Azure Blobs, Tables and Queues (for CRUD operations)
• Helper Classes for HTTP transport, AuthN/AuthZ, REST and Error Management
31.5. Architecture
Zend_Service_WindowsAzure provides access to Windows Azure's storage, computation
and management interfaces by abstracting the REST/XML interface Windows Azure provides
into a simple PHP API.
31.6. Zend_Service_WindowsAzure_Storage_Blob
Blob Storage stores sets of binary data. Blob storage offers the following three resources: the
storage account, containers, and blobs. Within your storage account, containers provide a way
to organize sets of blobs within your storage account.
1328
Zend_Service
Blob Storage is offered by Windows Azure as a REST API which is wrapped by the
Zend_Service_WindowsAzure_Storage_Blob class in order to provide a native PHP
interface to the storage account.
Using the following code, a blob storage container can be created on development storage.
Using the following code, a blob storage container can be removed from development storage.
Using the following code, a blob can be uploaded to a blob storage container on development
storage. Note that the container has already been created before.
Using the following code, a blob can be copied from inside the storage account. The advantage
of using this method is that the copy operation occurs in the Azure cloud and does not involve
downloading the blob. Note that the container has already been created before.
1329
Zend_Service
Using the following code, a blob can be downloaded from a blob storage container on
development storage. Note that the container has already been created before and a blob has
been uploaded.
By default, blob storage containers on Windows Azure are protected from public viewing. If any
user on the Internet should have access to a blob container, its ACL can be set to public. Note
that this applies to a complete container and not to a single blob!
Using the following code, blob storage container ACL can be set on development storage. Note
that the container has already been created before.
In order to work with the root container, it should first be created using the createContainer()
method, naming the container $root. All other operations on the root container should be issued
with the container name set to $root.
1330
Zend_Service
file operations on Windows Azure Blob Storage. For example, one can open a file from Windows
Azure Blob Storage with the fopen() function:
// ...
fclose($fileHandle);
In order to do this, the Windows Azure SDK for PHP blob storage client must be registered as a
stream wrapper. This can be done by calling the registerStreamWrapper() method:
// or:
Windows Azure Bob Storage provides a feature called "Shared Access Signatures". By default,
there is only one level of authorization possible in Windows Azure Blob Storage: either a
container is private or it is public. Shared Access Signatures provide a more granular method of
authorization: read, write, delete and list permissions can be assigned on a container or a blob
and given to a specific client using an URL-based model.
http://phpstorage.blob.core.windows.net/phpazuretestshared1?st=2009-08-17T09%3A06%3A17Z&se=2009-08-17T09%3A5
The above signature gives write access to the "phpazuretestshared1" container of the
"phpstorage" account.
When you are the owner of a Windows Azure Bob Storage account, you
can create and distribute a shared access key for any type of resource in
your account. To do this, the generateSharedAccessUrl() method of the
Zend_Service_WindowsAzure_Storage_Blob storage client can be used.
The following example code will generate a Shared Access Signature for write access in a
container named "container1", within a timeframe of 3000 seconds.
1331
Zend_Service
The following example code will generate a Shared Access Signature for read access in a blob
named test.txt in a container named "container1" within a time frame of 3000 seconds.
When you receive a Shared Access Signature from someone else, you can use the Windows
Azure SDK for PHP to work with the addressed resource. For example, the following signature
can be retrieved from the owner of a storage account:
http://phpstorage.blob.core.windows.net/phpazuretestshared1?st=2009-08-17T09%3A06%3A17Z&se=2009-08-17T09%3A5
The above signature gives write access to the "phpazuretestshared1" "container" of the
phpstorage account. Since the shared key for the account is not known, the Shared Access
Signature can be used to work with the authorized resource.
Note that there was no explicit permission to write to a specific blob. Instead, the Windows Azure
SDK for PHP determined that a permission was required to either write to that specific blob, or
1332
Zend_Service
to write to its container. Since only a signature was available for the latter, the Windows Azure
SDK for PHP chose those credentials to perform the request on Windows Azure blob storage.
31.7. Zend_Service_WindowsAzure_Storage_Table
The Table service offers structured storage in the form of tables.
Table Storage is offered by Windows Azure as a REST API which is wrapped by the
Zend_Service_WindowsAzure_Storage_Table class in order to provide a native PHP
interface to the storage account.
Note that development table storage (in the Windows Azure SDK) does not support all features
provided by the API. Therefore, the examples listed on this page are to be used on Windows
Azure production table storage.
Using the following code, a table can be created on Windows Azure production table storage.
Using the following code, a list of all tables in Windows Azure production table storage can be
queried.
1333
Zend_Service
The Table service does not enforce any schema for tables, so two entities in the same table may
have different sets of properties. Developers may choose to enforce a schema on the client side.
A table may contain any number of entities.
• Enforced schema
• No enforced schema
All examples will make use of the following enforced schema class.
/**
* @azure Age Edm.Int64
*/
public $Age;
/**
* @azure Visible Edm.Boolean
*/
public $Visible = false;
}
1334
Zend_Service
/**
* @azure Age Edm.Int64
*/
public $Age;
/**
* @azure Visible Edm.Boolean
*/
public $Visible = false;
}
/**
* @azure <property name in Windows Azure> <optional property type>
*/
public $<property name in PHP>;
Let's see how to define a propety "Age" as an integer on Windows Azure table storage:
/**
* @azure Age Edm.Int64
*/
public $Age;
Note that a property does not necessarily have to be named the same on Windows Azure table
storage. The Windows Azure table storage property name can be defined as well as the type.
1335
Zend_Service
Other properties can be added on the fly. Their Windows Azure table storage type will be
determined on-the-fly:
1336
Zend_Service
Using the following code, an entity can be inserted into a table named "testtable". Note that the
table has already been created before.
Using the following code, an entity can be retrieved by partition key and row key. Note that the
table and entity have already been created before.
Using the following code, an entity can be updated. Note that the table and entity have already
been created before.
If you want to make sure the entity has not been updated before, you can make sure the Etag
of the entity is checked. If the entity already has had an update, the update will fail to make sure
you do not overwrite any newer data.
1337
Zend_Service
Using the following code, an entity can be deleted. Note that the table and entity have already
been created before.
Using the following code, a table can be queried using a filter condition. Note that the table and
entities have already been created before.
1338
Zend_Service
Using the following code, a table can be queried using a fluent interface. Note that the table and
entities have already been created before.
This topic demonstrates how to use the table entity group transaction features provided by
Windows Azure table storage. Windows Azure table storage supports batch transactions on
entities that are in the same table and belong to the same partition group. A transaction can
include at most 100 entities.
The following example uses a batch operation (transaction) to insert a set of entities into the
"testtable" table. Note that the table has already been created before.
// Start batch
$batch = $storageClient->startBatch();
// Commit
$batch->commit();
When running a PHP application on the Windows Azure platform in a load-balanced mode
(running 2 Web Role instances or more), it is important that PHP session data can be
shared between multiple Web Role instances. The Windows Azure SDK for PHP provides the
Zend_Service_WindowsAzure_SessionHandler class, which uses Windows Azure Table
Storage as a session handler for PHP applications.
1339
Zend_Service
session_start();
if (!isset($_SESSION['firstVisit'])) {
$_SESSION['firstVisit'] = time();
}
// ...
31.8. Zend_Service_WindowsAzure_Storage_Queue
The Queue service stores messages that may be read by any client who has access to the
storage account.
A queue can contain an unlimited number of messages, each of which can be up to 8 KB in size.
Messages are generally added to the end of the queue and retrieved from the front of the queue,
although first in/first out (FIFO) behavior is not guaranteed. If you need to store messages larger
than 8 KB, you can store message data as a queue or in a table and then store a reference to
the data as a message in a queue.
Queue Storage is offered by Windows Azure as a REST API which is wrapped by the
Zend_Service_WindowsAzure_Storage_Queue class in order to provide a native PHP
interface to the storage account.
1340
Zend_Service
Using the following code, a queue can be removed from development storage.
Using the following code, a message can be added to a queue on development storage. Note
that the queue has already been created before.
Using the following code, a message can be read from a queue on development storage. Note
that the queue and message have already been created before.
The messages that are read using getMessages() will be invisible in the queue for 30 seconds,
after which the messages will re-appear in the queue. To mark a message as processed and
remove it from the queue, use the deleteMessage() method.
1341
Zend_Service
Using the following code, a queue can be checked for new messages. Note that the queue and
message have already been created before.
Note that messages that are read using peekMessages() will not become invisible in the queue,
nor can they be marked as processed using the deleteMessage() method. To do this, use
getMessages() instead.
32. Zend_Service_Yahoo
32.1. Introduction
Zend_Service_Yahoo est une API simple pour utiliser les APIs REST de Yahoo!
Zend_Service_Yahoo vous permet de rechercher dans Yahoo! Web Search, Yahoo! News,
Yahoo! Local et Yahoo! Images. Pour pouvoir utiliser l'API REST de Yahoo!, vous devez avoir un
identifiant Yahoo Application. Pour obtenir un identifiant Yahoo Application, veuillez compléter
et soumettre le formulaire de demande d'identifiant Yahoo! Application.
1342
Zend_Service
Exemple 846. Trouver des entreprises locales et des services avec Yahoo!
1343
Zend_Service
• Zend_Service_Yahoo_ResultSet
• Zend_Service_Yahoo_WebResultSet
• Zend_Service_Yahoo_ImageResultSet
• Zend_Service_Yahoo_VideoResultSet
• Zend_Service_Yahoo_LocalResultSet
1344
Zend_Service
• Zend_Service_Yahoo_NewsResultSet
• Zend_Service_Yahoo_InlinkDataResultSet
• Zend_Service_Yahoo_PageDataResultSet
• Zend_Service_Yahoo_Result
• Zend_Service_Yahoo_WebResult
• Zend_Service_Yahoo_ImageResult
• Zend_Service_Yahoo_VideoResult
• Zend_Service_Yahoo_LocalResult
• Zend_Service_Yahoo_NewsResult
• Zend_Service_Yahoo_InlinkDataResult
• Zend_Service_Yahoo_PageDataResult
• Zend_Service_Yahoo_Image
32.9.1. Zend_Service_Yahoo_ResultSet
Chaque jeu de résultat spécifique à la recherche est étendu de cette classe de base.
32.9.1.1. Zend_Service_Yahoo_ResultSet::totalResults()
int totalResults();
32.9.1.2. Propriétés
32.9.2. Zend_Service_Yahoo_WebResultSet
Zend_Service_Yahoo_WebResultSet représente un jeu de résultat Yahoo! Web Search.
1345
Zend_Service
Zend_Service_Yahoo_WebResultSet étend
Zend_Service_Yahoo_ResultSet.
32.9.3. Zend_Service_Yahoo_ImageResultSet
Zend_Service_Yahoo_ImageResultSet représente un jeu de résultat Yahoo! Image
Search.
Zend_Service_Yahoo_ImageResultSet étend
Zend_Service_Yahoo_ResultSet
32.9.4. Zend_Service_Yahoo_VideoResultSet
Zend_Service_Yahoo_VideoResultSet représente un jeu de résultat Yahoo! Video
Search.
Zend_Service_Yahoo_VideoResultSet étend
Zend_Service_Yahoo_ResultSet
32.9.5. Zend_Service_Yahoo_LocalResultSet
Zend_Service_Yahoo_LocalResultSet représente un jeu de résultats Yahoo! Local
Search.
Zend_Service_Yahoo_LocalResultSet étend
Zend_Service_Yahoo_ResultSet
32.9.6. Zend_Service_Yahoo_NewsResultSet
Zend_Service_Yahoo_NewsResultSet représente un jeu de résultat Yahoo! News Search.
Zend_Service_Yahoo_NewsResultSet étend
Zend_Service_Yahoo_ResultSet
1346
Zend_Service
32.9.7. Zend_Service_Yahoo_InlinkDataResultSet
Zend_Service_Yahoo_InlinkDataResultSet représente un jeu de résultat Yahoo!
Inbound Link Search.
Zend_Service_Yahoo_InlinkDataResultSet étend
Zend_Service_Yahoo_ResultSet
32.9.8. Zend_Service_Yahoo_PageDataResultSet
Zend_Service_Yahoo_PageDataResultSet représente un jeu de résultat Yahoo!
PageData Search.
Zend_Service_Yahoo_PageDataResultSet étend
Zend_Service_Yahoo_ResultSet
32.9.9. Zend_Service_Yahoo_Result
Chaque résultat spécifique à la recherche est étendu de cette classe de base.
32.9.9.1. Propriétés
32.9.10. Zend_Service_Yahoo_WebResult
Chaque résultat d'une recherche Web est retourné comme un objet
Zend_Service_Yahoo_WebResult.
32.9.10.1. Propriétés
1347
Zend_Service
32.9.11. Zend_Service_Yahoo_ImageResult
Chaque recherche d'image est retournée comme un objet
Zend_Service_Yahoo_ImageResult.
32.9.11.1. Propriétés
32.9.12. Zend_Service_Yahoo_VideoResult
Chaque recherche de vidéo est retournée comme un objet
Zend_Service_Yahoo_VideoResult.
32.9.12.1. Propriétés
1348
Zend_Service
32.9.13. Zend_Service_Yahoo_LocalResult
Chaque résultat de recherche locale est retourné dans un objet
Zend_Service_Yahoo_LocalResult.
32.9.13.1. Propriétés
32.9.14. Zend_Service_Yahoo_NewsResult
Chaque résultat de News Search est retourné comme un objet
Zend_Service_Yahoo_NewsResult.
1349
Zend_Service
32.9.14.1. Propriétés
32.9.15. Zend_Service_Yahoo_InlinkDataResult
Chaque résultat de Inbound Link Search est retourné comme un objet
Zend_Service_Yahoo_InlinkDatabResult.
32.9.16. Zend_Service_Yahoo_PageDataResult
Chaque résultat de Page Data Search est retourné comme un objet
Zend_Service_Yahoo_PageDataResult.
32.9.17. Zend_Service_Yahoo_Image
Toutes les images retournées, par Yahoo! Image Search ou Yahoo! News Search sont
représentées par un objet Zend_Service_Yahoo_Image.
32.9.17.1. Propriétés
1350
Zend_Session
1. Introduction
L'équipe Auth de Zend Framework apprécie considérablement votre feedback et vos
contributions sur notre liste émail : fw-auth@lists.zend.com.
Dans les applications Web écrites en PHP, une session représente un raccordement logique
entre le côté serveur, des données persistantes et un client particulier (par exemple, un
navigateur Web). Zend_Session aide à contrôler et à préserver les données de session,
un complément logique des données de type cookie, en cas de demande de page multiples
par le même client. À la différence des données de cookie, les données de session ne sont
pas stockées du côté client, et elles sont seulement partagées avec le client quand le code
source du côté serveur rend volontairement disponible les données par l'intermédiaire d'une
réponse à une demande du client. Dans le cadre de ce composant et de cette documentation,
le terme "données de session" se rapportent aux données du côté serveur stockées dans
$_SESSION, contrôlées par Zend_Session, et individuellement manipulées par des objets
Zend_Session_Namespace. Les espaces de noms de session permettent d'accéder aux
données de session en utilisant les espaces de noms classiques implémentés logiquement en
tant que groupes nommés de tableaux associatifs, indexés par des chaînes (semblables aux
tableaux habituels de PHP).
Les instances Zend_Session_Namespace sont des objets accesseurs pour les sous-parties
nommées de $_SESSION. Le composant Zend_Session encapsule l'extension session
de PHP existante avec une interface d'administration et de gestion, afin de fournir une
API à Zend_Session_Namespace pour les espaces de noms de session persistants.
Zend_Session_Namespace fournit une interface normalisée et orientée objet pour travailler
en espaces de noms persistants à l'intérieur du mécanisme standard des sessions de
PHP. Le support existe pour les espaces de noms anonymes et les espaces de nom de
session authentifiés (par exemple, "login"). Zend_Auth, le composant d'authentification de
Zend Framework emploie Zend_Session_Namespace pour stocker les informations liées aux
utilisateurs authentifiés. Puisque Zend_Session emploie les fonctions normales de l'extension
session de PHP, tous les options et réglages familiers de configuration s'appliquent (voir http://
www.php.net/session), avec en bonus la facilité d'accès par une interface orientée objet et un
comportement par défaut fournissant les meilleures pratiques et une intégration sans problèmes
dans Zend Framework. Ainsi, un id standard de session PHP, stocké soit dans un cookie côté
client ou incorporé dans l'URL, maintient l'association entre un client et des données de session.
2. Usage basique
Les instances Zend_Session_Namespace fournissent l'API primaire pour manipuler les
données de session dans Zend Framework. Les espaces de noms sont utilisés pour isoler toutes
1351
Zend_Session
les données de session, bien qu'un espace de noms par défaut existe pour ceux qui veulent juste
un endroit pour stocker toutes leurs données de session. Zend_Session utilise ext/session
et sa superglobale spéciale $_SESSION comme mécanisme de stockage pour les données
d'état de session. Bien que $_SESSION est toujours disponible dans l'espace global de PHP,
les développeurs devraient s'abstenir d'accéder directement à elle, alors que Zend_Session
et Zend_Session_Namespace fournissent plus efficacement et plus solidement leur suite de
fonctionnalités liées à session.
Il est possible d'utiliser Zend_Session conjointement avec d'autre code utilisant directement
$_SESSION. Cependant, pour éviter les problèmes, il est fortement recommandé que ce
code utilise seulement les parties de $_SESSION ne correspondant pas aux instances de
Zend_Session_Namespace.
if (isset($defaultNamespace->numberOfPageRequests)) {
$defaultNamespace->numberOfPageRequests++;
// ceci s'incrémente à chaque chargement de page.
} else {
$defaultNamespace->numberOfPageRequests = 1;
// première page
}
Les noms des espaces de noms sont limités à des chaînes de caractères PHP non-vides qui
ne commencent par un tiret-bas ("_"). De plus, seuls les composants coeur de Zend Framework
devraient employer un nom d'espace de noms commençant par "Zend".
1352
Zend_Session
Exemple 851. Nouvelle méthode : les espaces de noms évitent les collisions
L'exemple ci-dessus réalise la même chose que celui ci-dessous, excepté que les objets de
session ci-dessus préserve l'encapsulation des données de session dans leur espace de noms
respectif.
$_SESSION['Zend_Auth']['user'] = "monusername";
$_SESSION['Un_Service_Web']['user'] = "monwebusername";
$unNamespace =
new Zend_Session_Namespace('un_namespace_avec_des_donnes_presentes');
$namespace->foo = 100;
if (!isset($namespace->bar)) {
echo "\$namespace->bar n'existe pas\n";
}
unset($namespace->foo);
1353
Zend_Session
3. Utilisation avancée
Même si les exemples de l'utilisation basique sont une manière parfaitement acceptable d'utiliser
les sessions dans Zend Framework, il existe de bonnes pratiques à considérer. Cette section
détaille plus finement le traitement des sessions et présente des utilisations plus avancées du
composant Zend_Session.
Zend_Session::start();
Il existe quatre manières différentes pour démarrer une session, quand on utilise
Zend_Session. Deux sont mauvaises.
php_value session.auto_start 0
3. Correcte : utilisez Zend_Session::start(). Si vous voulez que toutes vos requêtes aient
et utilisent les sessions, alors placez cette fonction le plus tôt possible et sans condition dans
votre fichier d'amorçage. Les sessions ont un coût. Si certaines requêtes nécessitent les
sessions, mais que les autres n'en ont pas besoin, alors :
1354
Zend_Session
$userProfileNamespace =
new Zend_Session_Namespace('userProfileNamespace');
1355
Zend_Session
$s = new Zend_Session_Namespace('expireAll');
$s->a = 'apple';
$s->p = 'pear';
$s->o = 'orange';
$s->setExpirationSeconds(5, 'a');
// expire seulement pour la clé "a" dans 5 secondes
$s->setExpirationSeconds(60);
// L'espace de noms "expireAll" sera marqué "expired"
// soit à la première requête reçue après 60 secondes,
// soit dans 5 hops, en fonction de ce qui arrivera en premier.
Quand vous travaillez avec des données de session expirées dans la requête courante,
des précautions doivent être prises concernant leur utilisation. Bien que les données soient
retournées par référence, modifier les données expirées ne les rendra pas persistantes dans
la requête courante. Dans le but de remettre à zéro leur temps d'expiration, transférez les
données dans des variables temporaires, utilisez l'espace de nommage pour les effacer, et
ensuite réaffectez les clés appropriées de nouveau.
Le code suivant, partie d'un contrôleur destiné à afficher une question dans un test, initie
une variable booléenne pour représenter l'acceptation ou non d'une réponse à la question
soumise. Dans ce cas, l'utilisateur de l'application a 300 secondes pour répondre à la
question affichée.
Ci-dessous, le contrôleur qui analyse les réponses aux questions du test détermine
l'acceptation ou non d'une réponse en se basant sur le fait que l'utilisateur a répondu dans
le temps alloué :
1356
Zend_Session
Pour activer ce comportement, réglez à TRUE le second argument du constructeur quand vous
créez la dernière instance autorisée de Zend_Session_Namespace. Tout tentative suivante
d'instanciation du même espace de noms entraînera la levée d'une exception.
$authSpaceAccessor1->foo = 'bar';
assert($authSpaceAccessor2->foo, 'bar');
try {
$aNamespaceObject = new Zend_Session_Namespace('Zend_Auth');
} catch (Zend_Session_Exception $e) {
echo "Cannot instantiate this namespace "
. "since $authSpaceAccessor2 was created\n";
}
1357
Zend_Session
Si possible, évitez le problème en stockant les tableaux dans un espace de noms de session
seulement après que toutes les clés et les valeurs aient été définies :
Si vous utilisez une version de PHP affectée et avez besoin de modifier un tableau après l'avoir
assigné à une clé dans l'espace de noms, vous pouvez utiliser l'une des solutions suivantes :
Dans le code suivant, une copie du tableau stocké est créée, modifiée, et réassignée à la
place d'où provenait la copie, en effaçant le tableau original.
// copie du tableau
$tmp = $sessionNamespace->array;
// modification de la copie
$tmp['fruit'] = 'poire';
1358
Zend_Session
désérialisé après sa récupération à partir du stockage. L'implication est que le développeur doit
s'assurer que les classes des objets persistants doivent avoir été définies avant que l'objet ne soit
désérialisé du stockage. Si aucune classe n'est définie pour l'objet désérialisé, alors il devient
une instance de stdClass.
1359
Zend_Session
Exemple 864. Utilisation de PHPUnit pour tester le code écrit avec Zend_Session*
// tester setExpirationSeconds()
require 'tests/Zend/Session/SessionTestHelper.php';
// voir aussi SessionTest.php dans trunk/
$script = 'SessionTestHelper.php';
$s = new Zend_Session_Namespace('espace');
$s->a = 'abricot';
$s->o = 'orange';
$s->setExpirationSeconds(5);
Zend_Session::regenerateId();
$id = Zend_Session::getId();
session_write_close();
// relâche la session donc le processus suivant peut l'utiliser
sleep(4); // pas assez long pour les éléments expirent
exec($script . "expireAll $id expireAll", $result);
$result = $this->sortResult($result);
$expect = ';a === abricot;o === orange;p === pear';
$this->assertTrue($result === $expect,
"iteration over default Zend_Session namespace failed; "
. "expecting result === '$expect', but got '$result'");
sleep(2);
// assez long pour que les éléments expirent
// (total de 6 secondes écoulées, avec une expiration de 5)
exec($script . "expireAll $id expireAll", $result);
$result = array_pop($result);
$this->assertTrue($result === '',
"iteration over default Zend_Session namespace failed; "
. "expecting result === '', but got '$result')");
session_start(); // redémarre artificiellement une session suspendue
session_write_close();
// relâche la session donc le processus suivant peut l'utiliser
sleep(6); // pas assez long pour les éléments expirent
exec($script . "expireAll $id expireGuava", $result);
$result = $this->sortResult($result);
session_start(); // redémarre artificiellement la session suspendue
$this->assertTrue($result === ';p === plum',
"iteration over named Zend_Session namespace failed (result=$result)");
1360
Zend_Session
Pour assigner une option de configuration, passez son nom (la partie qui suit "session."
dans les options de configuration de ext/session) comme clé au tableau passé à
Zend_Session::setOptions(). La valeur correspondante dans le tableau sera alors utilisée
comme valeur de l'option. Si vous omettez une option, alors celles par défaut recommandées
par Zend_Session seront utilisées, sinon si elles n'existent pas, les valeurs par défaut de php.ini.
Les retours et les idées quant aux "options recommandées" sont appréciées et peuvent être
envoyées à fw-auth@lists.zend.com.
1361
Zend_Session
; Paramètres de production
[production]
; bug_compat_42
; bug_compat_warn
; cache_expire
; cache_limiter
; cookie_domain
; cookie_lifetime
; cookie_path
; cookie_secure
; entropy_file
; entropy_length
; gc_divisor
; gc_maxlifetime
; gc_probability
; hash_bits_per_character
; hash_function
; name doit être unique pour chaque application partageant le même nom de domaine
name = UNIQUE_NAME
; referer_check
; save_handler
; save_path
; serialize_handler
; use_cookies
; use_only_cookies
; use_trans_sid
require_once 'Zend/Session.php';
Zend_Session::setOptions($config->toArray());
La plupart des options ne nécessitent pas d'explications étant donné qu'elles font parti des
options de ext/session, documentées dans le manuel officiel de PHP, cependant les options
particulières méritent une description:
1362
Zend_Session
• int remember_me_seconds : temps de vie du cookie de session, une fois le navigateur client
fermé.
• string save_path : Cette valeur est dépendante du système sur lequel PHP est lancé. Un
chemin absolu vers un dossier lisible et écrivable à PHP devrait être utilisé (dans le cas
d'utilisation d'un dossier pour le support des sessions). Si le chemin n'est pas pleinement
accessible à PHP, Zend_Session lancera une exception à son démarrage (lorsque start()
est appelée.
Par exemple, un pirate peut visiter le site d'une victime pour obtenir un cookie
de session. Il modifie ensuite le chemin du cookie afin que celui-ci soit envoyé
à sa propre application (en partage sur le même serveur que le votre), et
il exécute var_dump($_SESSION). Il obtient alors des informations sur les
variables de session que vous stockez, et il peut les modifier pour retourner
sur votre site. L'empoisonnement a eu lieu. Même si deux applications sur
le même serveur ne partagent pas le même dossier save_path, si celui-ci
est devinable, l'attaquant peut alors l'utiliser sur sa propre application et dans
certaines configurations de PHP, accéder à la session de l'application victime.
La valeur du save_path ne doit pas être rendue publique ou devinable, le
dossier doit se trouver dans un endroit isolé et sécurisé.
• string name - La valeur doit être choisie de manière unique pour chaque application.
Risque de sécurité
Risque de sécurité
1363
Zend_Session
• Utiliser le buffer de sortie résout souvent le problème, et peut améliorer les performances. Par
exemple, une valeur php.ini, "output_buffering = 65535" active un buffer de 64K.
Même si le buffer de sortie peut améliorer les performances lorsqu'il est bien configuré, se
reposer sur lui concernant les erreurs "headers already sent" n'est pas suffisant. En effet, sa
taille peut être dépassé entraînant son vidage, et le problème revient.
• Aussi, il convient d'organiser l'application de manière à ce que les envois d'en-tête se passent
avant l'envoi de contenu.
• Supprimez tous les balises de fermeture "?>", si elles terminent du code PHP. Elles sont
facultatives et les nouvelles lignes blanches éventuelles en fin de fichier ne seront pas
envoyées, car parsées par PHP.
Changer l'identifiant de session permet d'aider contre le vol de données. Si un attaquant possède
l'identifiant d'une victime, le changer ne changera rien pour la victime, mais provoquera une
invalidation de la session de l'attaquant, qui ne connaît alors pas la nouvelle valeur de l'identifiant
de session. Non seulement regenerateId() change l'identifiant de session, mais en plus il
migre les données de l'ancien identifiant vers le nouveau, invalidant totalement l'ancien.
1364
Zend_Session
rememberMe(), n'appelez alors pas regenerateId(), car elle sera appelée de manière
automatique.
Le vol de session ne peut se remarqué que si vous arrivez à faire la différence entre l'attaquant
et la victime. Ce n'est pas chose simple, et les techniques utilisées ne sont jamais fiables
à 100%. L'IP peut être utilisée, même si celle-ci n'est pas totalement fiable. Les en-têtes du
navigateur Web, eux, le sont déjà plus (lorsque 2 requêtes successives avec le même identifiant
de session arrivent au serveur, si l'une prétend être issue de FireFox et l'autre d'Opéra, alors très
probablement qu'il s'agit de 2 personnes différentes, mais ayant le même identifiant de session.
Typiquement : l'attaquant et sa victime.) Il est très difficile de différencier l'attaquant et la victime,
c'est d'ailleurs impossible dans la suite de cas suivants :
• l'attaquant utilise une faille XSS pour envoyer un cookie de session à une victime, possédant
son propre identifiant de session (fixation).
if (!isset($defaultNamespace->initialized)) {
Zend_Session::regenerateId();
$defaultNamespace->initialized = true;
}
1365
Zend_Session
4.5. forgetMe()
Cette fonction est analogue à rememberMe() sauf qu'elle demande au cookie de session du
navigateur client d'être détruit à la fermeture de celui-ci (et non éventuellement après X temps).
4.6. sessionExists()
Utilisez cette méthode afin de savoir si une session existe pour le client (la requête) actuel. Ceci
doit être utilisé avant le démarrage de la session.
Si vous voyez le message d'erreur "Cannot modify header information - headers already
sent", alors tentez de ne pas utiliser TRUE comme valeur du premier argument (ceci demande
l'expiration du cookie de session, ou voyez Section 4.2, « L'erreur: "Headers Already Sent" ».
Ainsi, Zend_Session::destroy(true) doit être appelé avant tout envoi d'en-tête HTTP par
PHP, ou alors la bufferisation de sortie doit être activée (sans que celui-ci ne déborde).
Exception
Par défaut, $readonly est activé et toute opération future d'écriture dans la
session lèvera une exception.
4.8. stop()
Cette méthode ne fait rien d'autre que de verrouiller la session en écriture. Tout appel futur
d'écriture via des instances de Zend_Session_Namespace ou Zend_Session lèvera une
exception.
Exception
Par défaut, $readonly est activé, et donc tout appel d'écriture futur dans
la session générera une exception. Certaines applications peuvent nécessiter
1366
Zend_Session
4.10. expireSessionCookie()
Cette méthode envoie un cookie d'identifiant de session périmé au client. Quelque fois cette
technique est utilisée pour déconnecter le client de sa session.
4.11. setSaveHandler(Zend_Session_SaveHandler_Interface
$interface)
Cette méthode propose une correspondance orientée objet de
session_set_save_handler().
4.12. namespaceIsset($namespace)
Cette méthode permet de déterminer si un namespace existe dans la session.
Exception
Une exception sera levée si la session n'est pas lisible (n'a pas été démarrée).
4.13. namespaceUnset($namespace)
Utilisez Zend_Session::namespaceUnset($namespace) pour détruire un namespace
entier de la session. Comme pour les tableaux PHP, si le tableau est détruit, les objets à l'intérieur
ne le sont pas s'il reste des références vers eux dans d'autres tableaux ou objets toujours
accessibles. Ainsi namespaceUnset() ne détruit pas "en profondeur" la variable de session
associée au namespace. Voyez les références en PHP pour plus d'infos.
Exception
Une exception sera envoyée si le namespace n'est pas écrivable (après un appel
à destroy()).
4.14. namespaceGet($namespace)
Déprécié: Utilisez getIterator() dans Zend_Session_Namespace. Cette méthode
retourne un tableau du contenu du namespace $namespace. Si vous avez une raison de
conserver cette méthode, faites nous part de vos remarques à fw-auth@lists.zend.com.
Exception
Une exception sera levée si la session n'est pas lisible (n'a pas été démarrée).
4.15. getIterator()
getIterator() retourne un ArrayObject contenant tous les noms des namespaces de
session.
1367
Zend_Session
Exception
Une exception sera levée si la session n'est pas lisible (n'a pas été démarrée).
5. Zend_Session_SaveHandler_DbTable
Le paramétrage basique pour Zend_Session_SaveHandler_DbTable doit contenir au moins
quatre colonnes, décrites dans une configuration de type array ou objet Zend_Config :
"primary" qui est la clé primaire et reçoit par défaut l'ID de session dont le format est par défaut
une chaîne de 32 caractères ; "modifiedColumn" qui est le timestamp Unix de la date de dernière
modification ; "lifetimeColumn" qui est la durée de vie de la session ("modified" + "lifetime" doit
être supérieur à "time()") ; et "dataColumn" qui est la donnée sérialisée stockée en session.
// Démarrage de la session
Zend_Session::start();
Vous pouvez aussi utiliser des colonnes multiples pour votre clé primaire de
Zend_Session_SaveHandler_DbTable.
1368
Zend_Session
// Démarrage de la session
Zend_Session::start();
1369
Zend_Soap
1. Zend_Soap_Server
La classe Zend_Soap_Server a été créée pour simplifier la création d'un service Web SOAP
en PHP.
Elle peut être utilisée en mode WSDL ou non-WSDL, et elle utilise des fonctions ou des classes
pour définir le service Web rendu.
Si le mode non-WSDL est utilisé, alors toutes les options du protocole doivent être configurées.
• "classmap" ("classMap") : utilisé pour faire correspondre des types WSDL à des classes
PHP.
L'option doit être un tableau avec pour clés les types WSDL et pour valeur les classes PHP
correspondantes.
• "encoding" : encodage interne des caractères (l'encodage externe est toujours UTF-8).
1370
Zend_Soap
Vous devez aussi spécifier "uri" dans ce cas (voir juste après).
• "classmap" ("classMap") : utilisé pour faire correspondre des types WSDL à des classes PHP.
L'option doit être un tableau avec pour clés les types WSDL et pour valeur les classes PHP
correspondantes.
• "encoding" : encodage interne des caractères (l'encodage externe est toujours UTF-8).
...
class MyClass {
/**
* Cette méthode accepte ...
*
* @param integer $inputParam
* @return string
*/
public function method1($inputParam) {
...
}
/**
* Cette méthode accepte ...
*
* @param integer $inputParam1
* @param string $inputParam2
* @return float
*/
public function method2($inputParam1, $inputParam2) {
...
}
...
}
...
$server = new Zend_Soap_Server(null, $options);
// Connecte la classe au serveur Soap
$server->setClass('MyClass');
// Connecte un objet déjà initialisé au serveur Soap
$server->setObject(new MyClass());
3
Les options se configurent aussi plus tard, grâce à la méthode setOptions($options)
1371
Zend_Soap
...
$server->handle();
Important!
Vous devriez complètement décrire chaque méthode grâce aux blocs de
commentaires PHPDoc dans le cas où vous souhaitez utiliser l'auto découverte
du service pour préparer le WSDL correspondant.
La seconde manière de décrire l'API de votre service Web est d'utiliser des fonctions PHP
conjointement avec les méthodes addFunction() ou loadFunctions() :
...
/**
* Cette fonction ...
*
* @param integer $inputParam
* @return string
*/
function function1($inputParam) {
...
}
/**
* Cette fonction ...
*
* @param integer $inputParam1
* @param string $inputParam2
* @return float
*/
function function2($inputParam1, $inputParam2) {
...
}
...
$server = new Zend_Soap_Server(null, $options);
$server->addFunction('function1');
$server->addFunction('function2');
...
$server->handle();
1.3.1. Requête
La méthode Zend_Soap_Server::handle() utilise la requête depuis le flux d'entrée standard
('php://input'). Le comportement peut être changé en passant des paramètres à la méthode
handle() ou en spécifiant sa propre requête grâce à la méthode setRequest() :
1372
Zend_Soap
...
$server = new Zend_Soap_Server(...);
...
// Affecte une requête personnalisée
$server->handle($request);
...
// Affecte une requête personnalisée
$server->setRequest();
$server->handle();
• stdClass (__toString() est appelée et son contenu est vérifié comme XML valide)
...
$server = new Zend_Soap_Server(...);
...
$server->handle();
$request = $server->getLastRequest();
1.3.2. Réponse
Zend_Soap_Server::handle() émet automatiquement la réponse vers le flux standard de
sortie. Ce comportement peut être changé en utilisant setReturnResponse() avec une valeur
4
TRUE ou FALSE en paramètre. . La réponse générée par handle() est alors retournée et non
plus émise.
...
$server = new Zend_Soap_Server(...);
...
// Récupère la réponse plutôt que de l'émettre
$server->setReturnResponse(true);
...
$response = $server->handle();
...
...
$server = new Zend_Soap_Server(...);
...
$server->handle();
$response = $server->getLastResponse();
...
4
L'état actuel du drapeau de retour de la réponse peut être vérifié via la méthode setReturnResponse() sans paramètre.
1373
Zend_Soap
2. Zend_Soap_Client
Zend_Soap_Client est une classe destinée à simplifier l'interrogation de services SOAP.
Lorsque Zend_Soap_Client fonctionne en mode WSDL, il utilise le document WSDL pour définir
les options de la couche de transport des données.
Le fichier WSDL est en général fournit par le service auquel vous souhaitez accéder. Si
la description WSDL n'est pas disponible, vous pouvez vouloir utiliser Zend_Soap_Client
en mode non WSDL . Dans ce cas, toutes les options du protocole devront être définies
explicitement dans la classe Zend_Soap_Client.
Ces deux paramètres peuvent être insérés après construction, ceci grâce aux méthodes
setWsdl($wsdl) et setOptions($options).
Important!
Si vous utilisez Zend_Soap_Client en mode non WSDL, vous devez fournir les
options 'location' et 'uri'.
• 'classmap' ('classMap') : doit être utilisé pour faire correspondre des types WSDL à des classes
PHP.
Cette option doit être un tableau avec comme clés les types WSDL et comme valeurs les
noms des classes PHP.
• 'encoding' : encodage interne des caractères (l'encodage externe est toujours UTF-8).
Changer cette option peut faire basculer Zend_Soap_Client en mode WSDL ou non WSDL.
• 'uri' : cible du service SOAP (requis pour le mode non WSDL, inusité en mode WSDL).
• 'location' : l'URL à requêter (requis pour le mode non WSDL, inusité en mode WSDL).
1374
Zend_Soap
Chaque méthode du service Web est liée à une méthode virtuelle de l'objet
Zend_Soap_Client, qui s'utilise de manière tout à fait classique comme PHP le définit.
Voici un exemple :
...
//****************************************************************
// Code du serveur
//****************************************************************
// class MyClass {
// /**
// * Cette méthode utilise ...
// *
// * @param integer $inputParam
// * @return string
// */
// public function method1($inputParam) {
// ...
// }
//
// /**
// * Cette méthode utilise ...
// *
// * @param integer $inputParam1
// * @param string $inputParam2
// * @return float
// */
// public function method2($inputParam1, $inputParam2) {
// ...
// }
//
1375
Zend_Soap
// ...
// }
// ...
// $server = new Zend_Soap_Server(null, $options);
// $server->setClass('MyClass');
// ...
// $server->handle();
//
//****************************************************************
// Fin du code du serveur
//****************************************************************
3. WSDL
Si vous ne voulez pas l'utiliser pour vos propres besoins, vous pouvez alors
passer cette section de la documentation.
2. $uri - URI d'accès au fichier WSDL. (Une référence dans le système de fichier local est
possible.)
3.2. addMessage()
addMessage($name, $parts) ajoute un message de description au document WSDL (/
definitions/message de l'élément).
1376
Zend_Soap
Le paramètre $parts est un tableau de paramètre des messages décrivant les paramètres
d'appel SOAP. Le tableau est associatif: 'nom du paramètre' (nom du paramètre d'appel SOAP)
=> 'type du paramètre'.
Les paramètres de messages peuvent être soit "element", soit "type" (voyez
http://www.w3.org/TR/wsdl#_messages).
Tous les éléments non standards, qui doivent être ajoutés avec la méthode
Zend_Soap_Wsdl::addComplexType(), sont décrits en utilisant un noeud
"complexType" décrits dans la section "/definitions/types/schema/" du document
WSDL.
3.3. addPortType()
addPortType($name) ajoute un nouveau type de portage au document WSDL (/definitions/
portType).
Ceci fait la jointure entre des méthodes du service décrites en tant qu'implémentations de
Zend_Soap_Server.
3.4. addPortOperation()
addPortOperation($portType, $name, $input = false, $output = false,
$fault = false) ajoute des définitions de portage au portage défini dans le document WSDL
(/definitions/portType/operation).
Chaque opération de portage correspond à une méthode de classe (si le Web Service est basé
sur une classe) ou à une fonction (si le Web Service est basé sur des fonctions), ceci en terme
d'implémentation de Zend_Soap_Server.
Cette méthode ajoute aussi les messages d'opération correspondants aux portages, ceci dépend
des paramètres $input, $output and $fault.
1377
Zend_Soap
3.5. addBinding()
addBinding($name, $portType) ajoute de nouvelles correspondances (bindings) au
document WSDL (/definitions/binding).
Le noeud du document WSDL "binding" définit le format du message et les détails du protocole
pour les opérations et messages définis par un portage "portType" particulier (voyez http://
www.w3.org/TR/wsdl#_bindings).
3.6. addBindingOperation()
addBindingOperation($binding, $name, $input = false, $output = false,
$fault = false) ajoute une opération à l'élément de correspondance avec le nom spécifié
(/definitions/binding/operation).
Cette méthode prend un objet XML_Tree_Node tel que retourné par addBinding(), en
paramètre ($binding) pour ajouter un élément "operation" avec des entrées input/output/false
dépendantes des paramètres spécifiés.
3.7. addSoapBinding()
addSoapBinding($binding, $style = 'document', $transport = 'http://
schemas.xmlsoap.org/soap/http') ajoute des correspondances (bindings) SOAP
("soap:binding") à l'élément (déjà lié à un portage de type) avec le style et le transport spécifié
(Zend_Soap_Server utilise le style RPC sur HTTP).
3.8. addSoapOperation()
addSoapOperation($binding, $soap_action) ajoute une opération SOAP
("soap:operation") à l'élément de correspondance avec l'action spécifiée. L'attribut "style" de
l'élément "soap:operation" n'est pas utilisé alors que le modèle de programmation (RPC-oriented
ou document-oriented) devrait utiliser la méthode addSoapBinding()
1378
Zend_Soap
3.9. addService()
addService($name, $port_name, $binding, $location) ajoute un élément "/
definitions/service" au document WSDL avec le nom du Web Service spécifié, le nom du portage,
la correspondance, et l'adresse.
WSDL 1.1 autorise d'avoir plusieurs types de portage par service. Cette particularité n'est pas
utilisée dans Zend_Soap_Server et est non supportée par la classe Zend_Soap_Wsdl.
Utilisations de Zend_Soap_Server :
où $name est un nom de classe pour le Web Service utilisant des classes, ou un nom de script
pour le Web Service qui utilise des fonctions.
• Classe PHP <-> basé sur la stratégie des types complexes (Voir : Section 3.10.2, « Ajouter
7
des infos sur les types complexes ») .
• Si le type na aucune correspondance avec les valeurs ci-dessus, alors xsd:anyType est
utilisé.
1379
Zend_Soap
...
$wsdl = new Zend_Soap_Wsdl('My_Web_Service', $myWebServiceUri);
...
$soapIntType = $wsdl->getType('int');
...
class MyClass {
...
}
...
$soapMyClassType = $wsdl->getType('MyClass');
C'est automatiquement utilisé par la méthode getType() pour ajouter les types complexes des
paramètres de méthodes ou des types retournés.
Sa détection et son algorithme de construction est basé sur la détection de stratégie des
types complexes couramment active. Vous pouvez paramétrer la stratégie de détection soit
en spécifiant le nom de classe sous la forme d'une chaîne de caractères ou une instance
implémentant Zend_Soap_Wsdl_Strategy_Interface en tant que troisième paramètre
du constructeur ou en utilisant la fonction setComplexTypeStrategy($strategy) de
Zend_Soap_Wsdl. Les stratégies de détection suivantes existent couramment :
1380
Zend_Soap
Les propriétés des classes doivent posséder un bloc de documentation avec le type PHP en
question, afin que la propriété soit incluse dans la description WSDL.
addComplexType() vérifie sur le type est déjà décrit dans la section des types du document
WSDL.
Ceci évite les duplications et récursions si cette méthode est appelée plus d'une fois.
3.11. addDocumentation()
addDocumentation($input_node, $documentation) ajoute de la documentation lisible
("human readable") grâce à l'élément optionnel "wsdl:document".
L'élément "/definitions/binding/soap:binding" est utilisé pour dire que la correspondance est liée
au format du protocole SOAP.
4. Auto découverte
4.1. Introduction à l'auto découverte
Les fonctionnalités SOAP de Zend Framework sont proposées afin de simplifier l'accès aux
services Web de type SOAP.
SOAP est un protocole d'échange de données indépendant d'un langage. Il peut donc être utilisé
avec une autre technologie que PHP.
2. SOAP serveur application non PHP <---> SOAP client application PHP
3. SOAP serveur application PHP <---> SOAP client application non PHP
Il est indispensable de connaître les fonctionnalités qu'offre un serveur SOAP, afin de pouvoir
communiquer avec lui. WSDL est alors utilisé pour décrire en détail l'API des services disponibles
sur un serveur SOAP.
1381
Zend_Soap
Le langage WSDL est assez complexe (voyez http://www.w3.org/TR/wsdl pour les détails ). Il
est donc difficile d'écrire une définition WSDL correcte, à la main.
Un autre problème concerne la synchronisation des changements dans l'API avec des fichiers
WSDL déjà existants.
Ces 2 problèmes peuvent être résolus avec la génération automatique de WSDL, qui permet
d'analyser une classe ou des fonctions, d'en extraire les paramètres d'entrée/sortie, et de générer
un fichier WSDL correct et compréhensible par le serveur et les clients SOAP.
Il y a deux façons d'utiliser Zend Framework pour une application serveur SOAP:
Ces deux façons sont supportées par la fonctionnalité d'auto génération de Zend Framework.
Zend_Soap_AutoDiscovery supporte aussi la correspondance des types PHP vers les types
XSD.
Voici un exemple d'utilisation de l'auto découverte. La fonction handle() génère le fichier WSDL
et l'envoie au navigateur :
class My_SoapServer_Class {
...
}
Si vous avez besoin d'accéder au fichier WSDL généré soit pour le sauvegarder dans un fichier
ou en tant que chaîne de caractères, vous pouvez utiliser les méthodes dump($filename) ou
toXml() que la classe AutoDiscover fournit.
if(isset($_GET['wsdl'])) {
$autodiscover = new Zend_Soap_AutoDiscover();
$autodiscover->setClass('HelloWorldService');
$autodiscover->handle();
} else {
// pointing to the current file here
$soap = new Zend_Soap_Server("http://example.com/soap.php?wsdl");
$soap->setClass('HelloWorldService');
$soap->handle();
1382
Zend_Soap
Cet URI est aussi utilisé comme un espace de nom cible pour tous les noms du service
(incluant les types complexes décrits éventuellement).
Une méthode peut avoir plusieurs prototypes si des paramètres sont optionnels.
Important !
L'auto génération du fichier WSDL (avec auto découverte de la classe) utilise les
blocs de documentation de PHP insérés par le développeur dans ses classes,
afin de trouver les types retournés. De ce fait, pour les types scalaires, c'est le
seul moyen de les déterminer de manière sûre, et concernant les types de retour
des méthodes, c'est le seul moyen de les découvrir (PHP étant faiblement typé).
Ceci signifie que documenter de manière correcte vos classes et méthodes n'est
pas seulement une bonne pratique, c'est tout simplement essentiel pour partager
vos classes en tant que services SOAP auto générés.
1383
Zend_Soap
...
$autodiscover->handle();
Cet URI est aussi utilisé comme un espace de nom cible pour tous les noms du service
(incluant les types complexes décrits éventuellement).
Une fonction peut avoir plusieurs prototypes si des paramètres sont optionnels.
Important!
L'auto génération du fichier WSDL (avec auto découverte des fonctions) utilise
les blocs de documentation de PHP insérés par le développeur dans ses
fonctions, afin de trouver les types retournés. De ce fait, pour les types scalaires,
c'est le seul moyen de les déterminer de manière sûre, et concernant les types de
retour des méthodes, c'est le seul moyen de les découvrir (PHP étant faiblement
typé).
Ceci signifie que documenter de manière correcte vos fonctions n'est pas
seulement une bonne pratique, c'est tout simplement essentiel pour partager vos
fonctions en tant que services SOAP auto générés.
• Classe PHP <-> basé sur la stratégie des types complexes (Voir : Section 3.10.2, « Ajouter
8
des infos sur les types complexes ») .
1384
Zend_Soap
• type[] or object[] (c'est-à-dire int[]) <-> basé sur la stratégie des types complexes
• Si le type n'est pas reconnu en tant que l'un de ceux-ci, alors xsd:anyType est utilisé.
1385
Zend_Tag
1. Introduction
Zend_Tag est une suite de composants permettant de manipuler des entités taguables. Ce
composant propose 2 classes dans ce but, Zend_Tag_Item et Zend_Tag_ItemList. Aussi,
l'interface Zend_Tag_Taggable vous permet d'utiliser vos modèles dans des tags avec
Zend_Tag.
Zend_Tag_Item est un composant proposant les fonctionnalités basiques pour traiter des tags
dans Zend_Tag. Une entités taguables consiste en un titre et un poids (nombre d'occurrences).
Il existe aussi d'autres paramètres utilisés par Zend_Tag.
Cet exemple montre comment créer une liste de tags en pondérant chacun d'eux.
// Crée la liste
$list = new Zend_Tag_ItemList();
// Sortie
foreach ($list as $item) {
printf("%s: %d\n", $item->getTitle(), $item->getParam('weightValue'));
}
Ceci va afficher les 3 entités Code, Zend Framework et PHP avec les valeurs absolues 10,
1 et 2.
2. Zend_Tag_Cloud
Zend_Tag_Cloud est la partie qui s'occupe du rendu dans Zend_Tag. par défaut, elle utilise
un ensemble de décorateurs HTML , ce qui permet de créer des nuages de tags pour un site,
mais elle met aussi à votre disposition 2 classes abstraites vous permettant de créer vos propres
rendus, par exemple pour créer des tags rendus en PDF.
• cloudDecorator : défini le décorateur du nuage. Ceci peut être un objet, un nom de classe
qui sera chargée par pluginloader, une instance de Zend_Tag_Cloud_Decorator_Cloud
ou un tableau contenant les clés decorator et optionnellement options, qui est elle-même
un tableau passé comme options au constructeur du décorateur.
1386
Zend_Tag
• tagDecorator : le décorateur d'un tag individuel. Ceci peut être un objet, un nom de classe
qui sera chargée par pluginloader, une instance de Zend_Tag_Cloud_Decorator_Cloud
ou un tableau contenant les clés decorator et optionnellement options, qui est elle-même
un tableau passé comme options au constructeur du décorateur.
• itemList : une liste d'entités à utiliser. Doit être une instance de Zend_Tag_ItemList.
• tags : une liste de tags à assigner au nuage. Chacun doit implémenter Zend_Tag_Taggable
ou être un tableau qui pourra être utilisé pour instancier Zend_Tag_Item.
Cet exemple illustre les manipulations de base pour créer un nuage de tags, ajouter des
tags et afficher le rendu.
// Rendu du nuage
echo $cloud;
2.1. Decorateurs
Zend_Tag_Cloud a besoin de 2 types de décorateurs afin de rendre le nuage. Un
décorateur pour rendre chacun des tags, et un décorateur pour rendre le nuage lui-même.
Zend_Tag_Cloud propose un décorateur par défaut qui formate le nuage en HTML. Il utilise
par défaut des listes ul/li et des tailles de polices différentes selon les poids des tags.
Paramètre d'URL
Une ancre étant ajoutée à chaque tag, vous devez spécifier une URL pour chacun
d'eux.
Le décorateur de tags peut utiliser des tailles de police différentes pour chaque ancre, ou pour
chaque classe de liste. Les options suivantes sont disponibles:
1387
Zend_Tag
• fontSizeUnit : définit l'unité utilisée dans la taille des polices. em, ex, px, in, cm, mm, pt,
pc et %.
• minFontSize : Taille minimale de la police (poids le plus faible) (doit être un entier).
• maxFontSize : Taille maximale de la police (poids le plus fort) (doit être un entier).
• htmlTags : un tableau de tags HTML entourant l'ancre. Chaque élément peut être une chaîne
de caractères, utilisée comme type d'élément, ou un tableau contenant une liste d'attributs
pour l'élément. La clé du tableau est alors utilisée pour définir le type de l'élément.
• htmlTags : un tableau de balises HTML entourant chaque tag. Chaque élément peut être
une chaîne de caractères, utilisée comme type d'élément, ou un tableau contenant une liste
d'attributs pour l'élément. La clé du tableau est alors utilisée pour définir le type de l'élément.
1388
Zend_Test
1. Introduction
Zend_Test fournit les outils pour faciliter les tests unitaires de vos applications Zend
Framework. Pour le moment, nous offrons des éléments permettant de tester les applications
MVC réalisées avec Zend Framework.
2. Zend_Test_PHPUnit
Zend_Test_PHPUnit fournit un TestCase pour les applications MVC qui contient des
assertions qui permettent de tester toute une variété de responsabilités. La manière la plus facile
de comprendre ce qui peut être fait est de regarder l'exemple suivant.
1389
Zend_Test
•L'exemple
Quand un utilisateur
suivant se simple
est un connecte,test ilpour
doit être redirigé vers
un choses.
contrôleur sa page de profil,permettant
et cette page
Cet
• Leexemple
formulaire
Exemple particulier
de login
871. suppose
doit être
Exemple d'un différentes
affiché
TestCase Premièrement,
aux utilisateurs la majeure partie de
UserController
non-authentifiés.
d'une application de login de
doit
vérifieraffichée des
différentes infofrmations
choses particulières.
: déplacé dans un plugin. Ceci simplifie le paramétrage dans le
notre fichier d'amorçage a été
cas des tests en spécifiant rapidement votre environnement, et ainsi vous permet d'amorcer
votre application en une seule ligne. Ensuite, notre exemple suppose que le chargement
automatique ("autoload") est activé donc nous n'avons pas à nous soucier de charger les
class
classesUserControllerTest
appropriées (comme leextends Zend_Test_PHPUnit_ControllerTestCase
bon contrôleur, le bon plugin, etc.)
{
public function setUp()
{
$this->bootstrap = array($this, 'appBootstrap');
parent::setUp();
}
$this->resetRequest()
->resetResponse();
$this->request->setMethod('GET')
->setPost(array());
$this->dispatch('/user/view');
$this->assertRoute('default');
$this->assertModule('default');
$this->assertController('user');
$this->assertAction('view');
$this->assertNotRedirect();
$this->assertQuery('dl');
$this->assertQueryContentContains('h2', 'User: foobar');
}
Cet
} exemple pourrait être écrit plus simplement : toutes les assertions ne sont pas
nécessaires et sont fournies seulement à titre d'illustration. Cependant, il montre bien
combien il est simple de tester vos applications.
1390
Zend_Test
Si vous voulez tester votre application MVC, vous devez d'abord l'amorcer ("bootstrap"). Il existe
plusieurs manières pour faire ceci, toutes celles-ci s'articulent autour de la propriété publique
$bootstrap.
Deuxièmement, vous pouvez paramétrer cette propriété pour qu'elle pointe vers un fichier. Si
vous faîtes ceci, le fichier ne doit pas distribuer le contrôleur frontal, mais seulement paramétrer
celui-ci et faire tout réglage spécifique à votre application.
// ...
}
Troisièmement, vous pouvez fournir un callback PHP qui doit être exécuter pour amorcer votre
application. Cet exemple est montré dans l'exemple de login. Si le callback est une fonction ou
une méthode statique, ceci peut être paramétrer au niveau de la classe :
// ...
}
Dans le cas où une instance d'objet est nécessaire, nous recommandons de réaliser ceci dans
votre méthode setUp() :
1391
Zend_Test
L'amorçage doit être le proche possible de ce que fera réellement votre application. Cependant,
il y a plusieurs avertissements :
• N'espérez pas faire des tests spécifiques de serveur. Autrement dit, ces tests ne garantissent
pas que le code va s'exécuter sur un serveur avec une configuration spécifique, mais
simplement que l'application va fonctionner comme souhaité si le routeur est capable de router
une requête donnée. À cet effet, ne paramétrez pas d'en-têtes spécifiques au serveur dans
l'objet de requête.
Une fois que votre application est amorcée, vous pouvez commencer à écrire vos tests.
1392
Zend_Test
Il y a des moments, cependant, où vous devez fournir des informations supplémentaires - des
variables GET et POST, des informations de COOKIE, etc. Vous pouvez peupler la requête avec
ces informations :
// Distribuer :
$this->dispatch('/foo/bar');
// ...
}
}
Maintenant que la requête est construite, il est temps de créer des assertions.
The redirect action helper issues an exit() statement when using the method
gotoAndExit() and will then obviously also stops a test running for this
method. For testability of your application dont use that method on the redirector!
Due to its nature the redirector action helper plugin issues a redirect and exists
after this. Because you cannot test parts of an application that issue exit calls
Zend_Test_PHPUnit_ControllerTestCase automatically disables the exit part of the
1393
Zend_Test
redirector which can cause different behaviours in tests and the real application. To make sure
redirect work correctly you should it them in the following way:
2.3. Assertions
Les assertions sont le coeur des tests unitaires; vous les utilisez pour vérifier que le résultat
est bien celui que vous attendiez. A cette fin, Zend_Test_PHPUnit_ControllerTestCase
fournit un certain nombre d'assertions pour simplifier le test de vos applications et contrôleurs
MVC.
Cette fonctionnalité est fournie via Zend_Dom_Query, et intégré à un jeu d'assertions de type
"Query*". Chacune de ces assertions prend un sélecteur CSS en tant que premier argument,
avec optionnellement des arguments additionnels et/ou un message d'erreur, basé sur le type
d'assertion. Vous pouvez trouver les règles d'écriture des électeurs CSS dans le chapitre
Zend_Dom_Query - Aspect théorique. Les assertion "Query*" incluent :
1394
Zend_Test
De plus, toutes les méthodes ci-dessus possèdent une variante "Not" qui correspond
à l'assertion négative : assertNotQuery(), assertNotQueryContentContains(),
assertNotQueryContentRegex(), et assertNotQueryCount(). (Notez que les versions
CountMin et CountMax n'ont pas de variantes pour des raisons évidentes).
1395
Zend_Test
De plus, toutes les méthodes ci-dessus possèdent une variante "Not" qui correspond à
l'assertion négative.
De plus, toutes les méthodes ci-dessus possèdent une variante "Not" qui correspond à
l'assertion négative.
2.4. Exemples
Savoir comment configurer votre infrastructure de tests et comment faire des assertions est
seulement la moitié du travail ; maintenant il est temps de commencer à regarder quelques
scénarios réels de test pour voir comment vous pouvez les étendre.
1396
Considérons une tâche habituelle d'un site Web : l'authentification et l'enregistrement
d'utilisateurs. Dans notre exemple, nous avons défini un contrôleur "UserController"
$this->resetRequest()
public function testCallWithoutActionShouldPullFromIndexAction()
pour {gérer ceci, ->resetResponse();
il requiert le conditions suivantes :
Écrivons maintenant les tests : Zend_Test
$this->dispatch('/user');
$this->request->setPost(array());
$this->assertController('user');
$this->assertAction('index');
} // ...
Exemple
} 872. Test d'un contrôleur "UserController"
public function testLoginFormShouldContainLoginAndRegistrationForms()
//
{ ...
} $this->dispatch('/user');
$this->assertQueryCount('form', 2);
}
3. Zend_Test_PHPUnit_Db
Couper l'accès aux données au modèle métier requiert souvent l'utilisation d'une base de
données pour les tests. Mais la base est persistente entre les tests, et leur isolation est donc
rompue, de plus, configurer une base de données pour des tests peut vite s'avérer complexe.
L'extension sur les bases de données de PHPUnit simplifie les procédures de tests en offrant
des mécanismes de preconditions et postconditions sur la base entre les tests. Ce composant
étend donc l'extension base de données de PHPUnit en ajoutant du code spécifique à Zend
Framework.
Les tests de base de données peuvent être résumés en 2 notions : DataSets et DataTables. En
interne, PHPUnit peut créer un objet dont la structure est callée sur une base de données dont
les tables et les enregistrements sont montés depuis un fichier de configuration ou un contenu
réel. Cet objet abstrait peut alors être comparé à des structures. Un cas courant en tests de base
de données consiste à configurer des tables en les remplissant de données fictives, éxecuter du
code "utile", puis comparer la base de données avec une structure. Zend_Test_PHPUnit_Db
simplifie cette tâche en offrant la possibilité de créer des DataSets et des DataTables provenant
d'instances de Zend_Db_Table_Abstract ou Zend_Db_Table_Rowset_Abstract.
3.1. Quickstart
3.1.1. Configurer un cas de tests Database
Nous allons à présent écrire des tests pour la base de données Bug de la documentation
sur Zend_Db_Table. D'abord, nous testons qu'insérer un nouveau bug est bien
sauvegardé en base. Nous devons créer un cas de tests sous forme de classe étendant
Zend_Test_PHPUnit_DatabaseTestCase. Cette classe étend elle- même PHPUnit
Database Extension, qui étend alors PHPUnit_Framework_TestCase. Un cas de test pour
base de données contient deux méthodes abstraites à définir, une concernant la connexion à la
base et l'autre concernant les données à utiliser comme source pour les tests.
/**
* Retourne la connexion de test
*
* @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
*/
protected function getConnection()
{
if($this->_connectionMock == null) {
$connection = Zend_Db::factory(...);
$this->_connectionMock = $this->createZendDbConnection(
1398
Zend_Test
$connection, 'zfunittests'
);
Zend_Db_Table_Abstract::setDefaultAdapter($connection);
}
return $this->_connectionMock;
}
/**
* @return PHPUnit_Extensions_Database_DataSet_IDataSet
*/
protected function getDataSet()
{
return $this->createFlatXmlDataSet(
dirname(__FILE__) . '/_files/bugsSeed.xml'
);
}
}
Ici, nous créons la connexion à la base et nous y injectons des données fictives de test. Les
éléments suivants sont importants à noter :
• Le schéma de la base (tables et bases de données) n'est pas recrée entre chaque test. Les
bases de données et les tables doivent être créees à la main avant de lancer les tests.
• Les tests vident la base durant setUp() et y insèrent les données en provenance de
getDataSet().
1399
Zend_Test
</dataset>
Nous allons travailler sur ces quatre entrées dans la table "zfbugs" après. Le script MySQL
suivant est nécessaire pour l'exemple:
$data = array(
'created_on' => '2007-03-22 00:00:00',
'updated_on' => '2007-03-22 00:00:00',
'bug_description' => 'Something wrong',
'bug_status' => 'NEW',
'reported_by' => 'garfield',
'verified_by' => 'garfield',
'assigned_to' => 'mmouse',
);
$bugsTable->insert($data);
$this->assertDataSetsEqual(
$this->createFlatXmlDataSet(dirname(__FILE__)
. "/_files/bugsInsertIntoAssertion.xml"),
$ds
);
}
}
1400
Zend_Test
Il existe d'autres manière de vérifier que l'état actuel de la base est équivalent à un état attendu.
La table "Bugs" de l'exemple connait déja sont état interne, utilisons ceci à notre avantage.
L'exemple suivant teste que la suppression de données dans la table est possible:
$bugsTable->delete(
$bugsTable->getAdapter()->quoteInto("bug_id = ?", 4)
);
$this->assertDataSetsEqual(
$this->createFlatXmlDataSet(dirname(__FILE__)
. "/_files/bugsDeleteAssertion.xml"),
$ds
);
}
}
Ici nous créons un objet représentant un jeu de données pour une table, instance de
Zend_Test_PHPUnit_Db_DataSet_DbTableDataSet. Il prend en paramètre un objet
Zend_Db_Table_Abstract et l'ajoute au jeu de données avec le nom de la table, dans notre
exemple : "zfbugs". Vous pourriez ajouter plus de données dans le jeu de données en utilisant
addTable() plusieurs fois.
Ici nous ne possédons qu'une seule table et nous la comparons à un état défini dans
"bugsDeleteAssertion.xml" qui est en fait le jeu de données original moins la données supprimée :
celle ayant l'id 4.
Voyons maintenant comment vérifier que deux tables soient identiques (et non deux jeux de
données correspondants à de telles tables). Ajoutons un test à notre scénario qui va vérifier la
mise à jour de données.
1401
Zend_Test
$data = array(
'updated_on' => '2007-05-23',
'bug_status' => 'FIXED'
);
$bugsTable->update($data, $where);
$rowset = $bugsTable->fetchAll();
$this->assertTablesEqual(
$expectedRowsets, $ds
);
}
}
Tout comme les TestCase de vos contrôleurs, les tests de bases de données
sont efféctués au niveau intégration. Ils utilisent différentes couches applicatives
pour lancer les tests et à ce titre devraient être utilisés avec précaution.
1402
Zend_Test
de données, ceci dans le but d'utiliser des ressources provenant de Zend_Db. Voici le scénario
classique d'un test de base de données :
1. Pour chaque test, PHPUnit crée une instance de la classe de tests (TestCase) et lance la
méthode setUp().
2. Le scénario de test (TestCase) crée à son tour une instance du testeur de base de données
(Database Tester) qui s'occupe de la construction et destruction de la base de données.
4. Par défaut le testeur vide les tables spécifiées dans le jeu de données, puis peuple la base
avec le jeu de données.
6. Après que votre test ait fini, tearDown() est appelée. Cette méthode n'exécute aucune action
du la base de données elle-même car les actions à mener sont efféctuées en setUp() (vider
les tables).
1403
Zend_Test
Méthode Description
récupération des données est faite grâce
à fetchAll(), les paramètres additionnels
peuvent servir à limiter les données retournées.
createDbTableDataSet(array Crée un jeu de données basé sur les
$tables=array()) tables $tables, tableau d'instances de
Zend_Db_Table_Abstract.
$databaseFixture =
new PHPUnit_Extensions_Database_DataSet_FlatXmlDataSet(
dirname(__FILE__) . '/_files/initialUserFixture.xml'
);
$databaseTester->setupDatabase($databaseFixture);
}
}
Ici le jeu de données XML "initialUserFixture.xml" est utilisé pour monter des données en
base avant chaque test, exactement de la même manière que DatabaseTestCase le fait
en interne.
1404
Zend_Test
plus, cet adaptateur est très facilement déguisable car aucun paramètre de constructeur n'est
nécessaire.
L'adaptateur de tests agit comme une pile pour des résultats de base. L'ordre des résultats doit
être implémenté manuellement ce qui peut devenir assez complexe, mais cet adaptateur est très
pratique dans le cas où un ensemble logique de requêtes est éxecuté et que vous connaissez
l'ordre précis dans lequel les résultats doivent être retournés.
Le comportement des adaptateurs réels est simulé afin que des méthodes telles que
fetchAll(), fetchObject(), fetchColumn() puissent fonctionner avec l'adaptateur de
tests.
Bien sûr, INSERT, UPDATE et DELETE peuvent être empilés aussi, mais vous ne pourrez alors
tester que $stmt->rowCount() car ces types de requêtes ne retournent pas de résultats.
Par défaut, le profiler est activé pour que vous puissiez récupérer la requête éxecutée de manière
textuelle, avec ses paramètres donc.
$qp = $adapter->getProfiler()->getLastQueryProfile();
L'adaptateur de test ne vérifie jamais si la requête spécifiée est réellement de type SELECT,
DELETE, INSERT ou UPDATE. L'ordre exact de retour des données doit être spécifié
manuellement dans l'adaptateur de tests.
1405
Zend_Text
1. Zend_Text_Figlet
Zend_Text_Figlet est un composant qui permet aux développeurs de créer des textes
dénommés FIGlet. Un texte FIGlet test une chaîne de caractères, qui est représenté en "ASCII-
art". FIGlet utilise une format de police spécial , nommée FLT (FigLet Font). Par défaut, une
police standard est fourni avec Zend_Text_Figlet, mais vous pouvez ajouter des polices
additionnels à http://www.figlet.org.
Polices compressée
Zend_Text_Figlet supporte les polices compressées en gzip. Ceci veut
dire que vous pouvez prendre un fichier .flf et le gzipper. Pour permettre
à Zend_Text_Figlet de les reconnaître, les polices gzippées doivent avoir
l'extension .gz. De plus, pour pouvoir utiliser les polices compressées, vous
devez activer l'extension GZIP de PHP.
Encodage
Zend_Text_Figlet considère que vos chaînes sont encodées en UTF-8 par
défaut. Si ce n'est pas le cas, vous pouvez fournir le type d'encodage des
caractères en tant que second paramètre à la méthode render().
Il existe plusieurs options pour un FIGlet. Quand vous instanciez Zend_Text_Figlet, vous
pouvez les fournir sous la forme d'un tableau ou d'un objet Zend_Config.
• font : défini la police utilisée pour le rendu. Par défaut la police incorporé sera utilisée.
• outputWidth : défini la largeur maximum de la chaîne résultat. Ceci est utilisé pour le retour
à la ligne automatique ainsi que pour la justification. Attention cependant à de trop petites
valeurs, qui pourraient induire un comportement indéfini. La valeur par défaut est 80.
• handleParagraphs : un booléen qui indique, comment les nouvelles lignes sont gérées.
Réglé à TRUE, les nouvelles lignes uniques sont ignorées et traitées comme un espace unique.
Seules des nouvelles lignes multiples seront gérées comme telles. La valeur par défaut est
FALSE.
• smushMode : un entier qui définit comme chaque caractère est fusionné avec les autres. Peut
être la somme de multiple valeurs de type Zend_Text_Figlet::SM_*. Il existe les modes
de fusion suivant : SM_EQUAL, SM_LOWLINE, SM_HIERARCHY, SM_PAIR, SM_BIGX,
1406
Zend_Text
Cet exemple illustre une utilisation basique de Zend_Text_Figlet pour créer une texte
FIGlet simple :
En considérant que vous utilisez une police à espacement fixe, vous obtiendrez quelque
chose comme ceci :
2. Zend_Text_Table
Zend_Text_Table est un composant pour créer à la volée des tables de type texte avec
différents décorateurs. Ceci peut être utile, si vous souhaitez soit envoyé des données
structurées dans des emails textuels, qui sont sont utilisés pour leurs polices mono-
espacés, ou pour afficher des informations sous forme de tableaux dans une application CLI.
Zend_Text_Table supporte les colonnes multi-lignes, les fusions de colonnes ainsi que
l'alignement.
Encodage
Zend_Text_Table suppose que vos chaînes sont encodés en
UTF-8 par défaut. Si ce n'est pas le cas, vous pouvez fournir
l'encodage en tant que paramètre du constructeur ou à la
méthode setContent de Zend_Text_Table_Column. Alternativement
si vous avez un encodage différent dans le processus complet,
vous pouvez définir l'encodage d'entrée ("input") standard avec
Zend_Text_Table::setInputCharset($charset). Dans le cas où vous
avez besoin d'un autre encodage pour la sortie ("output") de la table, vous pouvez
le paramétrer avec Zend_Text_Table::setOutputCharset($charset).
Un objet Zend_Text_Table consiste en des lignes, qui contiennent des colonnes, représenté
par Zend_Text_Table_Row et Zend_Text_Table_Column. Lors de la création d'une table,
vous pouvez fournir un tableau avec les options pour la table. Celles-ci sont :
• decorator : le décorateur à utiliser pour les bordures de la table. Le défaut est unicode,
mais vous pouvez aussi spécifier ascii ou fournir une instance d'un objet décorateur
personnalisé.
1407
Zend_Text
• AutoSeparate : la manière comment les lignes sont séparées avec des lignes horizontales.
Par défaut, il y a une séparation entre chaque ligne. Ceci est défini entant que bitmask
contenant une ou plus des constantes de Zend_Text_Table suivantes :
• Zend_Text_Table::AUTO_SEPARATE_NONE
• Zend_Text_Table::AUTO_SEPARATE_HEADER
• Zend_Text_Table::AUTO_SEPARATE_FOOTER
• Zend_Text_Table::AUTO_SEPARATE_ALL
Où "header" est toujours la première ligne, et "footer" est toujours la dernière.
Les lignes sont simplement ajoutées à la table en créant une nouvelle instance de
Zend_Text_Table_Row, et en l'ajoutant à la table via la méthode appendRow. Les lignes
elle-même n'ont pas d'options. Vous pouvez aussi fournir un tableau directement à la méthode
appendRow, qui le convertira automatiquement en des objets Row, contenant les multiples objets
Column.
De la même manière vous pouvez ajouter les colonnes aux lignes. Créez un instance de
Zend_Text_Table_Column et ensuite paramétrer les options de colonnes soit dans le
constructeur ou plus tard par les méthodes set*. Le premier paramètre est le contenu de la
colonne qui peut avoir des lignes multiples, elles sont dans le meilleur des cas séparées par le
caractère \n. Le second paramètre définit l'alignement, qui est left par défaut et peut être l'une
des constantes de la classe Zend_Text_Table_Column :
• ALIGN_LEFT
• ALIGN_CENTER
• ALIGN_RIGHT
Le troisième paramètre est le colspan ("fusion") de la colonne. Par exemple, quand vous
choisissez "2 comme colspan, la colonne va déborder sur deux colonnes de la table. Le dernier
paramètre définit l'encodage du contenu, qui peut être fourni, si le contenu n'est ni de l'ASCII ni de
l'UTF-8. Pour ajouter la colonne à la ligne, vous appelez simplement appendColumn dans votre
objet Row avec l'objet Column en tant que paramètre. Alternativement vous pouvez directement
fournir la chaîne à la méthode appendColumn.
Pour finalement effectuer le rendu de la table, vous pouvez soit utiliser la méthode render
de la table, ou utilisez la méthode magique __toString en faisant echo $table; ou
$tableString = (string) $table.
1408
Zend_Text
Cet exemple illustre un utilisation basique de Zend_Text_Table pour créer une table
simple :
// Either simple
$table->appendRow(array('Zend', 'Framework'));
// Or verbose
$row = new Zend_Text_Table_Row();
$row->appendColumn(new Zend_Text_Table_Column('Zend'));
$row->appendColumn(new Zend_Text_Table_Column('Framework'));
$table->appendRow($row);
echo $table;
#################################
#Zend #Framework #
#################################
1409
Zend_TimeSync
1. Introduction
Zend_TimeSync est capable de recevoir une information temporelle depuis un serveur de
temps, en utilisant les protocoles NTP ou SNTP. Avec Zend_TimeSync, Zend Framework peut
fonctionner sans se soucier de la configuration du temps (de la date) du serveur sur lequel il
tourne.
En tâche de fond
Zend_TimeSync ne peut pas changer le temps sur le serveur, mais il peut en
revanche retourner un objet Zend_Date de différence de temps, pour travailler
avec.
En théorie, sur un serveur, un service (démon) tourne sur le système d'exploitation, pour
s'occuper de la synchronisation précise du temps, avec un serveur distant de temps. Cependant
il est possible que votre OS ne supporte pas ceci, ou que vous n'ayez pas les droits, ou la
possibilité de gérer un tel service de synchronisation. Zend_TimeSync prend alors tout son
sens.
Voici quelques exemples dans lesquels Zend_TimeSync est tout à fait adapté et utile :
Si votre application tourne sur un serveur qui ne possède pas de service de synchronisation du
temps, alors utiliser Zend_TimeSync vous permettra de gérer le temps dans votre application.
Si votre serveur de base de données est installé sur un serveur distant, et qu'il n'est
pas synchronisé temporellement avec votre serveur applicatif, alors vous rencontrerez des
problèmes si vous utilisez des timestamps.
• Serveurs multiples
Si votre application fonctionne au travers de multiples serveurs applicatifs, il est possible que
ceux-ci ne soient pas synchronisés au niveau du temps, et entre eux. Des problèmes vont
alors apparaître lorsque des données proviennent de plusieurs serveurs différents, et sont
traitées ensemble.
• Processus de Batch
Si vous utilisez des fichiers batchs, ou des applications en ligne de commande, et que celles-
ci nécessitent une synchronisation temporelle absente sur votre système.
1410
Zend_TimeSync
Dans tous ces cas là, Zend_TimeSync est une solution parfaite lorsque vous ne pouvez installer
de service sur vos serveurs.
• La distance
Bien entendu la distance entre votre serveur et le serveur de temps est importante. Afin de
réduire la charge réseau et d'obtenir des réponses plus rapides, veillez à utiliser un serveur
de temps proche géographiquement de votre serveur applicatif.
• La vitesse
La vitesse à laquelle les serveur de temps vont vous répondre est importante. Certains sont
souvent saturés. Faîtes des mesures.
• Leur nombre
N'utilisez pas systématiquement le même serveur de temps pour vous synchroniser. Si vous
envoyez trop de requêtes vers un serveur de temps, celui-ci pourra vous bannir. Utilisez un
pool de serveurs dont vous assurerez la rotation.
Mais où donc trouver des serveurs de temps ? Déjà, essayez votre LAN. En effet il est possible
que vous disposiez d'un serveur de temps interne à votre structure. Sinon vous pouvez utiliser
l'un des nombreux serveurs publics. Il sera alors intéressant d'utiliser un pool de serveurs. Un
pool est un serveur vous donnant aléatoirement l'adresse d'un serveur de temps, en fonction de
critères, comme la distance. C'est une solution tout à fait adaptée pour assurer une rotation des
serveurs, et éviter tous les problèmes mentionnés ci-dessus.
Voyez www.pool.ntp.org afin de récupérer le pool le plus proche de vous. Par exemple pour un
serveur en France 0.europe.pool.ntp.org pourra faire l'affaire.
1411
Zend_TimeSync
2. Utiliser Zend_TimeSync
Zend_TimeSync peut retourner le temps de n'importe quel serveur via NTP ou SNTP. Il peut
aussi gérer de multiples serveurs.
Dans tous les exemples qui suivront, nous utiliserons un pool de serveurs
générique public : 0.europe.pool.ntp.org. De votre côté, il est conseillé d'utiliser
le pool le plus proche possible géographiquement de votre position. Lisez http://
www.pool.ntp.org pour plus d'informations.
print $server->getDate()->getIso();
Que se passe-t-il en arrière plan de Zend_TimeSync ? Tout d'abord la syntaxe est vérifiée. Ainsi,
"0.pool.ntp.org" est vérifié et reconnu comme pouvant être un serveur de temps. Ensuite
l'appel à getDate() génère une requête vers le serveur de temps qui retourne son temps.
Zend_TimeSync va alors calculer la différence entre ce temps retourné, et le temps actuel du
serveur sur lequel le script tourne, et retourner finalement un objet Zend_Date représentant le
temps actuel, juste et corrigé.
Pour plus de détails sur les objets Zend_Date et leurs méthodes, voyez la documentation de
Zend_Date.
print $server->getDate()->getIso();
Vous devriez aussi nommer vos serveurs de temps. Ceci se fait au moyen de la clé du tableau
de serveurs, ou en second paramètre de la méthode addServer() :
1412
Zend_TimeSync
print $server->getDate()->getIso();
En nommant vos serveurs, vous pourrez en interroger un en particulier comme nous allons le
voir.
print $server->getDate()->getIso();
Zend_TimeSync peut donc gérer plusieurs serveurs, et plusieurs protocoles, en même temps.
Quelque fois il peut être nécessaire de préciser le port à utiliser. Si c'est le cas, rajoutez le
simplement à l'adresse du serveur à utiliser, sinon Zend_TimeSync utilisera celui par défaut.
print $server->getDate()->getIso();
L'option timeout définit le nombre de secondes au delà duquel la connexion sera considérée
comme cassée. La valeur par défaut est 1, donc Zend_TimeSync changera de serveur si la
réponse du serveur actuellement interrogé met plus d'une seconde à arriver.
Grâce à setOptions(), vous passez un tableau d'options que vous voulez. La clé du
tableau représente le nom de l'option en question et la valeur, sa valeur. Pour voir les options
déjà affectées, utilisez la méthode getOptions(). Elle accepte une clé nommant l'option à
récupérer, ou alors si aucun paramètre ne lui est passé, la méthode retournera toutes les options
connues.
1413
Zend_TimeSync
print $server->getDate()->getIso();
print_r(Zend_TimeSync::getOptions();
print "Timeout = " . Zend_TimeSync::getOptions('timeout');
Comme vous pouvez le noter, les options de Zend_TimeSync sont statiques, ainsi chaque
instance de Zend_TimeSync possèdera les mêmes options.
$actual = $server->getServer();
$server = $server->setServer('additional');
print_r ($server->getInfo());
$serverlist = array(
// invalid servers
'invalid_a' => 'ntp://a.foo.bar.org',
'invalid_b' => 'sntp://b.foo.bar.org',
);
try {
$result = $server->getDate();
echo $result->getIso();
} catch (Zend_TimeSync_Exception $e) {
$exceptions = $e->get();
1414
Zend_TimeSync
1415
Zend_Tool
1. Using Zend_Tool On The Command Line
The CLI, or command line tool (internally known as the console tool), is currently the primary
interface for dispatching Zend_Tool requests. With the CLI tool, developers can issue tooling
requests inside the "command line window", also commonly known as a "terminal" window. This
environment is predominant in the *nix environment, but also has a common implementation in
windows with the cmd.exe, console2 and also with the Cygwin project.
1.1. Installation
1.1.1. Download And Go
First download Zend Framework. This can be done by going to framework.zend.com and
downloading the latest release. After you've downloaded the package and placed it on your
system. The next step is to make the zf command available to your system. The easiest way to
do this, is to copy the proper files from the bin/ directory of the download, and place these files
within the same directory as the location of the php cli binary.
To install via PEAR, you must use the 3rd party zfcampus.org site to retrieve the latest Zend
Framework PEAR package. These packages are typically built within a day of an official Zend
Framework release. The benefit of installing via the PEAR package manager is that during the
install process, the ZF library will end up on the include_path, and the zf.php and zf scripts will
end up in a place on your system that will allow you to run them without any additional setup.
That is it. After the initial install, you should be able to continue on by running the zf command.
Go good way to check to see if it't there is to run zf --help
Installing by hand refers to the process of forcing the zf.php and Zend Framework library to work
together when they are placed in non-convential places, or at least, in a place that your system
cannot dispatch from easily (typical of programs in your system PATH).
If you are on a *nix or mac system, you can also create a link from somewhere in your path to
the zf.sh file. If you do this, you do not need to worry about having Zend Framework's library on
your include_path, as the zf.php and zf.sh files will be able to access the library relative to where
they are (meaning the ./bin/ files are ../library/ relative to the Zend Framework library).
There are a number of other options available for setting up the zf.php and library on your system.
These options revolve around setting specific environment variables. These are described in the
later section on "customizing the CLI environement". The environment variables for setting the
zf.php include_path, ZF_INCLUDE_PATH and ZF_INCLUDE_PATH_PREPEND, are the ones
of most interest.
1416
Zend_Tool
zf show version
zf --help
This will give you an overview of the various capabilities of the system. Sometimes, there are
more finite commands than can be run, and to gain more information about these, you might
have to run a more specialized help command. For specialized help, simply replace any of the
elements of the command with a "?". This will tell the help system that you want more information
about what commands can go in place of the question mark. For example:
zf ? controller
The above means "show me all 'actions' for the provider 'controller'"; while the following:
zf show ?
means "show me all providers that support the 'show' action". This works for drilling down into
options as well as you can see in the following examples:
1.2.3. Manifest
This will show what information is in the tooling systems manifest. This is more important for
provider developers than casual users of the tooling system.
zf show manifest
This will create a project in a directory called ./MyProjectName. From this point on, it is important
to note that any subsequent commands on the command line must be issued from within the
project directory you had just created. So, after creation, changing into that directory is required.
1417
Zend_Tool
1.3.2. Project
The module provider allows for the easy creation of a Zend Framework module. A module follows
the hMVC pattern loosely. When creating modules, it will take the same structure used at the
application/ level, and duplicate it inside of the chosen name for your module, inside of the
"modules" directory of the application/ directory without duplicating the modules directory itself.
For example:
This will create a module named Blog at application/modules/Blog, and all of the artifacts that
a module will need.
1.3.3. Controller
The controller provider is responsible for creating (mostly) empty controllers as well as their
corresponding view script directories and files. To utilize it to create an 'Auth' controlller, for
example, execute:
This will create a controller named Auth, specifically it will create a file at application/controllers/
AuthController.php with the AuthController inside. If you wish to create a controller for a module,
use any of the following:
Note: In the first command, 1 is the value for the "includeIndexAction" flag.
1.3.4. Action
To create an action within an existing controller:
1.3.5. View
To create a view outside of the normal controller/action creation, you would use one of the
following:
1.3.6. Model
The model provider is only responsible for creating the proper model files, with the proper name
inside the application folder. For example
1418
Zend_Tool
The above will create a 'Post' model inside of the 'Blog' module.
1.3.7. Form
The form provider is only responsible for creating the proper form file and init() method, with the
proper name inside the application folder. For example:
The above will create a 'Comment' form inside of the 'Blog' module.
1.3.8. DbAdapter
To configure a DbAdapter, you will need to provide the information as a url encoded string. This
string needs to be in quotes on the command line.
• adapter: Pdo_Mysql
• username: test
• password: test
• dbname: test
This assumes you wish to store this information inside of the 'production' space of the application
configuration file. The following will demonstrate an sqlite configuration, in the 'development'
section of the application config file.
1.3.9. DbTable
The DbTable provider is responsible for creating Zend_Db_Table model/data access files for
your application to consume, with the proper class name, and in the proper location in the
application. The two important pieces of information are the DbTable name, and the actual
database table name. For example:
1419
Zend_Tool
The DbTable provider is also capable of creating the proper files by scanning the database
configured with the above DbAdapter provider.
zf create dbtable.from-database
When executing the above, it might make sense to use the pretend / "-p" flag first so that you
can see what would be done, and what tables can be found in the database.
zf -p create dbtable.from-database
1.3.10. Layout
Currently, the only supported action for layouts is simply to enable them will setup the proper keys
in the application.ini file for the application resource to work, and create the proper directories
and layout.phtml file.
zf enable layout
zf --setup storage-directory
zf --setup config-file
• ZF_HOME
1420
Zend_Tool
• search order:
• ZF_STORAGE_DIRECTORY
• search order:
• $homeDirectory/.zf/ directory
• ZF_CONFIG_FILE
• search order:
• ZF_INCLUDE_PATH
• original behavior:
• ZF_INCLUDE_PATH_PREPEND
2. Extending Zend_Tool
2.1. Overview of Zend_Tool
Zend_Tool_Framework is a framework for exposing common functionalities such as the
creation of project scaffolds, code generation, search index generation, and much more.
Functionality may be written and exposed via PHP classes dropped into the PHP include_path,
providing incredible flexibility of implementation. The functionality may then be consumed by
1421
Zend_Tool
• Common interfaces and abstracts that allow developers to create functionality and capabilities
that are dispatchable by tooling clients.
• Base client functionality and a concrete console implementation that connect external tools
and interfaces to the Zend_Tool_Framework. The Console client may be used in CLI
environments such as unix shells and the Windows console.
• "Provider" and "Manifest" interfaces that can be utilized by the tooling system. "Providers"
represent the functional aspect of the framework, and define the actions that tooling clients
may call. "Manifests" act as metadata registries that provide additional context for the various
defined providers.
• An introspective loading system that will scan the environment for providers and determine
what is required to dispatch them.
• A standard set of system providers that allow the system to report what the full capabilities
of the system are as well as provide useful feedback. This also includes a comprehensive
"Help System".
Definitions that you should be aware of through this manual with respect to
Zend_Tool_Framework include:
• Client - The subsystem of Zend_Tool_Framework that exposes an interface such that tooling
clients can connect, query and execute commands.
• Console Client / Command Line Interface / zf.php - The tooling client for the command line.
• Provider - A subsystem and a collection of built-in functionality that the framework exports.
• Manifest - A subsystem for defining, organizing, and disseminating provider requirement data.
1422
Zend_Tool
requests inside the "command line windows", also commonly known as a "terminal" window. This
environment is predominant in the *nix environment, but also has a common implementation in
windows with the cmd.exe, console2 and also with the Cygwin project.
To issue tooling requests via the command line client, you first need to set up the client so that
your system can handle the "zf" command. The command line client, for all intents and purposes,
is the .sh or .bat file that is provided with your Zend Framework distribution. In trunk, it can be
found here: http://framework.zend.com/svn/framework/standard/trunk/bin/.
As you can see, there are 3 files in the /bin/ directory: a zf.php, zf.sh, and zf.bat. The
zf.sh and the zf.bat are the operating system specific client wrappers: zf.sh for the *nix
environment, and zf.bat for the Win32 environment. These client wrappers are responsible
for finding the proper php.exe, finding the zf.php, and passing on the client request. The
zf.php is the responsible for handling understanding your environment, constructing the proper
include_path, and passing what is provided on the command line to the proper library component
for dispatching.
Ultimately, you want to ensure two things to make everything work regardless of the operating
system you are on:
1. zf.sh/zf.bat is reachable from your system path. This is the ability to call zf from anywhere
on your command line, regardless of what your current working directory is.
Note: while the above are the most ideal requirements, you can simply download
Zend Framework and expect it to work as ./path/to/zf.php some command.
The most common setup in the *nix environment, is to copy the zf.sh and zf.php into the
same directory as your PHP binary. This can generally be found in one of the following places:
/usr/bin
/usr/local/bin
/usr/local/ZendServer/bin/
/Applications/ZendServer/bin/
To find out the location of your PHP binary, you can execute 'which php' on the command
line. This will return the location of the PHP binary you will be using to run PHP scripts in this
environment.
The next order of business is to ensure that Zend Framework library is set up correctly inside
of the system PHP include_path. To find out where your include_path is located, you can
execute php -i and look for the include_path variable, or more succinctly, execute php -i |
grep include_path. Once you have found where your include_path is located (this will generally
be something like /usr/lib/php, /usr/share/php, /usr/local/lib/php, or similar),
ensure that the contents of the /library/ directory are put inside your include_path specified
directory.
Once you have done those two things, you should be able to issue a command and get back
the proper response like this:
1423
Zend_Tool
If you do not see this type of output, go back and check your setup to ensure you have all of the
necessary pieces in the proper place.
There are a couple of alternative setups you might want to employ depending on your servers
configuration, your level of access, or for other reasons.
Alternative Setup involves keeping the Zend Framework download together as is, and creating
a link from a PATH location to the zf.sh. What this means is you can place the contents of
the ZendFramework download into a location such as /usr/local/share/ZendFramework,
or more locally like /home/username/lib/ZendFramework, and creating a symbolic link to
the zf.sh.
Assuming you want to put the link inside /usr/local/bin (this could also work for placing the
link inside /home/username/bin/ for example) you would issue a command similar to this:
ln -s /usr/local/share/ZendFramework/bin/zf.sh /usr/local/bin/zf
# OR (for example)
ln -s /home/username/lib/ZendFramework/bin/zf.sh /home/username/bin/zf
This will create a link which you should be able to access globally on the command line.
The most common setup in the Windows Win32 environment, is to copy the zf.bat and zf.php
into the same directory as your PHP binary. This can generally be found in one of the following
places:
1424
Zend_Tool
C:\PHP
C:\Program Files\ZendServer\bin\
C:\WAMP\PHP\bin
You should be able to run php.exe on the command line. If you are not able to, first check the
documentation that came with your PHP distribution, or ensure that the path to php.exe is in
your Windows PATH environment variable.
The next order of business is to ensure that Zend Framework library is set up correctly inside of
the system PHP include_path. To find out where your include_path is located, you can type php
-i and look for the include_path variable, or more succinctly execute php -i | grep include_path
if you have Cygwin setup with grep available. Once you have found where your include_path
is located (this will generally be something like C:\PHP\pear, C:\PHP\share, C:\Program
%20Files\ZendServer\share or similar), ensure that the contents of the library/ directory
are put inside your include_path specified directory.
Once you have done those two things, you should be able to issue a command and get back
the proper response like this:
If you do not see this type of output, go back and check your setup to ensure you have all of the
necessary pieces in the proper place.
There are a couple of alternative setups you might want to employ depending on your server's
configuration, your level of access, or for other reasons.
1425
Zend_Tool
Alternative Setup involves keeping the Zend Framework download together as is, and altering
both your system PATH as well as the php.ini file. In your user's environment, make sure to
add C:\Path\To\ZendFramework\bin, so that your zf.bat file is executable. Also, alter the
php.ini file to ensure that C:\Path\To\ZendFramework\library is in your include_path.
If for some reason you do not want Zend Framework library inside your include_path, there is
another option. There are two special environment variables that zf.php will utilize to determine
the location of your Zend Framework installation.
In general, a provider, on its own, is nothing more than the shell for a developer to bundle up
some capabilities they wish to dispatch with the command line (or other) clients. It is an analogue
to what a "controller" is inside of your MVC application.
By default Zend Tool uses the BasicLoader to find all the providers that you
can run. It recursivly iterates all include path directories and opens all files
that end with "Manifest.php" or "Provider.php". All classes in these files are
inspected if they implement either Zend_Tool_Framework_Provider_Interface or
Zend_Tool_Framework_Manifest_ProviderManifestable. Instances of the provider
interface make up for the real functionality and all their public methods are accessible as provider
actions. The ProviderManifestable interface however requires the implementation of a method
getProviders() which returns an array of instantiated provider interface instances.
The following naming rules apply on how you can access the providers that were found by the
IncludePathLoader:
• The last part of your classname split by underscore is used for the provider name, e.g.
"My_Provider_Hello" leads to your provider being accessible by the name "hello".
• If your provider has a method getName() it will be used instead of the previous method to
determine the name.
• If your provider has "Provider" as prefix, e.g. it is called My_HelloProvider it will be stripped
from the name so that the provider will be called "hello".
The IncludePathLoader does not follow symlinks, that means you cannot link
provider functionality into your include paths, they have to be physically present
in the include paths.
1426
Zend_Tool
You can expose your providers to Zend Tool by offering a manifest with
a special filename ending with "Manifest.php". A Provider Manifest is an
implementation of the Zend_Tool_Framework_Manifest_ProviderManifestable and requires
the getProviders() method to return an array of instantiated providers. In anticipation
of our first own provider My_Component_HelloProvider we will create the following
manifest:
class My_Component_Manifest
implements Zend_Tool_Framework_Manifest_ProviderManifestable
{
public function getProviders()
{
return array(
new My_Component_HelloProvider()
);
}
}
As an example, if a developer wants to add the capability of showing the version of a datafile that
his 3rd party component is working from, there is only one class the developer would need to
implement. Assuming the component is called My_Component, he would create a class named
My_Component_HelloProvider in a file named HelloProvider.php somewhere on the
include_path. This class would implement Zend_Tool_Framework_Provider_Interface,
and the body of this file would only have to look like the following:
class My_Component_HelloProvider
implements Zend_Tool_Framework_Provider_Interface
{
public function say()
{
echo 'Hello from my provider!';
}
}
Given that code above, and assuming the developer wishes to access this functionality through
the console client, the call would look like this:
% zf say hello
Hello from my provider!
As discussed in the architecture section Zend Tool allows to hook different clients for using your
Zend Tool providers. To keep compliant with different clients you should use the response object
to return messages from your providers instead of using echo() or a similiar output mechanism.
Rewritting our hello provider with this knowledge it looks like:
class My_Component_HelloProvider
extends Zend_Tool_Framework_Provider_Abstract
{
public function say()
1427
Zend_Tool
{
$this->_registry->getResponse
->appendContent("Hello from my provider!");
}
}
The above "Hello World" example is great for simple commands, but what about something more
advanced? As your scripting and tooling needs grow, you might find that you need the ability
to accept variables. Much like function signatures have parameters, your tooling requests can
also accept parameters.
Just as each tooling request can be isolated to a method within a class, the parameters of a
tooling request can also be isolated in a very well known place. Parameters of the action methods
of a provider can include the same parameters you want your client to utilize when calling that
provider and action combination. For example, if you wanted to accept a name in the above
example, you would probably do this in OO code:
class My_Component_HelloProvider
implements Zend_Tool_Framework_Provider_Interface
{
public function say($name = 'Ralph')
{
echo 'Hello' . $name . ', from my provider!';
}
}
The above example can then be called via the command line zf say hello Joe. "Joe" will be
supplied to the provider as a parameter of the method call. Also note, as you see that the
parameter is optional, that means it is also optional on the command line, so that zf say hello
will still work, and default to the name "Ralph".
There are cases when the workflow of your provider requires to prompt the user for input. This
can be done by requesting the client to ask for more the required input by calling:
class My_Component_HelloProvider
extends Zend_Tool_Framework_Provider_Abstract
{
public function say($name = 'Ralph')
{
$nameResponse = $this->_registry
->getClient()
->promptInteractiveInput("Whats your name?");
$name = $name->getContent();
1428
Zend_Tool
This command throws an exception if the current client is not able to handle interactive requests.
In case of the default Console Client however you will be asked to enter the name.
Another interesting feature you might wish to implement is pretendability. Pretendabilty is the
ability for your provider to "pretend" as if it is doing the requested action and provider combination
and give the user as much information about what it would do without actually doing it. This
might be an important notion when doing heavy database or filesystem modifications that the
user might not otherwise want to do.
Pretendability is easy to implement. There are two parts to this feature: 1) marking the provider
as having the ability to "pretend", and 2) checking the request to ensure the current request was
indeed asked to be "pretended". This feature is demonstrated in the code sample below.
class My_Component_HelloProvider
extends Zend_Tool_Framework_Provider_Abstract
implements Zend_Tool_Framework_Provider_Pretendable
{
public function say($name = 'Ralph')
{
if ($this->_registry->getRequest()->isPretend()) {
echo 'I would say hello to ' . $name . '.';
} else {
echo 'Hello' . $name . ', from my provider!';
}
}
}
You can also run your provider actions in "verbose" or "debug" modes. The semantics in regard
to this actions have to be implemented by you in the context of your provider. You can access
debug or verbose modes with:
class My_Component_HelloProvider
implements Zend_Tool_Framework_Provider_Interface
{
public function say($name = 'Ralph')
{
if($this->_registry->getRequest()->isVerbose()) {
echo "Hello::say has been called\n";
}
if($this->_registry->getRequest()->isDebug()) {
syslog(LOG_INFO, "Hello::say has been called\n");
}
}
}
Using the Enviroment variable ZF_CONFIG_FILE or the .zf.ini in your home directory
you can inject configuration parameters into any Zend Tool provider. Access to this
1429
Zend_Tool
configuration is available via the registry that is passed to your provider if you extend
Zend_Tool_Framework_Provider_Abstract.
class My_Component_HelloProvider
extends Zend_Tool_Framework_Provider_Abstract
{
public function say()
{
$username = $this->_registry->getConfig()->username;
if(!empty($username)) {
echo "Hello $username!";
} else {
echo "Hello!";
}
}
}
The storage allows to save arbitrary data for later reference. This can be useful for batch
processing tasks or for re-runs of your tasks. You can access the storage in a similar way like
the configuration:
class My_Component_HelloProvider
extends Zend_Tool_Framework_Provider_Abstract
{
public function say()
{
$aValue = $this->_registry->getStorage()->get("myUsername");
echo "Hello $aValue!";
}
}
class Zend_Tool_Framework_Client_Storage
{
public function setAdapter($adapter);
public function isEnabled();
public function put($name, $value);
public function get($name, $defaultValue=null);
public function has($name);
public function remove($name);
public function getStreamUri($name);
}
When designing your providers that are config or storage aware remember to
check if the required user-config or storage keys really exist for a user. You won't
run into fatal errors when none of these are provided though, since empty ones
are created upon request.
1430
Zend_Tool
Another important point to understand about projects is that typically, resources are organized
in a hierarchical fashion. With that in mind, Zend_Tool_Project is capable of serializing the
current project into a internal representation that allows it to keep track of not only what resources
are part of a project at any given time, but also where they are in relation to one another.
class My_Component_HelloProvider
extends Zend_Tool_Project_Provider_Abstract
{
public function say()
{
$profile = $this->_loadExistingProfile();
$this->_storeProfile();
}
}
1431
Zend_Tool_Framework
1. Introduction
Zend_Tool_Framework is a framework for exposing common functionalities such as the
creation of project scaffolds, code generation, search index generation, and much more.
Functionality may be written and exposed via PHP classes dropped into the PHP include_path,
providing incredible flexibility of implementation. The functionality may then be consumed by
writing implementation and/or protocol-specific clients -- such as console clients, XML-RPC,
SOAP, and much more.
• Common interfaces and abstracts that allow developers to create functionality and capabilities
that are dispatchable by tooling clients.
• Base client functionality and a concrete console implementation that connect external tools
and interfaces to the Zend_Tool_Framework. The Console client may be used in CLI
environments such as unix shells and the Windows console.
• "Provider" and "Manifest" interfaces that can be utilized by the tooling system. "Providers"
represent the functional aspect of the framework, and define the actions that tooling clients
may call. "Manifests" act as metadata registries that provide additional context for the various
defined providers.
• An introspective loading system that will scan the environment for providers and determine
what is required to dispatch them.
• A standard set of system providers that allow the system to report what the full capabilities
of the system are as well as provide useful feedback. This also includes a comprehensive
"Help System".
Definitions that you should be aware of through this manual with respect to
Zend_Tool_Framework include:
• Client - The subsystem of Zend_Tool_Framework that exposes an interface such that tooling
clients can connect, query and execute commands.
• Console Client / Command Line Interface / zf.php - The tooling client for the command line.
• Provider - A subsystem and a collection of built-in functionality that the framework exports.
• Manifest - A subsystem for defining, organizing, and disseminating provider requirement data.
1432
Zend_Tool_Framework
requests inside the "command line windows", also commonly known as a "terminal" window. This
environment is predominant in the *nix environment, but also has a common implementation in
windows with the cmd.exe, console2 and also with the Cygwin project.
As you can see, there are 3 files in the /bin/ directory: a zf.php, zf.sh, and zf.bat. The
zf.sh and the zf.bat are the operating system specific client wrappers: zf.sh for the *nix
environment, and zf.bat for the Win32 environment. These client wrappers are responsible
for finding the proper php.exe, finding the zf.php, and passing on the client request. The
zf.php is the responsible for handling understanding your environment, constructing the proper
include_path, and passing what is provided on the command line to the proper library component
for dispatching.
Ultimately, you want to ensure two things to make everything work regardless of the operating
system you are on:
1. zf.sh/zf.bat is reachable from your system path. This is the ability to call zf from anywhere
on your command line, regardless of what your current working directory is.
Note: while the above are the most ideal requirements, you can simply download
Zend Framework and expect it to work as ./path/to/zf.php some command.
/usr/bin
/usr/local/bin
/usr/local/ZendServer/bin/
/Applications/ZendServer/bin/
To find out the location of your PHP binary, you can execute 'which php' on the command
line. This will return the location of the PHP binary you will be using to run PHP scripts in this
environment.
The next order of business is to ensure that Zend Framework library is set up correctly inside
of the system PHP include_path. To find out where your include_path is located, you can
execute php -i and look for the include_path variable, or more succinctly, execute php -i |
grep include_path. Once you have found where your include_path is located (this will generally
be something like /usr/lib/php, /usr/share/php, /usr/local/lib/php, or similar),
ensure that the contents of the /library/ directory are put inside your include_path specified
directory.
Once you have done those two things, you should be able to issue a command and get back
the proper response like this:
1433
Zend_Tool_Framework
If you do not see this type of output, go back and check your setup to ensure you have all of the
necessary pieces in the proper place.
There are a couple of alternative setups you might want to employ depending on your servers
configuration, your level of access, or for other reasons.
Alternative Setup involves keeping the Zend Framework download together as is, and creating
a link from a PATH location to the zf.sh. What this means is you can place the contents of
the ZendFramework download into a location such as /usr/local/share/ZendFramework,
or more locally like /home/username/lib/ZendFramework, and creating a symbolic link to
the zf.sh.
Assuming you want to put the link inside /usr/local/bin (this could also work for placing the
link inside /home/username/bin/ for example) you would issue a command similar to this:
ln -s /usr/local/share/ZendFramework/bin/zf.sh /usr/local/bin/zf
# OR (for example)
ln -s /home/username/lib/ZendFramework/bin/zf.sh /home/username/bin/zf
This will create a link which you should be able to access globally on the command line.
1434
Zend_Tool_Framework
C:\PHP
C:\Program Files\ZendServer\bin\
C:\WAMP\PHP\bin
You should be able to run php.exe on the command line. If you are not able to, first check the
documentation that came with your PHP distribution, or ensure that the path to php.exe is in
your Windows PATH environment variable.
The next order of business is to ensure that Zend Framework library is set up correctly inside of
the system PHP include_path. To find out where your include_path is located, you can type php
-i and look for the include_path variable, or more succinctly execute php -i | grep include_path
if you have Cygwin setup with grep available. Once you have found where your include_path
is located (this will generally be something like C:\PHP\pear, C:\PHP\share, C:\Program
%20Files\ZendServer\share or similar), ensure that the contents of the library/ directory
are put inside your include_path specified directory.
Once you have done those two things, you should be able to issue a command and get back
the proper response like this:
1435
Zend_Tool_Framework
If you do not see this type of output, go back and check your setup to ensure you have all of the
necessary pieces in the proper place.
There are a couple of alternative setups you might want to employ depending on your server's
configuration, your level of access, or for other reasons.
Alternative Setup involves keeping the Zend Framework download together as is, and altering
both your system PATH as well as the php.ini file. In your user's environment, make sure to
add C:\Path\To\ZendFramework\bin, so that your zf.bat file is executable. Also, alter the
php.ini file to ensure that C:\Path\To\ZendFramework\library is in your include_path.
1436
Zend_Tool_Framework
3. Architecture
3.1. Registry
Because providers and manifests may come from anywhere in the include_path, a registry is
provided to simplify access to the various pieces of the toolchain. This registry is injected into
registry-aware components, which may then pull dependencies from them as necessary. Most
dependencies registered with the registry will be sub-component-specific repositories.
1437
Zend_Tool_Framework
interface Zend_Tool_Framework_Registry_Interface
{
public function setClient(Zend_Tool_Framework_Client_Abstract $client);
public function getClient();
public function setLoader(Zend_Tool_Framework_Loader_Abstract $loader);
public function getLoader();
public function setActionRepository(
Zend_Tool_Framework_Action_Repository $actionRepository
);
public function getActionRepository();
public function setProviderRepository(
Zend_Tool_Framework_Provider_Repository $providerRepository
);
public function getProviderRepository();
public function setManifestRepository(
Zend_Tool_Framework_Manifest_Repository $manifestRepository
);
public function getManifestRepository();
public function setRequest(Zend_Tool_Framework_Client_Request $request);
public function getRequest();
public function setResponse(Zend_Tool_Framework_Client_Response $response);
public function getResponse();
}
The various objects the registry manages will be discussed in their appropriate sections.
interface Zend_Tool_Framework_Registry_EnabledInterface
{
public function setRegistry(
Zend_Tool_Framework_Registry_Interface $registry
);
}
3.2. Providers
Zend_Tool_Framework_Provider represents the functional or "capability" aspect of the
framework. Fundamentally, Zend_Tool_Framework_Provider will provide the interfaces
necessary to produce "providers", or bits of tooling functionality that can be called and used inside
the Zend_Tool_Framework toolchain. The simplistic nature of implementing this provider
interface allows the developer a "one-stop-shop" of adding functionality or capabilities to
Zend_Tool_Framework.
The provider interface is an empty interface and enforces no methods (this is the Marker Interface
pattern):
interface Zend_Tool_Framework_Provider_Interface
{}
Or, if you wish, you can implement the base (or abstract) Provider which will give you access to
the Zend_Tool_Framework_Registry:
1438
Zend_Tool_Framework
3.3. Loaders
The purpose of a Loader is to find Providers and Manifest files that contain
classes which implement either Zend_Tool_Framework_Provider_Interface or
Zend_Tool_Framework_Manifest_Interface. Once these files are found by a loader,
providers are loaded into the Provider Repository and manifest metadata is loaded into the
Manifest Repository.
The _getFiles() method should return an array of files (absolute paths). The built-in
loader supplied with Zend Framework is called the IncludePath loader. By default, the Tooling
framework will use an include_path based loader to find files that might include Providers or
Manifest Metadata objects. Zend_Tool_Framework_Loader_IncludePathLoader, without
any other options, will search for files inside the include path that end in Mainfest.php,
Tool.php or Provider.php. Once found, they will be tested (by the load() method of
the Zend_Tool_Framework_Loader_Abstract) to determine if they implement any of the
supported interfaces. If they do, an instance of the found class is instantiated, and it is appended
to the proper repository.
class Zend_Tool_Framework_Loader_IncludePathLoader
extends Zend_Tool_Framework_Loader_Abstract
{
As you can see, the IncludePath loader will search all include_paths for the files that match the
$_filterAcceptFilePattern and not match the $_filterDenyDirectoryPattern.
1439
Zend_Tool_Framework
3.4. Manifests
In short, the Manifest shall contain specific or arbitrary metadata that is useful to any provider or
client, as well as be responsible for loading any additional providers into the provider repository.
interface Zend_Tool_Framework_Manifest_Interface
{
public function getMetadata();
}
Metadata objects are loaded (by a loader defined below) into the Manifest Repository
(Zend_Tool_Framework_Manifest_Repository). Manifests will be processed after all
Providers have been found to be loaded into the provider repository. This shall allow Manifests
to create Metadata objects based on what is currently inside the provider repository.
There are a few different metadata classes that can be used to describe metadata. The
Zend_Tool_Framework_Manifest_Metadata is the base metadata object. As you can see
by the following code snippet, the base metadata class is fairly lightweight and abstract in nature:
class Zend_Tool_Framework_Metadata_Basic
{
There are other built in metadata classes as well for describing more specialized metadata:
ActionMetadata and ProviderMetadata. These classes will help you describe in more
detail metadata that is specific to either actions or providers, and the reference is expected to be
a reference to an action or a provider respectively. These classes are described in the following
code snippet.
class Zend_Tool_Framework_Manifest_ActionMetadata
extends Zend_Tool_Framework_Manifest_Metadata
{
1440
Zend_Tool_Framework
class Zend_Tool_Framework_Manifest_ProviderMetadata
extends Zend_Tool_Framework_Manifest_Metadata
{
'Type' in these classes is used to describe the type of metadata the object is responsible for. In
the cases of the ActionMetadata, the type would be 'Action', and conversely in the case of the
ProviderMetadata the type is 'Provider'. These metadata types will also include additional
structured information about both the "thing" they are describing as well as the object (the
getReference()) they are referencing with this new metadata.
In order to create your own metadata type, all one must do is extend the base
Zend_Tool_Framework_Manifest_Metadata class and return these new metadata objects
via a local Manifest class or object. These user based classes will live in the Manifest Repository
Once these metadata objects are in the repository, there are then two different methods that can
be used in order to search for them in the repository.
class Zend_Tool_Framework_Manifest_Repository
{
/**
* To use this method to search, $searchProperties should contain the names
* and values of the key/value pairs you would like to match within the
* manifest.
*
* For Example:
* $manifestRepository->findMetadatas(array(
* 'action' => 'Foo',
* 'name' => 'cliActionName'
* ));
*
* Will find any metadata objects that have a key with name 'action' value
* of 'Foo', AND a key named 'name' value of 'cliActionName'
*
* Note: to either exclude or include name/value pairs that exist in the
* search criteria but do not appear in the object, pass a bool value to
* $includeNonExistentProperties
*/
public function findMetadatas(Array $searchProperties = array(),
$includeNonExistentProperties = true);
/**
* The following will return exactly one of the matching search criteria,
* regardless of how many have been returned. First one in the manifest is
* what will be returned.
*/
public function findMetadata(Array $searchProperties = array(),
$includeNonExistentProperties = true)
{
1441
Zend_Tool_Framework
$metadatas = $this->getMetadatas($searchProperties,
$includeNonExistentProperties);
return array_shift($metadatas);
}
}
Looking at the search methods above, the signatures allow for extremely flexible searching. In
order to find a metadata object, simply pass in an array of matching constraints via an array. If the
data is accessible through the Property accessor (the getSomething() methods implemented
on the metadata object), then it will be passed back to the user as a "found" metadata object.
3.5. Clients
Clients are the interface which bridges a user or external tool into the Zend_Tool_Framework
system. Clients can come in all shapes and sizes: RPC endpoints, Command Line Interface, or
even a web interface. Zend_Tool has implemented the command line interface as the default
interface for interacting with the Zend_Tool_Framework system.
To implement a client, one would need to extend the following abstract class:
/**
* This method should be implemented by the client implementation to parse
* out and set up the request objects action, provider and parameter
* information.
*/
abstract protected function _preDispatch();
/**
* This method should be implemented by the client implementation to take
* the output of the response object and return it (in an client specific
* way) back to the Tooling Client.
*
* (not required, but suggested)
*/
abstract protected function _postDispatch();
}
As you can see, there 1 method is required to fulfill the needs of a client (two others suggested),
the initialization, prehandling and post handling. For a more in depth study of how the command
line client works, please see the source code.
1442
Zend_Tool_Framework
The following naming rules apply on how you can access the providers that were found by the
IncludePathLoader:
• The last part of your classname split by underscore is used for the provider name, e.g.
"My_Provider_Hello" leads to your provider being accessible by the name "hello".
• If your provider has a method getName() it will be used instead of the previous method to
determine the name.
• If your provider has "Provider" as prefix, e.g. it is called My_HelloProvider it will be stripped
from the name so that the provider will be called "hello".
The IncludePathLoader does not follow symlinks, that means you cannot link
provider functionality into your include paths, they have to be physically present
in the include paths.
You can expose your providers to Zend Tool by offering a manifest with
a special filename ending with "Manifest.php". A Provider Manifest is an
implementation of the Zend_Tool_Framework_Manifest_ProviderManifestable and requires
the getProviders() method to return an array of instantiated providers. In anticipation
of our first own provider My_Component_HelloProvider we will create the following
manifest:
class My_Component_Manifest
implements Zend_Tool_Framework_Manifest_ProviderManifestable
{
public function getProviders()
{
return array(
new My_Component_HelloProvider()
);
}
}
1443
Zend_Tool_Framework
class My_Component_HelloProvider
implements Zend_Tool_Framework_Provider_Interface
{
public function say()
{
echo 'Hello from my provider!';
}
}
Given that code above, and assuming the developer wishes to access this functionality through
the console client, the call would look like this:
% zf say hello
Hello from my provider!
class My_Component_HelloProvider
extends Zend_Tool_Framework_Provider_Abstract
{
public function say()
{
$this->_registry->getResponse
->appendContent("Hello from my provider!");
}
}
Just as each tooling request can be isolated to a method within a class, the parameters of a
tooling request can also be isolated in a very well known place. Parameters of the action methods
of a provider can include the same parameters you want your client to utilize when calling that
provider and action combination. For example, if you wanted to accept a name in the above
example, you would probably do this in OO code:
class My_Component_HelloProvider
implements Zend_Tool_Framework_Provider_Interface
{
public function say($name = 'Ralph')
{
echo 'Hello' . $name . ', from my provider!';
1444
Zend_Tool_Framework
}
}
The above example can then be called via the command line zf say hello Joe. "Joe" will be
supplied to the provider as a parameter of the method call. Also note, as you see that the
parameter is optional, that means it is also optional on the command line, so that zf say hello
will still work, and default to the name "Ralph".
class My_Component_HelloProvider
extends Zend_Tool_Framework_Provider_Abstract
{
public function say($name = 'Ralph')
{
$nameResponse = $this->_registry
->getClient()
->promptInteractiveInput("Whats your name?");
$name = $name->getContent();
This command throws an exception if the current client is not able to handle interactive requests.
In case of the default Console Client however you will be asked to enter the name.
Pretendability is easy to implement. There are two parts to this feature: 1) marking the provider
as having the ability to "pretend", and 2) checking the request to ensure the current request was
indeed asked to be "pretended". This feature is demonstrated in the code sample below.
class My_Component_HelloProvider
extends Zend_Tool_Framework_Provider_Abstract
implements Zend_Tool_Framework_Provider_Pretendable
{
public function say($name = 'Ralph')
{
if ($this->_registry->getRequest()->isPretend()) {
echo 'I would say hello to ' . $name . '.';
} else {
echo 'Hello' . $name . ', from my provider!';
}
}
}
1445
Zend_Tool_Framework
class My_Component_HelloProvider
implements Zend_Tool_Framework_Provider_Interface
{
public function say($name = 'Ralph')
{
if($this->_registry->getRequest()->isVerbose()) {
echo "Hello::say has been called\n";
}
if($this->_registry->getRequest()->isDebug()) {
syslog(LOG_INFO, "Hello::say has been called\n");
}
}
}
class My_Component_HelloProvider
extends Zend_Tool_Framework_Provider_Abstract
{
public function say()
{
$username = $this->_registry->getConfig()->username;
if(!empty($username)) {
echo "Hello $username!";
} else {
echo "Hello!";
}
}
}
The storage allows to save arbitrary data for later reference. This can be useful for batch
processing tasks or for re-runs of your tasks. You can access the storage in a similar way like
the configuration:
class My_Component_HelloProvider
extends Zend_Tool_Framework_Provider_Abstract
{
public function say()
{
1446
Zend_Tool_Framework
$aValue = $this->_registry->getStorage()->get("myUsername");
echo "Hello $aValue!";
}
}
class Zend_Tool_Framework_Client_Storage
{
public function setAdapter($adapter);
public function isEnabled();
public function put($name, $value);
public function get($name, $defaultValue=null);
public function has($name);
public function remove($name);
public function getStreamUri($name);
}
When designing your providers that are config or storage aware remember to
check if the required user-config or storage keys really exist for a user. You won't
run into fatal errors when none of these are provided though, since empty ones
are created upon request.
1447
Zend_Tool_Framework
machine. These configuration values and files can be used by providers to extend functionality,
customize functionality, or any other reasons a provider sees fit.
The primary purpose, and the purpose most immediately used by existing providers is to allow
developers to customize the way the "out of the box" providers do work.
One of the more commonly requested features is to be able to provide custom project profiles to
Zend_Tool_Project's Project Provider. This would allow developers to store a custom profile
in a special place that can be used repeatedly by the Zend_Tool system in order to build custom
profiles. Another commonly requested feature is to be able to configure the behavior of providers
with a configuration setting. In order to achieve this, not only do we have to have a Zend_Tool
configuration file, but we also have to have a place to find this configuration file.
On *nix-based machines, PHP will be populated with an environment variable named HOME with
a path to the current users home directory. Typically, this path will be very similar to /home/
myusername.
If either a home directory cannot be found, or you wish to change the location of where
Zend_Tool_Framework Console Client finds the home directory, you can provide an
environment variable named ZF_HOME to specify where to find the home directory.
Assuming the home directory has been found (here noted as $HOME), the Console Client will
then look for the local storage directory in $HOME/.zf/. If found, it will set the local storage
directory to this location.
If the directory cannot be found, or the developer wishes to override this location, that can be
done by setting an environment variable. Regardless if $HOME has been previously set or not,
the developer may supply the environment variable ZF_STORAGE_DIR.
Once the path to a local storage directory is found, the directory must exist for it to be passed
into the Zend_Tool_Framework runtime, as it will not be created for you.
Assuming the home directory has been found (here noted as $HOME), the Console Client will
then attempt to look for the existence of a configuration file located at $HOME/.zf.ini. This
file, if found, will be used as the configuration file for Zend_Tool_Framework.
1448
Zend_Tool_Framework
If that location does not exist, but a local storage directory does, then the Console Client will
then attempt to locate the configuration file within the local storage directory. Assuming the local
storage directory exists in $LOCAL_STORAGE, then if a file exists as $LOCAL_STORAGE/zf.ini,
it will be found by the Console Client and utilized as the Zend_Tool_Framework configuration
file.
If the file cannot be autodiscovered or the developer wishes to specify the location of location
of the configuration file, the developer can do so by setting an environment variable. If the
environment variable ZF_CONFIG_FILE is set, then its value will be used as the location of
the configuration file to use with the Console Client. The ZF_CONFIG_FILE can point to any
Zend_Config readable INI, XML or PHP File.
If the file does not exist in either the autodiscovered or the provided location, it will not be used
as Zend_Tool_Framework does not attempt to create the file automatically.
project.profile = some/path/to/some-directory
The only reserved ini prefix is the value "php". The "php" prefix to values will be reserved to store
names and values of runtime settable php values, such as include_path or error_reporting. To
override the include_path and error_reporting with an ini value, a developer would set:
php.include_path = "/path/to/includes1:/path/to/includes2"
php.error_reporting = 1
The reserved prefix "php" only works with INI files. You can't set PHP INI values
with PHP or XML config.
1449
Zend_Tool_Project
1. Introduction
Zend_Tool_Project est construit à partir de Zend_Tool_Framework permettant ainsi
d'étendre ses capacités et de gérer un projet. En général, un projet est un effort prévu ou
une initiative. Dans le monde de l'informatique, les projets sont généralement une collection de
ressources. Ces ressources peuvent être des fichiers, des répertoires, des bases de données,
des schémas, des images, des styles, et parfois plus.
Ce même concept s'applique aux projets Zend Framework. Dans les projets Zend Framework,
vous avez des contrôleurs, des actions, des vues, des modèles, des bases de données et ainsi de
suite. En terme de Zend_Tool, nous avons besoin d'un moyen de pister ce type de ressources -
c'est-à-dire Zend_Tool_Project.
Un autre point important à comprendre concernant les projets est que typiquement, les
ressources sont organisées de manière hiérarchique. Avec cela à l'esprit, Zend_Tool_Project
est capable de construire le projet en cours dans une représentation interne qui lui permet de
maintenir non seulement quelles ressources de font partie d'un projet à un moment donné, mais
également où elles sont les unes par rapport aux autres.
2. Créer un projet
Pour démarrer avec Zend_Tool_Project, vous devez simplement créer un projet. La création
d'un projet est simple : allez où vous le souhaitez dans votre système de fichiers, créez un
dossier, allez dans le dossier créé, ensuite exécutez les commandes suivantes :
La table suivante décrit les fonctionnalités des fournisseurs qui sont disponibles. Comme vous
pouvez le voir, il existe un fournisseur "Project". Le fournisseur "Project" possède deux actions,
et avec ces actions un certain nombre d'options qui peuvent modifier le comportement de l'action
et du fournisseur.
1450
Zend_Tool_Project
3. Fournisseurs de Zend_Tool_Project
Ci-dessous, vous trouverez un tableau de tous les fournisseurs embarqués avec
Zend_Tool_Project.
1451
Zend_Translate
1. Introduction
Zend_Translate est la solution de Zend Framework pour des applications multilingues.
Dans des applications multilingues, le contenu doit être traduit en plusieurs langues et l'affichage
du contenu dépend de la langue de l'utilisateur. PHP offre déjà plusieurs manières de manipuler
de tels problèmes, toutefois la solution PHP a quelques problèmes :
• API contradictoire : Il n'y a pas d'API unique pour les différents formats de source. L'utilisation
du gettext par exemple est très compliquée.
• PHP supporte seulement gettext et les tableaux natifs : PHP lui-même offre seulement
le support des tableaux ou du gettext. Tous autres formats de source doivent être codés
manuellement, parce qu'il n'y a aucun support native.
• Pas de détection de la langue par défaut : La langue par défaut de l'utilisateur ne peut pas
être détectée sans une connaissance plus approfondie des différents navigateurs Web.
• Gettext n'est pas "thread-safe" : La bibliothèque gettext de PHP n'est pas "thread safe", et
elle ne devrait pas être employée dans un environnement multi-threading. C'est dû à des
problèmes de gettext lui-même, pas de PHP, mais c'est un problème existant.
Zend_Translate n'a pas les problèmes ci-dessus. C'est pourquoi nous recommandons
d'employer Zend_Translate au lieu des fonctions natives de PHP. Les avantages de
Zend_Translate sont :
• API générique et facile : L'API de Zend_Translate est très simple et exige seulement une
poignée de fonctions. Ainsi il est facile d'apprendre et facile à maintenir. Tous les formats de
source sont manipulés la même manière, ainsi si le format de vos fichiers source changent de
Gettext en TMX, vous devez seulement changer une ligne de code pour indiquer l'adaptateur
de stockage.
1452
Zend_Translate
Les sections suivantes vous guident par chacune des quatre étapes. Lisez les pages suivantes
pour créer votre propre application Web multilingue.
1453
Zend_Translate
Quand vous utilisez des sources basées sur le format XML comme TMX ou XLIFF
vous devez définir l'encodage dans l'en-tête des fichiers XML, car tout fichier
XML sans définition d'encodage sera traité par défaut en UTF-8 par un analyseur
XML. Vous devez aussi prendre en compte que les encodages des fichiers XML
sont limités aux encodages supportés par PHP, c'est-à-dire UTF-8, ISO-8859-1
and US-ASCII.
2.1.1. Zend_Translate_Adapter_Array
L'adaptateur de type tableau est l'adaptateur qui est le plus simple à utiliser pour les
programmeurs. Mais quand vous avez de nombreuses chaînes de traduction ou beaucoup de
langues vous devriez penser à un autre adaptateur. Par exemple, si vous avez 5000 chaînes de
traduction, l'adaptateur tableau n'est probablement pas le choix le plus approprié pour vous.
Vous devriez seulement utiliser cet adaptateur pour de petits sites avec quelques langues, et si
vous (ou votre équipe de programmeur) créez les traductions vous-même.
2.1.2. Zend_Translate_Adapter_Csv
L'adaptateur Csv est l'adaptateur qui est le plus simple à utiliser pour les clients. Les fichiers CSV
sont lisibles par les éditeurs de texte standard, mais souvent les éditeurs de texte ne supportent
pas les jeux de caractères utf8.
Vous devriez utiliser cet adaptateur seulement si votre client veut faire les traductions lui-même.
Prenez garde que l'adaptateur Csv a des problèmes quand vos fichiers Csv ont
un encodage différent que celui de votre environnement. Ceci est du à un bug
de PHP lui-même qui ne sera pas corrigé avant la PHP 6.0 (http://bugs.php.net/
bug.php?id=38471). Vous devez donc faire attention que l'adaptateur Csv ne
gère pas la locale à cause d'une restrictions PHP.
2.1.3. Zend_Translate_Adapter_Gettext
L'adaptateur Gettext est l'adaptateur qui est utilisé le plus souvent. Gettext est un format de
source de traduction qui a été présenté par GNU, et est maintenant employé dans le monde
entier. Il n'est pas lisible pour l'homme, mais il y a plusieurs outils gratuiciels (par exemple,
POEdit), qui sont très utiles. L'adaptateur Zend_Translate_Gettext n'est pas implémenté
en utilisant l'extension gettext de PHP. Vous pouvez utiliser l'adaptateur Gettext même si vous
1454
Zend_Translate
n'avez pas installer l'extension gettext de PHP. En outre l'adaptateur est "thread-safe" alors que
l'extension gettext de PHP ne l'est pas actuellement.
La plupart des personnes utiliseront cet adaptateur. Avec les outils disponibles, la traduction
professionnelle est très simple. Mais les données de gettext sont stockées dans un format
compréhensible par une machine, qui n'est pas lisible sans outils.
2.1.4. Zend_Translate_Adapter_Ini
L'adaptateur Ini est un adaptateur qui peut être directement utiliser par les clients. Les fichiers INI
sont lisibles par les éditeurs de texte standard, mais souvent les éditeurs de texte ne supportent
pas les jeux de caractères utf8.
Vous devriez utiliser cet adaptateur seulement si votre client veut faire les traductions lui-même.
N'utilisez pas cet adaptateur comme source de traduction générique.
2.1.5. Zend_Translate_Adapter_Tbx
L'adaptateur Tbx est un adaptateur qui sera utilisé par les clients qui utilisent déjà le format
TBX pour leur système de traduction interne. Tbx n'est pas un format de traduction standard,
mais plus une collection de chaînes de caractère sources déjà traduites et pré-traduites. Quand
vous utilisez cet adaptateur vous devez être sûrs que toute votre chaîne de caractère source
nécessaire est traduite. TBX est un fichier basé sur le format XML et un format complètement
nouveau. XML des fichiers sont lisibles par l'homme, mais l'analyse syntaxique n'est pas aussi
rapide qu'avec des fichiers gettext.
Cet adaptateur est parfait pour les sociétés dont les fichiers source pré-traduits existent déjà.
Les fichiers sont lisibles par l'homme et sont indépendants de système.
2.1.6. Zend_Translate_Adapter_Tmx
L'adaptateur Tmx est l'adaptateur qui sera employé par la plupart des clients qui ont des
systèmes multiples qui emploient la même source de traduction, ou quand la source de traduction
doit être indépendante du système. TMX est un format basé sur le format XML, qui est annoncé
pour être le prochain standard industriel. Les fichiers de XML sont lisibles par l'homme, mais
l'analyse n'est pas aussi rapide qu'avec des fichiers gettext.
La plupart des moyennes à grandes entreprises utilisent cet adaptateur. Les fichiers sont lisibles
par l'homme et sont indépendants du système.
2.1.7. Zend_Translate_Adapter_Qt
L'adaptateur Qt est destiné à tous les clients qui ont des fichiers TS faits par QtLinguist
comme source de traduction. QT est un fichier basé sur le format XML. Les fichiers XML sont
humainement lisible, mais l'analyse syntaxique n'est pas si rapide qu'avec des fichiers gettext.
Plusieurs grands acteurs ont construit leur logiciel sur le framework QT. Les fichiers sont lisibles
par l'homme et indépendants du système.
1455
Zend_Translate
2.1.8. Zend_Translate_Adapter_Xliff
L'adaptateur Xliff est l'adaptateur qui sera employé par la plupart des clients qui veulent avoir
des fichiers XML mais n'ont pas d'outils pour TMX. XLIFF est basé sur le format XML et est lié
à TMX mais est plus simple car il ne supporte pas toutes ses possibilités. Les fichiers XML sont
lisibles par l'homme, mais l'analyse n'est pas aussi rapide qu'avec des fichiers gettext.
La plupart des moyennes entreprises utilisent cet adaptateur. Les fichiers sont lisibles par
l'homme et sont indépendants du système.
2.1.9. Zend_Translate_Adapter_XmlTm
L'adaptateur XmlTm est l'adaptateur qui sera utilisé par les clients qui font leur mise en page
eux-mêmes. XmlTm est un format qui permet à la source HTML complète d'être incluse dans la
source de traduction, donc la traduction est couplée avec la mise en page. XmlTm est un fichier
basé sur le format XML, qui est proche de XLIFF, mais qui n'est pas aussi simple à lire.
Cet adaptateur devrait être seulement utilisé quand des fichiers source existent déjà. Les fichiers
sont lisibles par l'homme et sont indépendants du système.
Toute classe d'adaptateur que vous voulez utiliser avec Zend_Translate doit être une sous-
classe de Zend_Translate_Adapter. Zend_Translate_Adapter est une classe abstraite
qui définit déjà tout ce qui est nécessaire pour la traduction. Ce qui doit être fait par vous, est
la définition du lecteur des données traduites.
try {
$translate = new Zend_Translate(
'MonEntreprise_Translate_Adapter_MonFormat',
'/chemin/vers/translate.xx',
'fr',
array('monoption' => 'mavaleur'));
} catch (Exception $e) {
// Fichier non trouvé, pas de classe d'adaptateur...
// Echec de l'application
}
Pour utiliser le cache, vous devez juste fournir un objet de cache à la méthode
Zend_Translate::setCache(). Elle prend une instance de Zend_Cache comme seul
paramètre. En outre si vous utilisez n'importe quel adaptateur direct, vous pouvez employer
1456
Zend_Translate
$cache = Zend_Cache::factory('Core',
'File',
$frontendOptions,
$backendOptions);
Zend_Translate::setCache($cache);
$translate = new Zend_Translate('gettext',
'/chemin/vers/traduction.mo',
'en');
print "Exemple\n";
print "=======\n";
print "Ceci la ligne une\n";
print "Aujourd'hui nous sommes le " . date("d/m/Y") . "\n";
print "\n";
print "Correction de la langue ceci est la ligne deux\n";
L'exemple ci-dessus montre l'affichage sans le support de traduction. Vous écrivez probablement
votre code dans votre langue maternelle. Généralement vous devez traduire non seulement
l'affichage, mais également les messages d'erreur et les messages de log.
La prochaine étape est d'inclure Zend_Translate dans votre code existant. Naturellement il
est beaucoup plus facile si vous écrivez dès le début votre code en utilisant Zend_Translate
au lieu de modifier votre code après.
print $translate->_("Exemple")."\n";
print "=======\n";
print $translate->_("Ceci la ligne une")."\n";
printf($translate->_("Aujourd'hui nous sommes le %1\$s") . "\n",
date("d/m/Y"));
print "\n";
$translate->setLocale('fr');
print $translate->_("Correction de la langue ceci est la ligne deux") . "\n";
Maintenant regardons plus attentivement ce qui a été fait et la façon d'intégrer Zend_Translate
dans votre code.
1457
Zend_Translate
Dans cet exemple nous avons décidé d'utiliser l'adaptateur Gettext. Nous plaçons notre
fichier source-de.mo dans le dossier /chemin/vers. Le fichier gettext inclura la traduction
allemande. Et nous avons également ajouté un autre fichier de langue pour le français.
L'étape suivante est d'envelopper toutes les chaînes qui doivent être traduites. L'approche la
plus simple est d'avoir seulement des chaînes simples ou des phrases comme celle-ci :
print $translate->_("Exemple")."\n";
print "=======\n";
print $translate->_("Ceci la ligne une")."\n";
Certaines chaînes ne sont pas nécessairement traduites. La ligne séparatrice est toujours la
même, même dans d'autres langues.
Avoir des valeurs de données intégrées dans une chaîne de traduction est également supporté
par l'utilisation des paramètres inclus.
Au lieu de print(), utiliser la fonction printf() et remplacer tous les paramètres avec des
éléments de type %1\$s. Le premier est %1\$s, le second %2\$s, et ainsi de suite. De cette
façon une traduction peut être faite sans savoir la valeur exacte. Dans notre exemple, la date
est toujours le jour actuel, mais la chaîne peut être traduite sans connaissance du jour actuel.
Chaque chaîne est identifiée dans le stockage de traduction par un identificateur de message.
Vous pouvez employer l'identificateur de message au lieu des chaînes dans votre code, comme
ceci :
print $translate->_(1)."\n";
print "=======\n";
print $translate->_(2)."\n";
Vous ne pouvez pas voir ce que votre code devrait afficher juste en lisant celui-ci.
En outre vous obtiendrez des problèmes si certaines chaînes ne sont pas traduites. Vous devez
toujours imaginer comment la traduction fonctionne. Premièrement Zend_Translate vérifie
si la langue choisie a une traduction pour l'identificateur de message ou la chaîne fournie. Si
aucune chaîne de traduction n'a été trouvée, elle se reporte sur la langue suivante comme
définie dans Zend_Locale. Ainsi le "de_AT" devient seulement "de". Si aucune traduction n'est
trouvée pour le "de", alors le message original est retourné. De cette façon vous avez toujours
un affichage, au cas où la traduction de message n'existerait pas dans votre stockage des
messages. Zend_Translate ne lève jamais d'erreur ou d'exception en traduisant les chaînes.
1458
Zend_Translate
Vous devrez savoir où stocker vos fichiers sources de traduction. Avec Zend_Translate vous
n'avez aucune restriction. Les structures suivantes sont préférables :
/application
/languages
lang.en
lang.de
/library
Positif : Tous les fichiers sources pour chacune des langues peuvent être trouvés dans un
dossier. Aucun fractionnement des fichiers.
/application
/languages
/en
lang.en
other.en
/de
lang.de
other.de
/library
Positif : chaque langue est située dans un dossier. La traduction est facilitée car un seul
dossier doit être traduit par une équipe de langue. En outre l'utilisation de dossiers multiples
est transparente.
/application
/languages
lang.en
lang.de
other.en
other.de
Positif : tous les fichiers sources pour chacune des langues peuvent être trouvés dans un seul
dossier. Aucun fractionnement des fichiers.
Négatif : avoir des dossiers multiples pour la même langue est problématique.
/languages
/de
/LC_MESSAGES
lang.mo
other.mo
/en
/LC_MESSAGES
lang.mo
other.mo
Positif : de vieilles sources de gettext peuvent être utilisées sans changer la structure.
1459
Zend_Translate
Négatif : avoir des dossiers de dossiers peut être embrouillant pour les personnes qui n'ont
pas utilisé gettext avant.
/application
/models
mymodel.php
mymodel.de
mymodel.en
/views
/controllers
mycontroller.de
/document_root
/images
/styles
.htaccess
index.php
index.de
/library
/Zend
Négatif : de multiples petits fichiers sources de traduction rendent plus difficile la traduction.
En outre chaque fichier doit être ajouté comme source de traduction.
Les fichiers source uniques et structurés par langue sont les plus utilisés pour
Zend_Translate.
Maintenant, que nous connaissons la structure que nous voulons avoir, nous devons créer nos
fichiers sources de traduction.
Depuis la version 1.5 il est également possible d'avoir des tableaux inclus dans un fichier externe.
Vous devez simplement fournir le nom de fichier, Zend_Translate l'inclura automatiquement
et recherchera le tableau. Voir l'exemple suivant pour les détails :
// montableau.php
1460
Zend_Translate
return array(
'message1' => 'Nachricht1',
'message2' => 'Nachricht2',
'message3' => 'Nachricht3');
// contrôleur
$translate = new Zend_Translate('array',
'chemin/vers/montableau.php',
'de');
Les fichiers qui ne renvoient pas un tableau ne seront pas inclus. N'importe quel
rendu issu de ce fichier sera ignoré et également supprimé.
Comme vous pouvez le voir, les adaptateurs sont utilisés exactement de la même manière,
avec juste une petite différence : changer "array" en "gettext". Toutes autres utilisations
sont exactement les mêmes qu'avec tous autres adaptateurs. Avec l'adaptateur de gettext vous
ne devez plus vous occuper de la structure des répertoires, du "bindtextdomain" et du
"textdomain". Fournissez juste le chemin et le nom de fichier à l'adaptateur.
UTF-8 est un format portable qui supporte toutes les langues. Si vous employez
l'encodage UTF-8 pour toutes les langues, vous éliminez le problème des
encodages incompatibles.
La plupart des éditeur gettext ajoutent les informations de l'adaptateur comme chaines
de traduction vides. C'est pour cela que traduire des chaines vides ne fonctionne pas
avec l'adaptateur gettext. A la place, elles sont effacées de la table de traduction.
getAdapterInfo() retourne les informations de l'adaptateur gettext, notamment les
informations des fichiers gettext ajoutés.
1461
Zend_Translate
Les fichiers TMX peuvent avoir plusieurs langues dans le même fichier. Toute autre langue
incluse est ajoutée automatiquement, ainsi vous n'avez pas à appeler addLanguage().
Si vous voulez avoir seulement les langues spécifiées de la source traduite, vous pouvez
régler l'option defined_language à TRUE. Avec cette option vous pouvez ajouter les langues
souhaitées explicitement avec addLanguage(). La valeur par défaut pour cette option est
d'ajouter toutes les langues.
1462
Zend_Translate
Il existe trois options différentes pour l'adaptateur CSV. Vous pouvez paramétrer "delimiter",
"limit" et "enclosure".
Le délimiteur standard des fichiers CSV est le signe ";". Mais celui-ci n'est pas obligatoire. Avec
l'option "delimiter" vous pouvez décider d'utiliser un autre signe de séparation.
La taille limite d'une ligne de fichier CSV est par défaut "0" Ce qui veut dire que la fin de la ligne est
recherchée automatiquement. Si vous paramétrez l'option "limit" avec une valeur quelconque,
alors le fichier CSV sera lu plus rapidement, mais toute ligne dont la longueur excédera la limite
sera tronquée.
"L'échappement" par défaut d'un fichier CSV est le """. Vous pouvez en paramétrer un autre
avec l'option "enclosure".
[Test]
;Commentaires possibles
Message_1="Nachricht 1 (de)"
Message_2="Nachricht 2 (de)"
Message_3="Nachricht :3 (de)"
Les fichiers INI ont de multiples restrictions. Si une valeur dans le fichier INI contient un caractère
non-alphanumérique, il doit être entouré avec des guillemets doubles ("). Il y a aussi des mots
réservés qui ne doivent pas être utilisés en tant que clés des fichiers INI. Ceci inclut : NULL,
yes, no, TRUE et FALSE. Les valeurs NULL, no et FALSE sont retournées sous la forme "". yes
et TRUE sont retournés en "1". Les caractères {}|&~![()" ne doivent pas être utilisés dans la clé
1463
Zend_Translate
et ont une signification particulière dans la valeur. Ne les utilisez pas ou vous rencontrerez des
comportements inattendus.
...
Ici vous pouvez trouver toutes les options disponibles pour les différents adaptateurs avec une
description de leur utilisation :
1464
Zend_Translate
1465
Zend_Translate
Si vous souhaitez avoir vos propres définitions d'options, vous pouvez les utiliser avec tous les
adaptateurs. La méthode setOptions() peut être utilisée pour définir vos options. La méthode
setOptions() nécessite un tableau avec les options que vous voulez paramétrer. Si une
option fournie existe déjà, elle sera alors ré-assignée. Vous pouvez définir autant d'options que
nécessaire car elles ne seront pas vérifiées par l'adaptateur. Vérifiez simplement que vous ne
créez pas une option qui existe déjà dans l'adaptateur, vous affecteriez alors une nouvelle valeur.
Pour récupérer l'ensemble des options, vous pouvez utiliser la méthode getOptions(). Quand
getOptions() est appelée sans paramètre, elle retourne l'ensemble des options. Si un
paramètre est fourni, seule l'option particulière sera retournée.
La méthode getLocale() peut être utilisée pour récupérer la langue actuellement réglée. Elle
peut retourner soit une instance de Zend_Locale, soit un identifiant de localisation.
La méthode setLocale() règle une nouvelle langue standard pour la traduction. Ceci évite
de placer le paramètre facultatif de langue plus d'une fois lors de l'appel de la méthode
translate(). Si la langue donnée n'existe pas, ou si aucune donnée de traduction n'est
disponible pour la langue, setLocale() essaye de remonter à la langue sans région si elle
est indiquée. Une langue fr_FR serait remontée à fr. Si la remontée n'est pas possible, une
exception sera levée.
La méthode isAvailable() vérifie si une langue donnée est déjà disponible. Elle retourne
TRUE si des données existent pour la langue fournie.
Et enfin la méthode getList() peut être utilisée pour récupérer sous la forme d'un tableau
tous les langues paramétrées pour un adaptateur.
1466
Zend_Translate
...
// retourne la langue paramétrée actuelle
$actual = $translate->getLocale();
...
// vous pouvez utiliser le paramètre optionel au moment de la traduction
echo $translate->_("mon_texte", "fr");
// ou paramètrer une langue standard
$translate->setLocale("fr");
echo $translate->_("mon_texte");
// référence à la langue de base... fr_CH sera remonté à fr
$translate->setLocale("fr_CH");
echo $translate->_("mon_texte");
...
// vérifie si la langue existe
if ($translate->isAvailable("fr")) {
// la langue existe
}
Notez que tant que vous ajouterez les nouvelles sources de traduction seulement via la méthode
addTranslation(), Zend_Translate cherchera automatiquement la langue correspondant
au mieux à votre environnement quand vous utiliserez une des localisations automatiques
"auto" ou "browser". Donc normalement vous ne devriez pas appeler setLocale(). Ceci ne
doit être utilisé qu'en conjonction avec la détection automatique des sources de traduction.
1467
Zend_Translate
// Exemple 1 :
$translate = new Zend_Translate('gettext',
'\my_it.mo',
'auto',
array('scan' => Zend_Translate::LOCALE_FILENAME);
// pas de langue trouvée, on retourne le messageid
// Exemple 2 :
$translate = new Zend_Translate('gettext',
'\my_fr.mo',
'auto',
array('scan' => Zend_Translate::LOCALE_FILENAME);
// langue correspondante trouvée "en_US"
// Exemple 3 :
$translate = new Zend_Translate('gettext',
'\my_de.mo',
'auto',
array('scan' => Zend_Translate::LOCALE_FILENAME);
// langue correspondante trouvée "de" car "de_AT" est descendue à "de"
// Exemple 4 :
$translate = new Zend_Translate('gettext',
'\my_it.mo',
'auto',
array('scan' => Zend_Translate::LOCALE_FILENAME);
$translate->addTranslation('\my_ru.mo', 'ru');
$translate->setLocale('it_IT');
// retourne "it_IT" comme source de traduction et surcharge le réglage automatique
Depuis Zend Framework 1.7.0 Zend_Translate reconnait une locale globale pour l'application.
Vous pouvez ainsi simplement mettre un objet Zend_Locale dans le registre, comme montré
ci-après. Avec cette fonctionnalité, vous pouvez oublier le passage de la locale à votre objet de
traduction.
1468
Zend_Translate
L'utilisation est assez semblable à celle qui permet de spécifier une source de langue. Vous
devez simplement donner un dossier, et non plus un fichier, à l'adaptateur. Ce dossier sera alors
scanné
Notez que Zend_Translate cherche dans tous les sous-repertoires. L'utilisation devient alors
relativement simple. Aussi, Zend_Translate ignorera tout fichier qui ne l'interresse pas : des
fichiers non représentatifs de traductions ou encore des fichiers illisibles. Vérifiez donc que le
dossier principal ne contienne que des fichiers de traductions, car Zend_Translate ne renverra
aucune erreur dans le cas contraire, il ignorera simplement de tels fichiers.
Dans notre exemple, nous utilisons l'adaptateur TMX qui inclut la langue à utiliser dans le fichier
en question. D'autres adaptateurs n'agissent pas comme cela, ainsi les noms de fichiers devront
comporter les noms des langues à considérer pour de tels adaptateurs.
One way to include automatic language detection is to name the directories related to the
language which is used for the sources within this directory. This is the easiest way and is used
for example within standard gettext implementations.
Zend_Translate needs the 'scan' option to know that it should search the names of all directories
for languages. See the following example for details:
1469
Zend_Translate
This works only for adapters which do not include the language within the source
file. Using this option for example with TMX will be ignored. Also language
definitions within the filename will be ignored when using this option.
You should be aware if you have several subdirectories under the same structure.
Expect we have a structure like /language/module/de/en/file.mo. The
path contains in this case multiple strings which would be detected as locale. It
could be eigther de or en. As the behaviour is, in this case, not declared it is
recommended that you use file detection in such situations.
Another way to detect the langage automatically is to use special filenames. You can either name
the complete file or parts of a file with the used language. To use this way of detection you will
have to set the 'scan' option at initiation. There are several ways of naming the sourcefiles which
are described below:
Having the whole file named after the language is the simplest way but only usable if you have
only one file per directory.
/languages
1470
Zend_Translate
en.mo
de.mo
es.mo
Another very simple way if to use the extension of the file for the language detection. But this
may be confusing because you will no longer know which file extension the file originally was.
/languages
view.en
view.de
view.es
Zend_Translate is also captable of detecting the language if it is included within the filename.
But if you use this way you will have to seperate the language with a token. There are three
supported tokens which can be used: A point '.', a underline '_', or a hyphen '-'.
/languages
view_en.mo -> detects english
view_de.mo -> detects german
view_it.mo -> detects italian
The first found token which can be detected as locale will be used. See the following example
for details.
/languages
view_en_de.mo -> detects english
view_en_es.mo -> detects english and overwrites the first file
because the same messageids are used
view_it_it.mo -> detects italian
All three tokens are used to detect the locale. The first one is the point '.', the second is the
underline '_' and the third the hyphen '-'. If you have several tokens within the filename the first
found depending on the order of the tokens will be used. See the following example for details.
/languages
view_en-it.mo -> detects english because '_' will be used before '-'
view-en_it.mo -> detects italian because '_' will be used before '-'
view_en.it.mo -> detects italian because '.' will be used before '_'
1471
Zend_Translate
obtiendriez normalement la traduction fournie, mais avec $original réglé à TRUE, la méthode
isTranslated() retournera FALSE dans ce cas.
if ($translate->isTranslated('message1')) {
print "'message1' peut être traduit";
}
if (!($translate->isTranslated('message1', true, 'de'))) {
print "'message1' ne peut pas être traduit en 'de', "
. "il est seulement disponible en 'de_AT'";
}
if ($translate->isTranslated('message1', false, 'de')) {
print "'message1' peut être traduit en 'de_AT' "
. "et par conséquent en 'de'";}
You have to follow two or three simple steps. First, you have to create a instance of Zend_Log.
And then you have to attach this instance to Zend_Translate. See the following example:
$translate->translate('unknown string');
Now you will have in the log a new notice: Untranslated message within 'de': unknown
string.
You should note that any translation which can not be found will be logged. This
means all translations when a user requests a not supported language. But also
every request to a message which can not be translated will be logged. Be aware
that when 100 people request the same translation you will have 100 notices
logged.
This feature can not only be used to log messages but also to attach this not translated messages
into a empty translation file. To archive this you will have to write your own log writer which writes
the format you want to have and strips the prepending "Untranslated message" for you.
1472
Zend_Translate
You can also set the 'logMessage' option when you want to have your own log message. Use
the '%message%' token for placing the messageId within your log message, and the '%locale%'
token for the requested locale. See the following example for a self defined log message:
$translate->translate('unknown string');
The getMessageIds($locale = null) method returns all known message ids as array.
And the getMessages($locale = null) method returns the complete translation source as
array. The message id is used as key and the translation data as value.
Both methods accept an optional parameter $locale which, when set, returns the translation
data for the specified language. If this parameter is not given, the actual set language will be
used. Keep in mind that normally all translations should be available in all languages. Which
means that in a normal situation you will not have to set this parameter.
Additionally the getMessages() method is able to return the complete translation dictionary
with the pseudo-locale 'all'. This will return all available translation data for each added locale.
Attention: The returned array can be very big, depending on the count of added
locales and the amount of translation data.
...
// returns all known message ids
$messageids = $translate->getMessageIds();
print_r($messageids);
...
// or just for the specified language
$messageids = $translate->getMessageIds('en_US');
print_r($messageids);
...
// returns all the complete translation data
$source = $translate->getMessages();
print_r($source);
1473
Zend_Translate
Note that most of the described formats should be created by using a tool or a
generation process. These Tools and processes are not part of Zend Framework
and for most of the described formats free tools are available.
$english = array(
'message1' => 'message1',
'message2' => 'message2',
'message3' => 'message3');
$german = array(
'message1' => 'Nachricht1',
'message2' => 'Nachricht2',
'message3' => 'Nachricht3');
Since release 1.5 it is also supported to have arrays included within an external file. You just
have to provide the filename and Zend_Translate will automatically include it and look for the
array. See the following example for details:
// myarray.php
return array(
'message1' => 'Nachricht1',
'message2' => 'Nachricht2',
'message3' => 'Nachricht3');
// controller
$translate = new Zend_Translate('array', '/path/to/myarray.php', 'de');
Files which do not return an array will fail to be included. Also any output within
this file will be ignored and suppressed.
1474
Zend_Translate
As you can see the adapters are used exactly the same way, with one small difference: change
array to gettext. All other usages are exactly the same as with all other adapters. With the gettext
adapter you no longer have to be aware of gettext's standard directory structure, bindtextdomain
and textdomain. Just give the path and filename to the adapter.
You should always use UTF-8 as source encoding. Otherwise you will have
problems when using two different source encodings. E.g. one of your source
files is encoded with ISO-8815-11 and another one with CP815. You can set only
one encoding for your source file, so one of your languages probably will not
display correctly.
UTF-8 is a portable format which supports all languages. When using UTF-8 for
all languages, you will eliminate the problem of incompatible encodings.
Many gettext editors add adapter informations as empty translation string. This is the reason why
empty strings are not translated when using the gettext adapter. Instead they are erased from
the translation table and provided by the getAdapterInfo() method. It will return the adapter
informations for all added gettext files as array using the filename as key.
TMX files can have several languages within the same file. All other included languages are
added automatically, so you do not have to call addLanguage().
1475
Zend_Translate
If you want to have only specified languages from the source translated you can set the option
'defined_language' to TRUE. With this option you can add the wished languages explicitly
with addLanguage(). The default value for this option is to add all languages.
There are three different options for the CSV adapter. You can set 'delimiter', 'limit' and
'enclosure'.
The default delimiter for CSV string is ';', but with the option 'delimiter' you can decide to
use another one.
The default limit for a line within a CSV file is '0'. This means that the end of a CSV line is searched
automatically. If you set 'limit' to any value, then the CSV file will be read faster, but any line
exceeding this limit will be truncated.
The default enclosure to use for CSV files is '"'. You can set a different one using the option
'enclosure'.
$translate->addTranslation('/path/to/other.csv', 'fr');
When you are using non-ASCII characters within your CSV file, like umlauts or
UTF-8 chars, then you should always use enclosure. Omitting the enclosure can
lead to missing characters in your translation.
1476
Zend_Translate
[Test]
;TestPage Comment
Message_1="Nachricht 1 (de)"
Message_2="Nachricht 2 (de)"
Message_3="Nachricht :3 (de)"
INI files have several restrictions. If a value in the ini file contains any non-alphanumeric
characters it needs to be enclosed in double-quotes ("). There are also reserved words which
must not be used as keys for ini files. These include: NULL, yes, no, TRUE, and FALSE. Values
NULL, no and FALSE results in "", yes and TRUE results in 1. Characters {}|&~![()" must
not be used anywhere in the key and have a special meaning in the value. Do not use them as
it will produce unexpected behaviour.
You can set options temporarily when using addTranslation($data, $locale, array
$options = array()) as third and optional parameter. And you can use the method
setOptions() to set the options permanently.
...
Here you can find all available options for the different adapters with a description of their usage:
1477
Zend_Translate
1478
Zend_Translate
When you want to have self defined options, you are also able to use them within all adapters.
The setOptions() method can be used to define your option. setOptions() needs an array
with the options you want to set. If an given option exists it will be signed over. You can define
1479
Zend_Translate
as much options as needed as they will not be checked by the adapter. Just make sure not to
overwrite any existing option which is used by an adapter.
To return the option you can use the getOptions() method. When getOptions() is called
without a parameter it will return all options set. When the optional parameter is given you will
only get the specified option.
The getLocale() method can be used to get the currently set language. It can either hold an
instance of Zend_Locale or the identifier of a locale.
The setLocale() method sets a new standard language for translation. This prevents the need
of setting the optional language parameter more than once to the translate() method. If the
given language does not exist, or no translation data is available for the language, setLocale()
tries to downgrade to the language without the region if any was given. A language of en_US
would be downgraded to en. When even the downgraded language can not be found an
exception will be thrown.
The isAvailable() method checks if a given language is already available. It returns TRUE
if data for the given language exist.
And finally the getList() method can be used to get all currently set languages for an adapter
returned as array.
The algorithm will search for the best fitting locale depending on the user's browser and your
environment. See the following example for details:
1480
Zend_Translate
// Example 1:
// When no fitting language is found, the message ID is returned
$translate = new Zend_Translate(
'gettext',
'my_it.mo',
'auto',
array('scan' => Zend_Translate::LOCALE_FILENAME));
// Example 2:
// Best found fitting language is 'fr'
$translate = new Zend_Translate(
'gettext',
'my_fr.mo',
'auto',
array('scan' => Zend_Translate::LOCALE_FILENAME));
// Example 3:
// Best found fitting language is 'de' ('de_AT' will be degraded)
$translate = new Zend_Translate(
'gettext',
'my_de.mo',
'auto',
array('scan' => Zend_Translate::LOCALE_FILENAME));
// Example 4:
// Returns 'it' as translation source and overrides the automatic settings
$translate = new Zend_Translate(
'gettext',
'my_it.mo',
'auto',
array('scan' => Zend_Translate::LOCALE_FILENAME));
$translate->addTranslation('my_ru.mo', 'ru');
$translate->setLocale('it_IT');
After setting a language manually with the setLocale() method the automatic detection will
be switched off and overridden.
If you want to use it again, you can set the language auto with setLocale() which will reactivate
the automatic detection for Zend_Translate.
Since Zend Framework 1.7.0 Zend_Translate also recognises an application wide locale. You
can simply set a Zend_Locale instance to the registry like shown below. With this notation you
can forget about setting the locale manually with each instance when you want to use the same
locale multiple times.
1481
Zend_Translate
if (!$translate->isAvailable($locale->getLanguage())) {
// not available languages are rerouted to another language
$translate->setLocale($defaultlanguage);
}
$translate->getLocale();
The usage is quite the same as initiating a single translation source with one difference. You
must give a directory which has to be scanned instead a file.
So Zend_Translate does not only search the given directory, but also all subdirectories for
translation source files. This makes the usage quite simple. But Zend_Translate will ignore
all files which are not sources or which produce failures while reading the translation data. So
you have to make sure that all of your translation sources are correct and readable because you
will not get any failure if a file is bogus or can not be read.
Depending on how deep your directory structure is and how much files are within
this structure it can take a long time for Zend_Translate to complete.
In our example we have used the TMX format which includes the language to be used within
the source. But many of the other source formats are not able to include the language within
the file. Even this sources can be used with automatic scanning if you do some pre-requisits as
described below:
One way to include automatic language detection is to name the directories related to the
language which is used for the sources within this directory. This is the easiest way and is used
for example within standard gettext implementations.
1482
Zend_Translate
Zend_Translate needs the 'scan' option to know that it should search the names of all
directories for languages. See the following example for details:
This works only for adapters which do not include the language within the source
file. Using this option for example with TMX will be ignored. Also language
definitions within the filename will be ignored when using this option.
You should be aware if you have several subdirectories under the same structure.
Assuming we have a structure like /language/module/de/en/file.mo. In
this case the path contains multiple strings which would be detected as locale.
It could be either de or en. In such a case the behaviour is undefined and it is
recommended to use file detection in such situations.
Having the whole file named after the language is the simplest way but only viable if you have
only one file per language.
1483
Zend_Translate
/languages/
/languages/en.mo
/languages/de.mo
/languages/es.mo
Another simple way to use the extension of the file for language detection. But this may be
confusing since you will no longer have an idea which extension the file originally had.
/languages/
/languages/view.en
/languages/view.de
/languages/view.es
Zend_Translate is also capable of detecting the language if it is included within the filename.
But if you go this way you will have to separate the language with a token. There are three
supported tokens which can be used: a dot '.', an underscore '_', or a hyphen '-'.
/languages/
/languages/view_en.mo -> detects english
/languages/view_de.mo -> detects german
/languages/view_it.mo -> detects italian
The first found string delimited by a token which can be interpreted as a locale will be used. See
the following example for details.
/languages/
/languages/view_en_de.mo -> detects english
/languages/view_en_es.mo -> detects english and overwrites the first file
/languages/view_it_it.mo -> detects italian
All three tokens are used to detect the locale. When the filename contains multiple tokens, the
first found token depends on the order of the tokens which are used. See the following example
for details.
/languages/
/languages/view_en-it.mo -> detects english because '_' will be used before '-'
/languages/view-en_it.mo -> detects italian because '_' will be used before '-'
/languages/view_en.it.mo -> detects italian because '.' will be used before '_'
1484
Zend_Translate
$english = array(
'message1' => 'Nachricht 1',
'message2' => 'Nachricht 2',
'message3' => 'Nachricht 3');
if ($translate->isTranslated('message1')) {
print "'message1' can be translated";
}
You have to follow two or three simple steps. First, you have to create an instance of Zend_Log.
Then you have to attach this instance to Zend_Translate. See the following example:
$translate->translate('unknown string');
Now you will have a new notice in the log: Untranslated message within 'de': unknown
string.
You should note that any translation which can not be found will be logged. This
means all translations when a user requests a language which is not supported.
Also every request for a message which can not be translated will be logged. Be
aware, that 100 people requesting the same translation, will result 100 logged
notices.
1485
Zend_Translate
This feature can not only be used to log messages but also to attach this untranslated messages
into an empty translation file. To do so you will have to write your own log writer which writes the
format you want to have and strips the prepending "Untranslated message".
You can also set the 'logMessage' option when you want to have your own log message. Use
the '%message%' token for placing the messageId within your log message, and the '%locale%'
token for the requested locale. See the following example for a self defined log message:
$translate->translate('unknown string');
The getMessageIds($locale = null) method returns all known message IDs as array.
Both methods accept an optional parameter $locale which, if set, returns the translation data
for the specified language. If this parameter is not given, the actual set language will be used.
Keep in mind that normally all translations should be available in all languages. Which means
that in a normal situation you will not have to set this parameter.
Additionally the getMessages() method can be used to return the complete translation
dictionary using the pseudo-locale 'all'. This will return all available translation data for each
added locale.
Attention: the returned array can be very big, depending on the number of added
locales and the amount of translation data.
1486
Zend_Translate
Qu'est ce qu'un pluriel? En général, les pluriels sont des mots qui prennent en compte une
contenance numérique. Cependant chaque langue a sa propre définition du pluriel. En anglais,
par exemple, un seul pluriel par mot existe donc par exemple "car" donnera "cars" au pluriel.
D'autres langages comme le russe proposent plusieurs pluriels et plusieurs règles de pluriels.
Lorsque vous voulez utiliser les pluriels avec Zend_Translate vous n'avez pas besoin de
savoir comment ceux-ci sont définis, seul le traducteur sait. Vous devez juste savoir la langue
dans laquelle traduire.
Il existe 2 manières d'utiliser les pluriels. La traditionnelle qui vous fait utiliser une méthode, la
moderne, qui permet d'utiliser la même méthode pour plusieurs pluriels.
La quantité va être utilisée pour détecter le pluriel à retourner. Le 4ème paramètre optionnel
peut être utilisé pour spécifier une locale concernant la traduction.
Pour utiliser la traduction de pluriels avec translate(), passez un tableau comme messageId
plutôt qu'une chaine. Ce tableau doit contenir le messageId original, celui du pluriel, une quantité
et optionnellement une locale.
1487
Zend_Translate
Avec cette méthode, vous pouvez utiliser n'importe quelle langue comme messageId original.
Imaginons que nous voulions utiliser du russe et que le messageId est russe et non anglais.
Comme vous le voyez, vous pouvez donner plus d'un pluriel, mais il faut alors spécifier la langue
pour que Zend_Translate puisse trouver les règles de pluriel appropriées.
Si vous omettez la langue, l'anglais sera utilisé et tout pluriel superflu sera ignoré.
array(
'plural_0' => array(
'plural_0 (ru)',
'plural_1 (ru)',
1488
Zend_Translate
'plural_2 (ru)',
'plural_3 (ru)'
),
'plural_1' => ''
);
Dans cet exemple, plural_0 et plural_1 sont les définitions du pluriel depuis le code source.
Le tableau à l'index plural_0 possède les pluriels traduits. Regardez l'exemple suivant avec
du vrai contenu et une traduction anglais - allemand.
array(
'Car' => array(
'Auto',
'Autos'
),
'Cars' => ''
);
Si votre langue supporte plusieurs pluriels, ajoutez les simplement dans le tableau à la suite du
premier pluriel.
Toutes les traductions des pluriels doivent être ajoutées après le premier pluriel et tout les pluriels
suivants doivent être ajoutés après, mais sans traduction. Notez que le délimiteur est nécessaire
pour les pluriels vides.
Notez que gettext ne gère pas les langues à plusieurs pluriels, utilisez un autre
adaptateur dans ce cas là.
Also when you want to use a language which has no known plural rules, and would want to
define your own rules.
1489
Zend_Translate
As you see, your rule must accept one parameter. It is the number which you will use to return
which plural the translation has to use. In our example we defined that when we get a '10' the
plural definition 0 has to be used, in all other cases we're using 1.
Your rules can be as simple or as complicated as you want. You must only return an integer
value. The plural definition 0 stands for singular translation, and 1 stands for the first plural rule.
To activate your rule, and to link it to the wished locale, you have to call it like this:
Zend_Translate_Plural::setPlural('MyPlural', 'zh');
You can define one plural rule for every language. But you should be aware that you set the
plural rules before you are doing translations.
1490
Zend_Uri
1. Zend_Uri
1.1. Aperçu
Zend_Uri est un composant destiné à aider à la manipulation et à la validation des Uniform
Resource Identifiers (URIs). Zend_Uri existe dans le but d'aider les autres composants de
Zend Framework tels que Zend_Http_Client mais peut aussi être utilisé comme composant
individuel.
Les URIs commence toujours avec la définition de leur schéma, suivie d'un double-points. La
construction des différents schémas variant beaucoup, une fabrique est à votre disposition.
Zend_Uri possède une fabrique qui retourne des instances sous-classes d'elle même. Chaque
sous classe possède le nom du schéma dans son nom, comme Zend_Uri_<scheme>, où
<scheme> est le nom du schéma utilisé, tout en minuscule, sauf la première lettre. Une exception
à cette règle est HTTPS, qui est aussi géré par Zend_Uri_Http.
1
Pour créer un URI à partir de rien, passez simplement le schéma à Zend_Uri::factory() .
Si un schéma non supporté lui est passé, une Zend_Uri_Exception sera levée.
L'URI sera alors analysé et validé. S'il s'avère être invalide, une Zend_Uri_Exception sera
envoyée immédiatement. Sinon, Zend_Uri::factory() retournera une sous classe d'elle-
même qui spécialisera le schéma manipulé.
1
Actuellement, Zend_Uri ne supporte que les schémas HTTP et HTTPS
1491
Zend_Uri
Zend_Uri::check() retourne un simple booléen, ce qui est plus pratique que de passer par
Zend_Uri::factory() et de capturer les exceptions.
Zend_Uri peut être paramètré pour accepter ces caractères "imprudents" en réglant l'option
"allow_unwise" à TRUE en utilisant la méthode Zend_Uri::setConfig() :
$uri = Zend_Uri::factory('http://www.zend.com');
1492
Zend_Uri
$uri = Zend_Uri::factory('http://www.zend.com');
$uri = Zend_Uri::factory('http://www.zend.com');
La méthode valid() propose une façon de vérifier si l'URI est toujours valide.
1493
Zend_Validate
1. Introduction
Le composant Zend_Validate fournit un ensemble de validateurs usuels. Il fournit également
un mécanisme simple de chaînage permettant d'appliquer de multiples validateurs à une donnée
dans un ordre défini par l'utilisateur.
Par exemple, une application Web peut réclamer qu'un identifiant comprennent entre six et douze
caractères et ne contiennent que des caractères alphanumériques. Un validateur peut être utilisé
pour s'assurer que les identifiants remplissent ces règles. Si un identifiant donné ne respecte
pas l'une ou plusieurs de ces règles, il sera utile de savoir laquelle ou lesquelles en particulier.
if ($validator->isValid($email)) {
1494
Zend_Validate
Le second paramètre optionnel est une chaîne qui identifie le modèle de message d'échec de
validation qui doit être paramètré, ce qui est pratique quand une classe de validation définit
plus d'une cause d'échec. Si vous omettez ce second argument, setMessage() considère
que le message, que vous spécifiez, s'applique au premier message déclaré dans la classe de
validation. La plupart des classes de validation n'ayant qu'un seul message d'erreur, il n'est pas
nécessaire de spécifier distinctement dans ce cas quel message vous affectez.
$validator->setMessage(
'La chaîne \'%value%\' est trop courte ; '
. 'elle doit être au moins de %min% caractères',
Zend_Validate_StringLength::TOO_SHORT);
if (!$validator->isValid('word')) {
$messages = $validator->getMessages();
echo current($messages);
Vous pouvez régler des messages multiples en utilisant la méthode setMessages(). Son
argument dans ce cas est un tableau de paires clé/message.
$validator->setMessages( array(
Zend_Validate_StringLength::TOO_SHORT =>
'La chaîne \'%value%\' est trop courte',
Zend_Validate_StringLength::TOO_LONG =>
'La chaîne \'%value%\' est trop longue'
));
Si votre application exige une flexibilité encore plus grande avec laquelle elle rapporte les échecs
de validation, vous pouvez accéder aux propriétés par le même nom que les balises de message
supportées par une classe de validation donnée. La propriété value est toujours accessible
1495
Zend_Validate
if (!validator->isValid('word')) {
echo 'Echec du mot : '
. $validator->value
. ' ; sa longueur n\'est pas compris entre '
. $validator->min
. ' et '
. $validator->max
. "\n";
}
if (Zend_Validate::is($email, 'EmailAddress')) {
// l'email est valide
}
Vous pouvez aussi fournir un tableau de paramètres destinés au constructeur de la classe, s'ils
sont nécessaires pour votre classe de validation.
La méthode is() retourne une valeur booléenne, la même que la méthode isValid(). Lors
de l'utilisation de la méthode statique is(), les messages d'échec de validation ne sont pas
disponibles.
L'utilisation statique peut être pratique pour invoquer un validateur ad hoc, mais si vous avez
besoin d'exécuter un validateur pour des données multiples, il est plus efficace de suivre le
premier exemple ci-dessus, créant une instance de l'objet de validation et appelant sa méthode
isValid().
1496
Zend_Validate
// Yes, $value is ok
}
Zend_Validate allows also to set namespaces as default. This means that you can set them
once in your bootstrap and have not to give them again for each call of Zend_Validate::is().
The following code snippet is identical to the above one.
Zend_Validate::setDefaultNamespaces(array('FirstNamespace', 'SecondNamespace'));
if (Zend_Validate::is($value, 'MyValidator', array(array('min' => 1, 'max' => 12))) {
// Yes, $value is ok
}
For your convinience there are following methods which allow the handling of namespaces:
$validator->setTranslator($translate);
1497
Zend_Validate
When you have set an application wide locale within your registry, then this locale
will be used as default translator.
Sometimes it is necessary to disable the translator within a validator. To archive this you
can use the setDisableTranslator() method, which accepts a boolean parameter, and
translatorIsDisabled() to get the set value.
It is also possible to use a translator instead of setting own messages with setMessage(). But
doing so, you should keep in mind, that the translator works also on messages you set your own.
2.1. Alnum
Retourne TRUE si et seulement si $value contient seulement des caractères alphabétiques et
numériques. Ce validateur inclue une option permettant la validation possible des caractères
espace.
2.2. Alpha
Retourne TRUE si et seulement si $value contient seulement des caractères alphabétiques. Ce
validateur inclue une option permettant la validation possible des caractères espace.
2.3. Barcode
Ce validateur est instancié avec le type de code-barres suivant lequel vous souhaitez valider une
valeur. Il supporte actuellement les types de code-barres "UPC-A" (Universal Product Code) et
"EAN-13" (European Article Number), et la méthode isValid() retourne TRUE si et seulement
si la valeur fournie est validée par l'algorithme de validation du code-barres. Vous devez retirer
tous les caractères exceptés les nombres compris entre zéro et neuf (0-9) de la valeur à tester
avant de la fournir au validateur.
2.4. Between
Retourne TRUE si et seulement si $value est compris entre les valeurs minimum et maximum
définies. La comparaison inclue par défaut les bornes ($value peut égaler le minimum ou le
maximum), bien que ceci puisse être surchargé pour avoir une comparaison stricte, où $value
doit être strictement supérieur au minimum et strictement inférieur au maximum.
1498
Zend_Validate
2.5. Ccnum
Retourne TRUE si et seulement si $value respecte l'algorithme de Luhn ("module 10") pour les
numéros de cartes de crédits.
2.6. Date
Retourne TRUE si $value est une date valide au format YYYY-MM-DD. Si l'option locale est
fournie alors la date sera validé suivant le paramètre local. Enfin si l'option format est fournie
ce format est utilisé pour la validation. Pour plus de détails concernant les paramètres optionnel,
referez vous à Zend_Date::isDate().
Le code ci-dessus vérifie la présence d'une adresse email $emailaddress vis à vis d'un champ
d'une table de base de données.
Le code ci-dessus vérifie l'absence d'un nom d'utilisateur $username vis à vis d'un champ d'une
table de base de données.
1499
Zend_Validate
Lorsqu'un tableau est passé, l'opérateur != est utilisé et vous pouvez ainsi tester le reste de
la table.
//Vérifie qu'aucun autre utilisateur que celui dont l'id est spécifié, ne possède ce nom
$user_id = $user->getId();
$validator = new Zend_Validate_Db_NoRecordExists(
'users',
'username',
array(
'field' => 'id',
'value' => $user_id
)
);
if ($validator->isValid($username)) {
// OK
} else {
// KO
$messages = $validator->getMessages();
foreach ($messages as $message) {
echo "$message\n";
}
}
L'exemple ci dessus va vérifier qu'aucun utilisateur dont l'id n'est pas celui spécifié, possède ce
nom là $username.
La clause d'exclusion peut aussi être renseignée avec une chaine afin de pouvoir utiliser un
opérateur autre que !=.
$post_id = $post->getId();
$clause = $db->quoteInto('post_id = ?', $category_id);
$validator = new Zend_Validate_Db_RecordExists(
'posts_categories',
'post_id',
$clause
);
if ($validator->isValid($username)) {
// OK
} else {
// KO
$messages = $validator->getMessages();
foreach ($messages as $message) {
echo "$message\n";
}
}
1500
Zend_Validate
2.8. Digits
Retourne TRUE si et seulement si $value contient seulement des caractères numériques.
2.9. EmailAddress
Zend_Validate_EmailAddress permet de valider une adresse émail. Ce validateur éclate
d'abord l'adresse émail entre partie locale et domaine et essaie de valider ces deux parties
conformément aux spécifications des adresses émail et des noms de domaine.
Utilisation de base
Exemple basique :
Ceci validera l'adresse émail $email et, en cas d'échec, fournira des messages d'erreur
informatifs via $validator->getMessages().
La partie domaine d'une adresse émail est validée via Zend_Validate_Hostname. Par défaut,
seules les domaines qualifiés sous la forme domaine.com sont acceptés, même si, il vous est
possible d'accepter les adresses IP et les domaines locaux également.
1501
Zend_Validate
Le fait qu'une adresse électronique est dans un format correct, ne signifie pas nécessairement
que l'adresse électronique existe en réalité. Pour aider résoudre ce problème, vous pouvez
utiliser la validation MX pour vérifier si une entrée MX (l'émail) existe dans le l'enregistrement du
DNS pour le nom de domaine de l'émail. Cela vous dit que le nom de domaine accepte l'émail,
mais ne vous dit pas que l'adresse électronique elle-même est valable.
La vérification MX n'est pas active par défaut et est seulement supporté par des plates-formes
UNIX pour l'instant. Pour activer la vérification MX vous pouvez passer un deuxième paramètre
au constructeur Zend_Validate_EmailAddress.
$validator =
new Zend_Validate_EmailAddress(Zend_Validate_Hostname::ALLOW_DNS,
true);
En activant ce paramètre, les fonctions de réseau seront utilisés pour vérifier la présence d'un
enregistrement MX sur le nom de domaine de l'adresse électronique que vous voulez valider.
Faîtes cependant attention, cela ralentira probablement votre scénario.
$validator->hostnameValidator->setValidateIdn(false);
Notez cependant que les IDNs sont seulement validés si vous autorisez la validation des nom
de domaines.
Par défaut un nom de domaine sera vérifié grâce à une liste de TLDs connus. Ceci est activé
par défaut, quoique vous puissiez le mettre hors service en changeant le paramètre via l'objet
Zend_Validate_Hostname interne qui existe dans Zend_Validate_EmailAddress.
$validator->hostnameValidator->setValidateTld(false);
1502
Zend_Validate
Notez cependant que les TLDs sont seulement validés si vous autorisez la validation des nom
de domaines.
2.10. Float
Retourne TRUE si et seulement si $value est une valeur en virgule flottante. A partir de Zend
Framework 1.8 ce validateur prend en considération la locale courante issue du navigateur,
de l'environnement ou de l'application. Vous pouvez bien sûr utiliser les accesseurs get/
setLocale pour changer ou la fournir au moment de la création de l'instance de ce validateur.
2.11. GreaterThan
Retourne TRUE si et seulement si $value est supérieure à une valeur minimum fournie.
2.12. Hex
Retourne TRUE si et seulement si $value contient seulement des caractères numériques
hexadécimaux.
2.13. Hostname
Zend_Validate_Hostname vous permet de valider un nom de domaine sur la base d'un
ensemble de spécifications connues. Il est ainsi possible de valider trois types différents de noms
de domaine : un nom de domaine qualifié (ex : domaine.com), une adresse IP (ex : 1.2.3.4) ou
un nom de domaine local (ex : localhost). Par défaut, seul les noms de domaine qualifiés sont
acceptés.
Utilisation basique
Exemple simple :
Ceci validera le domaine $domaine et, en cas d'échec, fournira des messages d'erreur
informatifs via $validator->getMessages().
Il peut se trouver que vous souhaitez valider des adresses IP, des noms de domaine locaux ou
toute combinaison de tous les types disponibles. Cette opération peut être effectuée en passant
un paramètre à Zend_Validate_Hostname au moment de l'instanciation. Le paramètre doit
être un entier qui détermine quels types de noms de domaine sont admis. Il est recommandé
d'utiliser les constantes de la classe Zend_Validate_Hostname.
1503
Zend_Validate
autorise les domaines locaux et ALLOW_ALL qui autorise les trois types précédents. Pour vérifier
uniquement les adresses IP, vous pouvez utiliser l'exemple suivant :
Vous pouvez utiliser ALLOW_ALL pour accepter tous les types de domaines. De même, vous
pouvez créer des configurations combinant ces différents types. Par exemple, pour accepter les
domaines qualifiés et les domaines locaux, instanciez votre objet Zend_Validate_Hostname
de la manière suivante :
Certains noms de domaines nationaux (Country Code Top Level Domains ou ccTLD), comme .de
(Allemagne), supporte les caractères internationaux dans leurs noms de domaine. Ceci est
connu sous le nom de Nom de Domaine International (IDN). Ces domaines peuvent être
vérifiés par Zend_Validate_Hostname grâce aux caractères étendus qui sont utilisés dans
le processus de validation.
Pour vérifier un domaine IDN c'est aussi simple que d'utiliser le validateur standard de nom
de domaine puisque la validation IDN est activé par défaut. Si vous voulez mettre hors
service la validation IDN, cela peut être fait par le passage d'un paramètre au constructeur
Zend_Validate_Hostname ou via la méthode $validator->setValidateIdn().
Vous pouvez aussi désactiver la validation IDN en passant un second paramètre au constructeur
du Zend_Validate_Hostname comme ceci :
$validator =
new Zend_Validate_Hostname(
array(
'allow' => Zend_Validate_Hostname::ALLOW_DNS,
'idn' => false
)
);
Notez cependant que les IDNs sont seulement validés si vous autorisez la validation des noms
de domaine.
1504
Zend_Validate
Par défaut un nom de domaine sera vérifié grâce à une liste de TLDs connus. Si cette
fonctionnalité n'est pas nécessaire, elle peut être désactivée de la même façon que la
désactivation du support des IDNs. Vous pouvez désactiver la validation TLD en passant
un troisième paramètre au constructeur de Zend_Validate_Hostname. Dans l'exemple ci-
dessous, la validation IDN est supportée via le second paramètre.
$validator =
new Zend_Validate_Hostname(
array(
'allow' => Zend_Validate_Hostname::ALLOW_DNS,
'idn' => true,
'tld' => false
)
);
Notez cependant que les TLDs sont seulement validés si vous autorisez la validation des noms
de domaine.
2.14. Iban
Retourne TRUE si et seulement si $value contient un IBAN valide (International Bank Account
Number = Numéro de compte bancaire international). Les numéros IBAN sont validés suivant le
pays dans lequel ils sont utilisés et par un somme de contrôle.
Il existe deux manières de valider les numéros IBAN. En premier lieu, vous pouvez fournir une
locale qui représente un pays. Tout numéro IBAN fourni sera donc validé suivant ce pays.
Vous pouvez aussi vouloir valider les numéros IBAN pour des pays uniques. La manière la plus
simple est donc de ne pas fournir de locale comme dans l'exemple suivant.
Mais ceci montre un gros problème : si vous ne devez accepter que les numéros IBAN que pour
un pays en particulier, par exemple l'Allemagne, alors les numéros IBAN des autres pays seront
1505
Zend_Validate
aussi valides. Dans ce cas, souvenez vous d'une chose : quand vous devez valider un numéro
IBAN pour un pays donné, vous avez juste à fournir la locale correspondante. Et quand vous
acceptez tous les numéros IBAN sans restriction de pays, enlevez simplement cette locale.
2.15. InArray
Retourne TRUE si et seulement si $value est contenue dans un tableau. Si l'option stricte est
à TRUE, alors le type de $value est aussi vérifié.
2.16. Int
Retourne TRUE si et seulement si $value est une valeur entière. A partir de Zend Framework 1.8
ce validateur prend en considération la locale courante issue du navigateur, de l'environnement
ou de l'application. Vous pouvez bien sûr utiliser les accesseurs get/setLocale pour changer
ou la fournir au moment de la création de l'instance de ce validateur.
2.17. Ip
Retourne TRUE si et seulement si $value est une adresse IP valide.
2.18. LessThan
Retourne TRUE si et seulement si $value est inférieure à une valeur maximum fournie.
2.19. NotEmpty
Retourne TRUE si et seulement si $value est une valeur non vide.
2.20. Regex
Retourne TRUE si et seulement si $value respecte une expression régulière fournie.
2.21.1. Sitemap_Changefreq
Valide si oui ou non une chaîne utilisable en tant qu'élément "changefreq" dans un document
XML Sitemap. Les valeurs valides sont : "always", "hourly", "daily", "weekly", "monthly", "yearly",
or "never".
Retourne TRUE si et seulement si la valeur est une chaîne et qu'elle vaut une dès fréquences
ci-dessus.
2.21.2. Sitemap_Lastmod
Valide si oui ou non une chaîne utilisable en tant qu'élément "lastmod" dans un document XML
Sitemap. L'élément "lasmod" doit contenir une date sous la forme W3C, optionnellement en
omettant les informations concernant l'heure.
Retourne TRUE si et seulement si la valeur est une chaîne et qu'elle est valide suivant le
protocole.
1506
Zend_Validate
2.21.3. Sitemap_Loc
Valide si oui ou non une chaîne utilisable en tant qu'élément "loc" dans un document XML
Sitemap. Ceci utilise en interne la méthode Zend_Form::check(). Vous pouvez en lire
davantage avec la validation d'URI.
2.21.4. Sitemap_Priority
Valide si oui ou non une valeur est utilisable en tant qu'élément "priority" dans un document XML
Sitemap. La valeur doit être un nombre compris entre 0.0 et 1.0. Ce validateur accepte à la fois
des valeurs numériques ou textuelles.
2.22. StringLength
Retourne TRUE si et seulement si la longueur de la chaîne $value est supérieure à une valeur
minimum et inférieure à une valeur maximum (quand l'option max n'est pas à NULL). La méthode
setMin() lève une exception si la valeur fournie pour la longueur minimum est supérieure à
celle fournie pour la longueur maximum, et la méthode setMax() lève une exception si la valeur
fournie pour la longueur maximum est supérieure à celle fournie pour la longueur minimum.
Cette classe supporte l'UTF-8 et les autres encodages, en se basant sur la valeur courante de
iconv.internal_encoding . Si vous avez besoin de régler un autre encodage, vous pouvez
le faire avec les accesseurs getEncoding() et setEncoding().
3. Chaînes de validation
Souvent, de multiples validations doivent être appliquées à une valeur dans un ordre particulier.
Le code suivant décrit une méthode permettant de solutionner l'exemple de l'introduction, dans
lequel un identifiant doit contenir précisément entre 6 et 12 caractères alphanumériques.
1507
Zend_Validate
// Validation de l'identifiant
if ($validateurChaine->isValid($identifiant)) {
// l'identifiant est testé avec succès
} else {
// l'identifiant échoue aux tests, afficher pourquoi
foreach ($validateurChaine->getMessages() as $message) {
echo "$message\n";
}
}
Les validateurs sont exécutés dans leur ordre d'ajout à Zend_Validate. Dans l'exemple ci-
dessus, l'identifiant est d'abord testé pour vérifier que sa longueur est bien comprise entre
6 et 12 caractères, puis ensuite testé pour vérifier qu'il ne contient bien que des caractères
alphanumériques. Le second test est exécuté quelque soit le résultat du précédent. Ainsi, dans le
cas où les deux tests échouent, getMessages() retournera un message d'échec pour chacun
des validateurs.
Dans certains cas, il peut être utile d'interrompre le processus si l'un des tests échoue.
Zend_Validate permet ce cas de figure via l'usage du deuxième paramètre de la méthode
addValidator(). En positionnant $breakChainOnFailure à TRUE, le validateur ajouté
interrompra la procédure de test s'il échoue, ce qui permet d'éviter de lancer tout autre test qui
serait inutile ou inapproprié dans ce cas de figure. Si l'exemple précédent était écrit comme suit,
la validation alphanumérique ne serait pas lancé si la vérification de la longueur de la valeur
échouait :
$validateurChaine->addValidator(
new Zend_Validate_StringLength(array('min' => 6,
'max' => 12)),
true)
->addValidator(new Zend_Validate_Alnum());
Tout objet qui implémente Zend_Validate_Interface peut être utilisé dans une chaîne de
validation.
1508
Zend_Validate
une valeur booléenne pour savoir si une valeur est validée ou non. Elles fournissent également
des informations sur la raison pour laquelle la validation a échoué sur une valeur. La mise
à disposition de ces raisons d'échec de validation peut être utilisée par une application dans
différents buts, tels que fournir des statistiques pour l'analyse de la facilité d'utilisation.
L'exemple suivant démontre comment un validateur personnalisé très simple pourrait être
écrit. Dans ce cas-ci les règles de validation sont simplement que la valeur d'entrée doit être
une valeur en virgule flottante.
if (!is_float($value)) {
$this->_error();
return false;
}
return true;
}
}
La classe définit un modèle pour son message unique d'échec de validation, qui inclut le
paramètre magique intégré, %value%. L'appel à _setValue() prépare l'objet pour insérer
automatiquement la valeur examinée dans le message d'échec, si la validation de la valeur
échoue. L'appel à _error() trace la raison d'échec de validation. Puisque cette classe
définit seulement un message d'échec, il n'est pas nécessaire de fournir à _error() le
nom du modèle de message d'échec.
1509
Zend_Validate
public $minimum = 0;
public $maximum = 100;
if (!is_numeric($value)) {
$this->_error(self::MSG_NUMERIC);
return false;
}
return true;
}
}
Les propriétés publiques $minimum et $maximum ont été établies pour fournir les frontières
minimum et maximum d'une valeur pour qu'elle soit validée avec succès. La classe définit
également deux variables de message qui correspondent aux propriétés publiques et
permettent que min et max soient employés dans des modèles de message en tant que
paramètres
Noter que simagiques,
n'importe comme avec value.
quel élément de la validation vérifié dans isValid() échoue, un
message approprié d'échec est préparé, et la méthode renvoie immédiatement FALSE. Ces
règles de validation sont donc séquentiellement dépendantes. C'est-à-dire, que si un essai
échoue, il n'y a aucun besoin d'examiner les règles suivantes de validation. Ce besoin peut
exister, cependant. L'exemple suivant illustre comment écrire une classe ayant des règles
indépendantes de validation, où l'objet de validation peut renvoyer des raisons multiples
pour lesquelles une tentative particulière de validation a échoué.
1510
Zend_Validate
$isValid = true;
if (strlen($value) < 8) {
$this->_error(self::LENGTH);
$isValid = false;
}
if (!preg_match('/[A-Z]/', $value)) {
$this->_error(self::UPPER);
$isValid = false;
}
if (!preg_match('/[a-z]/', $value)) {
$this->_error(self::LOWER);
$isValid = false;
}
if (!preg_match('/\d/', $value)) {
$this->_error(self::DIGIT);
$isValid = false;
}
return $isValid;
}
}
Noter que les quatre critères d'essais dans isValid() ne renvoient pas immédiatement
FALSE. Ceci permet à la classe de validation de fournir toutes les raisons pour lesquelles
le mot de passe d'entrée n'a pas réussi à remplir les conditions de validation. Si, par
exemple, un utilisateur entre la chaîne "#$%" comme mot de passe, isValid() entraînera
que les quatre messages d'échec de validation seront retournés lors de l'appel suivant à
getMessages().
1511
Zend_Validate
5. Messages de validation
Chaque validateur basé sur Zend_Validate propose un ou plusieurs messages dans le cas
d'un echec. Vous pouvez utiliser ces informations pour créer vos propres messages ou pour
traduire les messages présents. La table suivante liste tous les messages retournés par tous
les validateurs.
1512
Zend_Validate
1513
Zend_Validate
1514
Zend_Validate
1515
Zend_Validate
1516
Zend_Validate
Il est possible de récupérer tous les messages d'erreurs d'un validator grâce à sa méthode
getMessageTemplates(). Celle-ci retourne un tableau.
1517
Zend_Validate
Zend_Validate::setMessageLength(100);
Notez que la taille des messages affecte aussi les messages personnalisés
enregistrés, si le validateur considéré étend Zend_Validate_Abstract.
1518
Zend_Version
1. Lire la version de Zend Framework
Zend_Version fournit la constante de classe Zend_Version::VERSION qui contient une
chaîne identifiant la version courante de Zend Framework. Zend_Version::VERSION contient
"1.7.2", par exemple.
// retourne -1, 0 or 1
$cmp = Zend_Version::compareVersion('2.0.0');
1519
Zend_View
1. Introduction
Zend_View est une classe qui permet de travailler avec la partie "Vue" du motif de conception
Modèle-Vue-Contrôleur. Elle existe pour aider à garder la vue séparée du modèle et des
scripts du contrôleur. Elle fournie un système d'aide, de filtres d'affichage, et d'échappement de
variables.
Zend_View est un système de template agnostique ; vous pouvez utiliser PHP comme langage
de template, ou créer des instances d'autres systèmes de templates, et les manipuler à travers
les scripts de vue.
1520
Zend_View
1.3. Options
Zend_View possède plusieurs options qui peuvent être réglées pour changer le comportement
de vos scripts de vues.
• basePath : indique le chemin de base où peuvent être trouvés les dossiers de scripts, d'aides
et de filtres. Il considère une structure de dossiers de ce type :
chemin/vers/
helpers/
filters/
scripts/
Ceci peut être paramétré via les méthodes setBasePath(), addBasePath(), ou l'option
basePath du constructeur.
• escape : indique le callback que doit utiliser escape(). Ceci pet être paramétré avec la
méthode setEscape() ou l'option escape du constructeur.
• filter : indique un filtre à utiliser avant d'effectuer le rendu d'un script de vue. Ceci peut
être paramétré avec les méthodes setFilter(), addFilter(), ou l'option filter du
constructeur.
• strictVars : force Zend_View à émettre des "notices" et des "warnings" quand des
variables non initialisées sont lues. Ceci peut être activé en appelant strictVars(true) ou
en passant l'option strictVars au constructeur.
1521
Zend_View
éléments pratiques à utiliser lors de la rédaction de vos scripts de vue, car elles rendent les
constructions plus laconiques, maintiennent les instructions sur des lignes uniques et éliminent
la chasse aux accolades à l'intérieur de l'HTML.
Dans les versions précédentes, nous recommandions souvent l'utilisation des balises courtes (<?
et <?=), car elles rendent les scripts de vues moins verbeux. Cependant, la valeur par défaut du
fichier php.ini pour le réglage short_open_tag est désactivé par défaut en production ou en
hébergement mutualisé ; rendant ainsi vos scripts peu portables. De plus, si vous modélisez du
XML dans vos scripts, la présence des balises courtes entrainera l'échec de la validation. Enfin,
si vous utilisez les balises courtes et que short_open_tag est désactivé, le script retournera
soit des erreurs, soit votre code PHP à l'utilisateur.
Ceci étant dit, de nombreux développeurs préfère utiliser la forme complète pour des
questions de validation ou de portabilité. Par exemple, short_open_tag est désactivé dans le
php.ini.recommended, et si vous avez du XML dans vos scripts de vue, alors les balises courtes
entraîneront un échec de validation du modèle.
De plus, si vous utilisez les balises courtes avec un réglage du paramètre à "off", alors les scripts
de vue vont soit entraîner des erreurs, soit simplement afficher le code à l'utilisateur.
Si malgré ces avertissements, vous souhaitez utiliser les balises courtes mais qu'elles sont
désactivées, vous avez deux options :
Ceci est seulement possible si vous êtes autorisé à créer et utiliser les fichiers .htaccess.
Cette directive peut aussi être ajoutée à votre fichier httpd.conf.
• Activer une enveloppe de flux ("stream wrapper") optionnelle pour convertir les balises courtes
en balises longues à la volée :
$view->setUseStreamWrapper(true);
Ceci enregistre Zend_View_Stream en tant que enveloppe de flux pour les scripts de vue,
et permet de s'assurer que votre code continue à fonctionner comme si les balises courtes
étaient activées.
1522
Zend_View
• clearVars() effacera toutes les variables assignées ; utile si vous souhaitez ré-utiliser un
objet de vue, ou contrôler les variables qui sont disponibles.
• getScriptPaths() récupérera tous les chemins vers les scripts de vues enregistrés.
2. Scripts de contrôleur
Le contrôleur est l'endroit où vous instanciez et configurez Zend_View. Vous assignez ensuite
des variables à la vue, et lui dites comment effectuer le rendu en utilisant un script particulier.
Cependant, ceci peut être pénible quand vous avez déjà collecté (dans un tableau ou dans un
objet) les valeurs à assigner.
La méthode assign() vous laisse assigner "en vrac" depuis un tableau ou un objet. Les
exemples suivants ont le même effet que celui ci-dessus.
Alternativement, vous pouvez utiliser la méthode assign() pour assigner les variables une par
une, en passant le nom de la variable, et sa valeur.
1523
Zend_View
Évidemment, vos scripts sont peut-être localisés ailleurs. Pour dire à Zend_View ou il doit
chercher, utilisez la méthode setScriptPath().
En fait, vous pouvez "empiler" les chemins en utilisant la méthode setScriptPath(). Comme
vous ajoutez des chemins dans la pile, Zend_View va rechercher le script de vue dans le chemin
le plus récemment ajouté. Cela vous permet de passer outre les vues par défaut, pour des
vues personnalisées. Ainsi vous pouvez créer des "thèmes" ou des "skins" pour certaines vues,
pendant que vous laissez les autres intactes.
1524
Zend_View
d'une entrée utilisateur, car vous pourriez ainsi avoir une vulnérabilité d'inclusion
de fichier non voulu si les chemins spécifiés par l'utilisateur sont traversant. Par
exemple, le code suivant peut générer un problème :
// $_GET['foo'] == '../../../etc'
$view->addScriptPath($_GET['foo']);
$view->render('passwd');
3. Scripts de vue
Une fois que le contrôleur a assigné les variables et appelé render(), Zend_View inclue le
script de vue requis et l'exécute "à l'intérieur" de la portée de l'instance Zend_View. Donc dans
vos scripts de vue, les références à $this pointent en fait sur l'instance Zend_View elle-même.
Les variables assignées à la vue depuis le contrôleur lui sont référées comme des propriétés
de l'instance. Par exemple, si le contrôleur a assigné une variable "quelquechose", vous vous
référerez à cette variable par $this->quelquechose dans le script de vue. (Cela vous permet
de garder une trace pour savoir quelles valeurs ont été assignées au script, et lesquelles sont
internes au script lui même.)
Pour rappel, voici l'exemple de script issu de l'introduction de ce chapitre sur Zend_View.
1525
Zend_View
La fonction ou méthode de rappel doit prendre la valeur à échapper dans le premier paramètre,
et tous les autres paramètres devraient être optionnels.
include_once 'template.inc';
$tpl = new Template();
if ($this->livres) {
$tpl->setFile(array(
"listelivre" => "listelivre.tpl",
"chaquelivre" => "chaquelivre.tpl",
));
1526
Zend_View
$tpl->pparse("output", "listelivre");
} else {
$tpl->setFile("nobooks", "pasdelivres.tpl")
$tpl->pparse("output", "pasdelivres");
}
/**
* Retourne l'objet moteur de gabarit actuel
*/
public function getEngine();
/**
* Affecte le dossier des scripts de gabarits
*/
public function setScriptPath($path);
/**
* Règle un chemin de base pour toutes les ressources de vue
*/
public function setBasePath($path, $prefix = 'Zend_View');
/**
* Ajoute un chemin de base supplémentaire pour les ressources de vue
*/
public function addBasePath($path, $prefix = 'Zend_View');
/**
* Récupère les chemins actuels vers les ressources de vue
*/
public function getScriptPaths();
/**
1527
Zend_View
/**
* Affectation manuelle de variable de gabarit, ou possibilité
* d'affecter des variables en masse.
*/
public function assign($spec, $value = null);
/**
* Efface toutes les variables du gabarit déjà affectées
*/
public function clearVars();
/**
* Effectue le rendu du gabarit nommé $name
*/
public function render($name);
En utilisant cette interface, il devient relativement facile d'encapsuler un moteur de gabarit tiers
comme une classe compatible Zend_View. Comme par exemple, le code suivant est une
encapsulation potentielle de Smarty :
/**
* Constructeur
*
* @param string $tmplPath
* @param array $extraParams
* @return void
*/
public function __construct($tmplPath = null,
$extraParams = array())
{
$this->_smarty = new Smarty;
/**
* Retourne l'objet moteur de gabarit
*
1528
Zend_View
* @return Smarty
*/
public function getEngine()
{
return $this->_smarty;
}
/**
* Affecte le dossier des scripts de gabarits
*
* @param string $path Le répertoire à affecter au path
* @return void
*/
public function setScriptPath($path)
{
if (is_readable($path)) {
$this->_smarty->template_dir = $path;
return;
}
/**
* Récupère le dossier courant des gabarits
*
* @return string
*/
public function getScriptPaths()
{
return array($this->_smarty->template_dir);
}
/**
* Alias pour setScriptPath
*
* @param string $path
* @param string $prefix Unused
* @return void
*/
public function setBasePath($path, $prefix = 'Zend_View')
{
return $this->setScriptPath($path);
}
/**
* Alias pour setScriptPath
*
* @param string $path
* @param string $prefix Unused
* @return void
*/
public function addBasePath($path, $prefix = 'Zend_View')
{
return $this->setScriptPath($path);
}
/**
* Affectation une variable au gabarit
*
1529
Zend_View
/**
* Autorise le fonctionnement du test avec empty() and isset()
*
* @param string $key
* @return boolean
*/
public function __isset($key)
{
return (null !== $this->_smarty->get_template_vars($key));
}
/**
* Autorise l'effacement de toutes les variables du gabarit
*
* @param string $key
* @return void
*/
public function __unset($key)
{
$this->_smarty->clear_assign($key);
}
/**
* Affectation de variables au gabarit
*
* Autorise une affectation simple (une clé => une valeur)
* OU
* le passage d'un tableau (paire de clé => valeur)
* à affecter en masse
*
* @see __set()
* @param string|array $spec Le type d'affectation à utiliser
(clé ou tableau de paires clé => valeur)
* @param mixed $value (Optionel) Si vous assignez une variable nommée,
utilisé ceci comme valeur
* @return void
*/
public function assign($spec, $value = null)
{
if (is_array($spec)) {
$this->_smarty->assign($spec);
return;
}
$this->_smarty->assign($spec, $value);
}
/**
* Effacement de toutes les variables affectées
*
* Efface toutes les variables affectées à Zend_View
1530
Zend_View
/**
* Exécute le gabarit et retourne l'affichage
*
* @param string $name Le gabarit à exécuter
* @return string L'affichage
*/
public function render($name)
{
return $this->_smarty->fetch($name);
}
}
1531
Zend_View
4. Aides de vue
Dans vos scripts de vue, il est souvent nécessaire d'effectuer certaines actions complexes
encore et encore : par exemple, formater une date, générer des éléments de formulaire, afficher
des liens d'action. Vous pouvez utiliser des classes d'aide pour effectuer ce genre de tâches.
Une aide est simplement une classe. Par exemple, nous voulons une aide nommée "foobar". Par
défaut, la classe est préfixée avec "Zend_View_Helper_" (vous pouvez spécifier un préfixe
personnalisé en paramétrant votre chemin d'aide), et le dernier segment du nom de classe est
le nom de l'aide ; ce segment peut être avec des CaracteresMajuscules ; le nom complet de
la classe est alors : Zend_View_Helper_FooBar. Cette classe doit contenir au moins une
méthode, nommée comme l'aide avec la notationCamel : fooBar().
Surveillez la casse
Les noms des aides sont toujours en notationCamel, c'est-à-dire qu'ils ne
commencent pas avec un caractère majuscule. Le nom de classe elle-même peut
être en casseMélangée, mais la méthode qui est exécutée est en notationCamel.
Pour utiliser une aide dans votre script de vue, appelez la en utilisant $this->nomAide().
Dans les coulisses, Zend_View va charger la classe Zend_View_Helper_NomAide, créer
une instance de cet objet, et appeler sa méthode nomAide(). L'instance de l'objet est
persistante dans l'instance de Zend_View, et est réutilisée pour tous les appels futurs à $this-
>nomAide().
• declareVars() : initialement prévu pour être utilisé avec strictVars(), cette aide peut
être utilisée pour déclarer les variables de modèle ("template") qui sont (ou pas) déjà déclarées
dans l'objet de vue, ou pour gérer des valeurs par défaut. Les tableaux passés comme
arguments à la méthode seront utilisés pour paramétrer des valeurs par défaut ; sinon, si la
variable n'existe pas, on lui affecte une chaîne vide.
Par défaut, quand aucune $value n'est fournie et qu'aucune $options n'est présente, alors
"0" est considéré comme la valeur non cochée et "1" comme la valeur cochée. Si une $value
1532
Zend_View
est fournie, mais qu'aucune $options n'est présente, l'état coché est considéré égal à la
$value fournie.
$options devrait être un tableau. Si ce tableau est indexé, la première valeur est la valeur
cochée, la seconde est la valeur non cochée ; et tout autre valeur est ignorée. Vous pouvez
aussi passer un tableau associatif avec les clés "checked" et "unChecked".
Si $options est fourni, et que $value correspond à la valeur cochée, alors l'élément sera
marqué comme coché. Vous pouvez aussi marquer l'élément comme coché ou décoché en
passant une valeur booléenne à l'attribut "checked".
Dans tous les cas, la balise est précédée d'un élément masqué ("hidden") avec la valeur de
l'état décoché ; ainsi, si la valeur est décochée, vous aurez toujours une valeur valide retournée
par votre formulaire.
• formErrors($errors, $options) : génère une liste non ordonnée XHTML pour montrer
des erreurs. $errors peut être une chaîne de caractères ou un tableau de chaînes ;
$options peut être tout attribut que vous pourriez vouloir placer dans la balise ouvrante de
la liste.
1533
Zend_View
Vous pouvez spécifier des éléments ouvrants, fermants et des séparateurs de contenu
alternatifs lors du rendu des erreurs en appelant les différentes méthodes suivantes de l'aide :
• url($urlOptions, $name, $reset) : crée un URL basé sur une route nommée.
$urlOptions doit être un tableau associatif avec des paires de clés/valeurs utilisées par une
route particulière.
1534
Zend_View
Les utiliser dans vos script de vue est très simple, voici un exemple. Notez que tout ce dont vous
avez besoin, c'est de les appeler; elles vont se charger et s'instancier elle-même si besoin est.
<!--
Dans votre script de vue, $this se réfère à l'instance de Zend_View.
Partons du principe que vous avez déjà assigné une série d'options
de sélection dans un tableau $pays =
array('us' => 'Etats-Unis', 'fr' => 'France', 'de' => 'Allemagne').
-->
<form action="action.php" method="post">
<p><label>Votre email :
<?php echo $this->formText('email',
'vous@exemple.fr',
array('size' => 32)) ?>
</label></p>
<p><label>Votre pays :
<?php echo $this->formSelect('country',
'us',
null,
$this->pays) ?>
</label></p>
<p><label>??? Would you like to opt in ???
<?php echo $this->formCheckbox('opt_in',
'oui',
null,
array('oui', 'non')) ?>
</label></p>
</form>
Les actions qui ont comme conséquence un _forward() ou une redirection sont considérées
invalide, et retourneront une chaîne vide.
1535
Zend_View
L'API pour l'aide de vue Action respecte le même schéma que la plupart les composants MVC
qui appellent des actions de contrôleur : action($action, $controller, $module =
null, array $params = array()). $action et $controller sont exigés ; si aucun
module n'est spécifié, le module par défaut est implicite.
/*
* Imaginons une URL de base dans page/application de "/mypage".
*/
/*
* affiche:
* <base href="/mypage/" />
*/
<base href="<?php echo $this->baseUrl(); ?>" />
/*
* affiche:
* <link rel="stylesheet" type="text/css" href="/mypage/css/base.css" />
*/
<link rel="stylesheet" type="text/css"
href="<?php echo $this->baseUrl('css/base.css'); ?>" />
1536
Zend_View
Pour séléctionner la valeur de la monnaie à utiliser, passez une chaine ou encore une locale
ce qui est recommandé car les informations provenant du client HTTP seront alors utilisées par
défaut.
Nous parlons bien de "locales" et non de "langues" car la langue peut varier
en fonction de la position géorgraphique au sein d'un même pays.Par exemple,
l'anglais est parlé dans différents dialectes : Anglais ou Américain. Une monnaie
est liée directement à une région, vous devrez donc utiliser une locale complète
c'est à dire représentant le pays et la région. Nous parlons donc de "locale" plutôt
que de "langue."
Pour utiliser une instance en registre, créez une instance de Zend_Currency et enregistrez
la dans Zend_Registry en utilisant la clé Zend_Currency.
Si vous préférez utiliser l'interface fluide, vous pouvez alors créer une instance dans votre vue
et la configurer après cela.
Pour utiliser l'interface fluide, créez une instance de Zend_Currency, appelez l'aide de
vue sans paramètre et appelez ensuite la méthode setCurrency().
Si vous utilisez l'aide sans Zend_View alors vous pouvez aussi l'utiliser de manière directe, via
instanciation manuelle.
1537
Zend_View
Comme déja vu, la méthode currency() est utilisée pour retourner la chaine représentant la
monnaie. Appelez la simplement avec la valeur que vous voulez voir affichée. Des options sont
aussi disponibles, elles servent à changer le comportement interne de l'aide.
Pour ajouter des éléments dans le cycle, spécifiez le simplement dans le constructeur ou
utilisez assign(array $data)
La sortie:
<tr style="background-color:'#F0F0F0'">
<td>First</td>
</tr>
<tr style="background-color:'#FFFFFF'">
<td>Second</td>
</tr>
1538
Zend_View
Une soeur de l'aide Partial, l'aide de vue de PartialLoop vous permet de passer des
données itératives, et effectue un rendu partiel pour chaque élément.
Compteur de PartialLoop
L'aide de vue PartialLoop assigne une variable à la vue nommée
partialCounter qui fournit la position courante du tableau au script de vue.
Ce qui permet simplement d'avoir des couleurs alternatives dans les lignes d'un
tableau par exemple.
L'utilisation de base des Partials est d'effectuer le rendu d'un fragment de modèle dans
sa propre portée de vue. Examinez le script partiel suivant :
<!--partiel.phtml-->
<ul>
<li>De : <?php echo $this->escape($this->de) ?></li>
<li>Sujet : <?php echo $this->escape($this->sujet) ?></li>
</ul>
Vous l'appelleriez alors dans votre script de vue en utilisant ce qui suit :
<ul>
<li>De : Equipe Framework</li>
<li>Sujet : vues partielles</li>
</ul>
• Tableaux ("array") : si un tableau est fourni, il devrait être associatif, car ses
paires de clé/valeur sont assignées à la vue avec les clés comme variables
de vue.
1539
Zend_View
Si votre modèle est un objet, vous pouvez vouloir le fournir en tant qu'objet au
script partiel, plutôt que de le sérialiser en un tableau de variables. Vous pouvez
faire ceci en paramétrant la propriété "objectKey" de l'aide de vue approprié :
1540
Zend_View
Typiquement, vous voudrez employer des Partials dans une boucle, pour rendre le même
fragment de contenu plusieurs fois ; de cette façon vous pouvez mettre de grands blocs de
contenu répété ou de logique complexe d'affichage dans un endroit unique. Toutefois ceci
a un impact d'exécution, car l'aide Partial doit être appelée une fois par itération.
L'aide de vue PartialLoop résout ce problème. Elle vous permet de fournir un élément
itérable (tableau ou objet implémentant Iterator) comme modèle. Elle réitère alors au-
dessus de celui-ci en fournissant les éléments au script partiel. Les éléments dans l'itérateur
peuvent être n'importe quel modèle que l'aide de vue Partial permet (cf. ci-dessus).
<!--partialLoop.phtml-->
<dt><?php echo $this->key ?></dt>
<dd><?php echo $this->value ?></dd>
Et le "modèle" suivant :
$model = array(
array('key' => 'Mammifère', 'value' => 'Chameau'),
array('key' => 'Oiseau', 'value' => 'Pingouin'),
array('key' => 'Reptile', 'value' => 'Asp'),
array('key' => 'Poisson', 'value' => 'Flounder')
);
Dans votre script de vue, vous pouvez maintenant appeler l'aide PartialLoop :
<dl>
<?php echo $this->partialLoop('partialLoop.phtml', $model) ?>
</dl>
<dl>
<dt>Mammifère</dt>
<dd>Chameau</dd>
<dt>Oiseau</dt>
<dd>Pingouin</dd>
<dt>Reptile</dt>
<dd>Asp</dd>
<dt>Poisson</dt>
<dd>Flounder</dd>
</dl>
1541
Zend_View
Parfois un partiel existera dans un module différent. Si vous connaissez le nom du module,
vous pouvez le fournir comme deuxième argument à partial() ou à partialLoop(),
en déplaçant l'argument $model à la troisième position.
Par exemple, s'il y a un gestionnaire de page partiel que vous souhaitez utiliser et qui est
dans le module "liste", vous pourriez le saisir comme suit :
De cette façon, vous pouvez réutiliser des partiels créés spécifiquement pour d'autre
modules. Ceci dit, il est probablement une meilleure pratique de mettre des partiels
réutilisables dans des dossiers partagés de script de vue.
L'aide de vue Placeholder est utilisé pour faire persister le contenu entre les scripts de vues
et les instances de vues. Il offre aussi des fonctionnalités utiles comme l'agrégation de contenu,
la capture de contenu de scripts de vues pour une utilisation ultérieure, et l'ajout de texte pré ou
post contenu (et la personnalisation des séparateurs de contenu).
L'utilisation basique des placeholders est la persistance des données de vue. Chaque appel
de l'aide Placeholder attend un nom de placeholder ; l'aide retourne un objet conteneur
que vous pouvez soit manipuler ou simplement envoyé à l'affichage.
1542
Zend_View
L'agrégation du contenu via les placeholders peut être aussi utile parfois. Par exemple,
votre script de vue peut avoir une variable sous forme de tableau à partir de laquelle vous
souhaitez récupérer des messages à afficher plus tard ; un autre script de vue peut ensuite
déterminer de la manière suivant laquelle ils seront affichés.
L'aide de vue Placeholder utilise des conteneurs qui étendent ArrayObject, fournissant
de riches fonctionnalités de manipulations des tableaux. De plus, il offre une variété de
méthodes pour le formatage du contenu stockée dans le conteneur :
• setPostfix($prefix) paramètre le texte qui sera placé après tout le contenu. Utilisez
getPostfix() à tout moment pour déterminer le réglage courant.
• setIndent($prefix) peut être utilisé pour paramétrer une indentation pour chaque
élément du contenu. Si un entier est fourni, il s'agira du nombre d'espaces à utiliser ;
si une chaîne est fournie, elle sera utilisée. Utilisez getIndent() à tout moment pour
déterminer le réglage courant.
1543
Zend_View
$type doit être une des constantes de Placeholder : APPEND ou SET. Si c'est
APPEND, le contenu capturé est ajouté à la liste de contenu courant dans le placeholder ;
si c'est SET, le contenu capturé remplace toute valeur existante dans le placeholder
(potentiellement permet de remplacer tout contenu préalable). Par défaut, $type vaut
APPEND.
$key peut être utilisé pour spécifier une clé en particulier dans le conteneur placeholder
dans laquelle vous souhaitez capturer le contenu.
La documentation pour chacun des éléments existe séparément, suivez les liens ci-dessous :
• Doctype
1544
Zend_View
• HeadLink
• HeadMeta
• HeadScript
• HeadStyle
• HeadTitle
• InlineScript
• XHTML11
• XHTML1_STRICT
• XHTML1_TRANSITIONAL
• XHTML1_FRAMESET
• XHTML_BASIC1
• HTML4_STRICT
• HTML4_LOOSE
• HTML4_FRAMESET
• HTML5
Vous pouvez aussi spécifier un doctype personnalisé du moment que celui-ci soit correctement
formé.
Vous pouvez spécifier le doctype à n'importe quel moment. Cependant, les aides de vues
qui utilisent pour leur affichage ne le reconnaissent qu'une fois qu'il a été paramètré, donc
la manière la plus simple est de le spécifier dans votre fichier d'amorçage :
1545
Zend_View
Si vous avez besoin du doctype, vous pouvez le récupérer par l'appel de getDoctype()
sur l'objet.
$doctype = $view->doctype()->getDoctype();
Typiquement, vous pouvez simplement vouloir savoir si le doctype est XHTML ou non ; pour
ceci, la méthode isXhtml() vous suffira :
if ($view->doctype()->isXhtml()) {
// faire qqch de différent
}
if ($view->doctype()->isHtml5()) {
// faire qqch de différent
}
L'aide HeadLink possède des méthodes pour ajouter des liens de feuilles de style dans sa pile :
La valeur par défaut de $media vaut screen, mais peut être n'importe quelle valeur de média
valide. $conditionalStylesheet est une chaîne ou le booléen FALSE, et sera utilisé au
moment du rendu pour déterminer si des commentaires spéciaux doivent être inclus pour
empêcher le chargement de la feuille de style sur certaines plate-formes. $extras est un tableau
de valeurs supplémentaires que vous voulez ajouter à la balise.
De plus, l'aide HeadLink possède des méthodes pour ajouter des liens alternatifs dans sa pile :
1546
Zend_View
Vous pouvez spécifier un headLink à n'importe quel moment. Typiquement, vous pouvez
spécifier des liens globaux dans votre script de disposition, et des liens spécifiques à
l'application dans vos scripts de vue. Dans votre script de disposition, dans la section
<head>, vous pourrez ensuite afficher le résultat de l'aide.
L'aide de vue HeadMeta supporte les méthodes suivantes pour le paramétrage et l'ajout de
métadonnées :
Le paramètre $keyValue est utilisé pour définir une valeur pour la clé "name" ou "http-equiv" ;
$content est la valeur pour la clé "content", et $modifiers est un tableau associatif optionel
qui peut contenir les clés "lang" et/ou "scheme".
Vous pouvez aussi spécifier les métadonnées en utilisant la méthode headMeta() qui a
la signature suivante : headMeta($content, $keyValue, $keyType = 'name',
$modifiers = array(), $placement = 'APPEND'). $keyValue est le contenu de la
clé spécifiée dans $keyType, qui peut être "name" ou "http-equiv". $placement peut être soit
"SET" (efface toutes les valeurs sauvegardées précédentes), soit "APPEND" (ajout en fin de
pile), soit "PREPEND" (ajout en début de pile).
1547
Zend_View
Vous pouvez spécifier une nouvelle métadonnée à n'importe quel moment. Typiquement,
vous pouvez spécifier les règles de mise en cache côté client ou les mots clés SEO (Search
Engine Optimization : pour l'optimisation des moteurs de recherche).
Par exemple, si vous souhaitez spécifier des mots clés SEO, vous devez créer une
métadonnée de type "name" ayant pour nom "keywords" et pour contenu les mots clés que
vous souhaitez associer à votre page :
Si vous souhaitez paramètrer des règles de mise en cache côté client, vous devez créer
une métadonnée de type "http-equiv" avec les règles que vous souhaitez imposer :
Une autre utilisation habituelle des métadonnées est le réglage du type de contenu ("content
type"), de l'encodage, et le langage :
Si vous proposez un document HTML5, vous pouvez fournir l'encodage de cette manière :
Et comme exemple final, une manière simple d'afficher un message de transition avant une
redirection est d'utiliser une métadonnée "refresh" :
Quand vous êtes prêts à placer vos métadonnées dans votre script de disposition, réalisez
un "echo" de l'aide :
1548
Zend_View
L'élément HTML <script> est utilisé pour à la fois pour fournir des éléments de scripts côté-
client dans le code HTML et aussi pour lier une ressource distante contenant du script côté-
client. L'aide de vue HeadScript vous permet de gérer ces deux cas.
L'aide de vue HeadScript supportent les méthodes suivantes pour paramétrer ou ajouter des
scripts :
Dans le cas des méthodes de type *File(), $src est l'emplacement distant du script à charger ;
c'est généralement sous la forme d'une URL ou d'un chemin de fichier. Pour les méthode de
type *Script(), $script sont les directives de script côté-client que vous souhaitez utiliser
dans l'élément.
// adding scripts
$this->headScript()->appendFile('/js/prototype.js',
'text/javascript',
array('conditional' => 'lt IE 7');
HeadScript vous permet aussi de capturer des scripts ; ceci peut être utile si vous voulez créer
du script côté-client par programmation, et ensuite le placer n'importe où. Une utilisation de ceci
est montré dans un exemple ci-dessous.
Enfin, vous pouvez aussi utiliser la méthode headScript() pour rapidement ajouter des
éléments de script ; le prototype dans ce cas est headScript($mode = 'FILE', $spec,
1549
Zend_View
$placement = 'APPEND'). Le $mode est soit "FILE", soit "SCRIPT", suivant si vous liez un
script ou que vous en définissiez un. $spec est soit le script à lier ou la source du script elle-
même. $placement est "APPEND", "PREPEND", ou "SET".
$this->headScript()->setAllowArbitraryAttributes(true);
1550
Zend_View
Vous devriez ajouter une nouvelle balise de script à chaque fois. Comme noté ci-dessus,
ceux-ci peuvent être des liens vers des ressources externes ou vers les scripts eux-mêmes.
// ajout de scripts
$this->headScript()->appendFile('/js/prototype.js')
->appendScript($onloadScript);
L'ordre est souvent important avec les scripts côté-client ; vous devez vous assurer de
charger les librairies dans un ordre spécifique en fonction de leurs dépendances ; utilisez à
la fois les directives append, prepend, et offsetSet pour vous aider dans cette tâche :
Quand vous êtes finalement prêt à afficher tous les scripts dans votre layout, faîtes
simplement un echo de l'aide :
Parfois vous devez générer des scripts côté-client par programme. Même si vous pouvez
employer la concaténation de chaînes, les "heredocs", ou tout autre équivalent, il est souvent
plus facile de faire juste la création des scripts et de les entourer par des balises PHP.
HeadScript vous permet de le faire, et capture ainsi l'élément dans la pile :
• Les déclarations de scripts sont ajoutées à la pile. Si vous souhaitez qu'elles remplacent la
pile ou qu'elles soient ajoutées en début de pile, vous devez fournir "SET" ou "PREPEND",
en tant que premier argument de captureStart().
1551
Zend_View
HeadLink devrait être utilisé pour inclure des feuilles de styles externes.
HeadStyle ne devrait être utilisé que si vous souhaitez définir des feuilles de
styles internes.
L'aide de vue HeadStyle supporte les méthodes suivantes pour l'ajout et le paramétrage des
déclarations de feuilles de styles :
Dans tous les cas, $content est le contenu des déclarations CSS. $attributes sont les
attributs additionnels que vous pouvez fournir à la balise style : "lang", "title", "media", ou "dir"
sont autorisés.
HeadStyle vous permet d'englober vos balises de style avec des commentaires
conditionnels, ce qui permet de les masquer pour des navigateurs spécifiques.
Pour ajouter les balises conditionnelles, fournissez le paramètre conditional
en tant que partie du paramètre $attrs lors de l'appel de la méthode.
// adding scripts
$this->headStyle()->appendStyle($styles, array('conditional' => 'lt IE 7'));
HeadStyle permet aussi la capture des déclarations de style ; ceci peut être utile si vous voulez
créer des déclarations par programme, et ensuite les placer à un autre endroit. L'utilisation de
cette fonctionnalité est montrée dans un exemple ci-dessous.
Enfin, vous pouvez utiliser la méthode headStyle() pour ajouter rapidement des éléments
de déclarations ; la signature de la méthode est headStyle($content$placement =
'APPEND', $attributes = array()). $placement peut être "APPEND", "PREPEND",
ou "SET".
1552
Zend_View
By default, Zend Framework uses UTF-8 as its default encoding, and, specific to
this case, Zend_View does as well. Character encoding can be set differently on
the view object itself using the setEncoding() method (or the the encoding
instantiation parameter). However, since Zend_View_Interface does not
define accessors for encoding, it's possible that if you are using a custom view
implementation with this view helper, you will not have a getEncoding()
method, which is what the view helper uses internally for determining the
character set in which to encode.
If you do not want to utilize UTF-8 in such a situation, you will need to implement
a getEncoding() method in your custom view implementation.
Vous pouvez spécifier une nouvelle balise de style à n'importe quel moment :
// ajout de styles
$this->headStyle()->appendStyle($styles);
L'ordre est très important avec les CSS ; vous pouvez devoir assurer que les déclarations
soient chargées dans un ordre spécifique dû à l'ordre de la cascade ; employez les diverses
directives "append", "prepend", et "offsetSet" pour faciliter cette tâche :
// - placer à la fin
$this->headStyle()->appendStyle($stylesFinaux);
// - placer au début
$this->headStyle()->prependStyle($stylesInitiaux);
Quand vous êtes finalement prêt à afficher toutes les déclarations de styles dans votre script
de layout, faîtes un simple echo de l'aide :
1553
Zend_View
Parfois vous devez produire des déclarations de styles CSS par programme. Même si vous
pouvez employer la concaténation de chaînes, les "heredocs", ou tout autre équivalent, il
est souvent plus facile de faire juste la création des styles et de les entourer par des balises
PHP. HeadStyle vous permet de le faire, et capture ainsi l'élément dans la pile :
• Les déclarations de styles sont ajoutées à la pile. Si vous souhaitez qu'elles remplacent la
pile ou qu'elles soient ajoutées en début de pile, vous devez fournir "SET" ou "PREPEND",
en tant que premier argument de captureStart().
L'élément HTML <title> est utilisé pour fournir un titre à un document HTML. L'aide
HeadTitle vous permet par programme de créer et de stocker le titre afin de le récupérer plus
tard et de l'afficher.
L'aide HeadTitle est une implémentation concrète de l'aide Placeholder. Elle surcharge la
méthode toString() pour forcer la génération d'un élément <title>, et ajoute une méthode
headTitle() pour des réglages faciles et rapides et pour l'aggrégation des éléments du titre.
La signature de la méthode est headTitle($title, $setType = 'APPEND') ; par défaut,
la valeur est ajoutée en fin de pile (pour aggréger les segments du titre), mais vous pouvez aussi
spécifier "PREPEND" (pour l'ajouter en début de pile) ou "SET" (pour remplacer la pile existante).
1554
Zend_View
Vous pouvez spécifier la balise de titre à n'importe quel moment. Un usage typique serait
de paramètrer les différents segments du titre à chaque niveau de profondeur de votre
application : site, module, contrôleur, action et ressources potentielles.
Quand vous êtes finalement prêt à afficher le titre dans votre script de disposition, faîtes
simplement un echo de l'aide :
Toutes ces aides partagent une interface similaire. Pour cette raison, cette documentation ne
présentera des exemples que pour deux de ces aides.
Inclure du Flash dans votre page est assez simple. Le seul argument requis est l'URI de
la ressource.
1555
Zend_View
Cependant vous pouvez aussi spécifier des attributs, des paramètres et du contenu qui peuvent
être affichés avec la balise <object>. Ceci peut être montré avec l'aide de vue htmlObject.
Le premier argument de l'aide Object est toujours requis. Il s'agit de l'URI de la ressource
à inclure. Le second argument est seulement requis par l'aide htmlObject. Les autres
aides contiennent déjà la bonne valeur pour cet argument. Le troisième argument est
utilisé pour fournir des attributs à l'élément object. Seul un tableau de paires clé/valeur
est accepté. classid ou codebase sont des exemples de tels attributs. Le quatrième
paramètre ne prend aussi qu'un tableau de paires clé/valeur est les utilise pour créer des
éléments <param>. Enfin, vous avez la possibilité de fournir un contenu additionnel à l'objet
en cinquième paramètre. Voici donc un exemple qui utilise tous le paramètres :
InlineScript, peut être utilisé quand vous souhaitez inclure des scripts dans
votre body HTML. Placer ces scripts en fin de votre document est une bonne
pratique pour améliorer la vitesse de distribution de votre page, particulièrement
quand vous utilisez des scripts d'analyses fournis par des tiers.
Certaines librairies JS doivent être incluses dans la partie head du HTML ; utilisez
l'aide vue HeadScript pour ces scripts.
1556
Zend_View
Content-Type: application/json
Beaucoup de librairies AJAX recherche cet en-tête quand elles analysent les réponses pour
déterminer comment le contenu doit être géré.
Each method in the JSON helper accepts a second, optional argument. This
second argument can be a boolean flag to enable or disable layouts, or an array
of options that will be passed to Zend_Json::encode() and used internally to
encode data.
To keep layouts, the second parameter needs to be boolean TRUE. When the
second parameter is an array, keeping layouts can be achieved by including a
keepLayouts key with a value of a boolean TRUE.
• Breadcrumbs, used for rendering the path to the currently active page.
1557
Zend_View
• Links, used for rendering navigational head links (e.g. <link rel="next" href="..." />)
• Sitemap, used for rendering sitemaps conforming to the Sitemaps XML format.
• getContainer() and setContainer() gets and sets the navigation container the helper
should operate on by default, and hasContainer() checks if the helper has container
registered.
• getTranslator() and setTranslator() gets and sets the translator used for translating
labels and titles. getUseTranslator() and setUseTranslator() controls whether the
translator should be enabled. The method hasTranslator() checks if the helper has a
translator registered.
• getAcl(), setAcl(), getRole() and setRole(), gets and sets ACL (Zend_Acl)
instance and role (String or Zend_Acl_Role_Interface) used for filtering out pages when
rendering. getUseAcl() and setUseAcl() controls whether ACL should be enabled. The
methods hasAcl() and hasRole() checks if the helper has an ACL instance or a role
registered.
• __toString(), magic method to ensure that helpers can be rendered by echoing the helper
instance directly.
In addition to the method stubs from the interface, the abstract class also implements the
following methods:
• getIndent() and setIndent() gets and sets indentation. The setter accepts a String or an
Integer. In the case of an Integer, the helper will use the given number of spaces for indentation.
I.e., setIndent(4) means 4 initial spaces of indentation. Indentation can be specified for all
helpers except the Sitemap helper.
• getMinDepth() and setMinDepth() gets and sets the minimum depth a page must have
to be included by the helper. Setting NULL means no minimum depth.
• getMaxDepth() and setMaxDepth() gets and sets the maximum depth a page can have
to be included by the helper. Setting NULL means no maximum depth.
• __call() is used for proxying calls to the container registered in the helper, which means
you can call methods on a helper as if it was a container. See example below.
1558
Zend_View
retrieved from getMinDepth() and getMaxDepth(). The deepest active page must be
between $minDepth and $maxDepth inclusively. Returns an array containing a reference to
the found page instance and the depth at which the page was found.
• accept() is used for determining if a page should be accepted when iterating containers.
This method checks for page visibility and verifies that the helper's role is allowed access to
the page's resource and privilege.
• The static method setDefaultAcl() is used for setting a default ACL object that will be
used by helpers.
• The static method setDefaultRole() is used for setting a default ACL that will be used
by helpers
Navigation view helpers use the magic method __call() to proxy method calls to the
navigation container that is registered in the view helper.
$this->navigation()->addPage(array(
'type' => 'uri',
'label' => 'New page'));
The call above will add a page to the container in the Navigation helper.
The navigation helpers support translation of page labels and titles. You can set a translator
of type Zend_Translate or Zend_Translate_Adapter in the helper using $helper-
>setTranslator($translator), or like with other I18n-enabled components; by adding the
translator to the registry by using the key Zend_Translate.
The proxy helper will inject its own translator to the helper it proxies to if the proxied helper doesn't
already have a translator.
There is no translation in the sitemap helper, since there are no page labels or
titles involved in an XML sitemap.
All navigational view helpers support ACL inherently from the class
Zend_View_Helper_Navigation_HelperAbstract. A Zend_Acl object can be assigned
to a helper instance with $helper->setAcl($acl), and role with $helper-
>setRole('member') or $helper->setRole(new Zend_Acl_Role('member')) . If
1559
Zend_View
ACL is used in the helper, the role in the helper must be allowed by the ACL to access a page's
resource and/or have the page's privilege for the page to be included when rendering.
If a page is not accepted by ACL, any descendant page will also be excluded from rendering.
The proxy helper will inject its own ACL and role to the helper it proxies to if the proxied helper
doesn't already have any.
This example shows the setup of a navigation container for a fictional software company.
• Unless otherwise is stated in other examples, the user is requesting the URL http://
www.example.com/products/server/faq/, which translates to the page labeled FAQ
under Foo Server.
• The assumed ACL and router setup is shown below the container setup.
/*
* Navigation container (config/array)
1560
Zend_View
1561
Zend_View
array(
'label' => 'Investor Relations',
'module' => 'company',
'controller' => 'about',
'action' => 'investors'
),
array(
'label' => 'News',
'class' => 'rss', // class
'module' => 'company',
'controller' => 'news',
'action' => 'index',
'pages' => array(
array(
'label' => 'Press Releases',
'module' => 'company',
'controller' => 'news',
'action' => 'press'
),
array(
'label' => 'Archive',
'route' => 'archive', // route
'module' => 'company',
'controller' => 'news',
'action' => 'archive'
)
)
)
)
),
array(
'label' => 'Community',
'module' => 'community',
'controller' => 'index',
'action' => 'index',
'pages' => array(
array(
'label' => 'My Account',
'module' => 'community',
'controller' => 'account',
'action' => 'index',
'resource' => 'mvc:community.account' // resource
),
array(
'label' => 'Forums',
'uri' => 'http://forums.example.com/',
'class' => 'external' // class
)
)
),
array(
'label' => 'Administration',
'module' => 'admin',
'controller' => 'index',
'action' => 'index',
'resource' => 'mvc:admin', // resource
'pages' => array(
array(
'label' => 'Write new article',
'module' => 'admin',
1562
Zend_View
// ...or simply:
$view->navigation($container);
// Setup ACL:
$acl = new Zend_Acl();
$acl->addRole(new Zend_Acl_Role('member'));
$acl->addRole(new Zend_Acl_Role('admin'));
$acl->add(new Zend_Acl_Resource('mvc:admin'));
$acl->add(new Zend_Acl_Resource('mvc:community.account'));
$acl->allow('member', 'mvc:community.account');
$acl->allow('admin', null);
Breadcrumbs are used for indicating where in a sitemap a user is currently browsing, and
are typically rendered like this: "You are here: Home > Products > FantasticProduct 1.0". The
1563
Zend_View
breadcrumbs helper follows the guidelines from Breadcrumbs Pattern - Yahoo! Design Pattern
Library, and allows simple customization (minimum/maximum depth, indentation, separator, and
whether the last element should be linked), or rendering using a partial view script.
The Breadcrumbs helper works like this; it finds the deepest active page in a navigation
container, and renders an upwards path to the root. For MVC pages, the "activeness"
of a page is determined by inspecting the request object, as stated in the section on
Zend_Navigation_Page_Mvc.
The helper sets the minDepth property to 1 by default, meaning breadcrumbs will not be
rendered if the deepest active page is a root page. If maxDepth is specified, the helper will stop
rendering when at the specified depth (e.g. stop at level 2 even if the deepest active page is
on level 3).
• {get|set}Partial() gets/sets a partial view script that should be used for rendering
breadcrumbs. If a partial view script is set, the helper's render() method will use the
renderPartial() method. If no partial is set, the renderStraight() method is used.
The helper expects the partial to be a String or an Array with two elements. If the partial is a
String, it denotes the name of the partial script to use. If it is an Array, the first element will
be used as the name of the partial view script, and the second element is the module where
the script is found.
1564
Zend_View
This example shows how to customze breadcrumbs output by specifying various options.
<?php
echo $this->navigation()
->breadcrumbs()
->setLinkLast(true) // link last page
->setMaxDepth(1) // stop at level 1
->setSeparator(' ▶' . PHP_EOL); // cool separator with newline
?>
Output:
<a href="/products">Products</a> &#9654;
<a href="/products/server">Foo Server</a>
/////////////////////////////////////////////////////
<?php
$this->navigation()->breadcrumbs()->setMinDepth(10);
echo $this->navigation()->breadcrumbs();
?>
Output:
Nothing, because the deepest active page is not at level 10 or deeper.
This example shows how to render customized breadcrumbs using a partial vew script. By
calling setPartial(), you can specify a partial view script that will be used when calling
render(). When a partial is specified, the renderPartial() method will be called. This
method will find the deepest active page and pass an array of pages that leads to the active
page to the partial view script.
In a layout:
$partial = ;
echo $this->navigation()->breadcrumbs()
->setPartial(array('breadcrumbs.phtml', 'default'));
Contents of application/modules/default/views/breadcrumbs.phtml:
Output:
The links helper is used for rendering HTML LINK elements. Links are used for describing
document relationships of the currently active page. Read more about links and link types at
1565
Zend_View
Document relationships: the LINK element (HTML4 W3C Rec.) and Link types (HTML4 W3C
Rec.) in the HTML4 W3C Recommendation.
There are two types of relations; forward and reverse, indicated by the keyords 'rel' and
'rev'. Most methods in the helper will take a $rel param, which must be either 'rel' or
'rev'. Most methods also take a $type param, which is used for specifying the link type (e.g.
alternate, start, next, prev, chapter, etc).
Relationships can be added to page objects manually, or found by traversing the container
registered in the helper. The method findRelation($page, $rel, $type) will first try to
find the given $rel of $type from the $page by calling $page->findRel($type) or $page-
>findRel($type). If the $page has a relation that can be converted to a page instance,
that relation will be used. If the $page instance doesn't have the specified $type, the helper
will look for a method in the helper named search$rel$type (e.g. searchRelNext() or
searchRevAlternate()). If such a method exists, it will be used for determining the $page's
relation by traversing the container.
Not all relations can be determined by traversing the container. These are the relations that will
be found by searching:
• searchRelNext(), forward 'next' relation; finds the next page in the container, i.e. the page
after the active page.
• searchRelPrev(), forward 'prev' relation; finds the previous page, i.e. the page before the
active page.
• searchRelChapter(), forward 'chapter' relations; finds all pages on level 0 except the 'start'
relation or the active page if it's on level 0.
• searchRelSection(), forward 'section' relations; finds all child pages of the active page if
the active page is on level 0 (a 'chapter').
• searchRelSubsection(), forward 'subsection' relations; finds all child pages of the active
page if the active pages is on level 1 (a 'section').
• searchRevSection(), reverse 'section' relation; finds the parent of the active page if the
active page is on level 1 (a 'section').
• searchRevSubsection(), reverse 'subsection' relation; finds the parent of the active page
if the active page is on level 2 (a 'subsection').
The helper also supports magic methods for finding relations. E.g. to find forward
alternate relations, call $helper->findRelAlternate($page), and to find reverse
1566
Zend_View
To customize which relations should be rendered, the helper uses a render flag. The render flag
is an integer value, and will be used in a bitwse and (&) operation against the helper's render
constants to determine if the relation that belongs to the render constant should be rendered.
• Zend_View_Helper_Navigation_Link::RENDER_ALTERNATE
• Zend_View_Helper_Navigation_Link::RENDER_STYLESHEET
• Zend_View_Helper_Navigation_Link::RENDER_START
• Zend_View_Helper_Navigation_Link::RENDER_NEXT
• Zend_View_Helper_Navigation_Link::RENDER_PREV
• Zend_View_Helper_Navigation_Link::RENDER_CONTENTS
• Zend_View_Helper_Navigation_Link::RENDER_INDEX
• Zend_View_Helper_Navigation_Link::RENDER_GLOSSARY
• Zend_View_Helper_Navigation_Link::RENDER_COPYRIGHT
• Zend_View_Helper_Navigation_Link::RENDER_CHAPTER
• Zend_View_Helper_Navigation_Link::RENDER_SECTION
• Zend_View_Helper_Navigation_Link::RENDER_SUBSECTION
• Zend_View_Helper_Navigation_Link::RENDER_APPENDIX
• Zend_View_Helper_Navigation_Link::RENDER_HELP
• Zend_View_Helper_Navigation_Link::RENDER_BOOKMARK
• Zend_View_Helper_Navigation_Link::RENDER_CUSTOM
• Zend_View_Helper_Navigation_Link::RENDER_ALL
1567
Zend_View
• searchRel{Start|Next|Prev|Chapter|Section|Subsection}() traverses a
container to find forward relations to the start page, the next page, the previous page, chapters,
sections, and subsections.
1568
Zend_View
This example shows how to render a menu from a container registered/found in the view
helper.
This example shows how to specify which relations to find and render.
Output:
<link rel="start" href="/" title="Home">
<link rel="next" href="/products/server/editions" title="Editions">
<link rel="prev" href="/products/server" title="Foo Server">
Output:
<link rel="alternate" href="/products/server/faq/format/xml">
<link rel="start" href="/" title="Home">
<link rel="next" href="/products/server/editions" title="Editions">
<link rel="prev" href="/products/server" title="Foo Server">
<link rel="chapter" href="/products" title="Products">
<link rel="chapter" href="/company/about" title="Company">
<link rel="chapter" href="/community" title="Community">
<link rev="subsection" href="/products/server" title="Foo Server">
Output:
<link rel="alternate" href="/products/server/faq/format/xml">
<link rel="start" href="/" title="Home">
<link rel="next" href="/products/server/editions" title="Editions">
<link rel="prev" href="/products/server" title="Foo Server">
<link rel="canonical" href="http://www.example.com/?page=server-faq">
<link rev="subsection" href="/products/server" title="Foo Server">
1569
Zend_View
The Menu helper is used for rendering menus from navigation containers. By default, the menu
will be rendered using HTML UL and LI tags, but the helper also allows using a partial view script.
• {get|set}Partial() gets/sets a partial view script that should be used for rendering menu.
If a partial view script is set, the helper's render() method will use the renderPartial()
method. If no partial is set, the renderMenu() method is used. The helper expects the partial
to be a String or an Array with two elements. If the partial is a String, it denotes the name of
the partial script to use. If it is an Array, the first element will be used as the name of the partial
view script, and the second element is the module where the script is found.
• htmlify() overrides the method from the abstract class to return span elements if the page
has no href.
If $container is not given, the container registered in the helper will be rendered.
$options is used for overriding options specified temporarily without rsetting the values in
the helper instance. It is an associative array where each key corresponds to an option in the
helper.
Recognized options:
If an option is not given, the value set in the helper will be used.
• renderPartial() is used for rendering the menu using a partial view script.
1570
<?php echo $this->navigation()->menu() ?>
Output:
This example shows how to render a menu from a container registered/found in the view
<ul class="navigation">
helper. Notice how pages are filtered out based on visibility and ACL.
<li>
<a title="Go Home" Zend_View
href="/">Home</a>
</li>
<li class="active">
<a href="/products">Products</a>
Exemple 958. Rendering a menu
<ul>
<li class="active">
<a href="/products/server">Foo Server</a>
<ul>
<li class="active">
<a href="/products/server/faq">FAQ</a>
</li>
<li>
<a href="/products/server/editions">Editions</a>
</li>
<li>
<a href="/products/server/requirements">System Requirements</a>
</li>
</ul>
</li>
<li>
<a href="/products/studio">Foo Studio</a>
<ul>
<li>
<a href="/products/studio/customers">Customer Stories</a>
</li>
<li>
<a href="/prodcts/studio/support">Support</a>
</li>
</ul>
</li>
</ul>
</li>
<li>
<a title="About us" href="/company/about">Company</a>
<ul>
<li>
<a href="/company/about/investors">Investor Relations</a>
</li>
<li>
<a class="rss" href="/company/news">News</a>
<ul>
<li>
<a href="/company/news/press">Press Releases</a>
</li>
<li>
<a href="/archive">Archive</a>
</li>
</ul>
</li>
</ul>
</li>
<li>
<a href="/community">Community</a>
<ul>
<li>
<a href="/community/account">My Account</a>
</li>
<li>
<a class="external" href="http://forums.example.com/">Forums</a>
</li>
</ul>
</li>
</ul>
1571
Zend_View
This example shows how to render a menu that is not registered in the view helper by calling
the renderMenu() directly and specifying a few options.
<?php
// render only the 'Community' menu
$community = $this->navigation()->findOneByLabel('Community');
$options = array(
'indent' => 16,
'ulClass' => 'community'
);
echo $this->navigation()
->menu()
->renderMenu($community, $options);
?>
Output:
<ul class="community">
<li>
<a href="/community/account">My Account</a>
</li>
<li>
<a class="external" href="http://forums.example.com/">Forums</a>
</li>
</ul>
1572
Zend_View
This example shows how the renderSubMenu() will render the deepest sub menu of the
active branch.
array(
'ulClass' => $ulClass,
'indent' => $indent,
'minDepth' => null,
'maxDepth' => null,
'onlyActiveBranch' => true,
'renderParents' => false
);
<?php
echo $this->navigation()
->menu()
->renderSubMenu(null, 'sidebar', 4);
?>
The output will be the same if 'FAQ' or 'Foo Server' is active:
<ul class="sidebar">
<li class="active">
<a href="/products/server/faq">FAQ</a>
</li>
<li>
<a href="/products/server/editions">Editions</a>
</li>
<li>
<a href="/products/server/requirements">System Requirements</a>
</li>
</ul>
1573
Zend_View
<?php
echo $this->navigation()
->menu()
->setMaxDepth(1);
?>
Output:
<ul class="navigation">
<li>
<a title="Go Home" href="/">Home</a>
</li>
<li class="active">
<a href="/products">Products</a>
<ul>
<li class="active">
<a href="/products/server">Foo Server</a>
</li>
<li>
<a href="/products/studio">Foo Studio</a>
</li>
</ul>
</li>
<li>
<a title="About us" href="/company/about">Company</a>
<ul>
<li>
<a href="/company/about/investors">Investor Relations</a>
</li>
<li>
<a class="rss" href="/company/news">News</a>
</li>
</ul>
</li>
<li>
<a href="/community">Community</a>
<ul>
<li>
<a href="/community/account">My Account</a>
</li>
<li>
<a class="external" href="http://forums.example.com/">Forums</a>
</li>
</ul>
</li>
</ul>
1574
Zend_View
<?php
echo $this->navigation()
->menu()
->setMinDepth(1);
?>
Output:
<ul class="navigation">
<li class="active">
<a href="/products/server">Foo Server</a>
<ul>
<li class="active">
<a href="/products/server/faq">FAQ</a>
</li>
<li>
<a href="/products/server/editions">Editions</a>
</li>
<li>
<a href="/products/server/requirements">System Requirements</a>
</li>
</ul>
</li>
<li>
<a href="/products/studio">Foo Studio</a>
<ul>
<li>
<a href="/products/studio/customers">Customer Stories</a>
</li>
<li>
<a href="/prodcts/studio/support">Support</a>
</li>
</ul>
</li>
<li>
<a href="/company/about/investors">Investor Relations</a>
</li>
<li>
<a class="rss" href="/company/news">News</a>
<ul>
<li>
<a href="/company/news/press">Press Releases</a>
</li>
<li>
<a href="/archive">Archive</a>
</li>
</ul>
</li>
<li>
<a href="/community/account">My Account</a>
</li>
<li>
<a class="external" href="http://forums.example.com/">Forums</a>
</li>
</ul>
1575
Zend_View
<?php
echo $this->navigation()
->menu()
->setOnlyActiveBranch(true);
?>
Output:
<ul class="navigation">
<li class="active">
<a href="/products">Products</a>
<ul>
<li class="active">
<a href="/products/server">Foo Server</a>
<ul>
<li class="active">
<a href="/products/server/faq">FAQ</a>
</li>
<li>
<a href="/products/server/editions">Editions</a>
</li>
<li>
<a href="/products/server/requirements">System Requirements</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
Exemple 964. Rendering only the active branch of a menu with minimum depth
<?php
echo $this->navigation()
->menu()
->setOnlyActiveBranch(true)
->setMinDepth(1);
?>
Output:
<ul class="navigation">
<li class="active">
<a href="/products/server">Foo Server</a>
<ul>
<li class="active">
<a href="/products/server/faq">FAQ</a>
</li>
<li>
<a href="/products/server/editions">Editions</a>
</li>
<li>
<a href="/products/server/requirements">System Requirements</a>
</li>
</ul>
</li>
</ul>
1576
Zend_View
Exemple 965. Rendering only the active branch of a menu with maximum depth
<?php
echo $this->navigation()
->menu()
->setOnlyActiveBranch(true)
->setMaxDepth(1);
?>
Output:
<ul class="navigation">
<li class="active">
<a href="/products">Products</a>
<ul>
<li class="active">
<a href="/products/server">Foo Server</a>
</li>
<li>
<a href="/products/studio">Foo Studio</a>
</li>
</ul>
</li>
</ul>
Exemple 966. Rendering only the active branch of a menu with maximum depth
and no parents
<?php
echo $this->navigation()
->menu()
->setOnlyActiveBranch(true)
->setRenderParents(false)
->setMaxDepth(1);
?>
Output:
<ul class="navigation">
<li class="active">
<a href="/products/server">Foo Server</a>
</li>
<li>
<a href="/products/studio">Foo Studio</a>
</li>
</ul>
1577
Zend_View
This example shows how to render a custom menu using a partial vew script. By calling
setPartial(), you can specify a partial view script that will be used when calling
render(). When a partial is specified, the renderPartial() method will be called. This
method will assign the container to the view with the key container.
In a layout:
In application/modules/default/views/menu.phtml:
Output:
The Sitemap helper is used for generating XML sitemaps, as defined by the Sitemaps XML
format. Read more about Sitemaps on Wikpedia.
By default, the sitemap helper uses sitemap validators to validate each element that is rendered.
This can be disabled by calling $helper->setUseSitemapValidators(false).
If you disable sitemap validators, the custom properties (see table) are not
validated at all.
The sitemap helper also supports Sitemap XSD Schema validation of the generated sitemap.
This is disabled by default, since it will require a request to the Schema file. It can be enabled
with $helper->setUseSchemaValidation(true).
Element Description
loc Absolute URL to page. An absolute URL will be
generated by the helper.
lastmod The date of last modification of the file, in
W3C Datetime format. This time portion can be
omitted if desired, and only use YYYY-MM-DD.
1578
Zend_View
Element Description
changefreq How frequently the page is likely to change.
This value provides general information to
search engines and may not correlate exactly
to how often they crawl the page. Valid values
are:
• always
• hourly
• daily
• weekly
• monthly
• yearly
• never
1579
<loc>http://www.example.com/products/server/requirements</loc>
echo $this->navigation()->sitemap();
</url>
<url>
<loc>http://www.example.com/products/studio</loc>
</url> Zend_View
<url>
<loc>http://www.example.com/products/studio/customers</loc>
</url>
Exemple 968. Rendering an XML sitemap
<url>
<loc>http://www.example.com/prodcts/studio/support</loc>
<?xml version="1.0" encoding="UTF-8"?>
</url>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<url>
<loc>http://www.example.com/company/about</loc>
<loc>http://www.example.com/</loc>
</url>
</url>
<url>
<url>
echo<loc>http://www.example.com/company/about/investors</loc>
$this->navigation()
<loc>http://www.example.com/products</loc>
</url> ->sitemap()
</url>
<url> ->setFormatOutput(true)
<url>
<loc>http://www.example.com/company/news</loc>
->setRole();
<loc>http://www.example.com/products/server</loc>
</url>
</url>
<url>
<url>
<loc>http://www.example.com/company/news/press</loc>
<loc>http://www.example.com/products/server/faq</loc>
</url>
</url>
<url>
<url>
<loc>http://www.example.com/archive</loc>
<loc>http://www.example.com/products/server/editions</loc>
Render the sitemap using no ACL role (should filter out /community/account):
</url>
</url>
<url>
<url>
<loc>http://www.example.com/community</loc>
<loc>http://www.example.com/products/server/requirements</loc>
</url>
</url>
<url>
<url>
<loc>http://www.example.com/community/account</loc>
<loc>http://www.example.com/products/studio</loc>
</url>
</url>
<url>
<url>
<loc>http://forums.example.com/</loc>
<loc>http://www.example.com/products/studio/customers</loc>
<?xml version="1.0" encoding="UTF-8"?>
</url>
</url>
<urlset
</urlset>xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<url>
<loc>http://www.example.com/prodcts/studio/support</loc>
<loc>http://www.example.com/</loc>
</url>
</url>
<url>
<url>
echo<loc>http://www.example.com/company/about</loc>
$this->navigation()
<loc>http://www.example.com/products</loc>
</url> ->sitemap()
</url>
<url> ->setFormatOutput(true)
<url>
<loc>http://www.example.com/company/about/investors</loc>
->setMaxDepth(1);
<loc>http://www.example.com/products/server</loc>
</url>
</url>
<url>
<url>
<loc>http://www.example.com/company/news</loc>
<loc>http://www.example.com/products/studio</loc>
</url>
</url>
<url>
<url>
<loc>http://www.example.com/company/news/press</loc>
<loc>http://www.example.com/company/about</loc>
Render the sitemap using a maximum depth of 1.
</url>
</url>
<url>
<url>
<loc>http://www.example.com/archive</loc>
<loc>http://www.example.com/company/about/investors</loc>
</url>
</url>
<url>
<url>
<loc>http://www.example.com/community</loc>
<loc>http://www.example.com/company/news</loc>
</url>
</url>
<url>
<url>
<loc>http://forums.example.com/</loc>
<loc>http://www.example.com/community</loc>
</url>
</url>
</urlset>
<url>
<loc>http://www.example.com/community/account</loc>
</url>
<url>
<loc>http://forums.example.com/</loc>
</url>
</urlset>
1580
Zend_View
If you do not want to utilize UTF-8 in such a situation, you will need to implement
a getEncoding() method in your custom view implementation.
The Navigation helper is a proxy helper that relays calls to other navigational helpers. It can be
considered an entry point to all navigation-related view tasks. The aforementioned navigational
helpers are in the namespace Zend_View_Helper_Navigation, and would thus require the
path Zend/View/Helper/Navigation to be added as a helper path to the view. With the
proxy helper residing in the Zend_View_Helper namespace, it will always be available, without
the need to add any helper paths to the view.
When proxying to other helpers, the Navigation helper can inject its container, ACL/role, and
translator. This means that you won't have to explicitly set all three in all navigational helpers,
nor resort to injecting by means of Zend_Registry or static methods.
• findHelper() finds the given helper, verifies that it is a navigational helper, and injects
container, ACL/role and translator.
Dans tous les exemples suivants nous allons utiliser l'adaptateur de traduction Array. Bien sûr
vous pouvez aussi utiliser toute instance de Zend_Translate ainsi que toutes sous-classes de
Zend_Translate_Adapter. Il y a plusieurs manières d'initialiser l'aide de vue Translate :
1581
Zend_View
Une instance préalablement enregistré de Zend_Translate est l'utilisation préférée pour cette
aide. Vous pouvez ainsi sélectionner la locale à utiliser avant d'ajouter l'adaptateur dans le
registre.
Nous parlons de locales et non de langues car une langue peut aussi contenir
une région. Par exemple l'anglais est parlé en différents dialectes. Il peut y avoir
une traduction pour l'anglais et une pour l'américain. Ainsi, nous disons "locale"
plutôt que "langues".
Si vous êtes plus familier avec l'interface fluide, alors vous pouvez aussi créer une instance à
l'intérieur de votre vue et initialiser l'aide ensuite.
Si vous utilisez votre aide sans Zend_View, alors vous pouvez aussi l'utiliser directement.
// initialiser l'adaptateur
$translate = new Zend_View_Helper_Translate($adapter);
print $translate->translate('simple');
// ceci retourne 'einfach'
Vous devriez utiliser cette façon de faire si vous ne travaillez pas avec Zend_View et que
vous avez besoin de créer des affichages traduits.
1582
Zend_View
Gardez à l'esprit que si vous utilisez des paramètres qui sont aussi des textes,
vous pouvez aussi avoir à traduire ces paramètres.
Parfois il est nécessaire de changer la locale pour une traduction. Ceci peut être fait soit
dynamiquement par traduction ou statiquement pour toutes les traductions suivantes. Et vous
pouvez utiliser ceci avec une liste de paramètres ou un tableau de paramètres. Dans les deux
cas la locale doit être fournie comme un paramètre unique final.
Cet exemple retourne la traduction italienne pour l'identifiant de message. Mais la locale ne sera
utilisée qu'une seule fois. La traduction suivante utilisera la locale de l'adaptateur. Normalement
1583
Zend_View
vous réglerez la locale au niveau de votre adaptateur avant de le mettre dans le registre. Mais
vous pouvez aussi paramétrer la locale avec l'aide de vue :
L'exemple ci-dessus paramètre 'it' comme nouvelle locale par défaut, elle sera utilisée pour
toutes les traductions ultérieures.
Bien sûr il existe aussi la méthode getLocale() pour récupérer le réglage courant de la locale.
$this->translate()->setLocale('it');
$this->translate("Today is %1\$s in %2\$s. Actual time: %3\$s", $date);
En fait, vous pouvez "empiler" les chemins en utilisant la méthode addHelperPath(). Comme
vous ajoutez des chemins dans la pile, Zend_View va regarder dans le chemin le plus
récemment ajouté, pour inclure la classe d'aide. Cela vous permet d'ajouter (ou bien de redéfinir)
la distribution initiale des aides, avec vos propres aides personnalisées.
1584
Zend_View
$view->addHelperPath('/autre/chemin/vers/des/aides',
'Votre_View_Helper');
• Bien qu'il ne soit pas strictement nécessaire, il est recommandé soit d'implémenter
Zend_View_Helper_Interface ou d'étendre Zend_View_Helper_Abstract quand
vous créez vos aides. Introduit en 1.6.0, ceux-ci définissent la méthode setView() ;
cependant, dans les prochaines releases, nous prévoyons d'implémenter un motif de
conception Stratégie qui permettra de simplifier en grande partie le schéma de nomination
détaillé ci-dessous. Contruire sur ces bases à partir de maintenant vous aidera pour vos codes
futurs.
• Le nom de la classe doit, au minimum, se terminer avec le nom de l'aide en utilisant une
notation en casseMélangée. Par exemple, si vous écrivez une aide appelée "actionSpeciale",
le nom de la classe doit être au minimum "ActionSpeciale". Vous devriez donner au nom de
la classe un préfixe, et il est recommandé d'utiliser "Ma_View_Helper" comme partie de ce
préfixe : "Ma_View_Helper_ActionSpeciale". (Vous devez alors fournir le préfixe, avec ou sans
le tiret bas, à addHelperPath() ou à setHelperPath()).
• La classe doit avoir une méthode publique dont le nom correspond au nom de l'aide ; c'est
la méthode qui sera appelée quand votre template appellera $this->actionSpeciale().
Dans notre exemple $this->actionSpeciale(), la déclaration de méthode requise serait
public function actionSpeciale().
• En général, la classe ne devrait pas afficher directement les données (via echo ou print).
Elle devrait retourner les valeurs pour être ensuite affichées. Les valeurs retournées devrait
être échappées de façon appropriées.
• La classe doit être dans un fichier ayant le même nom que la méthode d'aide. Si on utilise la
méthode actionSpeciale(), le fichier devra être nommé "ActionSpeciale.php"
Placez le fichier de classe d'aide quelque part dans la pile des chemins d'aide, et Zend_View
le chargera, l'instanciera, le rendra persistant, et l'exécutera automatiquement pour vous.
class Ma_View_Helper_ActionSpeciale
{
protected $_count = 0;
public function actionSpeciale()
{
$this->_count++;
$output = "J'ai vu 'The Big Lebowsky' {$this->_count} fois.";
return htmlspecialchars($output);
}
1585
Zend_View
Ensuite, dans un script de vue, vous pouvez appeler l'aide ActionSpeciale autant de fois que
vous le souhaitez ; elle sera instanciée une fois, et rendue persistante pendant toute la vie de
l'instance de Zend_View.
Quelquefois vous devez accéder à l'objet Zend_View appelant - par exemple, si vous devez
utiliser l'encodage enregistré ou voulez effectuer le rendu d'un autre script de vue comme une
sous partie de votre aide. Pour avoir accès à votre objet de vue, votre classe d'aide doit avoir
une méthode setView($view), comme ceci :
class Ma_View_Helper_ScriptPath
{
public $view;
Si votre classe d'aide a une méthode setView(), elle sera appelée quand votre classe sera
instanciée la première fois et fournira l'objet de la vue courante. Il est de votre responsabilité
de maintenir la persistance de l'objet dans votre classe, de même que de déterminer la façon
dont il peut être accéder.
$view->registerHelper($helper, 'foo');
If the helper has a setView() method, the view object will call this and inject itself into the
helper on registration.
1586
Zend_View
5. Zend_View_Abstract
Zend_View_Abstract est la classe de base à partir de laquelle Zend_View est construite ;
Zend_View elle-même l'étend simplement et déclare une implémentation concrète de la
méthode _run() (qui est invoquée par render()).
Zend_View est une sorte de moteur anti-template dans lequel on utilise nativement PHP pour
la réalisation du template. Avec comme résultat, tout le PHP est disponible, et les scripts de vue
héritent de la portée des objets appelants.
C'est ce dernier point qui est important dans le choix de la décision de ce design. En interne,
Zend_View::_run() réalise simplement ceci :
Ainsi, les scripts de vue ont accès à l'objet courant($this), et toute méthode ou membres et
cet objet. Puisque beaucoup d'opérations dépendent de membres ayant une portée limitée, ceci
pose un problème : les scrips de vue peuvent potentiellement faire des appels à ces méthodes ou
modifier des propriétés critiques directement. Imaginer un script surchargeant par inadvertance
$_path ou $_file - tout appel suivant à render() ou aux aides de vue sera cassé !
Heureusement, PHP 5 a une réponse à ceci avec ses déclarations de visibilité : les membres
privés se sont pas accessibles par un objet étendant une classe donnée. Ceci a permis la
conception actuelle : Zend_View étend Zend_View_Abstract, les scripts de vues sont ainsi
limités aux seuls méthodes et membres public ou protected de Zend_View_Abstract
- limitant effectivement les actions qu'il peut exécuter, et nous permettant de sécuriser des
secteurs critiques d'un risque de modification par les scripts de vue.
1587
Zend_Wildfire
1. Zend_Wildfire
Zend_Wildfire est un composant qui facilite la communication entre le code PHP et les
composants clients Wildfire.
Le but du projet Wildfire est de développer un canal de communication standardisé entre une
large variété de composants ainsi qu'une architecture de plugins dynamiques. Pour l'instant,
l'attention première est de fournir un système permettant au code PHP côté serveur d'injecter
des messages de logs vers la console Firebug.
1588
Zend_XmlRpc
1. Introduction
Sur sa page d'accueil, XML-RPC est décrit comme "un appel de procédure à distance utilisant
HTTP comme transport et XML comme encodage. XML-RPC est conçu pour être aussi simple
que possible, en permettant aux structures de données complexes d'être transmises, traitées
et rendues."
2. Zend_XmlRpc_Client
2.1. Introduction
Zend Framework possède la capacité de consommer des services distants XML-RPC, via la
classe Zend_XmlRpc_Client. Ses caractéristiques principales sont la conversion automatique
des types entre PHP et XML-RPC, un objet proxy de serveur, et des possibilités d'introspection
du serveur.
Pour appeler une méthode distante, utilisez la méthode call() de votre instance. Le code
suivant montre un exemple avec le serveur XML-RPC du site de Zend Framework. Vous pouvez
l'utiliser pour tester ou explorer les possibilités des composants Zend_XmlRpc.
echo $client->call('test.sayHello');
// hello
1589
Zend_XmlRpc
$arg1 = 1.1;
$arg2 = 'foo';
Le tableau de paramètres peut contenir des types PHP natifs, des objets Zend_XmlRpc_Value,
ou bien les deux à la fois.
Cependant, ceci peut mener malgré tout à des soucis. Premièrement, les
serveurs qui ne supportent system.methodSignature vont retourner une
requête de type échec, et Zend_XmlRpc_Client résultera en un cast de la
valeur de type tableau XML-RPC ("array"). De plus, ceci sous-entend que tout
1590
Zend_XmlRpc
$client->setSkipSystemLookup(true);
$result = $client->call('foo.bar', array(array()));
Les paramètres peuvent aussi être des objets Zend_XmlRpc_Value qui spécifient alors
exactement un type XML-RPC. Les raisons principales d'utiliser un tel procédé sont :
• Lorsque vous voulez être certain du type de paramètre (la méthode attend un entier et vous
le récupérez sous forme de chaîne de caractères depuis une base de données).
• Lorsque la conversion de types (cast) peut échouer (vous voulez passer une valeur XML-
RPC vide comme paramètre. Mais les valeurs vides en PHP sont représentés sous forme de
tableaux vides, or si vous passez un tableau vide à votre méthode call, il va être converti en
un tableau XML-RPC, comme ce n'est pas un tableau associatif).
Deux manières existent pour créer des objets Zend_XmlRpc_Value : instanciez une sous-
classe Zend_XmlRpc_Value directement, ou utilisez une fabrique ("factory method") telle que
Zend_XmlRpc_Value::getXmlRpcValue().
Conversion automatique
1591
Zend_XmlRpc
$server = $client->getProxy();
// Proxy l'espace de nom par défaut
La méthode getProxy() reçoit un argument optionnel désignant l'espace de nom à utiliser par
le proxy. Par défaut, il s'agit de l'espace général, voici un exemple utilisant un espace de nom
test :
$test = $client->getProxy('test');
// Proxy l'espace de nommage "test"
Si le serveur distant supporte les espaces de noms imbriqués, alors le proxy les supportera. Par
exemple, si le serveur dans l'exemple ci-dessus acceptait les espaces de noms imbriqués, alors
sa méthode test.foo.bar() aurait pu être appelée via $test->foo->bar().
Si une erreur HTTP survient, par exemple le serveur renvoie un 404 Not Found, alors une
Zend_XmlRpc_Client_HttpException sera levée.
1592
Zend_XmlRpc
try {
Lors de l'utilisation de la méthode call(), ou de l'objet proxy serveur, une erreur XML-RPC aura
pour effet de lancer une Zend_XmlRpc_Client_FaultException. Le code et le message
de l'exception seront rendus dans leurs valeurs respectives de la réponse XML-RPC.
try {
$client->call('badMethod');
// $e->getCode() retourne 1
// $e->getMessage() retourne "Unknown method"
Lors de l'utilisation de la méthode doRequest(), aucune exception ne sera levée si une erreur
XML-RPC survient. Simplement, l'objet Zend_XmlRpc_Response retourné contiendra l'erreur.
Vérifiez-en l'état avec isFault().
1593
Zend_XmlRpc
$client->doRequest($request);
Lorsqu'une méthode XML-RPC est appelée, quel qu'en soit le moyen, (call(), doRequest()
ou proxy serveur), le dernier objet de requête, et son homologue de réponse, seront toujours
disponibles, au travers des appels à getLastRequest() et getLastResponse().
L'objet client HTTP peut être récupéré à tout moment grâce à la méthode getHttpClient().
setHttpClient() permet d'injecter un objet Zend_Http_Client.
setHttpClient() est particulièrement utilisée pour les tests unitaires. Lorsque combinée avec
Zend_Http_Client_Adapter_Test, les services Web peuvent être déguisés (émulés) pour
les tests. Voyez les tests unitaires de Zend_XmlRpc_Client pour des exemples concrets.
3. Zend_XmlRpc_Server
3.1. Introduction
Zend_XmlRpc_Server fournit un serveur XML-RPC qui suit les spécifications dictées par
www.xmlrpc.com. Il fournit aussi la méthode system.multicall(), permettant le traitement
de requêtes multiples.
1594
Zend_XmlRpc
echo $server->handle();
3.4. Conventions
Zend_XmlRpc_Server permet d'attacher des classes et/ou des fonctions au serveur
XML-RPC. Grâce à Zend_Server_Reflection, l'introspection va utiliser les blocs de
commentaires pour déterminer les types d'arguments et de réponse de la fonction/classe.
Les types XML-RPC n'ont pas forcément de correspondance native vers un type PHP. Le code
fera de son mieux pour deviner le type de données approprié, en se basant sur les valeurs listées
dans les balises @param et @return. Certains types XML-RPC n'ont par contre pas d'équivalent
PHP direct, ils devront alors être spécifiés manuellement sous forme de balises phpdoc :
/**
* This is a sample function
*
* @param base64 $val1 Base64-encoded data
* @param dateTime.iso8601 $val2 An ISO date
* @param struct $val3 An associative array
* @return struct
*/
function myFunc($val1, $val2, $val3)
{}
PhpDocumentor ne vérifie (valide) pas les types des paramètres, mais les types sont obligatoires
pour que le serveur puisse lui, valider les paramètres passés aux appels des méthodes.
Il est parfaitement valide de spécifier plusieurs types pour les paramètres et les retours de
méthodes. La spécification XML-RPC suggère que system.methodSignature retourne un tableau
des possibilités au regard des paramètres d'entrée de la méthode, et de son type de sortie. Ceci
ce fait grâce au caractère '|' de PhpDocumentor
/**
1595
Zend_XmlRpc
Attention toutefois, une signature multiple peut prêter à confusion au regard des personnes
utilisant votre service. En général une fonction ne devrait posséder qu'une seule signature.
• system.listMethods
• system.methodHelp
• system.methodSignature
Si vous voulez ajouter un espace de noms aux méthodes que vous servez, procédez alors
comme suit :
1596
Zend_XmlRpc
Zend_XmlRpc_Server_Fault::attachFaultException('My_Project_Exception');
Si vous héritez correctement vos exceptions, vous pouvez alors passer en liste blanche
l'exception de plus bas niveau, et ainsi accepter plusieurs types d'exceptions qui en hériteront.
Évidemment, les Zend_XmlRpc_Server_Exceptions sont elles automatiquement mises en liste
blanche, afin de pouvoir traiter les requêtes vers des méthodes inexistantes, ou toute autre erreur
"générique".
Toute exception rencontrée, mais non mise en liste blanche, donnera naissance à une réponse
d'erreur avec le code "404" et le message "Unknown error".
Pour améliorer les performances, Zend_XmlRpc_Server_Cache peut être utilisé pour mettre
en cache la définition d'un serveur. Combiné à __autoload(), ceci améliore grandement les
performances.
Un exemple d'utilisation :
function __autoload($class)
{
Zend_Loader::loadClass($class);
}
if (!Zend_XmlRpc_Server_Cache::get($cacheFile, $server)) {
require_once 'My/Services/Glue.php';
require_once 'My/Services/Paste.php';
require_once 'My/Services/Tape.php';
$server->setClass('My_Services_Glue', 'glue');
// espace de noms glue
$server->setClass('My_Services_Paste', 'paste');
// espace de noms paste
$server->setClass('My_Services_Tape', 'tape');
// espace de noms tape
1597
Zend_XmlRpc
Zend_XmlRpc_Server_Cache::save($cacheFile, $server);
}
echo $server->handle();
/**
* Retourne le hash MD5 d'une valeur
*
* @param string $value Valeur à hasher
* @return string Hash MD5 de la valeur
*/
function md5Value($value)
{
return md5($value);
}
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';
1598
Zend_XmlRpc
require_once 'Services/Exception.php';
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';
require_once 'Services/Request.php';
require_once 'Services/Exception.php';
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';
echo $server->handle($request);
require_once 'Services/Request.php';
1599
Zend_XmlRpc
require_once 'Services/Response.php';
require_once 'Services/Exception.php';
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';
echo $server->handle($request);
require_once 'Services/Request.php';
require_once 'Services/Response.php';
require_once 'Services/Exception.php';
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';
// Sauve le cache
Zend_XmlRpc_Server_Cache::save($cacheFile, $server));
}
1600
Zend_XmlRpc
echo $server->handle($request);
1601
ZendX_Console_Process_Unix
1. ZendX_Console_Process_Unix
1.1. Introduction
ZendX_Console_Process_Unix allows developers to spawn an object as a new process,
and so do multiple tasks in parallel on console environments. Through its specific nature, it is
only working on *nix based systems like Linux, Solaris, Mac/OSx and such. Additionally, the
shmop_*, pcntl_* and posix_* modules are required for this component to run. If one of the
requirements is not met, it will throw an exception after instantiating the component.
The _run() method and every method which is called by it is executed by the child process.
Every other method which is called directly by the parent is executed by the parent process.
setVariable() and getVariable() can be used from both the parent- and the child process
to share variables. To observe the alive status, the child process should call _setAlive() in a
frequent interval, so that the parent process can check the last alive time via getLastAlive().
To get the PID of the child process, the parent can call getPid().
1602
ZendX_Console_Process_Unix
In this example a process is forked twice and executed. As every process runs 10 seconds,
the parent process will be finished after 10 seconds (and not 20).
1603
ZendX_JQuery
1. Introduction
As of version 1.7, Zend Framework integrates jQuery view and form helpers through its extras
library. The jQuery support is meant as an alternative to the already existing Dojo library
integration. Currently jQuery can be integrated into your Zend Framework applications in the
following ways:
• View helper to help setup the jQuery (Core and UI) environment
By default the jQuery javascript dependencies are loaded from the Google Ajax Library Content
Distribution Network. The CDN offers both jQuery Core and jQuery UI access points and the
view helpers therefore can already offer you most the dependencies out of the box. Currently the
Google CDN offers jQuery UI support up to version 1.5.2, but the jQuery view and form helpers
already make use of the UI library 1.6 version (AutoComplete, ColorPicker, Spinner, Slider). To
make use of these great additions you have to download the release candidate version of the
jQuery UI library from its website.
$view->addHelperPath("ZendX/JQuery/View/Helper", "ZendX_JQuery_View_Helper");
The jQuery() view helper implementation, like its dojo() pendant, follows the placeholder
architecture implementation; the data set in it persists between view objects, and may be directly
echo'd from your layout script. Since views specified in a Zend_Layout script file are rendered
before the layout itself, the jQuery() helper can act as a stack for jQuery statements and render
them into the head segment of the html page.
1604
ZendX_JQuery
Contrary to Dojo, themes cannot be loaded from a CDN for the jQuery UI widgets and have to
be implemented in your pages stylesheet file or loaded from an extra stylesheet file. A default
theme called Flora can be obtained from the jQuery UI downloadable file.
1605
ZendX_JQuery
If you need them throughout your application, you can register them in your bootstrap file
using access to the Controller Plugin ViewRenderer:
$view = new Zend_View();
$view->addHelperPath('ZendX/JQuery/View/Helper/', 'ZendX_JQuery_View_Helper');
Now in the view script we want to display a Date Picker and an Ajax based Link.
<?php echo $this->ajaxLink("Show me something",
"/hello/world",
array('update' => '#content'));?>
<div id="content"></div>
Both helpers now stacked some javascript statements on the jQuery helper and printed a
link and a form element respectively. To access the javascript we have to utilize the jQuery()
functionality. Both helpers already activated their dependencies that is they have called
jQuery()->enable() and jQuery()->uiEnable(). We only have to print the jQuery()
environment, and we choose to do so in the layout script's head segment:
<html>
<head>
<title>A jQuery View Helper Example</title>
<?php echo $this->jQuery(); ?>
</head>
<body>
<?php echo $this->layout()->content; ?>
</body>
</html>
1606
ZendX_JQuery
To make use of the NoConflict Mode in your own jQuery helper, you only have to use
the static method ZendX_JQuery_View_Helper_JQuery::getJQueryHandler()
method. It returns the variable jQuery is operating in at the moment, either $ or $j
• setVersion(): set the jQuery version that is used. This also decides on the library loaded
from the Google Ajax Library CDN
1607
ZendX_JQuery
• getVersion(): get the current jQuery that is used. This also decides on the library loaded
from the Google Ajax Library CDN
• getLocalPath(): If set, return the local path to the jQuery Core library
• setUiVersion(): set the jQuery UI version that is used. This also decides on the library
loaded from the Google Ajax Library CDN
• getUiVersion(): get the current jQuery UI that is used. This also decides on the library
loaded from the Google Ajax Library CDN
1608
ZendX_JQuery
• addStylesheet($path): Add a stylesheet file that is needed for a jQuery view helper to
display correctly.
• setCdnSsl($bool): Set if the CDN Google Ajax Library should be loaded from an SSL or
a Non-SSL location.
These are quite a number of methods, but many of them are used for internally by all the
additional view helpers and during the printing of the jQuery environment. Unless you want to
build your own jQuery helper or have a complex use-case, you will probably only get in contact
with a few methods of these.
For an example, if you would have merged jQuery Core and UI libraries as well as other files into
a single large file as well as merged stylesheets to keep HTTP requests low on your production
application. You could disallow the jQuery helper to render those parts, but render all the other
stuff with the following statement in your view:
$view->jQuery()
1609
ZendX_JQuery
->setRenderMode(ZendX_JQuery::RENDER_JAVASCRIPT |
ZendX_JQuery::RENDER_JQUERY_ON_LOAD);
This statement makes sure only the required javascript statements and onLoad blocks of the
current page are rendered by the jQuery helper.
2.1.5. Migrations
Prior to 1.8 the methods setCdnVersion(), setLocalPath() setUiCdnVersion() and
setUiLocalPath() all enabled the view helper upon calling, which is considered a bug from
the following perspective: If you want to use the any non-default library option, you would have to
manually disable the jQuery helper aftwards if you only require it to be loaded in some scenarios.
With version 1.8 the jQuery helper does only enable itsself, when enable() is called, which all
internal jQuery View helpers do upon being called.
This example creates a link with the label "Link Name" that fires an ajax request to url.php upon
click and renders the response into the div container "#container". The function header for the
ajaxLink is as follows: function ajaxLink($label, $url, $options, $params); The
options array is very powerful and offers you lots of functionality to customize your ajax requests.
1610
ZendX_JQuery
To enlighten the usage of this helper it is best to show another bunch of more complex examples.
This example assumes that you have only one view object that you want to display and don't
care a lot about html best practices, since we have to output the jQuery environment just before
the closing body tag.
<html>
<head>
<title>Zend Framework jQuery AjaxLink Example</title>
<script language="javascript"
type="text/javascript"
src="myCallbackFuncs.js"></script>
</head>
<body>
1611
ZendX_JQuery
You might have already seen that the 'update', 'complete', and 'beforeSend' options have to be
executed in specific order and syntax so that you cannot use those callbacks and override their
behaviour completely when you are using ajaxLink(). For larger use cases you will probably
want to write the request via jQuery on your own. The primary use case for the callbacks is effect
usage, other uses may very well become hard to maintain. As shown in Example Link 5, you can
also forward the beforeSend/complete Callbacks to your own javascript functions.
You can use shortcut effect names to make your links actions more fancy. For example the
Container that will contain the ajax response may very well be invisible in the first place.
Additionally you can use shortcut effects on the link to hide it after clicking. The following effects
can be used for callbacks:
1612
ZendX_JQuery
The method signature for all form view helpers closely resembles the Dojo View helpers
signature, helper($id, $value, $params, $attribs). A description of the parameters
follows:
• $id: Will act as the identifier name for the helper element inside a form. If in the attributes no
id element is given, this will also become the form element id, that has to be unique across
the DOM.
• $params: Widget specific parameters that customize the look and feel of the widget. These
options are unique to each widget and described in the jQuery UI documentation. The data
is casted to JSON, so make sure to use the Zend_Json_Expr class to mark executable
javascript as safe.
The following UI widgets are available as form view helpers. Make sure you use the correct
version of jQuery UI library to be able to use them. The Google CDN only offers jQuery UI up to
version 1.5.2. Some other components are only available from jQuery UI SVN, since they have
been removed from the announced 1.6 release.
1613
ZendX_JQuery
• slider($id, $value, $params, $attribs): Create a Sliding element that updates its
value into a hidden form field. Available since jQuery UI 1.5.
• spinner($id, $value, $params, $attribs): Create a Spinner element that can spin
through numeric values in a specified range. This is set on top of a form text field and is
available only from jQuery UI SVN
In this example we want to simulate a fictional web application that offers auctions on travel
locations. A user may specify a city to travel, a start and end date, and a maximum amount
of money he is willing to pay. Therefore we need an autoComplete field for all the currently
known travel locations, a date picker for start and end dates and a spinner to specify the
amount.
You can see the use of jQuery UI Widget specific parameters. These all correspond to those
given in the jQuery UI docs and are converted to JSON and handed through to the view
script.
1614
ZendX_JQuery
The jQuery UI Autocomplete Widget can load data from a remote location rather than from an
javascript array, making its usage really useful. Zend Framework currently providers a bunch of
server-side AutoComplete Helpers and there is one for jQuery too. You register the helper to
the controller helper broker and it takes care of disabling layouts and renders an array of data
correctly to be read by the AutoComplete field. To use the Action Helper you have to put this
rather long statement into your bootstrap or Controller initialization function:
Zend_Controller_Action_HelperBroker::addHelper(
new ZendX_JQuery_Controller_Action_Helper_AutoComplete()
);
You can then directly call the helper to render AutoComplete Output in your Controller
There is a wide range of Layout helpers that the UI library offers. The ones covered by Zend
Framework view helpers are Accordion, Dialog, Tabs. Dialog is the most simple one, whereas
Accordion and Tab extend a common abstract class and offer a secondary view helper for pane
generation. The following view helpers exist in the jQuery view helpers collection, an example
accompanies them to show their usage.
• tabContainer($id, $params, $attribs): Render a tab container with all the currently
registered panes. This view helper also offers to add panes with the following syntax: $this-
>tabContainer()->addPane($id, $label, $content, $options).
1615
ZendX_JQuery
For this example we assume the developer already wrote the controller and model side of
the script and assigned an array of news items to the view script. This array contains at most
5 news elements, so we don't have to care about the tab container getting to many tabs.
• setJQueryParam($name, $value): Set the jQuery option $name to the given value.
• setJQueryParams($params): Set key value pairs of jQuery options and merge them with
the already set options.
Each jQuery related Decorator also owns a getJQueryParams() method, to set options you
have to use the setDecorators(), addDecorator() or addDecorators() functionality of
a form element and set the jQueryParams key as option:
$form->setDecorators(array(
1616
ZendX_JQuery
'FormElements',
array('AccordionContainer', array(
'id' => 'tabContainer',
'style' => 'width: 600px;',
'jQueryParams' => array(
'alwaysOpen' => false,
'animated' => "easeslide"
),
)),
'Form'
));
1617
ZendX_JQuery
Utilizing the Container elements is a bit more complicated, the following example builds a Form
with 2 SubForms in a TabContainer:
1618
ZendX_JQuery
$form = new ZendX_JQuery_Form();
The following example makes use of all Form elements and wraps them into 2 subforms
$form->setAction('formdemo.php');
that are decorated with a tab'mainForm');
$form->setAttrib('id', container. First we build the basic ZendX_JQuery_Form:
$form->setAttrib('class', with
Exemple 990. SubForms TabContainer Decorator
'flora');
$form->setDecorators(array(
'FormElements',
array('TabContainer', array(
'id' => 'tabContainer',
'style' => 'width: 600px;',
)),the Form Id (in this case to 'mainForm') is an important step for the TabContainer. It
Setting
$subForm1
'Form', = new ZendX_JQuery_Form();
is needed that the subforms can relate to the Container Id in a later form building stage. We
$subForm1->setDecorators(array(
));
now initialize two SubForms that will be decorated with the TabPane decorators:
'FormElements',
array('HtmlTag',
array('tag' => 'dl')),
array('TabPane',
array('jQueryParams' => array('containerId' => 'mainForm',
'title' => 'DatePicker and Slider')))
));
1619
Annexe A. Configuration système
requise par Zend Framework
A.1. Introduction
Zend Framework requière un interpréteur PHP5 avec un serveur Web configuré pour gérer
correctement les scripts PHP. Certaines fonctionnalités requièrent des extensions additionnelles
ou des fonctionnalités du serveur Web ; dans la plupart des cas le framework peut être utilisé
sans celle-ci, bien que l'exécution puisse perdre en performance ou les fonctionnalités auxiliaires
peuvent ne pas être entièrement fonctionnelles. Un exemple d'un telle dépendance est le
mod_rewrite d'un environnement Apache, qui peut être utilisé pour implémenter les "jolies
URL" ("pretty URL") comme "http://www.exemple.com/utilisateur/edition". Si le mod_rewrite
n'est pas activé, Zend Framework peut être configuré pour supporter les URL de type
"http://www.exemple.com?controller=utilisateur&action=edition". Les jolies
URL peuvent être utilisées pour réduire la longueur des URLs avec une représentation textuelle
ou pour l'optimisation des moteurs de recherche (SEO), mais il n'affectent pas directement le
fonctionnement de l'application.
Zend Framework possède une collection étendue de tests automatisés, que vous pouvez lancer
avec PHPUnit 3.3.0 ou plus.
Une dépendance de type "forte" indique que le composant ou la classe ne pourra pas fonctionner
correctement si l'extension respective n'est pas disponible, tandis qu'une dépendance de type
"faible" indique que le composant peut utiliser l'extension si elle est disponible mais fonctionnera
malgré tout si elle ne l'est pas. Certains composants utiliseront automatiquement les extensions
si elles sont disponibles afin d'optimiser les performances mais pourront exécuter du code
similaire dans le propre code du composant si elles ne le sont pas.
1620
Configuration système
requise par Zend Framework
Tableau A.1. Extensions PHP utilisées par composant dans Zend Framework
1621
Configuration système
requise par Zend Framework
1622
Configuration système
requise par Zend Framework
1623
Configuration système
requise par Zend Framework
Une dépendance de type "forte" indique que le composant ou la classe ne pourra pas fonctionner
correctement si l'extension respective n'est pas disponible, tandis qu'une dépendance de type
"faible" indique que le composant peut utiliser l'extension si elle est disponible mais fonctionnera
malgré tout si elle ne l'est pas. Certains composants utiliseront automatiquement les extensions
si elles sont disponibles afin d'optimiser les performances mais pourront exécuter du code
similaire dans le propre code du composant si elles ne le sont pas.
1624
Configuration système
requise par Zend Framework
Tableau A.2. Zend Framework Components and the PHP Extensions they use
1625
Configuration système
requise par Zend Framework
1626
Configuration système
requise par Zend Framework
1627
Configuration système
requise par Zend Framework
Une dépendance de type "forte" indique que le composant ou la classe ne pourra pas fonctionner
correctement si le composant respectif n'est pas disponible, tandis qu'une dépendance de type
"faible" indique que le composant peut avoir besoin du composant dans certains cas.
Bien qu'il soit possible d'utiliser uniquement certains composants au lieu de Zend
Framework complet, vous devriez avoir à l'esprit que ceci peut entraîner des
problèmes quand des fichiers sont manquants ou que des composants sont
utilisés dynamiquement.
1628
Configuration système
requise par Zend Framework
Tableau A.3. Composants de Zend Framework et leurs dépendances envers les autres
composants de Zend Framework
1629
Configuration système
requise par Zend Framework
1630
Configuration système
requise par Zend Framework
1631
Configuration système
requise par Zend Framework
1632
Configuration système
requise par Zend Framework
1633
Configuration système
requise par Zend Framework
1634
Configuration système
requise par Zend Framework
1635
Configuration système
requise par Zend Framework
1636
Configuration système
requise par Zend Framework
1637
Configuration système
requise par Zend Framework
1638
Configuration système
requise par Zend Framework
1639
Configuration système
requise par Zend Framework
1640
Configuration système
requise par Zend Framework
1641
Configuration système
requise par Zend Framework
1642
Configuration système
requise par Zend Framework
1643
Configuration système
requise par Zend Framework
1644
Configuration système
requise par Zend Framework
1645
Configuration système
requise par Zend Framework
1646
Configuration système
requise par Zend Framework
1647
Configuration système
requise par Zend Framework
1648
Annexe B. Notes de migration de Zend
Framework
1649
Notes de migration
de Zend Framework
B.1.1. Zend_Feed_Reader
With the introduction of Zend Framework 1.10, Zend_Feed_Reader's handling of retrieving
Authors and Contributors was changed, introducing a break in backwards compatibility. This
change was an effort to harmonise the treatment of such data across the RSS and Atom classes
of the component and enable the return of Author and Contributor data in more accessible, usable
and detailed form. It also rectifies an error in that it was assumed any author element referred to
a name. In RSS this is incorrect as an author element is actually only required to provide an email
address. In addition, the original implementation applied its RSS limits to Atom feeds significantly
reducing the usefulness of the parser with that format.
The change means that methods like getAuthors() and getContributors no longer return
a simple array of strings parsed from the relevant RSS and Atom elements. Instead, the return
value is an ArrayObject subclass called Zend_Feed_Reader_Collection_Author which
simulates an iterable multidimensional array of Authors. Each member of this object will be a
simple array with three potential keys (as the source data permits). These include: name, email
and uri.
The original behaviour of such methods would have returned a simple array of strings, each
string attempting to present a single name, but in reality this was unreliable since there is no rule
governing the format of RSS Author strings.
The simplest method of simulating the original behaviour of these methods is to use the
Zend_Feed_Reader_Collection_Author's getValues() which also returns a simple
array of strings representing the "most relevant data", for authors presumed to be their name.
Each value in the resulting array is derived from the "name" value attached to each Author (if
present). In most cases this simple change is easy to apply as demonstrated below.
/**
* Before 1.10
*/
$feed = Zend_Feed_Reader::import('http://example.com/feed');
$authors = $feed->getAuthors();
/**
* With 1.10
*/
$feed = Zend_Feed_Reader::import('http://example.com/feed');
$authors = $feed->getAuthors()->getValues();
B.1.2. Zend_File_Transfer
B.1.2.1. Security change
For security reasons Zend_File_Transfer does no longer store the original mimetype and
filesize which is given from the requesting client into its internal storage. Instead the real values
will be detected at initiation.
Additionally the original values within $_FILES will be overridden within the real values at
initiation. This makes also $_FILES secure.
When you are in need of the original values you can eighter store them before initiating
Zend_File_Transfer or use the disableInfos option at initiation. Note that this option is
useless when its given after initiation.
1650
Notes de migration
de Zend Framework
When you are translating these messages within your code then use the new constants. As
benefit you don't need to translate the original string anymore to get a correct spelling.
B.1.3. Zend_Filter_HtmlEntities
In order to default to a more secure character encoding, Zend_Filter_HtmlEntities now
defaults to UTF-8 instead of ISO-8859-1.
Additionally, because the actual mechanism is dealing with character encodings and not
character sets, two new methods have been added, setEncoding() and getEncoding().
The previous methods setCharSet() and setCharSet() are now deprecated and proxy to
the new methods. Finally, instead of using the protected members directly within the filter()
method, these members are retrieved by their explicit accessors. If you were extending the filter
in the past, please check your code and unit tests to ensure everything still continues to work.
B.1.4. Zend_Filter_StripTags
Zend_Filter_StripTags contains a flag, commentsAllowed, that, in previous versions,
allowed you to optionally whitelist HTML comments in HTML text filtered by the class. However,
this opens code enabling the flag to XSS attacks, particularly in Internet Explorer (which
allows specifying conditional functionality via HTML comments). Starting in version 1.9.7 (and
backported to versions 1.8.5 and 1.7.9), the commentsAllowed flag no longer has any meaning,
and all HTML comments, including those containing other HTML tags or nested commments,
will be stripped from the final output of the filter.
B.1.5. Zend_Translate
B.1.5.1. Xliff adapter
In past the Xliff adapter used the source string as message Id. According to the Xliff standard
the trans-unit Id should be used. This behaviour was corrected with Zend Framework 1.10. Now
the trans-unit Id is used as message Id per default.
But you can still get the incorrect and old behaviour by setting the useId option to FALSE.
1651
Notes de migration
de Zend Framework
B.1.6. Zend_Validate
B.1.6.1. Adaptateurs personnels
Lorsqu'une erreur apparait dans un adaptateur crée de toute pièce, _error() doit être appelée.
Avant Zend Framework 1.10, il était possible d'appeler cette méthode sans aucun paramètre.
Le premier template de message d'erreur était alors utilisé.
Ce comportement est problématique lorsque vous avez des validateurs retournant plusieurs
messages. Aussi, étendre un validateur peut mener à des comportements inattendus dans une
telle situation, comme par exemple l'apparition du mauvais message d'erreur.
Pour éviter ces problèmes _error() doit desormais prendre obligatoirement un paramètre.
Depuis Zend Framework 1.10, les valeurs des constantes ont changé pour être uniques. Si vous
utilisiez les constantes comme le manuel le recommande, aucun changement n'est nécessaire.
Mais si vous utilisiez les messages d'erreurs, alors il faudra les changer. Voici les changements
opérés:
1652
Notes de migration
de Zend Framework
B.2.1. Zend_File_Transfer
B.2.1.1. MimeType validation
For security reasons we had to turn off the default fallback mechanism of the MimeType,
ExcludeMimeType, IsCompressed and IsImage validators. This means, that if the fileInfo
or magicMime extensions can not be found, the validation will always fail.
If you are in need of validation by using the HTTP fields which are provided by the user then you
can turn on this feature by using the enableHeaderCheck() method.
Security hint
You should note that relying on the HTTP fields, which are provided by your user,
is a security risk. They can easily be changed and could allow your user to provide
a malcious file.
// at initiation
$valid = new Zend_File_Transfer_Adapter_Http(array('headerCheck' => true);
// or afterwards
$valid->enableHeaderCheck();
B.2.2. Zend_Filter
Avant la version 1.9, Zend_Filter permettait l'utilisation de la méthode statique get(). Avec
la version 1.9 cette méthode a été renommée en filterStatic() afin d'être plus descriptive.
L'ancienne méthode get() est marquée comme dépréciée.
B.2.3. Zend_Http_Client
B.2.3.1. Changement dans le stockage interne des fichiers d'upload
Dans la version 1.9 de Zend Framework, il y a eu un changement dans la manière dont
Zend_Http_Client stocke en interne les informations concernant les fichiers ayant été
uploadés, affectés grâce à Zend_Http_Client::setFileUpload()
Ce changement a été mis en place de manière à permettre l'envoi de plusieurs fichiers avec le
même nom dans le formulaire, en tant que tableau de fichiers. Plus d'informations à ce sujet
peuvent être trouvées dans ce rapport de bug.
1653
Notes de migration
de Zend Framework
Exemple B.2. Stockage interne des informations sur les fichiers uploadés
// Uploade 2 fichiers avec le même nom d'élément de formulaire, en tant que tableau
$client = new Zend_Http_Client();
$client->setFileUpload('file1.txt', 'userfile[]', 'some raw data', 'text/plain');
$client->setFileUpload('file2.txt', 'userfile[]', 'some other data', 'application/octet-
Comme vous le voyez, ce changement permet l'utilisation du même élément de formulaire avec
plusieurs fichiers. Cependant ceci introduit un changement subtile dans l'API interne, il est donc
signalé ici.
If you subclass Zend_Http_Client and call this method, you should look into using the
Zend_Http_Client::_flattenParametersArray() static method instead.
Again, since this _getParametersRecursive is a protected method, this change will only
affect users who subclass Zend_Http_Client.
B.2.4. Zend_Locale
B.2.4.1. Méthodes dépréciées
Quelques méthodes de traductions spéciales ont été dépréciées car elles dupliquaient un
comportement existant. Notez cependant que les anciens appels vont toujours fonctionner, mais
une notice utilisateur, qui décrira le nouvel appel, sera émise. Ces méthodes seront effacées en
2.0. Ci-dessous la liste des anciens et nouveaux appels :
1654
Notes de migration
de Zend Framework
B.2.5. Zend_View_Helper_Navigation
Prior to the 1.9 release, the menu helper (Zend_View_Helper_Navigation_Menu) did
not render sub menus correctly. When the onlyActiveBranch was TRUE and the option
renderParents FALSE, nothing would be rendered if the deepest active page was at a depth
lower than the minDepth option.
In simpler words; if minDepth was set to 1 and the active page was at one of the first level
pages, nothing would be rendered, as the following example shows.
1655
Notes de migration
de Zend Framework
Before release 1.9, the code snippet above would output nothing.
<ul class="navigation">
<li>
<a href="#">Server</a>
</li>
<li>
<a href="#">Studio</a>
</li>
</ul>
B.2.6.1. Zend_Dojo_View_Helper_Editor
A slight change was made in the 1.9 series to modify the default usage of the Editor dijit to use
div tags instead of a textarea tag; the latter usage has security implications, and usage of div
tags is recommended by the Dojo project.
In order to still allow graceful degradation, a new degrade option was added to the view helper;
this would allow developers to optionally use a textarea instead. However, this opens applications
developed with that usage to XSS vectors. In 1.9.7, we have removed this option. Graceful
degradation is still supported, however, via a noscript tag that embeds a textarea. This solution
addressess all security concerns.
The takeaway is that if you were using the degrade flag, it will simply be ignored at this time.
B.2.6.2. Zend_Filter_HtmlEntities
In order to default to a more secure character encoding, Zend_Filter_HtmlEntities now
defaults to UTF-8 instead of ISO-8859-1.
Additionally, because the actual mechanism is dealing with character encodings and not
character sets, two new methods have been added, setEncoding() and getEncoding().
The previous methods setCharSet() and setCharSet() are now deprecated and proxy to
the new methods. Finally, instead of using the protected members directly within the filter()
method, these members are retrieved by their explicit accessors. If you were extending the filter
in the past, please check your code and unit tests to ensure everything still continues to work.
B.2.6.3. Zend_Filter_StripTags
Zend_Filter_StripTags contains a flag, commentsAllowed, that, in previous versions,
allowed you to optionally whitelist HTML comments in HTML text filtered by the class. However,
this opens code enabling the flag to XSS attacks, particularly in Internet Explorer (which
allows specifying conditional functionality via HTML comments). Starting in version 1.9.7 (and
backported to versions 1.8.5 and 1.7.9), the commentsAllowed flag no longer has any meaning,
and all HTML comments, including those containing other HTML tags or nested commments,
will be stripped from the final output of the filter.
1656
Notes de migration
de Zend Framework
B.3.1. Zend_Controller
B.3.1.1. Changement de la route standard
Comme les segments traduits ont été ajoutés dans la nouvelle route standard, le caractère @ est
maintenant un caractère spécial au début d'un segment de route. Pour être capable de l'utiliser
dans un segment statique, vous devez l'échapper en le préfixant avec un second @. La même
règle s'applique aussi au caractère :.
B.3.2. Zend_Locale
B.3.2.1. Default caching
As with Zend Framework 1.8 a default caching was added. The reason behind this change was,
that most users had performance problems but did not add caching at all. As the I18n core is
a bottleneck when no caching is used we decided to add a default caching when no cache has
been set to Zend_Locale.
Sometimes it is still wanted to prevent caching at all even if this decreases performance. To do
so you can simply disable caching by using the disableCache() method.
Zend_Locale::disableCache(true);
B.4.1. Zend_Controller
B.4.1.1. Changement dans l'interface Dispatcher
Les utilisateurs ont portés l'attention sur le fait que
Zend_Controller_Action_Helper_ViewRenderer utilisait une méthode de la classe
abstraite du distributeur standard qui n'était pas présente dans l'interface Dispatcher.La méthode
suivante a donc été ajoutée pour s'assurer que les distributeurs personnalisés continueront à
fonctionner avec les implémentations embarquées :
• formatModuleName() : devrait être utilisé pour prendre un nom de contrôleur brut, comme
un qui aurait été embarqué dans un objet requête, et pour le formater en un nom de classe
approprié qu'une classe étendant Zend_Controller_Action pourra utiliser.
B.4.2. Zend_File_Transfer
B.4.2.1. Changements quand vous utilisez des filtres ou des validateurs
Certaines remarques des utilisateurs indiquaient que les validateurs de Zend_File_Transfer
ne fonctionnaient pas correctement avec Zend_Config dû au fait qu'ils n'utilisait pas de
tableaux nommés.
1657
Notes de migration
de Zend Framework
De plus, tous les filtres et validateurs de Zend_File_Transfer ont été réécrits. Même si les
anciennes signatures continuent à fonctionner, elles ont été marqués comme dépréciées et
émettent une notice PHP vous informant de faire le changement.
La liste suivante vous montre les changements à réaliser pour une utilisation appropriée des
paramètres.
1658
Notes de migration
de Zend Framework
1659
Notes de migration
de Zend Framework
1660
Notes de migration
de Zend Framework
B.4.3. Zend_Locale
B.4.3.1. Changement dans l'utilisation de isLocale()
Conformément aux standards de codage isLocale() a été changé pour retourner un booléen.
Dans les versions précédentes une chaîne était retournée lors du succès. Pour la version 1.7 un
mode de compatibilité a été ajouté qui vous permet d'utiliser l'ancien comportement (avec une
chaîne retournée), mais ceci émet un warning pour vous informer de changer vers le nouveau
comportement. Le reroutage que l'ancien comportement de isLocale() pouvait avoir à faire
n'est plus nécessaire car tous les composants de l'I18N traiteront maintenant eux-mêmes le
reroutage.
Pour migrer vos scripts vers la nouvelle API, utilisez simplement la méthode décrite ci-dessous.
1661
Notes de migration
de Zend Framework
if (Zend_Locale::isLocale($locale)) {
}
Notez que vous pouvez utiliser le second paramètre pour voir si la locale est correcte sans
nécessiter de reroutage.
if (Zend_Locale::isLocale($locale, false)) {
if (Zend_Locale::isLocale($locale, true)) {
// pas de locale du tout
}
La signification de la méthode getDefault() a été changé étant donné que nous avons intégré
une locale de framework qui peut être paramétrée avec setDefault(). Ceci ne renvoie plus
la chaîne de la locale mais seulement la locale du framework.
Pour migrer vos scripts vers la nouvelle API, utilisez simplement la méthode décrite ci-dessous.
1662
Notes de migration
de Zend Framework
$locale = Zend_Locale::getOrder(Zend_Locale::BROWSER);
Par défaut l'ancien comportement est toujours actif, mais émet un warning.
Quand vous avez changé votre code vers le nouveau comportement, vous
devriez aussi changer le mode de compatibilité à FALSE ainsi aucun nouveau
warning ne sera émis.
B.4.4. Zend_Translate
B.4.4.1. Paramétrer les langues
Lors de l'utilisation de la détection automatique des langues, ou du réglage manuel des langues
de Zend_Translate, vous avez peut-être remarqué que de temps en temps une notice est
envoyée concernant le non-ajout de traductions vides. Dans certaines versions précédentes,
une exception était levée dans certains cas.
Ceci intervient quand un utilisateur requête une langue non existante, vous n'avez alors aucun
moyen simple de détecter ce qui ne va pas. Nous avons donc ajouté ces notices qui apparaîtront
dans votre historisation et qui vous diront qu'un utilisateur a requêté une langue que vous ne
supportez pas. Notez bien que votre code, même si une notice est déclenchée, fonctionnera
sans problèmes.
Mais quand vous utilisez votre propre gestionnaire d'erreur ou d'exception, comme xDebug,
toutes les notices vous seront retournées, même si ce n'est pas votre intention initiale. Ceci est
du au fait, que ces gestionnaires surchargent tous les réglages internes de PHP.
Pour vous affranchir de ces notices, vous pouvez simplement paramétrer la nouvelle option
disableNotices à TRUE, sa valeur par défaut étant FALSE.
1663
Notes de migration
de Zend Framework
Assumons que "fr" soit disponible et qu'un utilisateur requête pour "de" qui ne fait pas
partie de votre portefeuille de traductions.
Dans ce cas nous aurons une notice indiquant la non-disponibilité de la langue "de". Ajoutez
simplement l'option et les notices seront désactivées.
B.4.5. Zend_View
Avant la version 1.7.5, l'équipe de Zend Framework a été avertie d'une faille
potentielle d'inclusion de fichier local ("Local File Inclusion" (LFI)) dans la méthode
Zend_View::render(). Avant 1.7.5, la méthode acceptait par défaut la possibilité de spécifier
des scripts de vue comportant des indications de dossier parent (comme, "../" ou "..\"). Ceci ouvre
la possibilité à une attaque LFI si des données utilisateurs non filtrées sont passées directement
à la méthode render():
1664
Notes de migration
de Zend Framework
B.5.1. Zend_Controller
B.5.1.1. Changement dans l'interface Dispatcher
Les utilisateurs ont porté à notre connaissance le fait que Zend_Controller_Front
et Zend_Controller_Router_Route_Module utilisent tous les deux des méthodes du
distributeur qui ne sont pas dans l'interface associée. Nous avons donc ajouté les trois méthodes
suivantes pour s'assurer que les distributeurs personnalisés continueront à fonctionner avec les
implémentations embarquées :
B.5.2. Zend_File_Transfer
B.5.2.1. Changements quand vous utilisez des validateurs
Certaines remarques des utilisateurs indiquaient que les validateurs de Zend_File_Transfer
ne fonctionnaient pas comme ceux par défaut fournis avec Zend_Form. Zend_Form permet par
exemple l'utilisation du paramètre breakChainOnFailure qui stoppe la validation de tous les
validateurs suivants dès qu'une erreur de validation apparaît.
Nous avons donc ajouter ce paramètre à tous les validateurs existants pour
Zend_File_Transfer.
Pour migrer vos scripts vers la nouvelle API, ajoutez simplement un a FALSE après voir défini
le validateur souhaité.
B.6.1. Zend_Controller
Bien que la plupart des fonctionnalités de base demeurent les mêmes, et que toutes les
fonctionnalités documentées restent les mêmes, il existe une "fonctionnalité" particulière non
documentée qui a changé.
1665
Notes de migration
de Zend Framework
Quand vous écrivez des URLs, la manière documentée d'écrire les noms d'action en
notationCamel est d'utiliser un séparateur de mot ; ceux ci sont "." ou "-" par défaut, mais ils
peuvent être configurés dans le distributeur. Le distributeur en interne transforme les noms
d'action en minuscules, et utilise ces séparateurs de mots pour ré-assembler la méthode d'action
en utilisant la notationCamel. Cependant, comme les fonctions PHP ne sont pas sensibles à la
casse, vous pouvez toujours écrire les URLs en utilisant la notationCamel, et le distributeur les
résoudra de la même manière. Par exemple, "notation-camel" deviendra "notationCamelAction"
dans le distributeur, tandis que "notationCamel" deviendra "notationcamelAction" ; cependant, à
cause de l'insensibilité à la casse de PHP, dans les deux cas cela exécutera la même méthode.
Ceci pose des problèmes avec le ViewRenderer lors de la résolution des scripts de vue. La
manière canonique et documentée est que tous les séparateurs de mot sont convertis en tirets,
et les mots en minuscules. Ceci crée un lien sémantique entre les actions et les scripts de
vue, et la normalisation s'assure que les scripts peuvent être trouvés. Cependant, si l'action
"notationCamel" est appelée et est résolue, le séparateur de mot n'est pas pour autant présent,
et le ViewRenderer tente de résoudre un emplacement différent - "notationcamel.phtml" au
lieu de "notation-camel.phtml".
Quelques développeurs se sont fondés sur ce "dispositif", qui n'a jamais été prévu. Plusieurs
changements de l'arbre 1.5.0, cependant, l'ont fait de sorte que le ViewRenderer ne résout plus
ces chemins ; le lien sémantique est maintenant imposé. A partir de maintenant, le distributeur
impose la sensibilité à la casse dans les noms d'action. Ceci veut dire que la référence vers
vos actions dans l'URL en utilisant la notationCamel ne résoudra plus les mêmes méthodes
qu'en utilisant les séparateurs de mots (par ex., "notation-camel"). Ceci entraîne qu'à partir de
maintenant le ViewRenderer honorera seulement les actions en "mots-séparés" lors de la
résolution des scripts de vue.
Si vous constatez que vous comptiez sur ce "dispositif", vous avez plusieurs options :
• Meilleure option : renommez vos scripts de vue. Pour : compatibilité ascendante. Contre : si
vous avez beaucoup de scripts de vue basés sur l'ancien, comportement fortuit, vous aurez
beaucoup de renommage à faire.
$viewRenderer =
Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
$inflector = $viewRenderer->getInflector();
$inflector->setFilterRule(':action', array(
new Zend_Filter_PregReplace(
'#[^a-z0-9' . preg_quote(DIRECTORY_SEPARATOR, '#') . ']+#i',
''
),
'StringToLower'
));
Le code ci-dessus modifiera l'inflecteur pour ne plus séparer les mots avec un tiret ; vous
pouvez aussi vouloir supprimer le filtre StringToLower si vous voulez que vos scripts de
vues utilisent aussi la notationCamel.
Si le renommage de vos scripts de vue est trop fastidieux ou nécessite trop de temps, ceci est
la meilleure option avant de trouver le temps de le faire.
1666
Notes de migration
de Zend Framework
$front->setParam('useCaseSensitiveActions', true);
Ceci vous permettra d'utiliser la notationCamel dans l'URL et de toujours faire résoudre la
même action que si vous utilisez les séparateurs de mots. Cependant, ceci signifiera que les
problèmes décrits ci-dessus interviendront tôt ou tard ; vous devrez probablement utiliser la
deuxième option ci-dessus en plus de celle-ci pour que tout fonctionne correctement.
Notez, de plus, que l'utilisation de ce drapeau déclenchera une notice indiquant que cette
utilisation est dépréciée.
B.7.1. Zend_Controller
Les principaux changements introduits dans la version 1.0.0RC1 sont l'ajout et l'activation par
défaut du plugin ErrorHandleret de l'aide d'action ViewRenderer. Veuillez lire la documentation
de chacun des éléments directement pour apprendre leur fonctionnement et quels effets, ils
peuvent avoir sur vos applications.
$front->setParam('noErrorHandler', true);
L'aide d'action ViewRenderer automatise l'injection de vues dans les contrôleurs d'action en
tant qu'autogénération des scripts de vues suivant l'action courante. Le principal problème que
vous pourriez rencontrer intervient quand vous avez des actions qui ne rendent pas de scripts
de vues ni ne font suivre ou redirige, alors ViewRenderer va tenter de rendre un script de vue
basé sur le nom de l'action.
Il existe plusieurs possibilités pour mettre à jour votre code. Dans un premier temps, vous
pouvez globalement désactiver ViewRenderer dans votre fichier d'amorçage du contrôleur
frontal avant toute distribution :
Cependant, ceci n'est pas une bonne stratégie à long terme, car il apparaît aisément que vous
devrez écrire plus de code.
Quand vous serez prêt à utiliser la fonctionnalité ViewRenderer, il y a plusieurs choses à vérifier
dans votre code de contrôleur. Premièrement, regardez vos méthodes d'actions (les méthodes
se terminant par "Action"), et déterminez ce que chacune d'elle réalise. Si rien de ce qui suit
n'est réalisé, vous devrez réaliser des changements :
• Appel de $this->render()
1667
Notes de migration
de Zend Framework
• Appel de $this->_forward()
• Appel de $this->_redirect()
$this->_helper->viewRenderer->setNoRender();
Si vous trouvez qu'aucune de vos méthodes d'actions n'effectue de rendu, ne font suivre, ou
redirige, vous pouvez préférer mettre la ligne suivante dans la méthode preDispatch() ou
init() :
• Si vous rendez de multiples scripts de vues dans une seule action, vous n'avez rien à modifier.
• Si vous appelez simplement render() sans aucun argument, vous pouvez effacer ces lignes.
• Si vous appelez render() avec des arguments, et que vous ne réalisez pas ensuite
d'exécution de code ou effectuez le rendu de scripts de vues multiples, vous pouvez changer
ces appels par $this->_helper->viewRenderer().
Si vous n'utilisez pas la structure de dossier modulaire conventionnelle, il existe une variété
de méthodes pour paramétrer le chemin de base des vues et les spécifications du chemin
vers les scripts ainsi vous pourrez utiliser ViewRenderer. Veuillez lire la documentation de
ViewRendererpour plus d'informations sur ces méthodes.
Si vous utilisez un objet de vue issu du registre, ou que vous personnalisez votre objet
vue, ou que vous utilisez une implémentation de vue différente, vous pouvez vouloir injecter
ViewRenderer dans cet objet. Ceci peut être réalisé facilement à tout moment.
$viewRenderer =
Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
$viewRenderer->setView($view);
Il existe plusieurs manières de modifier ViewRenderer, incluant le réglage d'un script de vue
différent à rendre, la spécification d'un remplaçant pour tous les éléments remplaçables d'un
chemin de script de vues (incluant le suffixe), le choix d'un segment nommé de la réponse à
1668
Notes de migration
de Zend Framework
utiliser, et plus encore. Si vous n'utilisez pas la structure de dossier modulaire conventionnelle,
vous pouvez tout de même associer différentes spécifications de chemin à ViewRenderer.
Nous vous encourageons à adapter votre code pour utiliser ErrorHandler et ViewRenderer
puisqu'il s'agit maintenant de fonctionnalités natives.
B.7.2. Zend_Currency
Créer un objet Zend_Currency est devenu plus simple. Vous n'avez plus besoin de passer
un script ou de le mettre à NULL, le paramètre script est optionnel et peut être spécifié par la
méthode setFormat().
• format : Remplacement de l'ancien paramètre "locale" qui n'affecte plus de nouvelle monnaie,
mais seulement un format de nombre.
$currency->setFormat(array $options);
B.8.1. Zend_Controller
0.9.3 introduit les aides d'actions. En lien avec ce changement, les méthodes suivantes ont été
effacées puisqu'elles sont maintenant encapsulées dans l'aide d'action redirector :
1669
Notes de migration
de Zend Framework
B.9.1. Zend_Controller
Pour les versions précédentes, l'utilisation basique des composants MVC reste la même :
Zend_Controller_Front::run('/chemin/vers/controleurs');
Cependant, la structure des dossiers a subi une réorganisation, certains composants ont été
effacés, et d'autres ont été soit renommés soit ajoutés. Les changements incluent :
• Une nouvelle classe de route à utiliser avec le routeur de réécriture a été introduite,
Zend_Controller_Router_Route_Module ; elle couvre la route par défaut utilisée par le
MVC, et supporte les modules de contrôleurs.
$action est toujours obligatoire ; si aucun contrôleur n'est spécifié, une action dans le
contrôleur courant est considérée. $module est toujours ignoré à moins que $controller ne
soit spécifié. Pour finir, tout $params fourni sera ajouté à l'objet requête. Si aucun contrôleur
1670
Notes de migration
de Zend Framework
ou module n'est nécessaire, mais que des paramètres le sont, passez simplement NULL pour
ces valeurs.
B.10.1. Zend_Controller
L'utilisation de base des composants MVC n'a pas changé ; vous pouvez toujours faire comme
suit :
Zend_Controller_Front::run('/chemin/vers/controleurs');
/* -- créer un routeur -- */
$router = new Zend_Controller_RewriteRouter();
$router->addRoute('user', 'user/:username', array('controller' => 'user',
'action' => 'info'));
/* -- l'affecter à un contrôleur -- */
$ctrl = Zend_Controller_Front::getInstance();
$ctrl->setRouter($router);
Nous encourageons l'utilisation de l'objet Réponse pour agréger le contenu et les en-têtes. Ceci
permet un basculement plus flexible entre les formats d'affichage (par exemple, JSON ou XML
au lieu de XHTML) dans vos applications. Par défaut, dispatch() va effectuer le rendu de la
réponse, envoyant à la fois les en-têtes et tout contenu. Vous pouvez aussi avoir le contrôleur
frontal qui retourne la réponse en utilisant returnResponse(), et qui ensuite effectue le rendu
de la réponse suivant votre propre logique. Une version future du contrôleur frontal peut mettre
en application l'utilisation de l'objet Réponse via la bufferisation de sortie.
Il existe beaucoup d'autres fonctionnalités qui étendent l'API existante, et celles-ci sont décrites
dans la documentation.
Le changement le plus important auquel vous devrez faire attention apparaîtra quand vous
tenterez de sous-classer les différents composants. La clé se trouve ci-dessous :
$front->throwExceptions(true);
$response->renderExceptions(true);
$front->setResponse($response);
$front->dispatch();
1671
Notes de migration
de Zend Framework
// ou :
$front->returnResponse(true);
$response = $front->dispatch();
$response->renderExceptions(true);
echo $response;
• run() n'est plus défini en tant qu'élément final, mais n'est pas non plus
utilisé par le contrôleur frontal ; son seul but apparaît lors de l'utilisation
de la classe en tant que contrôleur de page. Il prend maintenant deux
arguments facultatifs, un Zend_Controller_Request_Abstract $request et un
Zend_Controller_Response_Abstract $response.
• indexAction() ne nécessite plus d'être défini, mais est recommandé en tant qu'action
par défaut. Ceci permet lors de l'utilisation de RewriteRouter et des contrôleurs d'action
de spécifier différentes méthodes d'action par défaut.
• __call() peut être surchargé pour gérer automatiquement les actions non définies.
1672
Notes de migration
de Zend Framework
1673
Annexe C. Convention de codage PHP
de Zend Framework
Table des matières
C.1. Vue d'ensemble .................................................................................................. 1674
C.1.1. Portée ..................................................................................................... 1674
C.1.2. Buts ......................................................................................................... 1675
C.2. Formatage des fichiers PHP ................................................................................ 1675
C.2.1. Général ................................................................................................... 1675
C.2.2. Indentation ............................................................................................... 1675
C.2.3. Longueur maximum d'une ligne ................................................................. 1675
C.2.4. Terminaison de lignes .............................................................................. 1675
C.3. Conventions de nommage ................................................................................... 1675
C.3.1. Classes ................................................................................................... 1675
C.3.2. Abstract Classes ...................................................................................... 1676
C.3.3. Interfaces ................................................................................................. 1676
C.3.4. Noms de fichiers ...................................................................................... 1676
C.3.5. Fonctions et méthodes ............................................................................. 1677
C.3.6. Variables ................................................................................................. 1677
C.3.7. Constantes .............................................................................................. 1678
C.4. Style de codage .................................................................................................. 1678
C.4.1. Démarcation du code PHP ....................................................................... 1678
C.4.2. Chaînes de caractères ............................................................................. 1678
C.4.3. Tableaux .................................................................................................. 1679
C.4.4. Classes ................................................................................................... 1680
C.4.5. Fonctions et méthodes ............................................................................. 1681
C.4.6. Structure de contrôle ................................................................................ 1684
C.4.7. Documentation intégrée ............................................................................ 1685
Les sujets traités dans les conventions de codage de Zend Framework sont :
• Conventions de nommage
1674
Convention de codage
PHP de Zend Framework
• Style de code
• Documentation en ligne
C.1.2. Buts
De bonnes conventions de codage sont importantes dans tout projet de développement, et plus
particulièrement lorsque plusieurs développeurs travaillent en même temps sur le projet. Avoir
ces conventions permet de s'assurer que le code est de haute qualité, peu buggé et facilement
maintenu.
C.2.2. Indentation
Utilisez une indentation de 4 espaces, sans tabulations.
Note : N'utilisez pas de retour chariots (CR) comme le font les Macintosh (0x0D) ou de
combinaison retour chariot/linefeed (CRLF) comme le font les ordinateurs sous Windows (0x0D,
0x0A).
1675
Convention de codage
PHP de Zend Framework
de Zend Framework est "ZendX/". Toutes les classes sont stockées de façon hiérarchique sous
ces dossiers racines.
Les noms de classes ne peuvent contenir que des caractères alphanumériques. Les nombres
sont autorisés, mais déconseillés. Les tirets bas ("_") ne sont autorisés que pour être utilisés
comme séparateur de chemin ; le nom de fichier "Zend/Db/Table.php" doit mener à la classe
appelée "Zend_Db_Table".
Si un nom de classe comprend plus d'un mot, la première lettre de chaque nouveau mot doit
être mis en majuscule. La mise en majuscule de lettres successives n'est pas autorisée, c'est-
à-dire qu'une classe "Zend_PDF" est interdit alors que "Zend_Pdf" est autorisé.
Ces conventions définissent un pseudo mécanisme d'espace de noms pour Zend Framework.
Zend Framework adoptera la fonctionnalité des espaces de noms de PHP quand celle-ci sera
disponible et qu'il sera possible pour les développeurs de l'utiliser dans leurs applications.
Regardez les noms de classes dans les librairies standard et extras pour avoir des exemples
de cette convention de nommage. IMPORTANT : le code qui opère avec le Framework mais qui
n'en fait par partie, c'est-à-dire le code écrit par un utilisateur et pas Zend ou une des entreprises
partenaires, ne doivent jamais commencer par "Zend_".
This naming convention is new with version 1.9.0 of Zend Framework. Classes
that pre-date that version may not follow this rule, but will be renamed in the
future in order to comply.
C.3.3. Interfaces
In general, interfaces follow the same conventions as classes, with one additional rule:
interface names may optionally end in the term, "Interface", but that term must not be
preceded by an underscore. As an example, Zend_Controller_Plugin_Interface
is considered an invalid name, but Zend_Controller_PluginInterface or
Zend_Controller_Plugin_PluginInterface would be valid names.
While this rule is not required, it is strongly recommended, as it provides a good visual cue to
developers as to which files contain interfaces rather than classes.
This naming convention is new with version 1.9.0 of Zend Framework. Classes
that pre-date that version may not follow this rule, but will be renamed in the
future in order to comply.
1676
Convention de codage
PHP de Zend Framework
Tout fichier contenant du code PHP doit se terminer par l'extension ".php". Ces exemples
montrent des noms de fichiers acceptables pour contenir les noms de classes issus des
exemples ci-dessus :
Zend/Db.php
Zend/Controller/Front.php
Zend/View/Helper/FormRadio.php
Les noms de fichiers doivent correspondre aux noms des classes décris ci-dessus.
Les noms de fonctions doivent toujours commencer avec une lettre en minuscule. Quand un
nom de fonction est composé de plus d'un seul mot, la première lettre de chaque mot doit être
mise en majuscule. C'est ce que l'on appelle communément la "notationCamel".
La clarté est conseillée. Le nom des fonctions devrait être aussi explicite que possible, c'est un
gage de compréhension du code.
filterInput()
getElementById()
widgetFactory()
Pour la programmation orientée objet, les accesseurs aux objets doivent toujours être préfixés
par soit "get" soit "set". Lorsque vous utilisez des motifs de conception, comme le Singleton ou la
Fabrique, le nom de la méthode doit contenir le nom du motif pour permettre une reconnaissance
plus simple et plus rapide du motif.
Pour des méthodes d'objet qui sont déclarées avec la construction "private" ou "protected", le
premier caractère du nom variable doit être un tiret bas simple ("_"). C'est la seule utilisation
autorisé d'un tiret bas dans un nom de méthode. Les méthodes déclarées "public" ne devraient
jamais commencer par un tiret bas.
Les fonctions à portée globale ("les fonctions flottantes") sont autorisées mais déconseillées. Il
est recommandé de mettre ces fonctions dans des classes statiques.
C.3.6. Variables
Les noms de variables ne peuvent contenir que des caractères alphanumériques. Les tirets bas
ne sont pas permis. Les nombres sont autorisés mais déconseillés.
Pour les variables membres de classe qui sont déclarées comme "private" ou "protected", le
premier caractère du nom de la variable doit être un tiret bas simple ("_"). C'est la seule utilisation
autorisé d'un tiret bas dans un nom de variable. Les variables membres "public" ne devraient
jamais commencer par un tiret bas.
1677
Convention de codage
PHP de Zend Framework
Tout comme les noms de fonction (cf la section 3.3 ci-dessus), les noms de variables doivent
toujours commencer par un caractère en minuscule et suivre la convention de capitalisation de
la "notationCamel".
La clarté est conseillée. Les variables devraient toujours être aussi claires que pratiques. Des
noms de variables comme "$i" et "$n" sont déconseillé pour tout autre usage que les petites
boucles. Si une boucle contient plus de 20 lignes de code, les variables pour les indices doivent
avoir des noms descriptifs.
C.3.7. Constantes
Les constantes peuvent contenir des caractères alphanumériques et des tirets bas. Les nombres
sont autorisés.
Les constantes doivent toujours être en majuscule, cependant les mots pouvant les composer
doivent être séparés par des tiret-bats ("_").
Les constantes doivent toujours être définies comme des membres d'une classe, en utilisant la
construction "const". Définir des constantes globales avec "define" est permis mais déconseillé.
<?php
?>
Les balises courtes d'ouvertures ("<?")ne sont pas autorisées. Pour les fichiers ne contenant que
du code PHP, la balise de fermeture doit toujours être omise (Voir Section C.2.1, « Général »).
1678
Convention de codage
PHP de Zend Framework
La syntaxe ci-dessus est préférée à l'échappement des apostrophes car elle est plus facile à lire.
Lors de la concaténation de chaînes avec l'opérateur ".", il est permis de couper le segment en
plusieurs lignes pour améliorer la lisibilité. Dans ces cas, chaque nouvelle ligne doit être remplie
avec des espaces, de façon à aligner le "." sous l'opérateur "=" :
C.4.3. Tableaux
C.4.3.1. Tableaux indexés numériquement
L'utilisation d'indices négatifs n'est pas permise.
Un tableau indexé peut commencer avec n'importe quel nombre positif, cependant cette méthode
est déconseillée. Il est conseillé de commencer l'indexation à 0.
Lors de la déclaration de tableaux indexés avec la construction array, un espace doit être ajouté
après chaque virgule délimitante, pour améliorer la lisibilité :
Il est aussi permis de déclarer des tableaux indexés sur plusieurs lignes en utilisant la
construction array. Dans ce cas, chaque nouvelle ligne doit être remplie par des espaces
jusqu'à ce que cette ligne s'aligne, comme il est montré dans l'exemple suivant :
Alternately, the initial array item may begin on the following line. If so, it should be padded at one
indentation level greater than the line containing the array declaration, and all successive lines
1679
Convention de codage
PHP de Zend Framework
should have the same indentation; the closing paren should be on a line by itself at the same
indentation level as the line containing the array declaration:
$sampleArray = array(
1, 2, 3, 'Zend', 'Studio',
$a, $b, $c,
56.44, $d, 500,
);
When using this latter declaration, we encourage using a trailing comma for the last item in the
array; this minimizes the impact of adding new items on successive lines, and helps to ensure
no parse errors occur due to a missing comma.
Alternately, the initial array item may begin on the following line. If so, it should be padded at
one indentation level greater than the line containing the array declaration, and all successive
lines should have the same indentation; the closing paren should be on a line by itself at the
same indentation level as the line containing the array declaration. For readability, the various
"=>" assignment operators should be padded such that they align.
$sampleArray = array(
'firstKey' => 'firstValue',
'secondKey' => 'secondValue',
);
When using this latter declaration, we encourage using a trailing comma for the last item in the
array; this minimizes the impact of adding new items on successive lines, and helps to ensure
no parse errors occur due to a missing comma.
C.4.4. Classes
C.4.4.1. Déclaration de classes
Les classes doivent être nommées conformément aux conventions de nommage de Zend
Framework.
Toutes les classes doivent avoir un bloc de documentation conforme aux standards
PHPDocumentor.
Le placement de code additionnel dans un fichier de classe est permis, mais déconseillé. Dans
ces fichiers, deux lignes vides doivent séparer la classe du code PHP additionnel.
1680
Convention de codage
PHP de Zend Framework
/**
* Bloc de documentation
*/
class SampleClass
{
// contenu de la classe
// qui doit être indenté avec 4 espaces
}
Classes that extend other classes or which implement interfaces should declare their
dependencies on the same line when possible.
If as a result of such declarations, the line length exceeds the maximum line length, break the line
before the "extends" and/or "implements" keywords, and pad those lines by one indentation level.
class SampleClass
extends FooAbstract
implements BarInterface
{
}
If the class implements multiple interfaces and the declaration exceeds the maximum line length,
break after each comma separating the interfaces, and indent the interface names such that
they align.
class SampleClass
implements BarInterface,
BazInterface
{
}
Toute variable déclarée dans une classe doit être listée en haut de cette classe, avant toute
déclaration de méthode.
La construction var n'est pas permise. Les variables membres déclarent toujours leur visibilité
en utilisant la construction private, protected, ou public. L'accès direct à ces variables
membres en les rendant publiques est permis mais déconseillé. Il est préférable d'utiliser des
accesseurs (set/get).
1681
Convention de codage
PHP de Zend Framework
Les fonctions internes aux classes doivent toujours déclarer leur visibilité en utilisant la
construction private, protected, ou public.
Tout comme les classes, l'accolade ouvrante est toujours écrite sous le nom de la fonction. Il
n'y a pas d'espace entre le nom de la fonction et les parenthèses des arguments. Il n'y a pas
d'espace entre la parenthèse fermante et l'accolade.
/*
* Bloc de documentation
*/
class Foo
{
/**
* Bloc de documentation
*/
public function bar()
{
// contenu de la fonction
// qui doit être indenté avec 4 espaces
}
}
In cases where the argument list exceeds the maximum line length, you may introduce line
breaks. Additional arguments to the function or method must be indented one additional level
beyond the function or method declaration. A line break should then occur before the closing
argument paren, which should then be placed on the same line as the opening brace of the
function or method with one space separating the two, and at the same indentation level as the
function or method declaration. The following is an example of one such situation:
/**
* Documentation Block Here
*/
class Foo
{
/**
* Documentation Block Here
*/
public function bar($arg1, $arg2, $arg3,
$arg4, $arg5, $arg6
) {
// all contents of function
// must be indented four spaces
}
}
NOTE : Le passage par référence est permis uniquement dans la déclaration de la fonction :
/**
* Bloc de documentation
*/
class Foo
{
/**
* Bloc de documentation
1682
Convention de codage
PHP de Zend Framework
*/
public function bar(&$baz)
{}
}
La valeur de retour ne doit pas être entourée de parenthèses. Ceci peut gêner à la lecture et
peut aussi casser le code si une méthode est modifiée plus tard pour retourner par référence.
/**
* Bloc de documentation
*/
class Foo
{
/**
* INCORRECT
*/
public function bar()
{
return($this->bar);
}
/**
* CORRECT
*/
public function bar()
{
return $this->bar;
}
}
threeArguments(1, 2, 3);
L'appel par référence est interdit. Référez vous à la section sur la déclaration de fonctions pour
la méthode correcte de passage des argument par référence.
Pour les fonctions dont les arguments peuvent être des tableaux, l'appel à la fonction doit inclure
la construction "array" et peut être divisé en plusieurs ligne pour améliorer la lecture. Dans ces
cas, les standards d'écriture de tableaux s'appliquent aussi :
threeArguments(array(
1, 2, 3, 'Zend', 'Studio',
$a, $b, $c,
56.44, $d, 500
), 2, 3);
1683
Convention de codage
PHP de Zend Framework
Pour la condition entre les parenthèses, les opérateurs doivent être séparés par des espaces
pour une meilleure lisibilité. Les parenthèses internes sont conseillées pour améliorer le
regroupement logique de longues conditions.
L'accolade ouvrante est écrite sur la même ligne que la condition. L'accolade fermante est
toujours écrite sur sa propre ligne. Tout contenu présent à l'intérieur des accolades doit être
indenté par 4 espaces.
if ($a != 2) {
$a = 2;
}
If the conditional statement causes the line length to exceed the maximum line length and has
several clauses, you may break the conditional into multiple lines. In such a case, break the
line prior to a logic operator, and pad the line such that it aligns under the first character of
the conditional clause. The closing paren in the conditional will then be placed on a line with
the opening brace, with one space separating the two, at an indentation level equivalent to the
opening control statement.
if (($a == $b)
&& ($b == $c)
|| (Foo::CONST == $d)
) {
$a = $d;
}
The intention of this latter declaration format is to prevent issues when adding or removing
clauses from the conditional during later revisions.
Pour les instruction "if" qui incluent "elseif" ou "else", les conventions de formatage sont similaires
à celles de la construction "if". Les exemples suivants montrent le formatage approprié pour les
structures "if" avec "else" et/ou les constructions "elseif" :
if ($a != 2) {
$a = 2;
} else {
$a = 7;
}
if ($a != 2) {
$a = 2;
} elseif ($a == 3) {
$a = 4;
} else {
$a = 7;
}
if (($a == $b)
&& ($b == $c)
1684
Convention de codage
PHP de Zend Framework
|| (Foo::CONST == $d)
) {
$a = $d;
} elseif (($a != $b)
|| ($b != $c)
) {
$a = $c;
} else {
$a = $b;
}
PHP permet que ces instructions soient écrites sans accolades dans certaines circonstances.
La convention de codage ne fait pas de différentiation et toutes les instructions "if", "elseif" et
"else" doivent utiliser des accolades.
C.4.6.2. Switch
Les instructions de contrôle avec "switch" ne doivent avoir qu'un seul espace avant la parenthèse
ouvrante de l'instruction conditionnelle, et aussi un seul espace après la parenthèse fermante.
Tout le contenu à l'intérieur de l'instruction "switch" doit être indenté avec 4 espaces. Le contenu
sous chaque "case" doit être indenté avec encore 4 espaces supplémentaires.
switch ($numPeople) {
case 1:
break;
case 2:
break;
default:
break;
}
La construction default ne doit jamais être oubliée dans une instruction switch.
NOTE : Il est parfois pratique d'écrire une clause case qui passe à travers le case suivant en
omettant l'inclusion de break ou return. Pour distinguer ce cas d'un bug, toute clause case
ne contenant pas break ou return doit contenir le commentaire "// break intentionally omitted".
Tous les fichiers de code source écrits pour Zend Framework ou qui opèrent avec ce framework
doivent contenir un docblock du fichier, en haut de chaque fichier, et un docblock de classe
immédiatement au dessus de chaque classe. Ci-dessous vous trouverez des exemples de tels
docblocs.
C.4.7.2. Fichiers
Chaque fichier qui contient du code PHP doit avoir un bloc d'entête en haut du fichier qui contient
au minimum ces balises phpDocumentor :
1685
Convention de codage
PHP de Zend Framework
/**
* Description courte du fichier
*
* Description longue du fichier s'il y en a une
*
* LICENSE: Informations sur la licence
*
* @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license BSD License
* @version $Id:$
* @link http://framework.zend.com/package/PackageName
* @since File available since Release 1.5.0
*/
C.4.7.3. Classes
Chaque classe doit avoir un docblock qui contient au minimum ces balises phpDocumentor :
/**
* Description courte de la classe
*
* Description longue de la classe, s'il y en a une
*
* @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license BSD License
* @version Release: @package_version@
* @link http://framework.zend.com/package/PackageName
* @since Class available since Release 1.5.0
* @deprecated Class deprecated in Release 2.0.0
*/
C.4.7.4. Fonctions
Chaque fonction, méthode, doit avoir un docblock contenant au minimum :
Il n'est pas nécessaire d'utiliser la balise "@access" parce que le niveau d'accès est déjà connu
avec les constructions "public", "private", "protected" utilisée pour déclarer la fonction.
1686
Annexe D. Zend Framework
Documentation Standard
D.1. Overview
D.1.1. Scope
This document provides guidelines for creation of the end-user documentation found within
Zend Framework. It is intended as a guide to Zend Framework contributors, who must write
documentation as part of component contributions, as well as to documentation translators. The
standards contained herein are intended to ease translation of documentation, minimize visual
and stylistic differences between different documentation files, and make finding changes in
documentation easier with diff tools.
You may adopt and/or modify these standards in accordance with the terms of our license.
1687
Zend Framework
Documentation Standard
XML files from translated languages must also include a revision tag containing the revision of
the corresponding English-language file the translation was based on.
D.2.3. Indentation
Indentation should consist of 4 spaces. Tabs are not allowed.
Tags which are at the same level must have the same indentation.
<sect1>
</sect1>
<sect1>
</sect1>
Tags which are one level under the previous tag must be indented with 4 additional spaces.
<sect1>
<sect2>
</sect2>
</sect1>
Multiple block tags within the same line are not allowed; multiple inline tags are allowed, however.
Note: Do not use carriage returns (CR) as is the convention in Apple OS's (0x0D) or the carriage
return - linefeed combination (CRLF) as is standard for the Windows OS (0x0D, 0x0A).
1688
Zend Framework
Documentation Standard
<para>
</para>
<!-- OK -->
This is the class <classname>Zend_Class</classname>.
Closing block tags may be preceded by whitespace equivalent to the current indentation level,
but no more than that amount.
<!-- OK -->
<sect1>
</sect1>
<!-- OK -->
This is the class <classname>Zend_Class</classname>
1689
Zend Framework
Documentation Standard
<para>
Another paragraph.
</para>
<!-- OK -->
<para>
Some text...
... and more text
</para>
<para>
Another paragraph.
</para>
<!-- OK -->
<para>
Some text...
</para>
<para>
More text...
</para>
The first child tag should open directly below its parent, with no empty line between them; the
last child tag should close directly before the closing tag of its parent.
<sect2>
</sect2>
<sect2>
</sect2>
<sect2>
</sect2>
</sect1>
<!-- OK -->
<sect1>
<sect2>
</sect2>
<sect2>
1690
Zend Framework
Documentation Standard
</sect2>
<sect2>
</sect2>
</sect1>
<para>Sibling paragraph.</para>
<programlisting language="php"><![CDATA[
<programlisting> sections must not add linebreaks or whitespace at the beginning or end of the
section, as these are then represented in the final output.
$render = "xxx";
]]></programlisting>
<!-- OK -->
<programlisting language="php"><![CDATA[
$render = "xxx";
]]></programlisting>
Ending CDATA and <programlisting> tags should be on the same line, without any indentation.
<!-- OK -->
<programlisting language="php"><![CDATA[
$render = "xxx";
]]></programlisting>
The <programlisting> tag should contain the "language" attribute with a value appropriate to
the contents of the program listing. Typical values include "css", "html", "ini", "javascript", "php",
"text", and "xml".
1691
Zend Framework
Documentation Standard
For program listings containing only PHP code, PHP tags (e.g., "<?php", "?>") are not required,
and should not be used. They simply clutter the narrative, and are implied by the use of the
<programlisting> tag.
<programlisting language="php"<![CDATA[
<?php
// ...
?>
]]></programlisting>
Line lengths within program listings should follow the coding standards recommendations.
Short tags (e.g., "<?", "<?=") should never be used within programlisting or the
narrative of a document.
<para>
The class <classname>Zend_Class</classname>.
</para>
D.2.8.2. varname
Variables must be wrapped in the <varname> tag. Variables must be written using the "$" sigil.
No other content is allowed within this tag, unless a class name is used, which indicates a class
variable.
<para>
The variable <varname>$var</varname> and the class variable
<varname>Zend_Class::$var</varname>.
</para>
1692
Zend Framework
Documentation Standard
D.2.8.3. methodname
Methods must be wrapped in the <methodname> tag. Methods must either include the full
method signature or at the least a pair of closing parentheses (e.g., "()"). No other content is
allowed within this tag, unless a class name is used, which indicates a class method.
<para>
The method <methodname>foo()</methodname> and the class method
<methodname>Zend_Class::foo()</methodname>. A method with a full signature:
<methodname>foo($bar, $baz)</methodname>
</para>
D.2.8.4. constant
Use the <constant> tag when denoting constants. Constants must be written in UPPERCASE.
No other content is allowed within this tag, unless a class name is used, which indicates a class
constant.
<para>
The constant <constant>FOO</constant> and the class constant
<constant>Zend_Class::FOO</constant>.
</para>
D.2.8.5. filename
Filenames and paths must be wrapped in the <filename> tag. No other content is allowed in
this tag.
<para>
The filename <filename>application/Bootstrap.php</filename>.
</para>
D.2.8.6. command
Commands, shell scripts, and program calls must be wrapped in the <command> tag. If the
command includes arguments, these should also be included within the tag.
<para>
Execute <command>zf.sh create project</command>.
</para>
D.2.8.7. code
Usage of the <code> tag is discouraged, in favor of the other inline tasks discussed previously.
<!-- OK -->
1693
Zend Framework
Documentation Standard
<title>Using Zend_Class</title>
D.3. Recommendations
D.3.1. Use editors without autoformatting
For editing the documentation, typically you should not use formal XML editors. Such editors
normally autoformat existing documents to fit their own standards and/or do not strictly follow the
docbook standard. As examples, we have seen them erase the CDATA tags, change 4 space
separation to tabs or 2 spaces, etc.
The style guidelines were written in large part to assist translators in recognizing the lines that
have changed using normal diff tools. Autoformatting makes this process more difficult.
When writing your examples for inclusion in the manual, follow all coding standards and
documentation standards.
Linking to other sections of the manual may be done using either the <xref> tag (which will
substitute the section title for the link text) or the <link> tag (to which you must provide link text).
<para>
"Xref" links to a section: <xref
linkend="doc-standard.recommendations.links" />.
</para>
<para>
"Link" links to a section, using descriptive text: <link
linkend="doc-standard.recommendations.links">documentation on
links</link>.
</para>
1694
Zend Framework
Documentation Standard
<para>
The <ulink url="http://framework.zend.com/">Zend Framework site</ulink>.
</para>
1695
Annexe E. Recommended Project
Structure for Zend Framework MVC
Applications
E.1. Overview
Many developers seek guidance on the best project structure for a Zend Framework project
in a relatively flexible environment. A "flexible" environment is one in which the developer can
manipulate their file systems and web server configurations as needed to achieve the most ideal
project structure to run and secure their application. The default project structure will assume
that the developer has such flexibility at their disposal.
The following directory structure is designed to be maximally extensible for complex projects,
while providing a simple subset of folder and files for project with simpler requirements. This
structure also works without alteration for both modular and non-modular Zend Framework
applications. The .htaccess files require URL rewrite functionality in the web server as
described in the Rewrite Configuration Guide, also included in this appendix.
It is not the intention that this project structure will support all possible Zend Framework project
requirements. The default project profile used by Zend_Tool reflect this project structure, but
applications with requirements not supported by this structure should use a custom project profile.
1696
Recommended Project Structure for
Zend Framework MVC Applications
helpers/
scripts/
Bootstrap.php
data/
cache/
indexes/
locales/
logs/
sessions/
uploads/
docs/
library/
public/
css/
images/
js/
.htaccess
index.php
scripts/
jobs/
build/
temp/
tests/
The following describes the use cases for each directory as listed.
• application/: This directory contains your application. It will house the MVC system, as
well as configurations, services used, and your bootstrap file.
• controllers/, models/, and views/: These directories serve as the default controller,
model or view directories. Having these three directories inside the application directory
provides the best layout for starting a simple project as well as starting a modular project
that has global controllers/models/views.
• layouts/: This layout directory is for MVC-based layouts. Since Zend_Layout is capable
of MVC- and non-MVC-based layouts, the location of this directory reflects that layouts are
not on a 1-to-1 relationship with controllers and are independent of templates within views/.
• modules/: Modules allow a developer to group a set of related controllers into a logically
organized group. The structure under the modules directory would resemble the structure
under the application directory.
• services/: This directory is for your application specific web-service files that are provided
by your application, or for implementing a Service Layer for your models.
• Bootstrap.php: This file is the entry point for your application, and should implement
Zend_Application_Bootstrap_Bootstrapper. The purpose for this file is to
bootstrap the application and make components available to the application by initializing
them.
• data/: This directory provides a place to store application data that is volatile and possibly
temporary. The disturbance of data in this directory might cause the application to fail. Also, the
1697
Recommended Project Structure for
Zend Framework MVC Applications
information in this directory may or may not be committed to a subversion repository. Examples
of things in this directory are session files, cache files, sqlite databases, logs and indexes.
• library/: This directory is for common libraries on which the application depends, and
should be on the PHP include_path. Developers should place their application's library code
under this directory in a unique namespace, following the guidelines established in the PHP
manual's Userland Naming Guide, as well as those established by Zend itself. This directory
may also include Zend Framework itself; if so, you would house it in library/Zend/.
• public/: This directory contains all public files for your application. index.php sets up and
invokes Zend_Application, which in turn invokes the application/Bootstrap.php
file, resulting in dispatching the front controller. The web root of your web server would typically
be set to this directory.
• scripts/: This directory contains maintenance and/or build scripts. Such scripts might
include command line, cron, or phing build scripts that are not executed at runtime but are part
of the correct functioning of the application.
• temp/: The temp/ folder is set aside for transient application data. This information would
not typically be committed to the applications svn repository. If data under the temp/ directory
were deleted, the application should be able to continue running with a possible decrease in
performance until data is once again restored or recached.
• tests/: This directory contains application tests. These could be hand-written, PHPUnit tests,
Selenium-RC based tests or based on some other testing framework. By default, library code
can be tested by mimicing the directory structure of your library/ directory. Additionally,
functional tests for your application could be written mimicing the application/ directory
structure (including the application subdirectory).
<modulename>
configs/
application.ini
controllers/
helpers/
forms/
layouts/
filters/
helpers/
scripts/
models/
services/
views/
filters/
helpers/
scripts/
Bootstrap.php
The purpose of these directories remains exactly the same as for the recommended project
directory structure.
1698
Recommended Project Structure for
Zend Framework MVC Applications
<VirtualHost my.domain.com:80>
ServerName my.domain.com
DocumentRoot /path/to/server/root/my.domain.com/public
RewriteEngine off
<Location />
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ /index.php [NC,L]
</Location>
</VirtualHost>
Note the slash ("/") prefixing index.php; the rules for .htaccess differ in this regard.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
There are many ways to configure mod_rewrite; if you would like more information, see Jayson
Minard's Blueprint for PHP Applications: Bootstrapping.
1699
Recommended Project Structure for
Zend Framework MVC Applications
1700
Annexe F. Guide de performance Zend
Framework
F.1. Introduction
Le but de cette annexe est de fournir des stratégies concrètes afin d'améliorer les performances
de vos applications Zend Framework. Ce guide est présenté sous le format "Question &
Réponse" et est divisé en différents sujets de préoccupation.
1701
Guide de performance
Zend Framework
$paths = array(
realpath(dirname(__FILE__) . '/../library'),
'.',
);
set_include_path(implode(PATH_SEPARATOR, $paths);
Vous pouvez utiliser des chemins relatifs - du moment qu'ils sont relatifs à un chemin absolu :
define('APPLICATION_PATH', realpath(dirname(__FILE__)));
$paths = array(
APPLICATION_PATH . '/../library'),
'.',
);
set_include_path(implode(PATH_SEPARATOR, $paths);
Une autre optimisation consiste en la combinaison de chemins. Par exemple, Zend Framework
suit la convention de nommage PEAR ; ainsi , si vous utilisez des librairies PEAR (ou d'autres
framework ou librairies de composants qui respectent la convention de nommage PEAR),
essayez de mettre toutes ces librairies dans le même chemin de l'include_path. Ceci peut
souvent être réalisé par quelque chose d'assez simple comme de créer des liens symboliques
vers une ou plusieurs bibliothèques dans un dossier commun.
1702
Guide de performance
Zend Framework
aussi être chargés. Cependant ces mêmes exemples montrent souvent ce dossier comme étant
le premier de la liste des include_path - ce qui veut dire l'arbre de dossiers courant est toujours
scanné en premier. La plupart du temps, avec Zend Framework, ce n'est pas nécessaire, et ce
dossier peut tout naturellement être mis en dernière position de la liste.
Essayons de mettre ensemble toutes ces suggestions. Considérons que nous utilisons une
ou plusieurs composants PEAR en conjonction avec Zend Framework - par exemple les
composants PHPUnit et Archive_Tar - et qu'il est occasionnellement nécessaire d'inclure
les fichiers relativement au fichier courant.
Premièrement, nous allons créer un dossier pour les librairies dans notre projet. Dans ce
même dossier, nous allons créer un lien symbolique vers notre dossier Zend Framework
"library/Zend", ainsi que les dossiers nécessaires dans notre installation PEAR :
library
Archive/
PEAR/
PHPUnit/
Zend/
Ceci nous permet d'ajouter notre propre librairie si nécessaire, tout en laissant intact les
librairies partagées.
Ensuite, nous optons pur la création de notre include_path par programme à l'intérieur
de notre fichier public/index.php. Ceci nous permet de déplacer notre code dans le
système de fichiers, sans devoir éditer l'include_path à chaque fois.
Nous emprunterons des idées à chacune des suggestions ci-dessus : nous utiliserons
les chemins absolus, déterminé en utilisant le realpath() ; nous positionnerons Zend
Framework au plus tôt dans l'include_path ; nous avons déjà vérifié les chemins
d'inclusions nécessaires ; et nous mettrons le dossier courant comme dernier chemin. En
fait, nous faisons tout bien ici - nous allons donc terminer avec seulement deux chemins.
$paths = array(
realpath(dirname(__FILE__) . '/../library'),
'.'
);
set_include_path(implode(PATH_SEPARATOR, $paths));
Cependant, la plupart des avantages que vous pourrez retirer de l'autoloading sont diminués si
le code de votre librairie exécute toujours des appels à require_once - ce qui est précisément
le cas de Zend Framework. La question est donc : comment éliminer ces déclarations
require_once dans le but de maximiser les performances de l'autoloader.
1703
Guide de performance
Zend Framework
% cd chemin/vers/la/librarie/ZendFramework
% find . -name '*.php' -not -wholename '*/Loader/Autoloader.php' \
-not -wholename '*/Application.php' -print0 | \
xargs -0 sed --regexp-extended --in-place 's/(require_once)/\/\/ \1/g'
Cette ligne unique (coupée en deux pour la lisibilité) itère parmi les fichiers PHP et y remplace
toute les instances de require_once par //require_once, c'est-à-dire en commentant
toutes ces lignes (tout en maintenant les appels à require_once dans Zend_Application
et Zend_Loader_Autoloader, puisque ces classes tomberont en erreur sans ceux-ci).
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance();
Le chargeur de plugins vous permet de définir des paires préfixe de classe / chemin, vous
autorisant ainsi à spécifier des fichiers de classe dans des chemins de dossiers non standard.
Chaque préfixe peut avoir de multiples chemins associés. En interne, le chargeur de plugins
boucle à travers chaque préfixe, et ensuite à travers chaque chemin lui étant associé, en testant
l'existence du fichier et s'il est accessible dans ce chemin. Il le charge ensuite, et teste pour voir
si la classe recherchée est bien disponible. Comme vous pouvez l'imaginer, tout ceci entraîne
des appels aux stats du système de fichiers.
Multipliez ceci par le nombre de composants qui utilisent le PluginLoader, et vous aurez une
idée de l'importance de ce problème. Au moment de l'écriture de ce document, les composants
suivants utilisent le PluginLoader :
• Zend_File_Transfer : adaptateurs
1704
Guide de performance
Zend Framework
• Zend_Paginator : adaptateurs
1705
Guide de performance
Zend Framework
Effectuez un EXPLAIN sur vos requêtes, et testez plusieurs approches jusqu'à obtenir un indice
le plus performant, ensuite écrivez en dur votre SQL en tant que propriété de la classe ou comme
constante.
Si votre SQL requiert des arguments variables, fournissez des emplacements réservés dans
votre SQL, et utilisez une combinaison de vsprintf() et array_walk() pour injecter les
valeurs dans votre SQL :
• Array : celui-ci est le plus rapide, puisqu'il est, par définition, analysé dans un format natif de
PHP immédiatement lors de son inclusion.
• CSV : utilises fgetcsv() pour analyser un fichier CSV file et le transforme en un format PHP
natif.
1706
Guide de performance
Zend Framework
• INI : utilises parse_ini_file() pour analyser un fichier INI file et le transforme en un format
PHP natif. Celui-ci et l'adaptateur CSV sont équivalent en terme de performance.
• Gettext : l'adaptateur Gettext de Zend Framework n'utilise pas l'extension gettext puisqu'elle
n'est pas thread safe et ne permet pas de spécifier plus d'une locale par serveur. En
conséquence, il est plus lent que d'utiliser l'extension Gettext directement, mais comme le
format Gettext est binaire, il reste plus rapide à analyser qu'un format XML.
Si l'un de vos besoins principaux est la performance, nous vous conseillons d'utiliser l'un des
adaptateurs ci-dessus.
Vous pouvez lire plus d'informations concernant la mise en cache d'informations de traduction
ou de localisation dans les paragraphes suivants :
Internally, Zend_View uses the PluginLoader to look up helper classes. This means that for
each helper you call, Zend_View needs to pass the helper name to the PluginLoader, which
then needs to determine the class name, load the class file if necessary, and then return the class
name so it may be instantiated. Subsequent uses of the helper are much faster, as Zend_View
keeps an internal registry of loaded helpers, but if you use many helpers, the calls add up.
1707
Guide de performance
Zend Framework
The question, then, is: how can you speed up helper resolution?
/**
* Proxy to url view helper
*
* @param array $urlOptions Options passed to the assemble method
* of the Route object.
* @param mixed $name The name of a Route to use. If null it will
* use the current Route
* @param bool $reset Whether or not to reset the route defaults
* with those provided
* @return string Url for the link href attribute.
*/
public function url(array $urlOptions = array(), $name = null,
$reset = false, $encode = true
) {
if (!array_key_exists('url', $this->_localHelperObjects)) {
$this->_localHelperObjects['url'] = new Zend_View_Helper_Url();
$this->_localHelperObjects['url']->setView($this);
}
$helper = $this->_localHelperObjects['url'];
return $helper->url($urlOptions, $name, $reset, $encode);
}
/**
* Echo a message
*
* Direct implementation.
*
* @param string $string
* @return string
*/
public function message($string)
{
return "<h1>" . $this->escape($message) . "</h1>\n";
}
}
1708
Guide de performance
Zend Framework
Either way, this technique will substantially reduce the overhead of the helper system by
avoiding calls to the PluginLoader entirely, and either benefiting from autoloading or bypassing
it altogether.
• $module: the name of the module in which the view script resides; or, if no third argument is
provided and this is an array or object, it will be the $model argument.
• $model: an array or object to pass to the partial representing the clean data to assign to the
view.
The power and use of partial() come from the second and third arguments. The $module
argument allows partial() to temporarily add a script path for the given module so that the
partial view script will resolve to that module; the $model argument allows you to explicitly pass
variables for use with the partial view. If you're not passing either argument, use render()
instead!
Basically, unless you are actually passing variables to the partial and need the clean variable
scope, or rendering a view script from another MVC module, there is no reason to incur the
overhead of partial(); instead, use Zend_View's built-in render() method to render the
view script.
1709
Guide de performance
Zend Framework
While the ActionStack still requires a dispatch cycle, this is still cheaper than the action() view
helper as it does not need to clone objects and reset internal state. Additionally, it ensures that
all pre and post dispatch plugins are invoked, which may be of particular concern if you are using
front controller plugins for handling ACL's to particular actions.
// bug/list.phtml:
1710
Guide de performance
Zend Framework
echo "<ul>\n";
foreach ($this->bugs as $bug) {
printf("<li><b>%s</b>: %s</li>\n",
$this->escape($bug->id),
$this->escape($bug->summary)
);
}
echo "</ul>\n";
This could be refactored to a view helper that looks like the following:
This has two benefits: it no longer incurs the overhead of the action() view helper, and also
presents a more semantically understandable API.
1711
Annexe G. Informations de copyright
Les droits ci-dessous sont applicables aux éléments de Zend Framework.
1712