Grâce aux nouvelles fonctionnalités du langage PHP V5, la maintenabilité et la fiabilité du code peuvent être considérablement améliorées. En lisant cet article, vous apprendrez comment profiter de ces nouvelles fonctionnalités pour migrer du code développé en PHP V4 vers PHP V5.
PHP V5 a apporté des améliorations significatives basées sur PHP V4. Les nouvelles fonctionnalités du langage facilitent la création et la maintenance de bibliothèques de classes fiables. De plus, la réécriture de la bibliothèque standard a permis d'aligner davantage PHP sur ses collègues idiomes du Web, tels que le langage de programmation Java™. Jetons un coup d'œil à certaines des nouvelles fonctionnalités orientées objet de PHP et apprenons comment migrer le code PHP V4 existant vers PHP V5.
Tout d'abord, regardons comment les nouvelles fonctionnalités du langage et le créateur de PHP ont modifié la façon dont les objets sont créés avec PHP V4. L'idée avec la V5 était de créer un langage de puissance industrielle pour le développement d'applications Web. Cela signifie comprendre les limites de PHP V4, puis extraire les bonnes architectures de langage connues d'autres langages (tels que Java, C#, C++, Ruby et Perl) et les incorporer dans PHP.
La première et la plus importante nouvelle fonctionnalité est la protection de l'accès aux méthodes de classe et aux variables d'instance - les mots-clés publics, protégés et privés. Cette nouvelle fonctionnalité permet aux concepteurs de classes de garder le contrôle sur les propriétés intrinsèques d'une classe tout en indiquant aux utilisateurs de la classe quelles classes sont accessibles et lesquelles ne le sont pas.
En PHP V4, tout le code est public. En PHP V5, les concepteurs de classes peuvent déclarer quel code est visible au monde extérieur (public) et quel code n'est visible qu'à l'intérieur de la classe (privé) ou uniquement aux sous-classes de la classe (protégé). Sans ces contrôles d'accès, le développement de code dans une grande équipe ou la distribution de code en tant que bibliothèque est entravé car les utilisateurs de ces classes sont susceptibles d'utiliser les mauvaises méthodes ou d'accéder au code qui devrait être des variables membres privées.
Une autre grande nouveauté est l'interface de mots-clés et le résumé, qui permettent la programmation de contrats. La programmation sous contrat signifie qu'une classe fournit un contrat à une autre classe - en d'autres termes : « C'est ce que je vais faire, et vous n'avez pas besoin de savoir comment cela se fait. » Toutes les classes qui implémentent l'interface adhèrent à ce contrat. Tous les utilisateurs d'une interface s'engagent à n'utiliser que les méthodes spécifiées dans l'interface. Le mot-clé abstract rend le travail avec les interfaces très simple, comme je l'expliquerai plus tard.
Ces deux fonctionnalités clés - le contrôle d'accès et la programmation contractuelle - permettent à de grandes équipes de codeurs de travailler plus facilement avec de grandes bases de codes. Ces fonctionnalités permettent également à l'EDI de fournir un ensemble plus riche de fonctionnalités intelligentes en termes de langage. Cet article aborde non seulement plusieurs problèmes de migration, mais consacre également du temps à expliquer comment utiliser ces nouvelles fonctionnalités linguistiques majeures.
Contrôle d'accès
Pour démontrer les nouvelles fonctionnalités du langage, j'ai utilisé une classe appelée Configuration. Cette classe simple contient des éléments de configuration pour l'application Web, par exemple le chemin d'accès au répertoire images. Idéalement, ces informations résideraient dans un fichier ou une base de données. Le listing 1 montre une version simplifiée.
Liste 1. access.php4
<?php
configuration de classe
{
var $_items = tableau();
fonction Configuration() {
$this->_items[ 'imgpath' ] = 'images';
}
fonction get( $clé ) {
return $this->_items[ $key ];
}
}
$c = nouvelle Configuration();
echo( $c->get( 'imgpath' )."n" );
?>
Il s'agit d'une classe PHP V4 complètement orthodoxe. La variable membre contient la liste des éléments de configuration, le constructeur charge les éléments et la méthode d'accès nommée get() renvoie la valeur de l'élément.
Après avoir exécuté le script, le code suivant apparaîtra sur la ligne de commande :
%php access.php4
photos
%
très bien! Ce résultat signifie que le code s'exécute normalement et que la valeur de l'élément de configuration imgpath est définie et lue normalement.
La première étape pour convertir cette classe en PHP V5 consiste à renommer le constructeur. En PHP V5, la méthode d'initialisation d'un objet (constructeur) s'appelle __construct. Ce petit changement est illustré ci-dessous.
Liste 2. access1.php5
<?php
configuration de classe
{
var $_items = tableau();
fonction __construct() {
$this->_items[ 'imgpath' ] = 'images';
}
fonction get( $clé ) {
return $this->_items[ $key ];
}
}
$c = nouvelle Configuration();
echo( $c->get( 'imgpath' )."n" );
?>
Les changements cette fois ne sont pas grands. Je viens de passer à la convention PHP V5. L'étape suivante consiste à ajouter un contrôle d'accès à la classe pour garantir que les utilisateurs de la classe ne peuvent pas lire et écrire directement la variable membre $_items. Ce changement est illustré ci-dessous.
Liste 3. access2.php5
<?php
configuration de classe
{
private $_items = array();
public function __construct() {
$this->_items[ 'imgpath' ] = 'images';
}
fonction publique get( $clé ) {
return $this->_items[ $key ];
}
}
$c = nouvelle Configuration();
echo( $c->get( 'imgpath' )."n" );
?>
Si un utilisateur de cet objet accédait directement au tableau d’éléments, l’accès serait refusé car le tableau est marqué privé. Heureusement, les utilisateurs ont découvert que la méthode get() fournit les autorisations de lecture les plus appréciées.
Pour illustrer comment utiliser les autorisations protégées, j'ai besoin d'une autre classe, qui doit hériter de la classe Configuration. J'ai appelé cette classe DBConfiguration et j'ai supposé que la classe lirait les valeurs de configuration de la base de données. Cette configuration est illustrée ci-dessous.
Liste 4. access3.php
<?php
configuration de classe
{
protégé $_items = array();
public function __construct() {
$this->load();
}
fonction protégée load() { }
fonction publique get( $clé ) {
return $this->_items[ $key ];
}
}
la classe DBConfiguration étend la configuration
{
fonction protégée load() {
$this->_items[ 'imgpath' ] = 'images';
}
}
$c = new DBConfiguration();
echo( $c->get( 'imgpath' )."n" );
?>
Cette liste montre l'utilisation correcte du mot-clé protégé. La classe de base définit une méthode nommée load(). Les sous-classes de cette classe remplaceront la méthode load() pour ajouter des données à la table des éléments. La méthode load() est interne à la classe et à ses sous-classes, elle n'est donc pas visible par tous les consommateurs externes. Si les mots-clés sont tous privés, la méthode load() ne peut pas être remplacée.
Je n'aime pas vraiment cette conception, mais je l'ai choisie parce que je devais donner à la classe DBConfiguration l'accès au tableau d'éléments. J'aimerais continuer à ce que le tableau d'éléments soit entièrement géré par la classe Configuration, de sorte qu'à mesure que d'autres sous-classes soient ajoutées, ces classes n'auront pas besoin de savoir comment gérer le tableau d'éléments. J'ai apporté les modifications suivantes.
Liste 5. access4.php5
<?php
configuration de classe
{
private $_items = array();
public function __construct() {
$this->load();
}
fonction protégée load() { }
fonction protégée ajouter( $clé, $valeur ) {
$this->_items[ $key ] = $value ;
}
fonction publique get( $clé ) {
return $this->_items[ $key ];
}
}
la classe DBConfiguration étend la configuration
{
fonction protégée load() {
$this->add( 'imgpath', 'images' );
}
}
$c = new DBConfiguration();
echo( $c->get( 'imgpath' )."n" );
?>
Les tableaux d'éléments peuvent désormais être privés car les sous-classes utilisent la méthode protégée add() pour ajouter des éléments de configuration à la liste. La classe Configuration peut modifier la façon dont elle stocke et lit les éléments de configuration sans tenir compte de ses sous-classes. Tant que les méthodes load() et add() sont exécutées de la même manière, le sous-classement ne devrait poser aucun problème.
Pour moi, le contrôle d'accès supplémentaire est la principale raison d'envisager de passer à PHP V5. Est-ce simplement parce que Grady Booch a dit que PHP V5 est l'un des quatre principaux langages orientés objet ? Non, car j'ai déjà accepté une tâche consistant à maintenir le code C++ 100KLOC dans lequel toutes les méthodes et tous les membres étaient définis comme publics. Il m'a fallu trois jours pour nettoyer ces définitions et, ce faisant, j'ai considérablement réduit le nombre d'erreurs et amélioré la maintenabilité. Pourquoi? Car sans contrôle d’accès, il est impossible de savoir comment les objets utilisent d’autres objets, et il est impossible d’apporter des modifications sans savoir quels obstacles surmonter. Avec C++, au moins j'ai toujours le compilateur disponible. PHP n'est pas livré avec un compilateur, ce type de contrôle d'accès devient donc encore plus important.
Programmation sous contrat
La prochaine fonctionnalité importante à exploiter lors de la migration de PHP V4 vers PHP V5 est la prise en charge de la programmation sous contrat via des interfaces, des classes abstraites et des méthodes. Le listing 6 montre une version de la classe Configuration dans laquelle les codeurs PHP V4 ont tenté de créer une interface de base sans utiliser du tout le mot-clé interface.
Liste 6. interface.php4
<?php
classe IConfiguration
{
fonction obtenir( $clé ) { }
}
la classe Configuration étend IConfiguration
{
var $_items = tableau();
fonction Configuration() {
$this->load();
}
fonction charger() { }
fonction get( $clé ) {
return $this->_items[ $key ];
}
}
la classe DBConfiguration étend la configuration
{
fonction charger() {
$this->_items[ 'imgpath' ] = 'images';
}
}
$c = new DBConfiguration();
echo( $c->get( 'imgpath' )."n" );
?>
La liste commence par une petite classe IConfiguration qui définit toutes les interfaces fournies par la classe Configuration ou les classes dérivées. Cette interface définira le contrat entre la classe et tous ses utilisateurs. Le contrat stipule que toutes les classes qui implémentent IConfiguration doivent être équipées d'une méthode get() et que tous les utilisateurs de IConfiguration doivent insister pour utiliser uniquement la méthode get().
Le code ci-dessous est exécuté en PHP V5, mais il est préférable d'utiliser le système d'interface fourni comme indiqué ci-dessous.
Liste 7. interface1.php5
<?php
interface IConfiguration
{
fonction get( $clé );
}
classe Configuration implémente IConfiguration
{
...
}
la classe DBConfiguration étend la configuration
{
...
}
$c = new DBConfiguration();
echo( $c->get( 'imgpath' )."n" );
?>
D'une part, les lecteurs peuvent comprendre plus clairement l'état d'exécution ; d'autre part, une seule classe peut implémenter plusieurs interfaces. Le listing 8 montre comment étendre la classe Configuration pour implémenter l'interface Iterator, qui est l'interface interne de PHP.
Liste 8. interface2.php5
<?php
interface IConfiguration {
...
}
classe Configuration implémente IConfiguration, Iterator
{
private $_items = array();
public function __construct() {
$this->load();
}
fonction protégée load() { }
fonction protégée ajouter( $clé, $valeur ) {
$this->_items[ $key ] = $value ;
}
fonction publique get( $clé ) {
return $this->_items[ $key ];
}
fonction publique rewind() { reset($this->_items })
public function current() { return current($this->_items);
clé de fonction publique() { clé de retour ($this->_items })
public function next() { return next($this->_items);
public function valid() { return ( $this->current() !== false });
}
la classe DBConfiguration étend la configuration {
...
}
$c = new DBConfiguration();
foreach( $c as $k => $v ) { echo( $k." = ".$v."n" );
?>
L'interface Iterator permet à n'importe quelle classe d'apparaître comme un tableau de ses consommateurs. Comme vous pouvez le voir à la fin du script, vous pouvez utiliser l'opérateur foreach pour réitérer tous les éléments de configuration dans l'objet Configuration. PHP V4 ne dispose pas de cette fonctionnalité, mais vous pouvez utiliser cette fonctionnalité de différentes manières au sein de votre application.
L’avantage du mécanisme d’interface est que les contrats peuvent être rapidement conclus sans avoir à mettre en œuvre de méthodes. La dernière étape consiste à implémenter l'interface, où vous devez implémenter toutes les méthodes spécifiées. Une autre nouvelle fonctionnalité utile de PHP V5 concerne les classes abstraites, qui facilitent l'implémentation de la partie principale d'une interface avec une classe de base, puis l'utilisation de cette interface pour créer des classes d'entités.
Une autre utilisation des classes abstraites consiste à créer une classe de base pour plusieurs classes dérivées dans laquelle la classe de base n'est jamais instanciée. Par exemple, lorsque DBConfiguration et Configuration existent en même temps, seule DBConfiguration peut être utilisée. La classe Configuration n’est qu’une classe de base – une classe abstraite. Par conséquent, vous pouvez forcer ce comportement à l’aide du mot-clé abstract comme indiqué ci-dessous.
Liste 9. abstract.php5
<?php
configuration de classe abstraite
{
protégé $_items = array();
public function __construct() {
$this->load();
}
fonction abstraite protégée load();
fonction publique get( $clé ) {
return $this->_items[ $key ];
}
}
la classe DBConfiguration étend la configuration
{
fonction protégée load() {
$this->_items[ 'imgpath' ] = 'images';
}
}
$c = new DBConfiguration();
echo( $c->get( 'imgpath' )."n" );
?>
Désormais, toutes les tentatives d'instancier un objet de type Configuration entraîneront une erreur car le système considère la classe comme abstraite et incomplète.
Méthodes et membres statiques
Une autre nouvelle fonctionnalité importante de PHP V5 est la prise en charge des membres et méthodes statiques sur les classes. En utilisant cette fonctionnalité, vous pouvez utiliser le modèle singleton populaire. Ce modèle est idéal pour la classe Configuration car l'application ne doit avoir qu'un seul objet de configuration.
Le listing 10 montre la version PHP V5 de la classe Configuration sous forme de singleton.
Liste 10. static.php5
<?php
configuration de classe
{
private $_items = array();
statique private $_instance = null;
fonction publique statique get() {
si ( self::$_instance == null )
self::$_instance = nouvelle configuration();
return self::$_instance ;
}
fonction privée __construct() {
$this->_items[ 'imgpath' ] = 'images';
}
fonction publique __get( $clé ) {
return $this->_items[ $key ];
}
}
echo( Configuration::get()->{ 'imgpath' }."n" );
?>
Le mot clé static a de nombreuses utilisations. Pensez à utiliser ce mot-clé lorsque vous devez accéder à certaines données globales pour tous les objets d'un même type.
Méthode magique
Une autre grande nouveauté de PHP V5 est la prise en charge des méthodes magiques, qui permettent aux objets de modifier rapidement l'interface de l'objet - par exemple, en ajoutant des variables membres pour chaque élément de configuration dans l'objet Configuration. Il n'est pas nécessaire d'utiliser la méthode get(), recherchez simplement un élément particulier et traitez-le comme un tableau, comme indiqué ci-dessous.
Liste 11. magic.php5
<?php
configuration de classe
{
privé $_items = tableau();
fonction __construct() {
$this->_items[ 'imgpath' ] = 'images';
}
fonction __get( $clé ) {
return $this->_items[ $key ];
}
}
$c = nouvelle Configuration();
echo( $c->{ 'imgpath' }."n" );
?>
Dans cet exemple, j'ai créé une nouvelle méthode __get() qui est appelée chaque fois que l'utilisateur recherche une variable membre sur l'objet. Le code de la méthode utilisera ensuite le tableau d'éléments pour trouver la valeur et renverra cette valeur comme s'il y avait une variable membre spécifiquement pour ce mot-clé. En supposant que l'objet est un tableau, à la fin du script, vous pouvez voir que l'utilisation de l'objet Configuration est aussi simple que de trouver la valeur de imgpath.
Lors de la migration de PHP V4 vers PHP V5, vous devez être conscient de ces fonctionnalités du langage qui sont totalement indisponibles dans PHP V4, et vous devez revalider les classes pour voir comment elles peuvent être utilisées.
Les exceptions
terminent enfin cet article en introduisant le nouveau mécanisme d'exception dans PHP V5. Les exceptions offrent une toute nouvelle façon d’envisager la gestion des erreurs. Tous les programmes génèrent inévitablement des erreurs – fichier introuvable, mémoire insuffisante, etc. Si aucune exception n’est utilisée, un code d’erreur doit être renvoyé. Veuillez consulter le code PHP V4 ci-dessous.
Listing 12. fichier.php4
<?php
fonction parseLine( $l )
{
//...
return array( 'erreur' => 0,
data => array() // données ici
);
}
fonction readConfig( $chemin )
{
if ( $path == null ) renvoie -1 ;
$fh = fopen( $chemin, 'r' );
if ( $fh == null ) return -2;
while( !feof( $fh ) ) {
$l = fgets( $fh );
$ec = parseLine( $l );
if ( $ec['erreur'] != 0 ) return $ec['erreur'];
}
fclose( $fh );
renvoie 0 ;
}
$e = readConfig( 'myconfig.txt' );
si ( $e != 0 )
echo( "Une erreur s'est produite (".$e.")n" );
?>
Ce code d'E/S de fichier standard lira un fichier, récupérera certaines données et renverra un code d'erreur si des erreurs sont rencontrées. J'ai deux questions sur ce script. Le premier est le code d'erreur. Que signifient ces codes d'erreur ? Pour découvrir la signification de ces codes d'erreur, vous devez créer un autre système pour mapper ces codes d'erreur en chaînes significatives. Le deuxième problème est que le résultat renvoyé par parseLine est très compliqué. J'en ai juste besoin pour renvoyer des données, mais il doit en fait renvoyer un code d'erreur et des données. La plupart des ingénieurs (moi y compris) deviennent souvent paresseux et renvoient simplement des données et ignorent les erreurs, car les erreurs sont difficiles à gérer.
Le listing 13 montre à quel point le code est clair lors de l'utilisation d'exceptions.
Listing 13. file.php5
<?php
fonction parseLine( $l )
{
// Analyse et lance une exception en cas de non-validité
return array(); // données
}
fonction readConfig( $chemin )
{
si ( $chemin == null )
throw new Exception( 'mauvais argument' );
$fh = fopen( $path, 'r' );
si ( $fh == null )
throw new Exception( 'impossible d'ouvrir le fichier' );
while( !feof( $fh ) ) {
$l = fgets( $fh );
$ec = parseLine( $l );
}
fclose( $fh );
}
essayer {
readConfig( 'myconfig.txt' );
} catch(Exception $e) {
écho( $e );
}
?>
Je n'ai pas besoin de m'inquiéter des codes d'erreur car l'exception contient un texte descriptif de l'erreur. Je n'ai pas non plus à réfléchir à la façon de retrouver le code d'erreur renvoyé par parseLine, car la fonction générera simplement une erreur si une erreur se produit. La pile s'étend jusqu'au bloc try/catch le plus proche, qui se trouve au bas du script.
Les exceptions révolutionneront la façon dont vous écrivez du code. Au lieu de gérer le casse-tête des codes d’erreur et des mappages, vous pouvez vous concentrer sur les erreurs que vous souhaitez gérer. Un tel code est plus facile à lire, à maintenir et je dirais même que vous encouragez à ajouter la gestion des erreurs, car cela rapporte généralement des dividendes.
Conclusion
Les nouvelles fonctionnalités orientées objet et l'ajout de la gestion des exceptions fournissent de bonnes raisons de migrer le code de PHP V4 vers PHP V5. Comme vous pouvez le constater, le processus de mise à niveau n’est pas difficile. La syntaxe qui s'étend à PHP V5 ressemble à PHP. Oui, ces syntaxes proviennent de langages comme Ruby, mais je pense qu'elles fonctionnent très bien ensemble. Et ces langages étendent la portée de PHP d'un langage de script pour petits sites à un langage pouvant être utilisé pour compléter des applications au niveau de l'entreprise.