Vous êtes sur la page 1sur 5

Outils

10 4/2009
S
PL correspond aux initiales de Standard
PHP Library. l'origine dveloppe par
Marcus Boerger (alias Helly), il s'agit
d'un ensemble de classes, de classes abstraites
et d'interfaces prtes lemploi pour rpondre
des problmes courants (d'o le terme Stan-
dard). Parmi ces objets, on retrouvera de quoi
manipuler les tableaux, beaucoup d'itrateurs
trs pratiques, et quelques exceptions suppl-
mentaires.
Pourquoi avoir recourt
la SPL ?
Pour rpondre un problme concret, le d-
veloppeur moyen aura tendance coder son
propre script. De cette manire, il est sr que
le rsultat rpondra clairement ses attentes et
aux besoins de son application. Cependant, il
est un principe simple en dveloppement, de
ne pas rinventer la roue (DRTW : Don't Rein-
vent The Wheel !). Autrement dit, plutt que de
perdre du temps dvelopper quelque chose, il
sera plus pertinent de rutiliser un composant
dj crit, probablement plus propre, plus op-
timis, mieux cod, voire test unitairement
dans le meilleur des cas. Aprs tout, Marcus
Boerger participe au dveloppement du mo-
dle objet de PHP 5, on peut aisment en sup-
poser la qualit du code et surtout le respect de
la philosophie PHP.
Connatre les classes
et extensions de la SPL
disponibles
La SPL est donc une extension de PHP 5 com-
prenant un certain nombre de classes. Pour
savoir lesquelles sont intgres, il existe une
fonction spl_classes() qui retourne un ta-
bleau contenant la liste des classes prdfinies
(Listing 1). En testant sur une installation
Wamp tout ce qu'il y a de plus basique, le nom-
bre de classes disponibles devrait tre aux alen-
tours de 43.
Attention, cette fonction liste les classes,
mais pas les interfaces disponibles. Pour ce
faire, il suffira d'utiliser la fonction get_
declared_interfaces () (Listing 2). Toujours
sur la mme installation, le nombre d'interfaces
devrait tourner autour de 11. Cette liste PHPin-
fo (voir Figure 1 PHPinfos-SPL).
Nous arrivons un total de 55 classes et
interfaces, un nombre plutt correct lorsque
l'on sait qu'au moment o PHP n'tait qu' la
version 5.0, on comptait peine une dizaine
de classes !
Dcomposition
de la classe ArrayObject
Pour bien commencer, il convient d'aborder
la classe la plus parlante de la SPL : la classe
de collection ArrayObject. Il s'agit d'un outil
extrmement pratique, d'une part parce qu'il
va bien au-del du simple tableau (array) PHP,
mais galement parce qu'il implmente auto-
matiquement les interfaces IteratorAggregate,
Traversable, Countable et ArrayAccess. Les deux
premiers sont ce que l'on appelle des itrateur-
set ils seront abords dans la dernire partie de
cet article.
Prsentation
de l'interface Countable
Pour compter, en PHP, les lments d'un ta-
bleau (array), on emploie la fonction count (ou
son alias sizeof, familier pour les gens venant
du langage C) qui retourne un entier. Cette in-
terface est simple d'utilisation, il suffit de dfi-
nir une mthode count() qui devra retourner
un entier. Une fois implmente, il suffit d'ap-
peler count() ou sizeof() sur une instance de
classe. L'exemple utilis sera une classe de bi-
bliothque contenant des livres (Listing 3).
Prsentation
de l'interface ArrayAccess
Les tableaux PHP sont de deux types : indexs
(valeur accessible par la position de l'objet dans
SPL :
Savoir utiliser les bons outils
PHP ne serait pas longtemps rest un langage aussi rpandu si par
dela sa simplicit ne se cachait pas un vritable potentiel. La SPL fait
partie de ces must-use que propose PHP 5 et permet de rpondre
proprement a des problemes complexes.
Cet article explique :
L'utilisation de plusieurs classes et interfaces
de la SPL.
Ce qu'il faut savoir :
tre familier avec le modle objet de PHP 5 car
il traitera de classes et d'interfaces.
Niveau de difficult
Figure 1. Liste des classes et interfaces de la SPL charges
Standard PHP Library
11 www.phpsolmag.org
le tableau) ou associatifs (valeur accessible par
la cl de l'objet sous forme de chane de carac-
tres). Il est donc possible d'accder un item
prcis de deux manires. Pour ce faire, il suf-
fit d'utiliser l'interface ArrayAccess et d'impl-
menter les quatre mthodes offsetExists,
offsetGet, offsetSet, offsetUnset. Pour
lexemple, la classe de bibliothque sera ru-
tilise mais disposera d'une information sup-
plmentaire : un nom. Cet exemple permet
de vrifier l'existence d'un attribut (via isset
et appelant la mthode offsetExists),
qu'il n'est pas vide (via empty et utilisant
offsetExists puis offsetGet), de spcifier
une valeur pour cet attribut (via offsetSet) et
enfin de le supprimer (via offsetUnset) (Lis-
ting 4). Cette mthode est rutilisable pour
naviguer dans une instance de classe car tout
attribut dun objet est accessible via un tableau
du type $instance[nomattribut].
Refactorisation de la classe de biblioth-
que en utilisant la classe ArrayObject. Il est
maintenant temps de visualiser ces deux in-
terfaces en fonctionnement, en tendant la
classe ArrayObject. Cet exemple montre une
bibliothque au fonctionnement similaire,
permettant d'ajouter, rcuprer et supprimer
des livres. Il ne garde pas la gestion du titre de
la bibliothque dans la mesure o il sert juste
illustrer le fonctionnement de l'interface
ArrayAccess (Listing 5). Il ne devrait pas tre
ncessaire de rappeler le temps de dveloppe-
ment gagn avec une classe de collection aussi
pratique !
Utilisation des exceptions mises
disposition par la SPL
Pour rappeler trs brivement, une exception
est un signal d'erreur interrompant l'excu-
tion d'un programme. C'est un principe de la
programmation oriente objet que l'on retrou-
vera dans des langages tels que l'ActionScript 3
ou encore Java. Il est possible de rcuprer ce
signal pour le traiter en consquence, grce
un bloc try/catch, ou par l'implmentation
d'un exception_handler http://fr.php.net/ma-
nual/fr/function.set-exception-handler.php).
Il est possible de lancer manuellement ce
genre d'exception dans un script, via la syn-
taxe throw (http://fr.php.net/manual/fr/languag
e.exceptions.php). Par dfaut, il suffit d'utiliser
la classe Exception, qui suffira interrom-
pre l'excution du script en cas de problme.
Cependant, la SPL met notre disposition
une dizaine d'exceptions tendues (i.e. hri-
tant de la classe de base Exception), telles que
OutOfBoundsException, RuntimeException ou
encore LogicException.
Pourquoi faudrait-il les utiliser, alors que la
classe Exception suffit largement? D'une part,
parce qu'il sera possible de traiter l'erreur dif-
fremment suivant son type, mais galement
(et surtout), pour des raisons de smantique :
en utilisant un type d'exception particulier, le
code source de l'application n'en sera que plus
clair et plus cohrent. Un exemple tout ce qu'il
y a de plus basique est disponible dans le Lis-
ting 6.
Utiliser les itrateurs
de la SPL
Un itrateur est un objet permettant de par-
courir une source d'informations de manire
simple et cohrente. Dans la plupart des cas,
cette source d'information est un arbre ou une
liste de donnes. En PHP, il s'agit de parcourir
l'instance d'un objet de type iterator (typ via
une classe parente ou une interface) en uti-
lisant la mthode foreach (http://fr.php.net/
manual/fr/control-structures.foreach.php). Cette
fonction foreach va automatiquement appe-
ler 5 mthodes au comportement spcifique
(rewind, current, key, next et valid) :
rewind : appele avant la premire itra-
tion, elle permet de rinitialiser la source
de donnes et de s'assurer qu'elle part bel
et bien du dbut.
current : retourne la valeur de l'objet
point par l'itration courante (lors d'un
foreach($aTable as $iKey=>$mValue)
{} cette valeur sera contenue dans la varia-
ble $mValue).
key : retourne la cl identifiant l'objet cou-
rant. Dans le cas de l'itration d'un tableau
index, cette valeur correspond la posi-
tion de l'objet dans ce tableau.
next : dcale le pointeur d'un cran (l'objet
identifi tant ainsi le suivant).
Figure 2. Arborescence de dossiers a parcourir
Listing 1. Liste des classes de la SPL disponibles
<?php
$aLoadedClasses = spl_classes();
printf('Il a %d classes disponibles :<br/>', sizeof($aLoadedClasses));
echo '<pre>';
print_r($aLoadedClasses);
echo '</pre>';
Listing 2. Liste des interfaces de la SP disponibles
<?php
$aLoadedInterfaces = get_declared_interfaces();
printf('Il a %d interfaces disponibles :<br/>', sizeof($aLoadedInterfaces));
echo '<pre>';
print_r($aLoadedInterfaces);
echo '</pre>';
Listing 3. Implmentation de l'interface Countable'
<?php
class Library implements Countable {
protected $bookList;
public function __construct() {
$this->bookList = array();
public function addBook($sTitle) {
$this->bookList[] = $sTitle;
}
public function count() {
return sizeof($this->bookList);
}
}
$oLibrary = new Library();
$oLibrary->addBook('Programmation Flex 3');
$oLibrary->addBook('Practical Symfony : Jobeet');
printf('Il y a %d livre(s) dans cette bibliothque.',sizeof($oLibrary));
Outils
12 4/2009
valid() : vrifie que l'objet courant exis-
te. Dans le cas de l'itration d'un tableau
index, il s'agit de vrifier que l'objet poin-
t existe bel et bien dans le tableau. Cette
mthode renvoie un boolen, si celui-ci est
false, l'itration est interrompue.
Cette information est donne pour le princi-
pe, il n'est pas ncessaire de connatre ces m-
thodes pour utiliser les itrateurs fournis par
la SPL. Pour crer un itrateur personnalis,
il suffit d'avoir recours, par exemple, l'inter-
face Iterator : http://www.apprendre-php.com/
tutoriels/tutoriel- 43- utiliser-l-interface-iterator-
avec-php-5.html. Certaines classes sont direc-
tement parcourables, en utilisant foreach sur
une instance de classe, mais il est galement
possible d'utiliser des itrateurs externes, c'est
par exemple le cas de l'itrateur ArrayIterator.
Utilisation d'un itrateur externe :
ArrayIterator
L'itrateur ArrayIterator va permette de par-
courir le contenu d'un tableau (Listing 7). Il
implmente un autre Itrateur nomm Seeka-
bleIterator qui permet de naviguer directement
vers une position dans la source de donnes.
Cet exemple n'est pas trs pertinent, il permet
simplement d'illustrer l'implmentation basi-
que d'un itrateur.
Rcuprer un itrateur associ : IteratorAg-
gregate.
Certaines classes permettent de rcuprer
directement un itrateur en particulier. C'est
possible grce une interface de la SPL nom-
me IteratorAggregate. Cette interface s'assure
de la prsence de la mthode getIterator qui
doit retourner l'itrateur idal. Cette interface
est implmente dans la classe ArrayObject
(Listing d'acceptance.
Les itrateurs d'acceptance :
FilterIterator
Le principe d'un itrateur d'acceptance est tou-
jours de parcourir une source de donnes. La
seule diffrence tant que les objets retourns
seront uniquement ceux satisfaisant une cer-
taine condition dfinie dans une mthode.
Pour reprendre le fonctionnement d'itrateur
abord plus haut, voici l'ordre des mthodes
s'excutant en interne :
appel de la mthode accept(),
appel de la mthode rewind(), s'il s'agit de
la toute premire itration,
appel de la mthode next(), s'il ne s'agit
pas de la toute premire itration,
appel de la mthode valid(),
appel de la mthode current(),
appel de la mthode key().
Le vritable intrt d'un itrateur d'acceptan-
ce tant de ne pas avoir se soucier une nou-
velle fois des 5 mthodes abordes plus haut.
Listing 4. Implmentation de l'interface ArrayAccess'
<?php
class Library implements Countable, ArrayAccess {
protected $bookList;
protected $name;
public function __construct() {
$this->_bookList = array();
}
public function addBook($sTitle) {
$this->_bookList[] = $sTitle;
}
public function count() {
return sizeof($this->bookList);
}
public function offsetExists($sOffsetName) {
return property_exists($this,$sOffsetName);
}
public function offsetGet($sOffsetName) {
if($this->offsetExists($sOffsetName)) {
return $this->$sOffsetName;
}
}
public function offsetSet($sOffsetName,$mOffsetValue) {
if($this->offsetExists($sOffsetName)) {
return $this->$sOffsetName = $mOffsetValue;
}
}
public function offsetUnset($sOffsetName) {
if($this->offsetExists($sOffsetName)) {
$this->$sOffsetName = null;
}
}
}
$oLibrary = new Library();
$oLibrary->addBook('Programmation Flex 3');
$oLibrary->addBook('Practical Symfony : Jobeet');
// verifcation que la cl name est disponible
if(isset($oLibrary['name'])) {
// si l'attribut "name" est vide
if(empty($oLibrary['name'])) {
// spcifcation d'un nom par dfaut
$oLibrary['name'] = 'Les meilleurs livres pour apprendre coder beau & propre !';
}
// affchage du nom de la bibliothque
echo 'Titre de la bibliothque : ',$oLibrary['name'];
// suppression du nom
unset($oLibrary['name']);
}
Listing 5. Refactorisation de la bibliothque grce l'objet ArrayObject'
<?php
class Library extends ArrayObject {
}
$oLibrary = new Library();
$oLibrary->append('Programmation Flex 3');
$oLibrary->append('Practical Symfony : Jobeet');
// ajout direct d'un livre en 3e position
$oLibrary[3] = 'CSS2 : pratique du design web';
// rcupration du premier livre
printf('Le titre du premier livre est "%s"<br/>',$oLibrary[0]);
// affchage du nombre de livres
printf('Il y a %d livres dans la bibliothque<br/>',sizeof($oLibrary));
// suppression du deuxieme livre
unset($oLibrary[1]);
// affchage du nombre de livres
printf('Il y a fnalement %d livres dans la bibliothque<br/>',sizeof($oLibrary));
Standard PHP Library
13 www.phpsolmag.org
Listing 6. Implmentation de difrentes classes d'exception
<?php
class MultipleExceptions {
public function doSomethingWrong($mParam) {
if(!is_int($mParam)) {
throw new InvalidArgumentException(__METHOD__.'() : Param must be a valid int ');
} else if($mParam<0) {
throw new OutOfRangeException(__METHOD__.'() : Param must be a positive int');
} else {
throw new Exception(__METHOD__.'() : You really thought something was
gonna happen ?');
}
}
}
// essai de passage dun parametre qui nest pas de type integer
try {
$oClass = new MultipleExceptions();
$oClass->doSomethingWrong('bad argument !');
} catch(InvalidArgumentException $e) {
echo 'Woops, an argument error occured : ',$e->getMessage();
}
?>
<hr/>
<?php
// essai de passage d'un paramtre ngatif
try {
$oClass = new MultipleExceptions();
$oClass->doSomethingWrong(-1);
} catch(InvalidArgumentException $e) {
echo 'Woops, an argument error occured : ',$e->getMessage();
} catch(OutOfRangeException $e) {
echo 'Woops, a range error occured : ',$e->getMessage();
}
?>
<hr/>
<?php
// essai de passage d'un paramtre valide
try {
$oClass = new MultipleExceptions();
$oClass->doSomethingWrong(42);
} catch(InvalidArgumentException $e) {
echo 'Woops, an argument error occured : ',$e->getMessage();
} catch(OutOfRangeException $e) {
echo 'Woops, a range error occured : ',$e->getMessage();
} catch(Exception $e) {
echo 'Woops, a basic error occured : ',$e->getMessage();
}
Listing 7. Utilisation d'un itrateur de Tableau'
<?php
$aBooks = array('Programmation Flex 3','Practical Symfony : Jobeet');
$oIterator = new ArrayIterator($aBooks);
foreach($oIterator as $iKey=>$sValue) {
echo $iKey,'=>',$sValue,'<br/>';
}
Listing 8. Rcupration d'un itrateur externe
<?php
class Library extends ArrayObject {
}
$oLibrary = new Library();
$oLibrary->append('Programmation Flex 3');
$oLibrary->append(Practical Symfony : Jobeet);
$oLibraryIterator = $oLibrary->getIterator();
foreach($oLibraryIterator as $iKey=>$sValue) {
printf('%d => %s <br/>',$iKey,$sValue);
}
Listing 9. Implmentation d'un itrateur
d'acceptance
<?php
class Library extends ArrayObject
{ }
abstract class Book {
protected $_sName;
public function __
construct($sName) {
$this->_sName = $sName;
}
public function __toString() {
return $this->_sName;
}
}
class TechnicalBook extends Book{}
class Novel extends Book {}
class TechnicalBooksIterator extends
FilterIterator{
public function __
construct(ArrayIterator $oIt) {
parent::__construct($oIt);
}
public function accept() {
$oIt = $this->
getInnerIterator();
if($oIt->current() instanceof
TechnicalBook) {
return true;
} else {
return false;
}
}
}
$oLibrary = new Library();
$oLibrary->append(new Novel('Harry
Potter and the Half Blood Prince'));
$oLibrary->append(new TechnicalBook
('Programmation Flex 3'));
$oLibrary->append(new
Novel('Twilight - chapter 1 :
Fascination'));
$oLibrary->append(new
TechnicalBook('Practical Symfony :
Jobeet'));
$oLibraryIterator = $oLibrary->
getIterator();
$oIt = new TechnicalBooksIterator(
$oLibraryIterator);
foreach($oIt as $iKey=>$sBookTitle)
{
printf('Technical Book #%d :
"%s"<br/>',$iKey,$sBookTitle);
}
Un bon exemple tant la classe abstraite
FilterIterator, qui implique de dfinir une
mthode accept(). Cette mthode retourne
un boolen : true si l'objet courant est valid
et doit tre retourn, false sinon. En repre-
nant toujours l'exemple de la bibliothque, le
Listing 9 montre comment retourner simple-
ment les objets de type techniques, et d'igno-
rer les autres. Il est important de remarquer la
prsence de la mthode getInnerIterator(),
venant de linterface OuterIterator. Elle per-
met davoir accs litrateur pass en param-
tre du constructeur.
Outils
14 4/2009
Il existe la classe RegexIterator qui hrite de
FilterIterator, l'utilisation la plus basique de
cette classe implique de dfinir une expression
rgulire qui sera utilise automatiquement
chaque itration : l'objet courant sera retour-
n uniquement s'il correspond cette expres-
sion, comme le montre le Listing 10, qui per-
met de n'afficher que les classes de la SPL au
nom terminant par Exception.
Limiter
le nombre d'itrations
Il peut tre intressant de vouloir limiter le
nombre d'itrations. Un premier rflexe est de
crer une variable comptant le nombre d'it-
rations, et un test interrompant l'itration au
bout d'un certain nombre. C'est l qu'inter-
vient l'itrator LimitIterator, qui prend 3 para-
mtres : l'itrateur de la source de donnes
parcourir, la position de dpart de l'itration
(grce l'itrateur SeekableIterator), et le nom-
bre d'itrations effectuer partir de ce point
(Listing 11). Un cas concret d'utilisation de cet
itrateur tant par exemple l'implmentation
d'un systme de pagination.
Parcourir une source de donnes
de manire rcursive
Il y a une spcificit dont cet article na pas
encore parl : la rcursivit, ou comment
parcourir simplement une liste de donnes
complexe. Cette notion est bien entendue
prvue dans la SPL. Grce litrateur,
parcourir un tableau de tableaux (Listing
12), ou une arborescence de dossiers (voir
Figure 2 Arborescence et Listing 13). Ce
dernier exemple permet galement dintro-
duire une nouvelle notion de la SPL, la classe
SPLFileInfo permettant de tout connatre
dun fichier, sans avoir systmatiquement
utiliser des fonctions telles que filemtime,
fileowner, filegroup, etc.
Conclusion
Cest ici que sarrte cet article, sans avoir pu
pour autant traiter toutes les classes de la SPL.
Pour les frileux du modle objet de PHP, il est
trs important de savoir que la majorit de la
SPL a t dveloppe directement en C. Elle
est donc compile, et ne posera pas de pro-
blme de performance. Mme si PHP ne sera
probablement jamais un langage entirement
orient objet au mme titre que Java, il y a ici
un groupe doutils trs utiles et trs intuitifs
qui permettront de faire face des problmes
complexes.
ROMAIN POUCLET
Romain Pouclet est tudiant en licence profession-
nelle Annecy. Dveloppeur PHP passionn depuis
7 ans, il rdige des articles ce sujet sur son blog
et sur le site daide aux dbutants Apprendre-PHP
(http://www.apprendrephp.com).
Listing 10. Implmentation d'un itrateur d'acceptance
<?php
class OnlyExceptionIterator extends RegexIterator {
public function __construct(Iterator $it) {
parent::__construct($it,$sPattern);
}
}
$aSplClassList = spl_classes();
$oIt = new ArrayIterator($aSplClassList);
$oExceptionIterator = new OnlyExceptionIterator($oIt);
foreach($oExceptionIterator as $iKey=>$sValue) {
echo $sValue,'<br/>';
}
Listing 11. Limitation du nombre d'itration
$oLibrary = new Library();
$oLibrary->append(new Novel('Harry Potter and the Half Blood Prince'));
$oLibrary->append(new TechnicalBook('Programmation Flex 3'));
$oLibrary->append(new TechnicalBook('AIR 1.5 : CookBook'));
$oLibrary->append(new TechnicalBook('Pratique d\'ActionScript 3'));
$oLibrary->append(new Novel('Twilight - chapter 1 : Fascination'));
$oLibrary->append(new TechnicalBook('Practical Symfony : Jobeet'));
$oIt = $oLibrary->getIterator();
$oLimitIt = new LimitIterator($oIt,0,3);
foreach($oLimitIt as $iKey=>$sValue) {
printf('Book #%d : %s<br/>',$iKey,$sValue);
}
Listing 12. Implmentation d'un itrateur rcursif
<?php
$aDataSource = array(
array(1,2,array('Lorem','Ipsum',array('Dolor','Sin'),'Amet'),4),
array('Flex','AIR','Flash','PHP',
array('XML','xHTML','CSS')
) );
$oArrayIterator = new RecursiveArrayIterator($aDataSource);
$oRecursiveIterator = new RecursiveIteratorIterator
($oArrayIterator,RecursiveIteratorIterator::SELF_FIRST);
foreach($oRecursiveIterator as $iKey=>$mValue) {
if(!is_array($mValue)) {
echo str_repeat('-',$oRecursiveIterator->getDepth());
echo $mValue;
echo '<br/>';
}
}
Listing 13. Afchage d'une arborescence de
rpertoire'
<?php
$oRecursiveDirectoryIterator =
new RecursiveDirectoryIterator
(dirname(__FILE__).'/../',
RecursiveDirectoryIterator::
KEY_
$oIt = new RecursiveIteratorIterat
or($oRecursiveDirectoryIterator,
RecursiveIteratorIterator::
SELF_FIRST);
foreach($oIt as $iKey=>$oValue) {
echo str_repeat('-',$oIt
->getDepth());
echo $oValue;
echo '<br/>';
}

Vous aimerez peut-être aussi