1. Introduction

PHP5 est arrivé! Comme l'a dit Rasmus Ledorf, l'inventeur de PHP, PHP5 n'est pas une révolution mais une évolution. Cette évolution introduit quelques changements majeurs tout en conservant une compatibilité totale avec la version antérieure.

Les principales nouvelles fonctionnalités sont:

  • SQLite:Un SGBD embarqué dont nous verrons les principales forces et faiblesses
  • SimpleXML:Un nouveau parseur XML très efficace et très simple
  • Un nouveau modèle POO:Le modèle objet complètement remanié, l'ancien restant correctement interprété par php

Vous trouverez plus de détails sur ces fonctionnalités dans le reste de l'article.

Note: tous les exemples de code fournis dans ce tutoriel ont été testés avec PHP 5.0.0 et SQLite 2.8.14.

2. Les fonctionnalités en détail

2.1. SQLite

2.1.1. Description du produit

Selon moi, l'introduction de SQLite est le changement majeur entre PHP5 et PHP4.x. SQLite est un SGBD embarqué, donc compilé par défaut, ce qui signifie que le simple fait d'installer PHP vous permet de l'utiliser. Contrairement à MYSQL, il ne nécessite donc pas de processus indépendant comme MYSQL Server. SQLite est très léger et très rapide, selon le site officiel de SQLite, il serait 2 à 3 fois plus rapide que Mysql pour la plupart des opérations et parfois 10 fois plus rapide que Postgress et probablement aussi plus rapide que beaucoup de SGBD. Ceci est assez normal puisqu'il n'a pas d'architecture client-serveur, l'interface et le moteur étant en fait contenu dans le même package.

Tempérons toutefois ces résultats, il faudrait qu'un organisme indépendant effectue aussi ce genre de tests et dans des environnements plus complexes pour pouvoir tirer des conclusions définitives. Il n'empêche que ces résultats sont tout de même encourageant!
Outre le moteur compilé par défaut, SQLite fournit aussi des classes facilitant son interaction avec PHP. L'architecture de SQLite est assez simpliste puisque toute base de donnée réside dans un et un seul fichier. Les utilisateurs voulant accéder cette base de données sont tributaires des droits accordés au niveau du système d'exploitation. SQLite semble convenir à merveille pour des applications ne nécessitant pas trop de transactions concurrentes. En effet, ce qui différencie entre autre, SQLite des grands SGBD (Oracle, Sybase, SQL Server...) est qu'il ne permet pas d'insertions concurrentes. A chaque fois qu'un processus effectue un ordre DML et DDL(INSERT,UPDATE,DELETE, CREATE...), la base est entièrement verrouillée, ce qui, vous en conviendrez n'est pas très pratique. Des accès concurrents et presque illimités sont par contre permis en lecture seule (SELECT). Dans un environnement où un nombre réduit de processus est chargé d'écrire et tous les autres de lire, SQLite est probablement une excellente solution. SQLite implémente la norme SQL 92.

2.1.2. Fonctionnalités

SQLite est doté d'atouts intéressants, voici une liste non-exhaustive de ceux-ci

  • Possibilité de gérer des transactions
  • Création de vues
  • La gestion de contraintes d'intégrité dans un futur proche
  • La gestion des contraintes NOT NULL et UNIQUE
  • Gestion des triggers
  • Gestion des exceptions
  • Possibilité d'utiliser les fonctions PHP internes directement dans les requêtes
  • Possibilité d'utiliser ses propres fonctions codées en PHP dans les requêtes
  • Possède beaucoup de fonctions internes

Chaque ordre DML (UPDATE, INSERT, DELETE) démarre automatiquement une transaction. Si cette transaction est implicite, elle sera validée à la fin de l'exécution de l'ordre. On peut décider de démarrer une transaction explicite dont la validation ou l'annulation sera gérée par le développeur. Des exemples en section 2.1.3 vous montreront comment cela fonctionne. SQLite ne permet pas de gérer des transactions imbriquées. Donc seulement 1 transaction peut être démarrée à la fois.

La création de vue est possible en SQLite. Pour rappel, une vue est un "recordset" correspondant à un ordre SELECT défini par le développeur. Lorsque l'on sait que les besoins en matière de requête des utilisateurs finaux sont souvent les mêmes, il est bon de créer des vues correspondant déjà à leurs aspirations. Ces vues accélèrent la lecture de la base de données.

Les triggers signifiant "déclencheurs" en Français permettent de déclencher un ordre SQL lors d'un évènement précis. Nous verrons comment utiliser des triggers avec SQLite.

2.1.3. Exemples d'utilisation via la classe compilée SQLiteDatabase

Exemples d'ordre DDL

 
Sélectionnez

<?	
//l'instanciation de l'objet prend en paramètre le nom de la db. L'objet va alors
//tenter de se connecter à cette db ou de la créer dans le répertoire courant si celle-ci n'existe pas.
$db = new SQLiteDatabase('DEVELOPPEZ'); 
$requete  = "
//création d'une table nommée adresses_clients
CREATE TABLE ADRESSES_CLIENTS ( ID_ADRESSE INTEGER PRIMARY KEY, ID_CLIENT INTEGER , ADRESSE VARCHAR(250) );
//création d'une table clients
CREATE TABLE CLIENTS ( ID_CLIENT INTEGER, NOM_CLIENT VARCHAR(60), PRENOM_CLIENT VARCHAR(60) );
//création d'une vue faisant une jointure entre les tables clients et adresses_clients
CREATE VIEW CLIENTSADR AS SELECT NOM_CLIENT,PRENOM_CLIENT,ADRESSE FROM CLIENTS INNER JOIN ADRESSES_CLIENTS
 ON ADRESSES_CLIENTS.ID_CLIENT=CLIENTS.ID_CLIENT;
//insertion de deux enregistrements
INSERT INTO CLIENTS VALUES ('1', 'Eyskens', 'Stéphane');
INSERT INTO ADRESSES_CLIENTS VALUES ('1', '1', 'démo adresse');
";
$db->query($requete);
unset($db);
 
?>
 

Ce code simpliste met en évidence la chose suivante: il est désormais possible d'exécuter plusieurs ordres SQL en appelant une seule fois la méthode query. Ceci est très pratique mais peut aussi s'avérer dangereux. Si vous utilisez des données provenant d'un formulaire utilisateur, méfiez vous des injections SQL. Avec mysql_query, il n'était pas possible d'exécuter plusieurs requêtes via un seul appel, le risque d'injection était donc moindre. Les ordres SQL doivent simplement être séparés par un point virgule. Les seuls ordres DDL actuellement implémentés dans SQLite sont CREATE et DROP. Il n'existe pas d'instruction ALTER comme dans la plupart des SGBD. En attendant une évolution possible de SQLite, la manière la plus simple de modifier la structure d'une table est d'en créer une nouvelle avec la structure désirée, copier l'originale dans une table temporaire et de supprimer l'originale et la table temporaire en fin de traitement. Lorsque l'on crée une table, il n'est pas indispensable de spécifier le type de colonne car SQLite n'implémente en réalité qu'un seul type qui est "INTEGER PRIMARY KEY". Tous les autres types sont considérés comme étant des strings. Concrètement, cela signifie que si vous définissez une colonne de type SMALLINT et que vous y mettez du string, SQLite laissera faire. Le pendant de PHPMyadmin pour MYSQL a déjà été réalisé pour SQLite. Il s'appelle SQLiteManager et est téléchargeable ici

Les triggers
Comme je l'ai mentionné plus haut, les contraintes d'intégrité de type clé étrangère ne sont pas encore supportées par SQLite. Il est néanmoins possible de les remplacer par des triggers. Voici un exemple qui crée un trigger sur la table "adresses_clients". Ce trigger vérifie que pour chaque nouvelle insertion dans cette table, le numéro de client existe bien dans la table clients. Si tel est le cas, la ligne est validée, sinon elle génère une exception.

 
Sélectionnez

<?	
$db = new SQLiteDatabase('DEVELOPPEZ'); 
$requete  = "
CREATE TRIGGER VERIF_CLIENT BEFORE INSERT ON ADRESSES_CLIENTS FOR EACH ROW 
BEGIN 
SELECT CASE 
WHEN (SELECT COUNT(*) FROM CLIENTS WHERE ID_CLIENT=new.ID_CLIENT)=0 THEN RAISE(FAIL,\"Le client n'existe pas\")
END; 
END;";
$db->query($requete);
unset($db);
?>
 

Grâce à ce trigger, vous pouvez désormais vous assurer que les numéros de clients définis dans la table adresses_clients ont bien été préalablement enregistrés dans la table clients. Les triggers ont bien sûr une multitude de cas d'utilisation. On pourrait par exemple les utiliser pour logger ce qui se passe dans la base de données comme par exemple ceci:.

 
Sélectionnez

<?	
$db = new SQLiteDatabase('DEVELOPPEZ'); 
$requete  = "
//on crée un trigger qui crée une ligne dans la table LOG pour chaque ligne insérée dans client. Il enregistre l'heure
CREATE TRIGGER LOG_CLI_INS AFTER INSERT ON CLIENTS
FOR EACH ROW 
BEGIN 
//Imaginons que nous ayons préalablement créé la table LOG
  INSERT INTO LOG ( NOM_TABLE,OPERATION,DATE_HEURE) VALUES ('CLIENTS','INSERT',php('date','Y-m-d G:i:s')); 
END;
 
CREATE TRIGGER LOG_CLI_UPD AFTER UPDATE ON CLIENTS
FOR EACH ROW 
BEGIN 
  INSERT INTO LOG ( NOM_TABLE,OPERATION,DATE_HEURE) VALUES ('CLIENTS','UPDATE',php('date','Y-m-d G:i:s')); 
END;
";
 
$db->query($requete);
unset($db);
?>

Le code ci-dessous permet donc d'enregistrer le type d'opération et l'heure à laquelle elle a été exécutée dans la table LOG. Les triggers peuvent gérer les évènements "on DELETE" "on UPDATE" "on INSERT" et peuvent spécifier la colonne sur laquelle le déclencheur doit être activé.

Les transactions
Pour comprendre à quoi servent les transactions, je vous renvoie vers un de nos tutoriels Les transactions. En attendant, voici un exemple très simple d'utilisation

 
Sélectionnez

<?	
$db = new SQLiteDatabase('DEVELOPPEZ'); 
$requete  = "
BEGIN TRANSACTION; //on démarre une transaction
DELETE FROM CLIENTS; //on supprime tous les enregistrements de la table clients
COMMIT TRANSACTION; //On valide les changements, donc la suppression est effective
BEGIN TRANSACTION; //on démarre une autre transaction. On insère un enregistrement
INSERT INTO CLIENTS(ID_CLIENT,NOM_CLIENT,PRENOM_CLIENT) VALUES(156,'DVP NOM','DVP PRENOM');
ROLLBACK TRANSACTION;//On annule les changements, donc l'insertion n'est pas effectuée
 
";
$db->query($requete); //On exécute la requête
 
$result=$db->query('select NOM_CLIENT FROM CLIENTS'); //On selectionne tous les enreg. de la table clients
while($result->valid()){ //On parcours le recordset
 $row=$result->current();
 echo $row['NOM_CLIENT']; 
 $result->next();
}
unset($db);
?>

On remarquera que l'exécution de ce script n'affichera rien. En effet si on le décortique, on peut constater ceci:

  • La 1ère transaction supprimant tous les enregistrements de la table clients est validée via le COMMIT
  • La 2ème transaction insérant un enregistrement est annulée via le ROLLBACK
  • La table est donc vide, il est normal que la deuxième requête ne récupère aucun enregistrement

L'utilisation des transactions est importante avec SQLite car outre les aspects relevés dans le tutoriel "Les transactions", SQLite est beaucoup plus rapide lorsqu'on les utilise.

Les fonctions personnelles
L'utilisation de fonctions personnelles peut s'avérer être d'une grande utilité! Pour vous montrer comment cela fonctionne, je vous propose un exemple tout à fait fantaisiste.

 
Sélectionnez

<?
function transforme_chaine($chaine){ 
 if(empty($chaine)){return NULL;}
 $ch=NULL;
 
 for($i=0;$i<strlen($chaine);$i++){
  if(($i % 2)==0){
    $ch.=strtoupper($chaine[$i]);
  }
  else{
    $ch.=strtolower($chaine[$i]);		
  } 
 } 
 return $ch;
}
 
 
$db = new SQLiteDatabase('DEVELOPPEZ'); 
//cette méthode enregistre notre fonction dans SQLite, le nom que l'on veut donner dans SQLite
//peut être différent du nom de la fonction php. Le dernier paramètre, ici "1" est le nombre d'arguments que
//notre fonction PHP accepte
$db->createFunction('transforme_chaine','transforme_chaine',1);	
$result=$db->query('select transforme_chaine(NOM_CLIENT) as NOM,transforme_chaine(PRENOM_CLIENT) as PRENOM FROM CLIENTS');
while($result->valid()){
 $row=$result->current();
 echo 'Nom:'.$row['NOM'].'Prénom:'.$row['PRENOM'];
 $result->next();
}
}
 
 
unset($db);
 
?>

Il est à noter que l'enregistrement de notre fonction personnelle dans SQLite ne durera que le temps d'exécution du script. L'exemple ci-dessus retournera les noms et prénoms des clients transformés. Une lettre sur deux étant transformée en majuscule et l'autre en minuscule. Ce qui donne ceci:

Nom:DvP NoM Prénom:DvP PrEnOm Nom:DvP1 nOm Prénom:DvP PrEnOm

Utilisation des fonctions internes de php

 
Sélectionnez

<?	
$db = new SQLiteDatabase('DEVELOPPEZ'); 
//La syntaxe d'utilisation d'une fonction php dans une requête SQLite est php('nom de la fonction',paramètre)
$result=$db->query("select php('strtoupper',NOM_CLIENT) as NOM,php('strtolower',PRENOM_CLIENT) as PRENOM FROM CLIENTS");
while($result->valid()){
 $row=$result->current();
 echo 'Nom:'.$row['NOM'].' Prénom:'.$row['PRENOM']; 
 $result->next();
}
unset($db);
?>

Cet exemple est assez parlant et ne nécessite guère d'explication.

SQLite supporte les requêtes imbriquées, voici un petit exemple

 
Sélectionnez

<?
$db = new SQLiteDatabase('DEVELOPPEZ'); 
$result=$db->query("select NOM_CLIENT FROM CLIENTS WHERE ID_CLIENT IN 
                   (SELECT ID_CLIENT FROM ADRESSES_CLIENTS GROUP BY ID_CLIENT)");
while($result->valid()){
 $row=$result->current();
 echo 'Nom:'.$row['NOM_CLIENT']; 
 $result->next();
}
unset($db);
?>

Cette requête affichera le nom des clients ayant une ou plusieurs adresse(s) définie(s) dans la table ADRESSES_CLIENTS. Il est bien sûr possible d'obtenir ces informations avec une jointure classique mais c'est juste pour vous fournir un exemple.

2.1.4. Exemples d'utilisation en mode procédural

En mode procédural, les fonctions php attaquant une base de données SQLite sont très semblables à toutes les autres fonctions utilisées dans les accès à tous les SGBD. En effet, ces fonctions sont toutes préfixées de la chaîne "sqlite_". Ceci peut-être très pratique dans l'optique d'une migration de scripts procéduraux gérant des accès SGBD. Dans la plupart des cas, il suffira de remplacer mysql_... par sqlite_... ou encore mssql_... par sqlite_...

Je ne vais donc pas trop m'étendre sur le mode procédural puisqu'il est très similaire à celui utilisé avec mysql et dont vous êtes déjà familiarisé. Je vais néanmoins vous fournir deux ou trois exemples.

Exemple DDL,DML

 
Sélectionnez

<?
$db = sqlite_open('test.sqlite');
sqlite_query($db,'CREATE TABLE MODE_PROCEDURAL(COL1)');
sqlite_query($db,"INSERT INTO MODE_PROCEDURAL(COL1) VALUES('exemple')");
$result=sqlite_query($db,"SELECT COL1 FROM MODE_PROCEDURAL");
 
while($row=sqlite_fetch_object($result)){
 echo $row->COL1;  
}
unset($db);
?>

Exemple avec les transactions

 
Sélectionnez

<?	
$db = sqlite_open('test.sqlite');
sqlite_query($db,'BEGIN TRANSACTION;');
sqlite_query($db,'DELETE FROM MODE_PROCEDURAL');
sqlite_query($db,'COMMIT TRANSACTION;');
sqlite_query($db,'BEGIN TRANSACTION;');
sqlite_query($db,"INSERT INTO MODE_PROCEDURAL(COL1) VALUES('exemple')");
sqlite_query($db,"ROLLBACK TRANSACTION");
$result=sqlite_query($db,"SELECT COL1 FROM MODE_PROCEDURAL");
 
while($row=sqlite_fetch_object($result)){
 echo $row->COL1;  
}
unset($db);
?>

Cet exemple a exactement le même effet que l'exemple d'utilisation des transactions via la classe SQLiteDatabase.

Je pense que l'utilisation du mode procédural peut surtout être utile lors des migrations. Pour tout nouveau développement, je préconise le mode POO en utilisant la classe SQLiteDatabase. Celle-ci permet en outre l'exécution de multi-requête, ce qui peut s'avérer intéressant. Vous pourriez bien sûr réinventer la roue en vous créant votre propre classe!

2.2. SimpleXML

L'introduction de SimpleXML est certainement une belle avancée dans le domaine d'interaction entre PHP et XML. Là où l'utilisation des objets existant était parfois complexe, SimpleXML, qui porte bien son nom, améliore le confort du développeur pour interpréter des fichiers XML.

Prenons par exemple un fichier XML très simple au format UTF8 que nous nommerons nouvelles.xml

 
Sélectionnez

<?xml version="1.0"?> 
<nouvelles> 
    <nouvelle>
        <contenu>Monsieur et Madame X viennent de se marier! Félicitations</contenu>
        <date>10/10/2004</date>
    </nouvelle>
    <nouvelle>
        <contenu>Confirmant les tendances pessimistes, Monsieur et Madame X viennent de divorcer</contenu>
        <date>10/10/2005</date>
    </nouvelle>
</nouvelles>

Voici le code php servant à le parcourir

 
Sélectionnez

<?php 
$nouvelles = simplexml_load_file('nouvelles.xml'); 
foreach($nouvelles->nouvelle as $nouvelle) {
    echo 'Contenu : ' ,utf8_decode($nouvelle->contenu).'<br>';
    echo 'Date : ' ,$nouvelle->date.'<br>';
}
?>
 
Affichage généré par le script
Contenu : Monsieur et Madame X viennent de se marier! Félicitations
Date : 10/10/2004
Contenu : Confirmant les tendances pessimistes, Monsieur et Madame X viennent de divorcer
Date : 10/10/2005

Interaction avec DOMXML
Il est possible de transférer à SimpleXML un objet DOM préalablement instancié. Cette manipulation se fait comme ceci:

 
Sélectionnez

$xml = new domDocument ;
$xml->load('nouvelles.xml') ;
$simpleXml = simplexml_import_dom($xml);

Utilisation de la méthode xpath
La méthode xpath permet de pointer directement un nœud spécifique.

 
Sélectionnez

<?
$nouvelles = simplexml_load_file('nouvelles.xml'); 
$xpath = '/nouvelles/nouvelle/contenu';
$nouvelle = $nouvelles->xpath($xpath) ;
 
foreach( $nouvelle as $news ) { echo utf8_decode($news);}
?>
Affichage généré par le script
Monsieur et Madame X viennent de se marier! Félicitations
Confirmant les tendances pessimistes, Monsieur et Madame X viennent de divorcer

Exemple avec un fichier RSS
Soit le fichier RSS suivant:

 
Sélectionnez

<?xml version="1.0" encoding="UTF-8"?>
<rss version="0.91">
 <channel>
  <item>
   <title>Utilisation de PHPMailer</title>
   <link>http://stephaneey.developpez.com/tutoriel/php/phpmailer/</link>
   <description>Cet article vous expliquera comment utiliser php mailer...</description>
  </item>
  <item>
   <tilte>Créer son propre composant en FLASH MX2004</tilte>
   <link>http://stephaneey.developpez.com/tutoriel/flashmx2004/composant/</link>
   <description>Cet article vous montre comment créer votre propre composant en Flash MX 
	        et vous en propose un en téléchargement</description>
  </item>
 </channel>
</rss>

Ce fichier RSS reprend deux de mes articles, contient les liens vers ceux-ci et une brève description.
Voici le code php avec SimpleXML nécessaire pour le parser

 
Sélectionnez

<html>
<body>
<?
$xml = simplexml_load_file('http://stephaneey.developpez.com/tutoriel/php/php5_nouveautes/exemple.rss') ;
foreach($xml->channel->item as $item) {
echo '<h1><a href='.utf8_decode($item->link).'>'.utf8_decode($item->title).'</a></h1>'.utf8_decode($item->description);
}
?>
</body>
</html>

Visualisez le résultat ici

Conclusion

Nous avons un peu découvert le potentiel de SimpleXML. Il y a bien sûr d'autres contextes d'utilisation. Je vous laisse les découvrir. Ce qu'il faut retenir de SimpleXML est qu'il stocke dans des tableaux imbriqués tous les nœuds d'un document XML. Cela me fait penser à peu de choses près aux méthodes couramment utilisées dans d'autres langages où des collections de nœuds sont implémentées, avec SimpleXML, ces collections sont des tableaux.

2.3. Le nouveau modèle objet

L'introduction des constructeurs et des destructeurs

Avant PHP5, les constructeurs étaient des fonctions portant le même nom que la classe dans laquelle ils se trouvaient. Désormais, PHP5 implémente des méthodes spécialement conçues pour les constructeurs et les destructeurs.

 
Sélectionnez

<?
 
class dvpClass {
   function __construct() {
       echo 'Constructeur déclenché<br>';
   }
 
   function dvp_exemple(){
	echo 'Exemple Objet<br>';
   }
 
   function __destruct() {
       echo 'Destructeur déclenché';
   }
}
//A l'instanciation de la classe, le constructeur est automatiquement déclenché
$obj = new dvpClass();
//L'appel de cette méthode est le dernier endroit dans le script  l'objet $obj est référencé, après cet
//appel, le destructeur sera automatiquement déclenché.
$obj->dvp_exemple();
?> 
Affichage généré par le script
Constructeur déclenché
Exemple Objet
Destructeur déclenché

La grande nouveauté concerne surtout les destructeurs. Comme vous pouvez le constater, le destructeur est automatiquement déclenché dès lors que plus aucune référence à l'objet instancié n'est trouvée.

Note: Par souci de compatibilité, l'ancienne manière définir un constructeur est toujours possible. A savoir, définir une méthode portant le même nom que la classe.

Lors de l'héritage de classe, le constructeur et le destructeur de la classe mère ne sont pas automatiquement déclenchés. Si vous l'estimez nécessaire, vous devez les déclencher explicitement.

 
Sélectionnez

<?
class dvpClassParent{
   function __construct(){
     echo 'Constructeur père déclenché<br>';
   }	
   function __destruct(){
     echo 'Destructeur père déclenché<br>';
   }
}
 
class dvpClassFille extends dvpClassParent {
   function __construct() {
       parent::__construct();		
       echo 'Constructeur fille déclenché<br>';
   }
 
   function dvp_exemple(){
	echo 'Exemple Objet<br>';
   }
 
   function __destruct() {
       echo 'Destructeur fille déclenché<br>';
       parent::__destruct();			
   }
}
 
$obj = new dvpClassFille();
$obj->dvp_exemple();
 
?> 
Affichage généré par le script
Constructeur père déclenché
Constructeur fille déclenché
Exemple Objet
Destructeur fille déclenché
Desctructeur père déclenché

La portée des membres de classes

En PHP5, le modèle objet est tout à fait conforme aux grands langages classiques de la POO. L'introduction de la portée des membres d'une classe est donc implémentée comme dans tous les langages orientés objets. Nous voyons donc apparaître les mots clés suivants:

  • private: Propriété ou méthode uniquement accessible au sein de la classe où elle a été définie. L'objet instancié ne pourra pas référencer directement cette propriété/méthode
  • protected: Propriété ou méthode uniquement accessible au sein de la classe et au sein des classes héritant de celle-ci. L'objet instancié ne pourra pas référencer directement cette propriété/méthode
  • public: Propriété ou méthode accessible à tous
  • interface: Permet de définir des interfaces
  • implements: Permet d'implémenter une interface préalablement définie
  • abstract: Classe ne pouvant être instanciée. Toute classe contenant une méthode de type abstract doit elle-même être de type abstract
  • Le principe de surcharge est implémenté
  • final: Empêche les classes filles de reimplémenter une méthode de la classe mère
  • static: permet de définir des propriétés/méthodes accessibles en dehors du contexte d'objet
  • La "clonisation" des objets Permet de copier un objet de manière personnalisée si le développeur a défini une méthode __clone() au sein de la classe clonée

Voici deux exemples qui vont vous permettre de bien cerner la portée des propriétés et méthodes (private, public et protected)

 
Sélectionnez

<?
class ExempleMethodes {   
   public $propriete1;
   private $propriete2;
   protected $propriete3;
 
   function __construct(){
    $this->propriete1=1;
    $this->propriete2=2;
    $this->propriete3=3;
   }
 
   public function methode1(){
    echo 'méthode publique';
    $this->propriete2++; //autorisé car référencé au sein de la classe
    $this->propriete3++; //autorisé car référencé au sein de la classe
    $this->methode2(); //autorisé car référencé au sein de la classe
    $this->methode3(); //autorisé car référencé au sein de la classe
   }
   protected function methode2(){
    echo 'méthode protégée';
   }	
   private function methode3(){
    echo 'méthode privée';
   }
 
 
}
 
$obj = new ExempleMethodes();
$obj->methode1(); //autorisé
echo $obj->propriete1; // autorisé
echo $obj->propriete2; // non autorisé
echo $obj->propriete3; // non autorisé
$obj->methode2(); //non autorisé
$obj->methode3(); //non autorisé
 
?> 

Voici un autre exemple permettant de bien cerner la différence entre private et protected.

 
Sélectionnez

<?
class ExempleMethodes {   
   public $propriete1;
   protected $propriete2;
   private $propriete3;
 
   function __construct(){
    $this->propriete1=1;
    $this->propriete2=2;
    $this->propriete3=3;
   }
 
   public function methode1(){
    echo 'méthode publique';
    $this->propriete2++;
    $this->propriete3++;
    $this->methode2();
    $this->methode3();
   }
   protected function methode2(){
    echo 'méthode protégée';
   }	
   private function methode3(){
    echo 'méthode privée';
   }
 
 
}
 
class ExempleFille extends ExempleMethodes{
  function __construct(){
   parent::__construct();	
   echo $this->propriete1; //autorisé
   echo $this->propriete2; //autorisé
   echo $this->propriete3; //non autorisé
   $this->methode1(); //autorisé
   $this->methode2(); //ok car la classe fille hérite des prop. et mét. protégées implémentées dans la classe mère
   $this->methode3(); //non autorisé, private réserve un accès exclusif à la classe contenant la prop/méthode	
  }
}
 
$obj=new ExempleFille();
 
?> 

Note: Par souci de compatibilité avec les versions antérieures, la déclaration explicite de private, public et protected n'est pas obligatoire. Toutes les propriétés et méthodes dont la portée n'est pas déclarée explicitement seront considérées comme publiques.

interface et implements

Une interface est un modèle contenant le prototype des méthodes qui doivent être obligatoirement implémentées par la classe implémentant l'interface. La déclaration des méthodes doit être strictement identique à celle définie dans l'interface.

 
Sélectionnez

<?
interface dvpInterface
{
//Prototypes des méthodes
//Aucune propriété ne peut être définie dans une interface
  public function methode1($p1);
  public function methode2($p1);
}
 
class dvpImplements implements dvpInterface
{
  private $prop;
 
//On DOIT implémenter les méthodes définies au niveau de l'interface
  public function methode1($p1)
  {
   $this->prop = $p1;
  }
 
  public function methode2($p1)
  {
   $this->prop = $p1;
  }
 
//On peut bien sûr définir des méthodes n'étant pas définies dans l'interface
  private function methode3($p1){
   $this->prop = $p1;
  }
}
?> 

abstract

Une classe définie via abstract ne peut-être instanciée. Si une classe contient une méthode de type abstract, la classe doit l'être aussi. Une classe héritant d'une classe abstract doit implémenter les prototypes de méthodes définies au niveau de la classe abstract, ces prototypes étant eux-mêmes préfixés du mot clé abstract. C'est le même principe que pour les interfaces. La différence avec les interfaces est que une classe abstract peut contenir autre chose que des prototypes de méthodes.

 
Sélectionnez

<?
abstract class dvpAbstract{ 
  abstract public function methode1(); //Prototype de méthode
 
  protected function notAbstract(){ //Méthode propre à la classe abstract.
    echo "ceci n'est pas un prototype mais une méthode ordinaire<br>";
  }
}
 
class dvpClass extends dvpAbstract{  
  public function methode1(){
   echo 'Implémentation du prototype hérité de la classe dvpAbstract<br>';
   $this->notAbstract();
  }
 
}
$obj=new dvpClass();
$obj->methode1();
?> 

static

static permet de définir des propriétés et méthodes accessibles en dehors du contexte d'instanciation.

 
Sélectionnez

<?php
class dvpClass {
  public static $propriete = 1;
}
 
echo dvpClass::$propriete; //autorisé, affiche 1
$obj=new dvpClass(); 
echo $obj->propriete; //non autorisé
 
 
?> 

final

final permet de prévenir une classe fille de réimplémenter une méthode définie au niveau de la classe mère. Ce petit exemple est très parlant

 
Sélectionnez

<?
class dvpClass {
 
   public function methode1(){
     echo 'méthode 1';
   }
   final public function methode2() {
       echo 'méthode 2 de type final';
   }
}
 
class dvpClassFille extends dvpClass {
   public function methode1() {
       echo 'tentative de réimplémentation de la méthode methode1';
   }
   public function methode2(){
       echo 'tentative de réimplémentation de la méthode methode2 -> INVALIDE';
   }
}
 
?> 

clone

L'instruction clone vous permet de copier un objet, soit entièrement, soit en personnalisant la copie. Si vous désirez copier un objet dans son intégralité, vous ne devez pas définir de méthode __clone au sein de la classe clonée. Par contre, si vous désirez qu'un comportement spécifique intervienne lors de la copie, vous devez définir une méthode __clone afin que les instructions codées dans cette méthode soient exécutées. Voici un exemple très clair.

 
Sélectionnez

<?
class dvpClass{
 public $propriete;
 
 function __construct($val){
   $this->propriete=$val;
 }
 
 
}
 
$obj=new dvpClass(1); 
echo $obj->propriete;
 
$clone=clone $obj;
echo $clone->propriete;
?>
 
Affichage généré par le script
11
 
Sélectionnez

<?
class dvpClass{
 public $propriete;
 
 function __construct($val){
   $this->propriete=$val;
 }
 function __clone(){
  $this->propriete=2;
 } 
 
 
}
 
$obj=new dvpClass(1); 
echo $obj->propriete;
 
$clone=clone $obj;
echo $clone->propriete;
?>
Affichage généré par le script
12

On voit très clairement que lorsqu'une méthode __clone est définie, les instructions qui y sont contenues sont exécutées lors de l'appel à l'instruction clone permettant de copier un objet.

La gestion des exceptions via la classe Exception

Les célèbres "try" et "catch" sont enfin implémentés en PHP5. Voici un petit exemple d'utilisation

 
Sélectionnez

<?
class dvpExempleException {
 
    public function genererException($probleme) {
 
        if(!is_int($probleme)) {
            throw new Exception ("L'argument -$probleme- n'est pas numérique");
        }
    }
 
}
 
 
$Obj = new dvpExempleException();
 
 
try {
    $Obj->genererException('chaîne au lieu d\'un numérique');
} 
 
catch (Exception $exception) {
    echo $exception->getMessage().','.$exception->getLine().','.$exception->getFile();
}
?>
Affichage généré par le script
L'argument -chaîne au lieu d'un numérique- n'est pas numérique,7,c:\webroot\dvpscript.php

Les méthodes getMessage(), getLine() et getFile() sont des méthodes appartenant à la classe Exception. Elles affichent respectivement le message d'erreur, le numéro de ligne où l'exception est générée (throw) et enfin le nom du script où l'exception s'est produite.

La classe Exception étant assez basique, vous pouvez bien sûr l'étendre avec une de vos propres classes (maclasse extends Exception...). Je vous laisse le soin de l'étudier en détail.

Voilà, nous avons à peu près fait le tour du nouveau modèle objet. Il y a encore quelques autres concepts que je n'aborderai pas dans ce tutoriel car je dois moi-même encore les approfondir.