Lire un extrait ( PDF 149 Ko)

Transcription

Lire un extrait ( PDF 149 Ko)
6
PHP orienté objet
Ce chapitre présente les concepts de la programmation orientée objet (POO) et
montre comment ils peuvent être implémentés en PHP.
L’implémentation de la POO en PHP possède toutes les fonctionnalités que vous
seriez en droit d’attendre d’un langage orienté objet complet. Nous signalerons chacune de ces fonctionnalités à mesure que nous avancerons dans ce chapitre.
Concepts de la programmation orientée objet
Les langages de programmation modernes supportent généralement, voire
requièrent, une approche orientée objet du développement logiciel. Le développement orienté objet (DOO) consiste à exploiter les classifications, les relations et les
propriétés des objets d’un système pour faciliter le développement des programmes
et la réutilisation du code.
Classes et objets
Dans le contexte de la POO, un objet peut être quasiment tout élément ou concept,
c’est-à-dire un objet physique comme un bureau ou un client, ou un objet conceptuel
qui n’existe que dans le programme lui-même, comme un champ de saisie ou un
fichier. Le plus souvent, le développeur s’intéresse aux objets du monde réel et aux
objets conceptuels qui doivent être représentés dans le programme.
Un logiciel orienté objet est conçu et construit sous la forme d’un ensemble d’objets indépendants dotés à la fois d’attributs et d’opérations qui interagissent pour
répondre à nos besoins. Les attributs sont des propriétés ou des variables qui se rapportent à l’objet. Les opérations sont des méthodes, des actions ou des fonctions que
l’objet peut accomplir, soit pour se modifier lui-même, soit pour produire un effet
externe (le terme attribut est utilisé de manière interchangeable avec les termes
variable membre et propriété, tandis que le terme opération est utilisé de manière
interchangeable avec le terme méthode).
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson
174
PHP & MySQL
Le principal avantage d’un logiciel orienté objet réside dans sa capacité à prendre en
charge et à encourager l’encapsulation, également appelée masquage de données.
Pour l’essentiel, les données contenues dans un objet ne sont accessibles que par le
biais des opérations de celui-ci, qui forment l’interface de l’objet.
La fonctionnalité d’un objet est liée aux données qu’il utilise. Les détails de
l’implémentation d’un objet peuvent être facilement modifiés pour améliorer les
performances, ajouter de nouvelles caractéristiques ou corriger des bogues, sans
qu’il soit nécessaire de changer l’interface. Le fait de modifier cette interface peut,
en effet, avoir des répercussions en cascade dans le projet, alors que l’encapsulation
vous permet d’effectuer des modifications et de réaliser des corrections de bogues
sans que vos actions ne se répercutent dans les autres parties du projet.
Dans les autres secteurs du développement logiciel, la POO s’est imposée comme
la norme et le développement orienté fonctions est désormais considéré comme
démodé. Cependant, pour diverses raisons, la plupart des scripts web restent malheureusement conçus et écrits avec une approche ad hoc, utilisant une méthodologie
orientée fonctions.
Ce "retard" a plusieurs causes. Une majorité de projets web sont de petite taille et
relativement simples. Nul besoin d’élaborer un plan détaillé pour construire une étagère avec une scie. De la même manière, la plupart des projets logiciels pour le Web
peuvent être réalisés de cette façon parce qu’ils sont de petite taille. En revanche, si
vous prenez votre scie pour construire une maison sans avoir formellement planifié
vos travaux, vous ne pourrez pas obtenir des résultats de qualité, si tant est que
vous obteniez des résultats. Il en va exactement de même pour les projets logiciels
importants.
Nombre de projets web évoluent d’un ensemble de pages reliées entre elles par des
hyperliens vers des applications complexes. Ces applications complexes, qu’elles
soient présentées via des boîtes de dialogue et des fenêtres ou via des pages HTML
dynamiques, requièrent une méthodologie de développement mûrement réfléchie.
L’orientation objet peut aider à gérer la complexité des projets logiciels, à augmenter
la réutilisabilité du code et, par conséquent, à réduire les coûts de maintenance.
En POO, un objet est une collection unique et identifiable de données et d’opérations agissant sur ces données. Par exemple, considérons le cas de deux objets
représentant des boutons. Même si ces boutons portent tous deux l’intitulé "OK",
ont une largeur de 60 pixels, une hauteur de 20 pixels et divers autres attributs
identiques, nous devons pouvoir les distinguer et les manipuler séparément l’un de
l’autre. D’un point de vue logiciel, cette distinction s’effectue via des variables séparées, qui servent de descripteurs (d’identificateurs uniques) pour les objets.
Les objets peuvent être regroupés en "classes". Une classe est un ensemble d’objets
qui peuvent être différents les uns des autres, mais qui ont certains points communs.
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson
Chapitre 6
PHP orienté objet
175
Une classe contient des objets qui présentent tous des opérations se comportant de
la même manière et des attributs identiques représentant les mêmes choses, bien que
les valeurs de ces attributs puissent varier d’un objet à l’autre au sein de la classe.
Vous pouvez ainsi considérer le nom bicyclette comme celui d’une classe d’objets
qui décrit les nombreuses bicyclettes distinctes qui présentent toutes des caractéristiques ou attributs communs, comme deux roues, une couleur et une taille, et des
opérations, comme le déplacement.
Ma propre bicyclette pourrait ainsi être considérée comme un objet appartenant à
la classe bicyclette. Elle a des caractéristiques identiques à toutes les bicyclettes, y
compris l’opération de déplacement, qui est tout à fait comparable à l’opération de
déplacement des autres bicyclettes, même si elle n’est utilisée que très rarement.
Les attributs de ma bicyclette ont toutefois des valeurs qui leur sont propres ; par
exemple, sa couleur est verte, ce qui n’est pas le cas de toutes les bicyclettes.
Polymorphisme
Un langage de programmation orienté objet doit prendre en charge le polymorphisme,
qui signifie que différentes classes peuvent avoir des comportements différents pour
la même opération. Par exemple, supposez que nous ayons défini une classe voiture
et une classe bicyclette. Ces deux classes peuvent avoir des opérations de déplacement différentes. Dans l’univers des objets réels, cette différentiation pose rarement
problème : il est peu probable en effet qu’une bicyclette soit confondue avec une
voiture et démarrée avec une opération de déplacement de voiture au lieu d’une
opération de déplacement de bicyclette. En revanche, un langage de programmation
ne possède pas le sens commun du monde réel : il doit par conséquent disposer du
polymorphisme pour qu’il soit possible de distinguer l’opération de déplacement
appropriée à un objet particulier.
Le polymorphisme est plus une caractéristique des comportements que des objets
eux-mêmes. En PHP, seules les fonctions membres d’une classe peuvent être
polymorphiques. Les verbes des langages naturels sont un peu l’équivalent dans le
monde réel des fonctions membres d’une classe. Considérez la manière dont peut
être utilisée une bicyclette dans la vie réelle. Vous pouvez la nettoyer, la déplacer, la
démonter, la réparer ou la peindre, entre autres choses.
Les verbes de cette dernière phrase décrivent des actions génériques parce que le
type d’objet auquel ils peuvent être appliqués n’est pas précisé (ce type d’abstraction
concernant les objets et les actions est, du reste, l’une des caractéristiques distinctives de l’intelligence humaine).
Le déplacement d’une bicyclette, par exemple, exige des actions totalement différentes de celles requises pour déplacer une voiture, même si les concepts sont
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson
176
PHP & MySQL
similaires. Le verbe "déplacer" peut donc être associé à un ensemble particulier
d’actions, mais uniquement après que l’objet auquel il s’applique a été défini.
Héritage
L’héritage permet de créer une relation hiérarchique entre les classes au moyen de
sous-classes. Une sous-classe hérite des attributs et des opérations de sa superclasse. Les voitures et les bicyclettes ont, par exemple, des points communs et nous
pourrions donc définir une classe véhicule contenant des attributs comme la couleur
et des opérations comme le déplacement qui sont communs à tous les véhicules.
Il suffirait ensuite de laisser les classes voiture et bicyclette hériter de la classe
véhicule.
Les termes sous-classe, classe dérivée et classe fille sont utilisés de manière
interchangeable. Il en va de même des termes superclasse, classe de base et classe
parente.
Grâce au concept d’héritage, nous pouvons élaborer et enrichir l’ensemble des
classes existantes. À partir d’une simple classe de base, des classes plus complexes et plus spécialisées peuvent être créées au fur et à mesure des besoins. Cette
approche rend le code plus réutilisable et constitue l’un des atouts indéniables de la
programmation orientée objet.
Exploiter la notion d’héritage peut permettre d’économiser du temps et des efforts
de développement lorsque des opérations peuvent être implémentées une seule fois
dans une superclasse, au lieu de l’être à chaque fois dans des sous-classes séparées.
Cette approche favorise également une modélisation plus précise des relations du
monde réel. Lorsque la phrase décrivant la relation entre deux classes peut contenir
les mots "est un" ou "est une", alors, le concept d’héritage peut être exploité. La
phrase "une voiture est un véhicule", par exemple, est tout à fait sensée, tandis que
la phrase "un véhicule est une voiture" n’est pas vraie dans le monde réel. Par conséquent, les voitures peuvent hériter de la classe véhicule.
Création de classes, d’attributs et d’opérations en PHP
Jusqu’ici, la description que nous avons donnée des classes est plutôt abstraite. Plus
concrètement, la création d’une classe en PHP s’effectue au moyen du mot-clé class.
Structure d’une classe
La définition minimale d’une classe se formule de la manière suivante :
class nomclass {
}
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson
Chapitre 6
PHP orienté objet
177
Pour qu’une classe ait une quelconque utilité, elle doit être dotée d’attributs et d’opérations. Pour créer des attributs, il faut déclarer des variables au sein d’une définition
de classe en les précédant de mots-clés indiquant leur visibilité : public, private ou
protected (ces mots-clés seront présentés plus loin dans ce chapitre). Le code qui suit
crée une classe nomclasse dotée des deux attributs publics, $attribut1 et $attribut2 :
class classname {
public $attribut1;
public $attribut2;
}
La création d’opérations dans une classe s’effectue en déclarant des fonctions dans
la définition de la classe. Le code suivant crée une classe nomclasse dotée de deux
opérations qui n’effectuent rien de particulier. L’opération operation1() ne prend
aucun paramètre, tandis que operation2() attend deux paramètres.
class nomclasse {
function operation1() {}
function operation2($param1, $param2) {}
}
Constructeurs
La plupart des classes disposent d’un type spécial d’opération appelé constructeur.
Un constructeur est appelé lors de la création d’un objet et effectue généralement
des tâches d’initialisation comme l’assignation de valeurs initiales aux attributs ou
la création d’autres objets nécessaires à l’objet concerné.
Un constructeur se déclare de la même manière que les autres opérations, sauf qu’il
porte le nom spécial __construct().
Bien qu’un constructeur puisse être appelé manuellement, son rôle principal est
d’être appelé automatiquement à la création d’un objet. Le code qui suit déclare une
classe dotée d’un constructeur :
class nomclasse {
function __construct($param) {
echo "Constructeur appelé avec le paramètre " . $param . "<br />";
}
}
Destructeurs
L’opposé d’un constructeur est un destructeur. Les destructeurs permettent d’exécuter
un traitement particulier juste avant qu’un objet ne soit détruit, ce qui aura lieu automatiquement lorsque toutes les références à cet objet ont été indéfinies ou hors de portée.
Le destructeur d’une classe doit s’appeler _destruct() et ne peut prendre aucun
paramètre.
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson
178
PHP & MySQL
Instanciation des classes
Une fois que nous avons déclaré une classe, nous devons créer un objet (c’est-à-dire
un individu particulier appartenant à la classe) avec lequel nous pourrons ensuite
travailler. Cette étape s’appelle également création d’une instance ou instanciation
d’une classe. On crée un objet à l’aide du mot-clé new. Il faut également préciser la
classe dont l’objet sera l’instance et fournir les paramètres éventuellement requis par
le constructeur de la classe.
Le code qui suit déclare une classe nomclasse avec un constructeur, puis crée trois
objets de type nomclasse :
class nomclasse {
function __construct($param) {
echo "Constructeur appelé avec le paramètre " . $param ."<br />";
}
}
$a = new nomclasse("Premier");
$b = new nomclasse("Second");
$c = new nomclasse();
Le constructeur étant invoqué à chaque création d’objet, ce code produit le résultat
suivant :
Constructeur appelé avec le paramètre Premier
Constructeur appelé avec le paramètre Second
Constructeur appelé avec le paramètre
Utilisation des attributs de classe
À l’intérieur d’une classe, vous avez accès à un pointeur spécial appelé $this. Si
un attribut de la classe courante porte le nom $attribut, vous pouvez le désigner
par $this->attribut lorsque vous l’initialisez ou que vous y accédez à partir d’une
opération située dans la classe.
Le code qui suit illustre la définition et l’accès à un attribut à l’intérieur d’une classe :
class nomclasse {
public $attribut;
function operation($param) {
$this->attribut = $param
echo $this->attribut;
}
}
La possibilité d’accéder à un attribut depuis l’extérieur de la classe est déterminée
par des modificateurs d’accès, comme vous le verrez dans la suite de ce chapitre.
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson
Chapitre 6
PHP orienté objet
179
Cet exemple ne restreignant pas l’accès à l’attribut, vous pouvez y accéder depuis
l’extérieur de la classe :
class nomclasse {
public $attribut;
}
$a = new nomclasse();
$a->attribut = "valeur";
echo $a->attribut;
Il est toutefois déconseillé d’accéder directement aux attributs depuis l’extérieur
d’une classe. L’un des intérêts de l’approche orientée objet réside justement dans le
fait que l’encapsulation y est encouragée.
Vous pouvez garantir cette encapsulation à l’aide des fonctions __get et __set. Au
lieu d’accéder directement aux attributs d’une classe, vous pouvez écrire des fonctions accesseurs de sorte à effectuer tous vos accès par le biais d’une seule section
de code. Une fonction accesseur peut se formuler de la manière suivante :
class nomclasse {
public $attribut;
function __get($nom) {
return $this->$nom;
}
function __set ($nom, $valeur) {
$this->$nom = $valeur;
}
}
Ce code se contente de fournir des fonctions minimales permettant d’accéder à l’attribut $attribut. La fonction __get() renvoie simplement la valeur de $attribut,
tandis que la fonction set() affecte une nouvelle valeur à $attribut.
Notez que __get() ne prend qu’un seul paramètre – le nom d’un attribut – et renvoie la valeur de cet attribut. De manière similaire, la fonction __set() prend deux
paramètres : le nom d’un attribut et la valeur que vous souhaitez lui donner.
Ces fonctions ne s’appellent pas directement. Le double blanc souligné devant le
nom indique que ces fonctions possèdent une signification spéciale en PHP, tout
comme les fonctions __construct() et __destruct().
Si vous instanciez la classe :
$a = new nomclasse();
vous pouvez utiliser les fonctions __get() et __set() pour tester et modifier la
valeur de n’importe quel attribut.
Si vous tapez :
$a->$attribut = 5;
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson
180
PHP & MySQL
cette instruction appellera implicitement la fonction __set() avec la valeur $nom
positionnée à "attribut" et la valeur $valeur initialisée à 5. Si vous souhaitez
effectuer des contrôles sur les valeurs affectées à l’attribut, vous devez écrire la
fonction __set() en conséquence.
La fonction __get() fonctionne de manière similaire. Dans votre code, si vous
écrivez :
$a->attribut
l’expression appellera implicitement la fonction __get() avec le paramètre $nom
positionné à "attribut". C’est à vous d’écrire la fonction __get() pour retourner
la valeur de l’attribut.
Au premier coup d’œil, ce code peut sembler n’avoir que peu ou pas d’intérêt. Sous
sa forme actuelle, c’est probablement le cas, mais il existe une raison simple de proposer des fonctions d’accès : vous n’aurez alors qu’une unique section de code qui
accède à cet attribut particulier.
Avec un seul point d’accès, vous pouvez implémenter des contrôles de validité afin
de vous assurer que les données stockées sont cohérentes. Si vous jugez par la suite
que la valeur de $attribut doit être comprise entre 0 et 100, vous pouvez ajouter
quelques lignes de code et opérer la vérification avant d’autoriser les modifications.
Vous pourriez ainsi modifier la fonction __set() de la manière suivante :
function __set ($nom, $valeur) {
if( $nom == "attribut" && $valeur >= 0 && $valeur <= 100 ) {
$this->attribut = $valeur;
}
}
Avec un unique point d’accès, vous êtes libre de modifier l’implémentation sousjacente. Si, pour une raison ou pour une autre, vous deviez modifier la manière dont
$attribut est stocké, les fonctions accesseurs vous permettraient de le faire en ne
modifiant le code qu’à un seul emplacement.
Il se peut que vous décidiez, au lieu de stocker $attribut sous forme de variable,
de le récupérer à partir d’une base de données selon les besoins, de calculer une
nouvelle valeur à chaque fois qu’elle est requise, de déduire une valeur à partir de
valeurs d’autres attributs ou d’encoder les données sous un type de données plus
compact. Quelle que soit la modification que vous souhaitiez opérer, il suffit de
modifier les fonctions accesseurs. Les autres sections du code ne seront pas affectées, pour autant que vous faites en sorte que les fonctions accesseurs continuent
d’accepter ou de renvoyer les données que les autres parties du programme s’attendent à pouvoir utiliser.
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson
Chapitre 6
PHP orienté objet
181
Contrôler l’accès avec private et public
PHP utilise des modificateurs d’accès qui contrôlent la visibilité des attributs et
des méthodes et qui sont placés devant les déclarations d’attribut et de méthode.
PHP dispose des trois modificateurs d’accès suivants :
■
L’option par défaut est public. Cela signifie que, si vous n’avez pas spécifié de
modificateur d’accès pour un attribut ou une méthode, ceux-ci seront publics. L’accès aux éléments publics peut se faire depuis l’intérieur ou l’extérieur de la classe.
■
Le modificateur d’accès private signifie qu’il n’est possible d’accéder à l’élément marqué que depuis l’intérieur de la classe. Vous pouvez l’utiliser sur tous
les attributs si vous n’utilisez pas __ get() et __set(). Vous pouvez également
choisir de rendre certaines méthodes privées, par exemple s’il s’agit de fonctions utilitaires à utiliser à l’intérieur de la classe uniquement. Les éléments
privés ne sont pas hérités (nous y reviendrons dans la suite de ce chapitre).
■
Le modificateur d’accès protected signifie que l’on ne peut accéder à l’élément
marqué que depuis l’intérieur de la classe. Il existe également dans toutes les
sous-classes. Nous reviendrons aussi sur cette question lorsque nous traiterons
de l’héritage dans la suite de ce chapitre. Pour l’instant, considérez que protected est à mi-distance entre private et public.
Le code suivant montre l’utilisation du modificateur public :
class nomclasse {
public $attribut;
public function __get($nom) {
return $this->$nom;
}
public function __set ($nom, $valeur) {
$this->$nom = $valeur;
}
}
Ici, chaque membre de classe est précédé d’un modificateur d’accès qui indique s’il
est privé ou public. Le mot-clé public peut être omis car il s’agit du réglage par
défaut, mais le code est plus simple à comprendre lorsque vous l’incluez, notamment si vous utilisez d’autres modificateurs.
Appel des opérations d’une classe
Nous pouvons appeler une opération de classe en procédant de la même manière
que pour un attribut de classe. Si nous déclarons la classe suivante :
class nomclasse {
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson
182
PHP & MySQL
public function operation1() { }
public function operation2($param1, $param2) { }
}
et que nous créons un objet de type nomclasse, comme ici :
$a = new nomclasse();
nous pouvons appeler des opérations en procédant de la même façon que pour l’appel à d’autres fonctions : en spécifiant leur nom, suivi des paramètres requis, placés
entre parenthèses. Ces opérations appartenant à un objet, à la différence des fonctions normales, il est nécessaire de préciser l’objet concerné. Le nom de cet objet est
indiqué de la même manière que pour ses attributs :
$a->operation1();
$a->operation2(12, "test");
Si les opérations renvoient des valeurs, elles peuvent être récupérées de la manière
suivante :
$x = $a->operation1();
$y = $a->operation2(12, "test");
Implémentation de l’héritage en PHP
Une classe peut être déclarée comme étant une sous-classe d’une autre classe en
utilisant le mot-clé extends. Le code qui suit crée une classe B qui hérite d’une
classe A précédemment définie :
class B extends A {
public $attribut2;
public function operation2() { }
}
En admettant que la classe A ait été déclarée comme suit :
class A {
public $attribut1;
public function operation1() { }
}
tous les accès suivants aux attributs et opérations d’un objet de la classe B seraient
corrects :
$b = new B();
$b->operation1();
$b->attribut1 = 10;
$b->operation2();
$b->attribut2 = 10;
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson
Chapitre 6
PHP orienté objet
183
Notez que, la classe B héritant de la classe A, nous pouvons faire référence à
operation1() et $attribut1 bien que ceux-ci soient déclarés dans la classe A. En
tant que sous-classe de A, la classe B en possède les caractéristiques et les données.
En outre, B a déclaré un attribut et une opération qui lui sont propres.
L’héritage ne fonctionne que dans un sens. La sous-classe (la fille) hérite de sa
superclasse (le parent) mais le parent n’hérite pas de son enfant. Il s’ensuit que les
deux dernières lignes du code suivant sont incorrectes :
$a = new A();
$a->operation1();
$a->attribut1 = 10;
$a->operation2();
$a->attribut2 = 10;
En effet, la classe A ne possède pas d’opération operation2() ni d’attribut attribut2.
Contrôler la visibilité via l’héritage avec private et protected
Les modificateurs d’accès private et protected permettent de contrôler l’héritage.
Un attribut ou une méthode déclaré private ne sera pas hérité, alors qu’un attribut ou une méthode déclaré protected ne sera pas visible en dehors de la classe
(comme un élément privé) mais sera hérité.
Considérez l’exemple suivant :
<?php
class A {
private function operation1() {
echo "Appel de operation1";
}
protected function operation2() {
echo "Appel de operation2";
}
public function operation3() {
echo "Appel de operation3";
}
}
class B extends A {
function __construct() {
$this->operation1();
$this->operation2();
$this->operation3();
}
}
$b = new B;
?>
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson
184
PHP & MySQL
Ce code crée une opération de chaque type dans la classe A : public, protected et
private. B hérite de A. Dans le constructeur de B, vous essayez donc d’appeler des
opérations du parent.
La ligne suivante :
$this->operation1();
produit une erreur fatale :
Fatal error: Call to private method A::operation1() from context ’B’
Cet exemple montre que les opérations privées ne peuvent pas être appelées depuis
une classe fille.
Si vous mettez cette ligne en commentaire, vous remarquerez que les deux autres
appels de fonction marchent bien. La fonction protected est héritée mais elle ne
peut être utilisée que depuis l’intérieur de la classe fille, comme nous le faisons ici.
Si vous essayez d’ajouter la ligne suivante :
$b->operation2();
en bas du fichier, vous obtenez l’erreur suivante :
Fatal error: Call to protected method A::operation2() from context ‘’
En revanche, vous pouvez appeler operation3() depuis l’extérieur de la classe :
$b->operation3();
Cet appel est possible car la fonction est déclarée public.
Redéfinition (overriding)
Nous venons de voir le cas d’une sous-classe dans laquelle sont déclarés de nouveaux attributs et opérations. Il est également permis et parfois utile de redéclarer
les mêmes attributs et opérations. Une telle redéclaration, qualifiée de "redéfinition" (overriding), permet en effet de donner à un attribut d’une sous-classe une
valeur par défaut différente de celle du même attribut dans la superclasse, ou bien
encore de donner à une opération d’une sous-classe une fonctionnalité différente de
la même opération dans la superclasse.
Par exemple, si nous disposons de la classe A suivante :
class A {
public $attribut = "Valeur par défaut";
public function operation() {
echo "Quelque chose<br />";
echo ’La valeur de $attribut est ’ . $this->attribut . "<br />";
}
}
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson
Chapitre 6
PHP orienté objet
185
et qu’il nous apparaisse nécessaire de modifier la valeur par défaut de $attribut et
de fournir une nouvelle fonctionnalité à operation(), nous pourrions créer la classe
B en redéfinissant $attribut et operation() de la façon suivante :
class B extends {
public $attribut = "Valeur différente";
public function operation() {
echo "Autre chose<br />";
echo ’La valeur de $attribut est ’ . $this->attribut . "<br />";
}
}
Déclarer B n’affecte en rien la définition originale de A. Considérez les deux lignes
de code suivantes :
$a = new A();
$a -> operation();
Elles créent un objet de type A et appellent sa fonction operation(). Le résultat
obtenu à l’exécution de ces lignes est le suivant :
Quelque chose
La valeur de $attribut est Valeur par défaut
Ce résultat montre bien que la création de B n’a pas affecté A. Si nous créons un objet
de type B, nous obtiendrons un résultat différent.
Les lignes suivantes :
$b = new B();
$b -> operation();
produiront :
Autre chose
La valeur de $attribut est Valeur différente
De la même manière que fournir de nouveaux attributs ou opérations dans une
sous-classe n’affecte pas la superclasse, la redéfinition d’attributs ou d’opérations
dans une sous-classe n’affecte pas la superclasse.
Une sous-classe hérite de tous les attributs et opérations non privés de sa superclasse,
à moins que vous effectuiez des remplacements. Si vous fournissez une définition
de remplacement, celle-ci devient prépondérante et remplace la définition d’origine.
Le mot-clé parent vous permet d’appeler la version originale de l’opération dans la
classe parente. Par exemple, pour appeler l’opération A::operation depuis l’intérieur de la classe B, vous utiliseriez :
parent::operation();
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson
186
PHP & MySQL
La sortie produite est cependant différente. Bien que vous appeliez l’opération
depuis la classe parente, PHP utilise les valeurs d’attribut de la classe courante.
Vous obtiendrez donc la sortie suivante :
Quelque chose
La valeur de $attribut est Valeur différente
L’héritage peut être implémenté sur plusieurs niveaux. Par exemple, nous pourrions
déclarer une classe appelée C qui hérite de B, c’est-à-dire qui hérite à la fois des
propriétés de B et de celles du parent de B, c’est-à-dire de A. Tout comme pour la
classe B, nous serions libres de redéfinir et de remplacer dans la classe C des attributs et opérations des parents.
Empêcher l’héritage et les redéfinitions avec final
PHP dispose du mot-clé final. Lorsque vous utilisez ce mot-clé devant une déclaration de fonction, la fonction ne pourra plus être redéfinie dans aucune sous-classe.
Vous pouvez par exemple l’ajouter à la classe A de l’exemple précédent :
class A {
public $attribut = "Valeur par défaut";
final function operation() {
echo "Quelque chose<br />";
echo ’La valeur de $attribut est ’ . $this->attribut . "<br />";
}
}
Cette approche vous empêche de redéfinir operation() dans la classe B. Si vous
essayez de le faire, vous obtenez l’erreur suivante :
Fatal error: Cannot override final method A::operation()
Vous pouvez également utiliser le mot-clé final pour empêcher la création de sousclasses à partir d’une classe. Pour empêcher la création de sous-classes à partir de
la classe A, ajoutez le mot-clé de la manière suivante :
final class A {...}
Si vous essayez ensuite d’hériter de A, vous obtiendrez une erreur comme celle-ci :
Fatal error: Class B may not inherit from final class (A)
Héritage multiple
Quelques langages OO (dont C++ et Smalltalk) prennent en charge l’héritage multiple mais, comme la plupart des autres, ce n’est pas le cas de PHP. Il s’ensuit que
chaque classe ne peut hériter que d’un seul parent. En revanche, aucune restriction
n’impose une limite sur le nombre d’enfants que peut engendrer un même parent.
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson
Chapitre 6
PHP orienté objet
187
Les implications de cet état de fait ne sont pas nécessairement évidentes à première
vue. La Figure 6.1 montre trois modes d’héritage différents.
Figure 6.1
A
PHP ne prend pas
en charge l’héritage
multiple.
A
A
B
B
B
C
Héritage simple
C
Héritage multiple
C
Héritage simple
Dans le mode le plus à gauche, la classe C hérite de la classe B, qui hérite à son tour
de la classe A. Chaque classe possède au plus un parent : ce mode d’héritage unique
est parfaitement valide en PHP.
Dans le mode du centre, les classes B et C héritent toutes deux de la classe A. Là
encore, chaque classe possède au plus un parent : ce mode d’héritage unique est
également valide en PHP.
Dans le mode le plus à droite, la classe C hérite à la fois des classes A et B. Dans ce
cas, la classe C possède deux parents : il s’agit là d’une situation d’héritage multiple,
non supportée par PHP.
Implémentation d’interfaces
Si vous devez implémenter une fonctionnalité analogue à l’héritage multiple, vous
pouvez le faire grâce aux interfaces, qui sont un moyen de contourner l’absence de
l’héritage multiple. Leur implémentation est semblable à celle des autres langages
orientés objet, dont Java.
Le principe d’une interface consiste à préciser un ensemble de fonctions qui devront
être implémentées dans les classes qui implémentent l’interface. Par exemple, vous
pourriez souhaiter qu’un ensemble de classes soient capables de s’afficher. Au lieu
de créer une classe parente avec une fonction Afficher() dont hériteraient toutes
les sous-classes et qu’elles redéfiniraient, vous pouvez utiliser une interface de la
manière suivante :
interface Affichable {
function Afficher(){
}
class pageWeb implements Affichable {
function Afficher() {
// ...
}
}
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson
188
PHP & MySQL
Cet exemple présente une alternative à l’héritage multiple car la classe pageWeb peut
hériter d’une seule classe et implémenter une ou plusieurs interfaces.
Si vous n’implémentez pas les méthodes spécifiées dans l’interface (dans le cas présent, Afficher()), vous obtiendrez une erreur fatale.
Conception de classes
Maintenant que nous avons passé en revue les principaux concepts de l’approche
orientée objet concernant les objets et les classes, ainsi que la syntaxe de leur
implémentation en PHP, nous pouvons nous intéresser à la conception de classes
qui nous seront utiles.
Souvent, les classes implémentées dans le code représentent des classes ou des catégories
d’objets réels. Dans le cadre d’un développement web, les classes peuvent notamment
servir à représenter des pages web, des composants d’interface utilisateur, des paniers
virtuels, des gestionnaires d’erreur, des catégories de produits ou des clients.
Les objets de votre code peuvent également représenter des instances spécifiques des
classes énumérées plus haut, comme une page d’accueil, un bouton particulier ou le
panier d’achat de Jean Dupont à un moment donné. Jean Dupont lui-même pourrait
d’ailleurs être représenté par un objet de type client. Chaque article acheté par Jean peut
être représenté sous la forme d’un objet, appartenant à une catégorie ou à une classe.
Dans le chapitre précédent, nous nous sommes servis de fichiers inclus pour donner une apparence cohérente à toutes les pages du site de l’entreprise fictive TLA
Consulting. Une version plus élaborée de ce site pourrait être obtenue en utilisant
des classes et en exploitant le concept puissant d’héritage.
Nous voulons maintenant pouvoir ajouter rapidement de nouvelles pages web au site
de TLA, qui aient la même présentation et un comportement similaire. Nous voulons toutefois être en mesure de modifier ces pages pour les adapter aux différentes
parties du site.
Pour les besoins de cet exemple, nous allons créer une classe Page dont le rôle principal est de limiter la quantité de code HTML nécessaire à la création d’une nouvelle
page. Cette classe devra nous permettre de modifier les sections qui changent d’une
page à l’autre, tout en produisant automatiquement les éléments communs à toutes
les pages. La classe Page devra donc fournir un cadre flexible pour la création de
nouvelles pages, sans compromettre notre liberté.
Comme les pages seront produites à partir d’un script et non avec du code HTML
statique, nous pouvons implémenter diverses possibilités astucieuses :
■
N’autoriser la modification d’éléments de la page qu’en un emplacement seulement. Par exemple, s’il apparaît nécessaire de changer la déclaration de
© 2012 Pearson France – PHP & MySQL, 4e éd. – Luke Welling, Laura Thomson

Documents pareils