• PHP > Les classes

      https://apprendre-php.com/tutoriels.html

       

       

      Les exceptions - 1ère partie

      Le mécanisme des exceptions a été introduit à PHP dans sa version 5 en complément de son nouveau modèle orienté objet. Au même titre qu’en Java, C++, Action Script 3 ou bien Visual Basic (pour ne citer que ces langages de programmation), les exceptions permettent de simplifier, personnaliser et d’organiser la gestion des « erreurs » dans un programme informatique. Ici le mot « erreurs » ne signifie pas « bug », qui est un comportement anormal de l’application développée, mais plutôt « cas exceptionnel » à traiter différemment dans le déroulement du programme. Etudions donc comment fonctionnent les exceptions.

      Nous introduirons dans un premier temps la classe native Exception de PHP 5. Puis nous étudierons comment lancer et attraper des exceptions dans un programme. A partir de là, nous serons capables d’étendre le modèle Exception pour développer des exceptions dérivées et de types différents. Enfin, nous aborderons la notion de gestion évènementielle des exceptions au moyen du handler d’exception natif de php.

      La classe native Exception

      Comme nous l’avons déjà expliqué dans les précédents tutoriels du chapitre de programmation orientée objet, PHP dispose depuis sa version 5 d’un modèle objet semblable à celui de Java. Pour agrémenter ce nouveau moteur, l’équipe de développement de PHP a intégré en natif une classe Exception. Cette classe très particulière permet au développeur de simplifier le traitement des cas exceptionnels susceptibles d’apparaître pendant l’exécution d’un programme en générant des objets de type Exception. En fin de compte, retenez qu’une exception n’est rien de plus qu’un objet (instance d’une classe). Le listing ci-après présente le code de la classe Exception.

      <?php
      class Exception
      {
        protected $message = 'exception inconnu'; // message de l'exception
       protected $code = 0;                      // code de l'exception défini par l'utilisateur
        protected $file;                          // nom du fichier source de l'exception
        protected $line;                          // ligne de la source de l'exception
        function __construct(string $message=NULL, int code=0);
        final function getMessage();              // message de l'exception
       final function getCode();                 // code de l'exception
       final function getFile();                 // nom du fichier source
        final function getLine();                 // ligne du fichier source
        final function getTrace();                // un tableau de backtrace()
        final function getTraceAsString();        // chaîne formattée de trace
        /* Remplacable */
        function __toString();                    // chaîne formatée pour l'affichage
      }
      ?>

      Nous identifions ici très clairement 4 parties distinctes qui composent cette classe Exception. Le premier bloc de code déclare les 4 données membres (attributs) de la classe en visibilité protégée. Respectivement, ces attributs enregistrent le message d’erreur, son code, le fichier source concerné et la ligne à laquelle l’erreur a été générée dans le programme.

      Puis vient la déclaration du constructeur de la classe qui prend 2 paramètres facultatifs. Le premier est le message d’erreur et le second le code d’erreur.

      La troisième partie du corps de la classe concerne la déclaration de 6 méthodes accesseur qu’il est impossible de redéfinir par héritage. Cette restriction est représentée par le mot-clé final. Ces méthodes permettent respectivement de récupérer le message d’erreur, son code d’erreur, le fichier source concerné, la ligne de l’erreur dans le fichier, et la pile des appels.

      Enfin, nous découvrons la déclaration de la méthode magique __toString() qui pourra être rédéfinie ensuite par héritage. Cette méthode particulière, lorsqu’elle est surchargée, permet de spécifier de quelle manière on décide de représenter l’état en cours de l’objet sous forme de chaine de caractères.

      Générer, lancer et attraper des exceptions à travers le programme

      Générer une exception

      La création d’une exception est réalisée par l’appel au constructeur de la classe native Exception. Le code ci-dessous illustre cette étape.

      <?php
      // Création de l'objet Exception
      $e = new Exception('Une erreur s\'est produite');
      // Affiche le message d'erreur
      echo $e->getMessage();
      ?>

      Remarquez la simplicité. La première ligne créer l’objet de type Exception ($e) et assigne automatiquement le message d’erreur dans le constructeur. La seconde ligne de code affiche le message d’erreur enregistré sur la sortie standard.

      Note [1] : en développement informatique, les programmeurs ont pris l’habitude de nommer une exception uniquement avec la lettre miniscule "e". C’est à la fois une convention de nommage et une bonne pratique très largement répandue. Toutefois, aucune règle n’oblige les développeurs à l’adopter.

      Note [2] : la classe native Exception est chargée automatiquement par PHP, c’est pourquoi il n’est pas nécessaire d’avoir recours à un quelconque import avant de pouvoir l’utiliser.

      Lancer une exception à travers le programme

      Notre code précédent nous a permis de générer des exceptions. En l’état, ce script ne nous sert strictement à rien puisqu’une exception n’est utile que si elle est créée lorsqu’un évènement exceptionnel se déroule pendant l’exécution du programme. Par exemple : une requête SQL qui échoue, un fichier impossible à ouvrir, une valeur d’un formulaire inattendue pour un champ…

      Lorsqu’un tel évènement se produit, c’est que quelque chose d’inhabituel s’est passé. Par conséquent, la poursuite de l’exécution du programme doit être interrompue et signaler l’incident. C’est là qu’interviennent réllement les exceptions. Pour réaliser cette opération, le programme doit automatiquement « lancer » (retenez le vocabulaire de la POO) une exception. Le lancement d’une exception provoque immédiatement l’interruption du déroulement normal du programme

      Le lancement d’une exception à travers le programme est réalisée grâce au mot-clé « throw ».

      Exemple de lancement d'une Exception à travers le programme
      <?php
      $password = 'Toto';
      if('Emacs' !== $password) {
      throw new Exception('Votre password est incorrect !');
      }
      // Cette ligne ne sera jamais exécutée
      // car une exception est lancée pour interrompre
      // l'exécution normale du programme
      echo 'Bonjour Emacs';
      ?>

      L’utilisation du mot-clé throw permet de stopper l’exécution du programme et rediriger l’exception à travers ce dernier. La commande echo() quant à elle ne sera jamais exécutée. Le mot de passe $password n’étant pas égal à la chaine ‘Emacs’, l’exception est automatiquement générée puis lancée.

      Note [1] : remarquez que l’exception généréese fait à la volée. Du fait qu’elle est automatiquement renvoyée auprogramme, nul besoin de stocker l’objet créé dans une variable.

      Note [2] : les exceptions peuvent également être lancées depuis l’intérieur d’une classe.

      Cet exemple est encore loin d’être exploitable en l’état. En effet, l’exception qui est lancée au programme est ici perdue pour toujours. Comme si l’on avait oublié de stocker dans une variable le résultat retourné par une fonction. Nous avons déclaré au tout début de ce cours que les exceptions devaient permettre de traiter différemment les cas exceptionnels survenant au cours d’un programme. Pour cela, il est nécessaire de pouvoir « intercepter / attraper » l’exception générée pour appliquer le traitement adéquat. C’est là qu’intervient le bloc try / catch.

      Intercepter / attraper une exception générée

      Comme en Java, AS 3, VB, C++… PHP dispose d’une structure conditionnelle capable d’intercepter les exceptions en plein vol afin de permettre d’appliquer des traitements particuliers. Il s’agit donc des blocs try { } catch() { }. Cette structure particulière se présente de la manière suivante :

      Structure conditionnelle try { } catch() { }
      <?php
      try {
      // Liste d'actions à appeller
      // Ces actions peuvent potentiellement
      // lancer des exceptions à travers le programme
      }
      catch(Exception $e)
      {
      // Bloc des actions spéciales lorsqu'une
      // exception $e de type Exception est levée
      }
      ?>

      Le bloc try « essaie » d’exécuter le script entre les deux premières accollades. Si une exception est lancée dans ce bloc, elle est immédiatement « attraper » dans le bloc catch() et les traitements particuliers sont exécutés à la place.

      Concrètement cela donne le listing ci-après avec notre exemple précédent :

      Exemple d'interception d'une exception
      <?php
      try {
      $password = 'Toto';
      if('Emacs' !== $password) {
      throw new Exception('Votre password est incorrect !');
      }
      echo 'Bonjour Emacs';
      }
      catch(Exception $e)
      {
      echo 'L\'erreur suivante a été générée : '."\n";
      echo $e->getMessage();
      }
      ?>

      En exécutant ce code, nous découvrons que c’est le code du bloc catch() qui a été exécuté car l’exception lancée dans le bloc try { } a été interceptée en plein vol.

      Note [1] : lorsqu’une exception est levée, elle remonte dans le programme et est interceptée par le premier bloc try {} catch() {} englobant qu’elle rencontre. Si aucun bloc try {} catch() {} n’entoure l’exception, alors l’objet sera perdu et l’exécution du programme interrompue.

      Note [2] : nous verrons plus loin dans ce tutoriel que nous pouvons appliquer plusieurs bloc catch() sous le bloc try {} afin d’identifier chaque type d’exception potentiellement générée.

      Bien sûr, il est possible d’imbriquer les blocs try { } catch() { } comme il est naturellement possible d’imbriquer les blocs if(). Notre exemple peut-être amélioré de la manière suivante :

      Exemple d'utilisation de bloc try { } catch() { } imbriqués
      <?php
      $login = 'Titi';
      $password = 'Toto';
      try {
      if('Hello' !== $login) {
      throw new Exception('Votre login est incorrect !');
      }
      try {
      if('Emacs' !== $password) {
      throw new Exception('Votre password est incorrect !');
      }
      echo 'Bonjour Emacs';
      }
      catch(Exception $e)
      {
      echo 'L\'erreur suivante a été générée : '."\n";
      echo $e->getMessage();
      }
      }
      catch(Exception $e)
      {
      echo 'L\'erreur suivante a été générée : '."\n";
      echo $e->getMessage();
      }
      ?>

      Dans cet exemple, la valeur du login est d’abord testée. Comme la valeur du login est incorrecte, une exception est lancée à travers le programme, et c’est le bloc catch() le plus bas qui l’intercepte et exécute le traitement associé. Si maintenant nous remplissons correctement la variable $login, alors le premier bloc try est passé puis l’exception du second bloc try est lancée. Cette dernière est alors interceptée dans le bloc catch() associé pour exécuter le traitement adéquat de l’erreur.

      Cet exemple n’est cependant pas très pertinent car il ne profite pas véritablement de toute la puissance des exceptions. La seconde partie de ce cours montre pas à pas comment concevoir des exceptions typées et personnalisées, et comment profiter de celles-ci pour simplifier et optimiser le code.

      Introduction à la seconde partie de ce tutoriel

      Dans la suite de ce cours, nous étudierons comment rendre l’utilisation des exceptions pertinentes en créant nos propres classes d’exception personnalisées. Nous aurons recours au concept de l’héritage, pilier majeur dans la méthodologie de l’approche orientée objet. Enfin, nous présenterons un aspect pratique de PHP qui permet d’intercepter toutes les exceptions à volée et d’exécuter automatiquement une fonction de callback (de rappel) pour simplifier et centraliser leur traitement.



      Les commentaires

      1. Par BLANCHON Vincent le 17/06/2008 14:06

      "[...]‘objet sera perdu mais et l’exécution du programme interrompue.[...]" un "mais" qui traine … Très sympa les cours il en faudrait un sur les méthodes magiques

      2. Par Emacs le 17/06/2008 19:30

      Encore merci à toi pour ces corrections

      3. Par Emacs le 17/06/2008 23:03

      Concernant les méthodes magiques, c’est prévu mais pour le moment je n’ai plus le temps d’écrire pour Apprendre-PHP.com car je travaille sur un projet très intéressant de PHP dont je parlerai très prochainement dans une actualité. Wait and see

      4. Par Valentin le 27/10/2008 23:33

      Bonjour,

      Je parcours plusieurs des tutos proposés sur ce site et souhaite simplement vous félicitez pour la clarté de vos explications et exemples.
      Les exceptions étaient pour moi un morceau difficile, tout est maintenant beaucoup plus clair!

      Bonne continuation

      5. Par saturn1 le 14/12/2008 18:45

      C’est intéressant.
      Mais concrètement cela sert à la même chose que if/else ^ _ ^

      6. Par Emacs le 14/12/2008 19:41

      Non pas du tout ! Relis bien le tutoriel pour comprendre ce que sont véritablement les exceptions.

      7. Par forever le 16/05/2009 23:47

      je suis un pro du c# et je trouve que php n’est pas très au point à part le fait d’être un peu facile, néanmoins ce tuto m’a beaucoup facilité le transfert des compétences
      merci pour ce très bon tuto

      8. Par Emacs le 17/05/2009 01:05

      @forever : PHP est un langage très professionnel bien qu’il ne soit pas encore parfait. Quand tu affirmes qu’il n’est pas au point, j’ai du mal à saisir ce que tu insinues. En quoi n’est-il pas au point ? Performances ? Modèle objet ? API ? syntaxe ?

      PHP est un langage tout aussi professionnel que Java ou autres depuis sa version 5 qui a introduit un modèle objet presque complet. Quand on pense que PHP n’est pas suffisamment professionnel ou "au point" comme tu le cites, c’est aussi que l’on ne connaît pas suffisamment le langage et ses subtilités. Je ne connais pas du tout C#, je ne peux donc juger de son niveau et de la réponse qu’il fournit aux besoins des développeurs. Mais aujourd’hui, PHP est un très bon langage performant, mais il toutefois très difficile de trouver de très bons développeurs PHP car PHP jouit encore de sa mauvaise image de langage accessible à tous, contrairement au Java et autres dérivés du C.

      Ces langages sont en effet accessibles à des développeurs ayant déjà été formés à la programmation au cours de leur formation post bac. C’est plutôt rare des développeurs Java, C++, C#… qui sont autodidactes et qui n’ont jamais eu de formation en programmation.

      Au contraire, avec PHP, on trouve très facilement des jeunes d’une quinzaine d’années qui s’y mettent car la syntaxe leur est accessible, bien qu’ils n’aient pas les concepts fondamentaux de programmation et d’algorithmique qui vont avec.

      A l’opposé, on trouve aussi quelques rares excellents développeurs PHP qui ont plusieurs années d’expériences avec le langage PHP (et d’autres bien sûr). Ils en connaissent les spécificités, les astuces, les librairies qui ont fait leurs preuves (PHPDoc, PHPUnit, Propel, Doctrine, phpUnderControl…), ainsi que les frameworks professionnels (Zend Framework, Symfony et autres).

      9. Par forever le 18/05/2009 12:13

      @Emacs : je parlais de la POO dans le PHP; j’ai toujours l’impression de travailler avec un langage procédural comme le vb6, le soucis de compatibilité ascendant limite trop ce PHP à typage faible, et c’est peut être ça qui a fait sa popularité.
      Si on travaille avec le ASP.net on est dans un environement très bien organisé avec des namespace et ce qui va avec, ou même JAVA avec ses classes et librairies à importer , mais le PHP me rappel trop le C avec ses tas de fonctions natives éparpillées.
      tout de même il faut avouer que la programmation WEB devraient être plus accessible, et aussi avancé pour répondre au besoin d’entreprise .. ce qui est la nouvelle tendance avec le PHP5 et le 6 en cours de développement afin de palier ce défaut.
      merci pour tes explications, et encore merci pour ce merveilleux cours ; il me reste plus que quelques pages pour me lancer dans des testes plus avancés et ce qui dit reste l’avis d’un débuteur en PHP !

      10. Par eilijah le 20/06/2009 09:39

      @forever : Rien ne t’empeche de programmer en typage fort avec php, par exemple :
      <?php
      class Foo {}
      function useFoo(Foo $obj) { /* … */ }
      $a = new Foo();
      useFoo($a); // OK…
      $b = new StdClass();
      useFoo($b);
      // Fatal error: Argument 1 must be an instance of Foo
      ?>

      @Emacs : "Comme elle est fausse, l’exception est générée est le bloc catch()"
      Y’a pas une faute qui te dérange là?

      Excelents tuto par ailleurs

      11. Par Emacs le 20/06/2009 15:35

      @elijah : merci c’est corrigé

      Les exceptions - 2ème partie

      • Par Emacs
      • 8 commentaires
      • 15544 lectures
      • RSSAtom

      La première partie de ce tutoriel a été l’occasion de présenter le mécanisme des exceptions de manière très théorique. Au travers d’exemples simples et concrets, nous avons découvert comment générer, lancer et attraper des exceptions en plein vol. A ce stade, nous sommes encore loin de pouvoir profiter pleinement des exceptions dans des applications plus conséquente. C’est pourquoi cette seconde et dernière partie s’intéressera à la manière de dériver la classe Exception pour créer des exceptions personnalisées. Enfin, nous étudierons un mécanisme natif de PHP qui permet de centraliser et d’unifier le traitement des exceptions non capturées dans une fonction de callback appelée automatiquement par l’exception handler.

      Dériver la classe Exception et créer des exceptions personnalisées

      Principe de création de classes personnalisées

      Cette partie du tutoriel est sans aucun doute la plus triviale. Elleexplique comment dériver la classe Exception (via le concept del’héritage de programmation orientée objet) afin de générer ensuite desexceptions personnalisées. En effet, comme une classe = un type,il est alors possible de créer de nouvelles classes (donc denouveaux types) qui héritent des propriétés et méthodes de la classeException.

      Quel sont les avantages à créer des classes dérivées ? La raison estsimple. Avoir plusieurs types d’exceptions permet tout d’abord de lesdistinguer plus facilement. En distinguant chaque type d’erreur, on peut appliquer des traitements appropriés différents.Enfin, la dérivation de la classe Exception permet au développeur desurchager l’objet en lui ajoutant des propriétés et méthodessupplémentaires. Le code ci-dessous présente la manière la plus simplede développer une nouvelle classe d’exception personnalisée.

      Principe de création d'une classe dérivée de Exception
      <?php
      /**
      * Fichier MyChildException.class.php
      */
      class MyChildException extends Exception
      {
      public function __construct($message=NULL, $code=0)
      {
      parent::__construct($message, $code);
      }
      }
      ?>

      Tout d’abord le nom de la nouvelle classe est déclarée (MyChildException) ainsi que la classe mère (Exception) qu’elle dérive et dont elle hérite les propriétés et méthodes. C’est grâce au mot-clé extends que l’héritage a lieu.

      Puis nous redéfinissons la méthode constructeur de la classe parente Exception. Lorsque le constructeur de la classe MySuperException sera appelé, il appellera automatiquement le constructeur de la classe parente. Cela aura pour effet de créer un objet de type MySuperException mais aussi de type Exception par héritage.

      Lorsque l’on redéfinit le constructeur parent, il faut donc bienévidemment penser à récupérer au moins ses paramètres pour lesréinjecter dans le constructeur fils.

      Exemples de classes d’exceptions personnalisées

      Développons maintenant à titre d’exemple deux classes personnaliséespour gérer les exceptions liées aux fichiers. La première classe permetde contrôler l’inexistence d’un fichier sur un serveur tandis que laseconde permettra de générer des erreurs indiquant que le fichier n’estpas accessible en écriture. Ajoutons en plus à chacune de ces deuxclasses, un nouvel attribut stockant la date à laquelle s’est produitel’erreur et un accesseur pour récupérer cette valeur. Nous obtenonsdonc :

      Classes FileNotFoundException et FileNotWriteableException
      <?php
      /**
      * Fichier FileNotFoundException.class.php
      */
      class FileNotFoundException extends Exception
      {
      protected $timestamp;
      public function __construct($message=NULL, $code=0)
      {
      parent::__construct($message, $code);
      $this->timestamp = time();
      }
      public function getTimestamp() {
      return $this->timestamp;
      }
      }
      /**
      * Fichier FileNotWriteableException.class.php
      */
      class FileNotWriteableException extends Exception
      {
      protected $timestamp;
      public function __construct($message=NULL, $code=0)
      {
      parent::__construct($message, $code);
      $this->timestamp = time();
      }
      public function getTimestamp() {
      return $this->timestamp;
      }
      }
      ?>

      Profitons tout de suite de l’héritage pour faire dériver ces deux classes de la même classe FileExceptionet qui contiendra l’attribut et la méthode commune. Cette nouvelleclasse, quant à elle, dérivera bien entendu la classe native Exception.

      Classes de gestion des erreurs liées au fichiers
      <?php
      /**
      * Fichier FileException.class.php
      */
      class FileException extends Exception
      {
      protected $timestamp;
      public function __construct($message=NULL, $code=0)
      {
      parent::__construct($message, $code);
      $this->timestamp = time();
      }
      public function getTimestamp() {
      return $this->timestamp;
      }
      }
      /**
      * Fichier FileNotFoundException.class.php
      */
      class FileNotFoundException extends FileException
      {
      public function __construct($message=NULL, $code=0)
      {
      parent::__construct($message, $code);
      $this->timestamp = time();
      }
      }
      /**
      * Fichier FileNotWriteableException.class.php
      */
      class FileNotWriteableException extends FileException
      {
      public function __construct($message=NULL, $code=0)
      {
      parent::__construct($message, $code);
      $this->timestamp = time();
      }
      }
      ?>

      L’exemple qui suit montre comment récupérer chaque type d’exceptionavec plusieurs blocs catch() associés au même bloc try {}. Grâce à cesblocs catch() en ligne et les différents types d’exceptions, il estalors très simple de reconnaître les erreurs levées et donc exécuterles traitements les plus adaptés.

      Exemple d'utilisation des 3 classes de gestion des erreurs de fichier
      <?php
      // Import des 3 classes précédentes
      require_once(dirname(__FILE__).'/FileException.class.php');
      require_once(dirname(__FILE__).'/FileNotFoundException.class.php');
      require_once(dirname(__FILE__).'/FileNotWriteableException.class.php');
      // Variables
      $fichier = '/var/www/projet/toto.txt';
      try
      {
      // Le fichier existe-t-il ?
      if(!file_exists($fichier)) {
      throw new FileNotFoundException('Le fichier '. $fichier .' est inexistant');
      }
      // Le fichier est-il inscriptible ?
      if(!is_writeable($fichier)) {
      throw new FileNotWriteableException('Le fichier '. $fichier .' n\'a pas les droits d\'écriture');
      }
      // A-t-on ouvert le fichier en mode écriture ?
      if(!($fp = @fopen($fichier,'w'))) {
      throw new FileException('L\'ouverture du fichier '. $fichier .' a échoué');
      }
      // J'écris dans mon fichier
      fwrite($fp, "Coucou Emacs\n");
      // Puis je ferme mon fichier
      fclose($fp);
      }
      catch(FileNotFoundException $e)
      {
      // Je crée le fichier
      }
      catch(FileNotWriteableException $e)
      {
      // Je change les droits du fichier
      }
      catch(FileException $e)
      {
      // Je stoppe tout
      exit($e->getMessage());
      }
      catch(Exception $e)
      {
      // Je stoppe tout
      exit($e->getMessage());
      }
      ?>

      Vous remarquez ici les quatre blocs catch() en ligne permettantd’intercepter les trois types d’exceptions potentiellement jetablesdepuis le bloc try(). En précisant le type de l’exception interceptéedans le bloc catch(), nous savons à quel genre d’erreur nous avonsaffaire. N’est-ce pas plus clair et maintenable à présent ?

      Note : il est important de conserverl’ordre des blocs catch(), c’est-à-dire de mettre en premier le bloccatch() indiquant l’erreur la plus précise (donc la fille la plus bassepar héritage). De ce fait, la classe Exception, la plus générale, doitarriver dans le dernier bloc catch(). Si le bloc catch() du typeException avait été placé avant les autres, alors toutes les exceptions(tout type confondu) auraient toujours été levées dans celui-ci. Eneffet, une exception de type FileNotFoundException est aussi parhéritage une exception de type FileException et donc aussi uneexception de type Exception.

      Centraliser le traitement des erreurs non capturées

      Présentation du mécanisme d’interception automatique des exceptions

      Jusqu’à maintenant nous savons que le moyen le plus simple de capturer une exception lancée est d’avoir recours à des blocs try { } catch() { }. Rappelez-vous le tout premier exemple de la 1ère partie du tutoriel, l’exception générée n’était pas capturable donc elle était forcément perdue à jamais.

      Exemple de lancement d'une Exception à travers le programme
      <?php
      $password = 'Toto';
      if('Emacs' !== $password) {
      throw new Exception('Votre password est incorrect !');
      }
      // Cette ligne ne sera jamais exécutée
      // car une exception est lancée pour interrompre
      // l'exécution normale du programme
      echo 'Bonjour Emacs';
      ?>

      Heureusement PHP dispose d’un mécanisme qui permet de capturer automatiquement toutes les exceptions qui sont lancées mais qui ne sont pas entourées de blocs try {} catch() {} comme c’est le cas dans le listing ci-dessus. Il s’agit de l’exception handler.

      L’exception handler, lorsqu’il intercepte une exception, interrompt complètement l’exécution du programme et appelle une fonction personnalisée de callback qui se chargera du traitement adéquat de ces exceptions perdues. Le code ci-après illustre sa mise en place.

      Mise en place du mécanisme d'interception automatique des exceptions
      <?php
      /**
      * Fonction de rappel appellée automatiquement par l'exception handler
      *
      * @param Exception $e une exception lancée et perdue dans le programme
      * @return void
      */
      function traitementExceptionPerdue(Exception $e) {
      echo 'Une exception orpheline a été attrapée : ';
      echo $e->getMessage(), "\n";
      exit;
      }
      /**
      * Enregistrement de la fonction de rappel dans l'exception handler de PHP
      */
      set_exception_handler('traitementExceptionPerdue');
      // Exemple de génération d'exception perdu
      $password = 'Toto';
      if('Emacs' !== $password) {
      throw new Exception('Votre password est incorrect !');
      }
      // Cette ligne ne sera jamais exécutée
      // car une exception est lancée pour interrompre
      // l'exécution normale du programme
      echo 'Bonjour Emacs';
      ?>

      Quelques implications s’imposent car le principe n’est pas si simple à assimiler. Dans un premier temps, nous déclarons la fonction de rappel qui personnalisera le traitement des exceptions orphelines interceptées. C’est une fonction utilisateur qui prend un seul et unique paramètre. Ce paramètre n’est autre qu’un objet de type Exception (ou type dérivé d’Exception). Le corps de la fonction effectue les traitement particuliers pour les exceptions orphelines. Ici nous affichons simplement un message d’erreur, suivi du message de l’exception. Puis nous stoppons strictement toute la suite du programme PHP. Nous aurions pu par exemple redirigé automatiquement l’utilisateur vers une page d’erreur de type 500 en utilisant la fonction header().

      Note : en PHP 5, tous les objets sont automatiquement passés par référence (pointeur) en paramètre de fonction. Il n’est donc pas nécessaire de les préfixer du symbole & dans la liste des paramètres de la signature.

      La seconde partie de ce listing concerne l’enregistrement du nom de cette fonction comme gestionnaire d’exception par défaut à appeller automatiquement à la capture d’une exception orpheline. Il suffit simplement d’appeller la fonction set_exception_handler() de PHP, et de lui indiquer en paramètre le nom de la fonction traitementExceptionPerdue(). PHP prend ensuite le relais à la place du développeur.

      Note : PHP interprête le code PHP en le lisant de haut en bas, c’est pourquoi il faut toujours déclarer la fonction de rappel avant d’appeller la fonction set_exception_handler().

      Enfin la dernière partie reprend l’exemple de code qui génère une exception non capturable. Au moment où l’exception est levée, PHP appelle automatiquement la fonction de rappel en lui passant en paramètre l’exception orpheline qui vient d’être générée. Le résultat ci-après est obtenu sur la sortie standard :

      Une exception orpheline a été attrapée : Votre password est incorrect !

      Le mécanisme, bien que peu facile à assimiler de prime abord, se révèle très simple à mettre en place et efficace pour gérer les cas exceptionnels inattendus. Grâce à cet outil, les développeurs peuvent écrire une fonction complète pour faciliter le debugging en affichant par exemple tout le contexte d’éxécution du programme : variables globales, fichiers concernés, pile des appels de fonctions…

      Effets de bord néfastes avec set_exception_handler

      Attention, l’utilisation de set_exception_handler() doit être utilisé avec prudence car elle peut entraîner des effets de bords particulièrement gênants si elle est mal maîtrisée. En effet, si le corps de la fonction de rappel fait appel à d’autres fonctions ou méthodes susceptibles de lancer des exceptions, le gestionnaire d’exception sera solllicité en boucle. C’est la boucle infinie !!! Il faut donc s’assurer que le corps de la fonction de callback n’exécute pas de traitements susceptibles de générer des exceptions non maîtrisées.

      Inconvénients et limitation des exceptions en PHP

      L’utilisation des exceptions apporte un intérêt non négligeable au développeur lorsqu’il développe son application. Nous avons compris tout au long de ce tutoriel que ce mécanisme permettait d’identifier clairement les types d’erreurs générées dans le but de les traiter spécifiquement et efficacement. Néanmoins, nous pouvons relever quelques limitations au sujet des exceptions :

      • PHP 5 n’exploite pas en natif les exceptions. C’est-à-dire que les fonctions PHP continuent de générer des erreurs et non des exceptions. De ce fait, le développeur est obligé de gérer à la fois les exceptions et les erreurs PHP dans ses procédures de debugging.
      • Lorsqu’une exception est soulevée, le contexte local contenu dans un bloc try est sauvegardé, ceci peut être amplifié par l’imbrication de bloc try. L’abus de l’utilisation des exceptions peut donc diminuer considérablement les performances. (extrait du tutoriel des exceptions sur le site Developpez.com)
      • Par choix. La plupart des langages choisissent de ne pas supprimer la gestion des erreurs au profit des exceptions pour des raisons de performances et de style de programmation (les exceptions sont équivalentes pour certains programmateurs à l’affreux goto, de plus les exceptions augmentent considérablement le nombre de lignes de code). D’autres langages, comme l’ADA, n’utilisent que les exceptions. (extrait du tutoriel des exceptions sur le site Developpez.com)

      Conclusion

      Nous arrivons à l’issue de ce tutoriel concernant la manipulation des exceptions. Depuis le début, nous avons appris à générer une exception, la lancer à travers le programme puis l’intercepter dans un bloc try {} catch() {} en vue d’un traitement spécifique. Puis nous nous sommes intéressés à la création d’exceptions personnalisées dans le but de faciliter la compréhension du code et l’identification des types d’erreurs générées dans le programme. Enfin nous nous sommes arrêtés sur le mécanisme d’interception automatique d’exception orpheline intégré nativement dans le langage PHP. Nous sommes donc parés pour développer des applications orientées objets capables de générer et de traiter avec adéquation les cas exceptionnels d’erreur.



      Les commentaires

      1. Par Michael le 31/03/2008 17:57

      Article presque parfait, à l’exception (c’est le cas de le dire) du fait que cela ne fonctionne pas

      parent::__construct($message=NULL,$code=0);
      // va litérallement "ecraser" le message à null et le code à 0.

      Bonne syntaxe : parent::__construct($message,$code);

      Cela vous arrive de tester vos codes ?

      2. Par Emacs le 01/04/2008 00:17

      Oups c’est exact… Au temps pour moi ! Merci Michaël pour la correction. Il s’agissait en fait de vilains copiés / collés.

      Je n’ai pas testé ces codes car ce sont des codes que je maîtrise (d’habitude…. lol). J’aurais du me relire davantage

      Encore merci pour ta remarque

      Hugo.

      3. Par Julien Pellegrain le 02/05/2008 08:05

      J’ajouterais que le code n’est pas du tout optimisé.
      Tu écris "Profitons de l’héritage" pour créer 2 classes filles à ta classe FileException mais tu réécris à l’identique les constructeurs ! Cela ne sert à rien…

      Un simple
      class MaClasse extends FileException {} suffit

      4. Par Emacs le 02/05/2008 09:13

      @Julien : Oui je sais que le constructeur parent d’une classe fille est appelé implicitement et qu’il n’est pas nécessaire de l’écrire explicitement. Il est nécessaire quand on veut surcharger le constructeur fils. Ici, c’est ce que j’ai fait si tu remarques bien. J’ai ajouté un attribut $timestamp dans lequel je stocke la date à laquelle a été levée l’exception. Je suis donc obligé à ce moment là d’appeler explicitement le constructeur parent.

      Enfin, je dirai qu’appeler ou non explicitement le constructeur parent lorsque l’on dérive une classe est une question de goût et de bonnes pratiques. En Java, me semble-t-il, l’appelle au mot-clé super() est obligatoire pour dériver. Donc avec PHP je fais pareil. J’appelle le constructeur parent explicitement. Mais je suis d’accord avec toi que ce n’est pas forcément nécessaire

      5. Par mRk le 31/07/2008 16:41

      "Exxemple d’utilisation des 3 classes de gestion des erreurs de fichier"

      Un petit ‘x’ de trop dans ‘exemple’.

      6. Par Emacs le 01/08/2008 13:01

      Corrigé ! Merci mRk

      7. Par Clebert le 05/09/2008 12:12

      Très bon tutorial, j’ai bien compris le système. Par contre, il est dommage de ne pas pousser les choses plus loin, en illustrant avec une classe de Log par exemple. C’est ce que je suis en train de faire et j’avoue galérer légèrement …

      8. Par krifur le 22/01/2009 17:10

      Super site, super infos, super tutos, super super.
      Petite coquille je pense
      "Quelques implications s’imposent (…)"
      =>
      "Quelques explications s’imposent (…)"

      Méthodes magiques : __sleep() et __wakeup()

      • Par Palleas
      • 6 commentaires
      • 14512 lectures
      • RSSAtom

      Nous avons étudié dans les précédents articles les méthodes magiques__clone(), __set(), __get() et __call(). PHP ajoute à ces dernières deux méthodes magiques supplémentaires __sleep() et __wakeup() qui permettent de surcharger le processus natif de sérialisation et de désérialisation des données de PHP. C’est ce que nous allons expliquer au cours de tutoriel avec quelques exemples concrets et faciles à comprendre.

      Qu’est ce que la sérialisation de données ?

      Pour résumer très simplement, l’action de sérialiser une variable consiste à convertir celle-ci en une chaine de caractère pour, par exemple, la stocker dans un fichier texte. A contrario, l’action de désérialiser une chaine de caractères consiste à appliquer le procédé inverse afin d’en récupérer la variable d’origine. Ce procédé ne se limite pas à PHP, vous le retrouverez dans beaucoup de langages tels que Java, Action Script, C, C#… pour ne citer qu’eux.

      Les mots sérialisation et désérialisation sont sans doute les mots les plus courants mais vous entendrez / lirez peut-être quelque part les mots linéarisation / délinéarisation ou bien encore marshalling / unmarshalling. Sérialiser, linéariser, marshaller (respectivement désérialiser, délinéariser et unmarshaller) définissent les notions de sérialisation et désérialisation.

      Syntaxiquement parlant, la serialisation de données se traduit par l’utilisation de la fonction serialize(), l’action inverse se traduit par unserialize(). Voyons quelques exemples.

      Serialisation / Désérialisation d’une variable de type integer

      <?php
      $iVar = 1;
      $sSerialized = serialize($iVar);
      $iUnserialized = unserialize($sSerialized);
      echo '<pre>';
      var_dump($sSerialized,$iUnserialized);
      echo '</pre>';
      ?>

      Le premier var_dump() affiche la chaine suivante sur la sortie standard :

      string(4) "i:1;"

      Et le second affiche la suivante :

      int(1)

      Vous déduirez bien sûr que ce n’est pas intéressant de serializer un integer (ou bien une chaîne de caractères évidemment) étant donné que vous pouvez le stocker directement. Néanmoins, cet exemple reste utile à l’illustration d’un principe simple : la serialisation d’une variable préserve le type de celle-ci. Ainsi on aura bien au retour un integer, au même titre que l’on conservera un Array ou encore un objet.

      Sérialisation / désérialisation d’un tableau (Array)

      Prenons à présent un tableau comme exemple. La sérialisation et la désérialisation deviennent ici beaucoup plus intéressantes dans la mesure où elles vont nous permettre de transformer (et de récupérer) l’intégralité d’un tableau.

      <?php
      $aTableau = array('Riri','Fifi','Loulou','Donald','Picsou');
      $sSerialized = serialize($aTableau);
      $aUnserialized = unserialize($sSerialized);
      echo '<pre>';
      var_dump($sSerialized,$aUnserialized);
      echo '</pre>';
      ?>

      Le premier var_dump affiche alors :

      string(87) "a:5:{i:0;s:4:"Riri";i:1;s:4:"Fifi";i:2;s:6:"Loulou";i:3;s:6:"Donald";i:4;s:6:"Picsou";}"

      La sérialisation du tableau finalement construit une chaîne de caractères d’une longueur de 87 caractères. Etudions rapidement la structure de cette chaine.

      • Le premier caractères de la chaine indique le type de la variable sérialisée. Ici, la lettre "a" indique donc que nous venons de sérialiser un tableau (array).
      • Puis, nous lisons le chiffre 5 qui indique le nombre d’éléments dans le tableau. Entre les deux parenthèses, nous trouvons tous les éléments du tableau.
      • La première le lettre d’un groupe élément indique le type de l’index dans le tableau suivi de sa valeur. Ici nous avons sérialisé un tableau indexé numériquement, c’est pourquoi chaque index est un entier ("i") dont la valeur est comprise entre 0 et 4 compris. Si nous avions utilisé un tableau associatif, les indexes seraient devenus des chaines de caractères ("s").
      • Nous lisons ensuite pour chaque valeur stockée, son type ("s"), sa longueur (4) et sa valeur stockée dans le tableau ("Riri").

      Grâce à toutes ces informations, la fonction unserialize() est capable de reconstruire entièrement le tableau en analysant la chaine formattée.

      C’est pourquoi le second var_dump() affiche le résultat suivant dans lequel nous retrouvons toutes les informations de la chaîne sérialisée :

      array(5) {
      [0]=>
      string(4) "Riri"
      [1]=>
      string(4) "Fifi"
      [2]=>
      string(6) "Loulou"
      [3]=>
      string(6) "Donald"
      [4]=>
      string(6) "Picsou"
      }

      Tentons maintenant de sérialiser / désérialiser un objet.

      Sérialisation et désérialisation d’un objet

      Voici maintenant l’exemple le plus intéressant, la sérialisation d’un objet. Prenons une classe basique avec différentes propriétés. Nous utilisons volontairement des propriétés publiques, privées et protégées afin de visualiser comment se comporte la sérialisation d’un objet avec des propriétés différentes.

      <?php
      class Dormeur {
      /**
      * Age du nain Dormeur
      *
      * @var integer
      * @access protected
      */
      protected $_age;
      /**
      * Le nom porte-t-il un bonnet ?
      *
      * @var boolean
      * @access protected
      */
      protected $_aSonBonnet;
      /**
      * Sa couleur préférée
      *
      * @var string
      * @access private
      */
      private $_couleurPreferee;
      /**
      * Ses hobbies
      *
      * @var array
      * @access public
      */
      public $_gouts;
      /**
      * Constructeur de la classe
      */
      public function __construct() {
      $this->_age = 19;
      $this->_aSonBonnet = true;
      $this->_couleurPreferee = 'rouge';
      $this->_gouts = array('musique','cinéma','curling');
      }
      }
      ?>

      Le nom de la classe vous semblera sans doute débile, mais nous trouvions plutôt de circonstance d’avoir une classe nommée « Dormeur » pour introduire une méthode nommée « __sleep() ».

      Passons maintenant successivement à son instanciation, sa serialisation et sa deserialisation :

      <?php
      $oDormeur = new Dormeur();
      $sSerialized = serialize($oDormeur);
      $oUnserialized = unserialize($sSerialized);
      echo '<pre>';
      var_dump($sSerialized,$oUnserialized);
      echo '</pre>';
      ?>

      Nous obtenons alors respectivement les résultats suivants :

      string(181) "O:7:"Dormeur":4:{s:7:" *_age";i:19;s:14:" *_aSonBonnet";b:1;s:25:" Dormeur_couleurPreferee";s:5:"rouge";s:6:"_gouts";a:3:{i:0;s:7:"musique";i:1;s:7:"cinéma";i:2;s:7:"curling";}}
      object(Dormeur)#2 (4) {
      ["_age:protected"]=>
      int(19)
      ["_aSonBonnet:protected"]=>
      bool(true)
      ["_couleurPreferee:private"]=>
      string(5) “rouge”
      ["_gouts"]=>
      array(3) {
      [0]=>
      string(7) “musique”
      [1]=>
      string(7) “cinéma”
      [2]=>
      string(7) “curling”
      }
      }

      Vous saviez déjà que le type des variables était préservé, il en va de même pour la visiblité (private, protected et public), cela ne change en aucun cas la structure de la classe. Nous vous parlions de stockage d’instance, il faut bien faire attention à ce que votre classe soit déclarée avant la désérialisation, sans quoi vous aurez une erreur :

      Fatal error: Class ‘Dormeur’ not found in /home/path/to/your/script.php on line xx

      Vous remarquerez le « o » comme premier caractère de la chaîne sérialisée qui indique que nous venons bien de sérialiser un objet.

      Méthodes magiques __sleep() et __wakeup()

      Maintenant que nous avons bien saisi le principe de la serialisation, nous allons pouvoir entrer dans le vif du sujet et parler des méthodes magiques __sleep() et __wakeup().

      Ces méthodes seront respectivement appelée par votre script lors de l’utilisation de serialize() et de unserialize(). Voici une démonstration, nous allons ajouter les méthodes __sleep() et _wakeup() à notre Dormeur.

      La méthode magique __sleep() doit cependant effectuer une action pour que la serialisation se passe bien. C’est ici que l’on peut voir le premier intérêt de la serialization d’une instance de classe : vous allez selectionner quelles propriétés de votre instance vous souhaitez stocker. Pour ce faire, la méthode __sleep() doit retourner un tableau contenant les noms des propriétés à conserver :

      <?php
      class Dormeur {
      /**
      * Méthode magique __sleep() Appelée lors d’un serialize()
      *
      * @return Array la liste des paramètres à conserver
      */
      public function __sleep() {
      echo 'Bon ben moi, je vais dormir.';
      return array('_age','_aSonBonnet','_couleurPreferee');
      }
      /**
      * Méthode magique __wakeup() Appelée lors d’un unserialize()
      *
      * @return void
      */
      public function __wakeup() {
      echo 'bon ben moi, je vais me faire un café.';
      }
      }
      ?>

      Ici on ne choisit de garder que l’âge de Dormeur, sa couleur préférée et si oui, ou non, il a son bonnet sur la tête.

      En exécutant de nouveau le code précédent, vous allez voir les deux echo s’afficher à la suite («Bon ben moi, je vais dormir. » puis « bon ben moi, je vais me faire un café. ») confirmant que les méthodes __sleep() et __wakeup() ont bien été successivement appelées.

      Le résultat de la sérialisation est le suivant :

      string(108) "O:7:"Dormeur":3:{s:7:"*_age";i:19;s:14:"*_aSonBonnet";b:1;s:25:"Dormeur_couleurPreferee";s:5:"rouge";}"

      On constate que l’on ne conserve pas les goûts de Dormeur. Et voici ce que vous affichera le deuxième var_dump :

      object(Dormeur)#2 (4) {
      ["_age:protected"]=>
      int(19)
      ["_aSonBonnet:protected"]=>
      bool(true)
      ["_couleurPreferee:private"]=>
      string(5) “rouge”
      ["_gouts"]=>
      NULL
      }

      Vous remarquerez que seules les propriétés spécifiées dans le __sleep() ont été mémorisées, la propriété « _gouts », quant à elle, est égale à NULL, son contenu n’a pas été conservé. Bien entendu, l’intérêt ne s’arrête pas là, vous allez pouvoir effectuer les actions que vous désirez dans ces méthodes : synchronisation avec une base de données, fermer une connexion à une base de données lors de la sérialisation, la réouvrir lors de la deserialisation, etc.

      Pour les amoureux du bricolage, il est également possible d’implémenter la notion de « transtypage » d’objet, totalement absente de PHP à l’heure actuelle.

      Conclusion

      Nous venons de découvrir les notions de sérialisation et de désérialisation d’une variable, d’un tableau ou bien d’un objet. Puis, nous avons montré comment il était possible de surcharger le comportement original de sérialisation / désérialisation d’un objet grâce aux méthodes magiques __sleep() et __wakeup().

       



      Les commentaires

      1. Par Monem le 24/12/2008 09:51

      Excellent tuto
      C’est le cours en français le plus avancé que j’ai lue
      Contenue.

      2. Par Niko le 07/07/2009 22:00

      Excellent vraiment.

      3. Par abdelseo le 12/08/2009 06:02

      Merci pour beaucoup,excellent tuto

      4. Par Ixabro le 19/08/2009 00:08

      Excellent, tutoriel.
      Tout est clair pour moi.
      C’est très bien expliqué.
      Et les exemples sont très compréhensifs.

      5. Par mani le 22/09/2009 23:57

      Toujours aussi excellent je vais me coucher et je reviendrais demain matin continuer ma lecture. Je crois tout de même avoir relevé deux coquilles.
      -Entre les deux parenthèses, nous trouvons tous les éléments du tableau.-
      Ce sont des accolades.
      -Le nom porte-t-il un bonnet ?- ce n’est pas le nom mais le nain qui porte ou pas un bonnet .

      Cela n’enlève rien à la qualité de votre tuto merci encore.

      6. Par aznrei le 14/01/2013 15:13

      Personnellement je vois pas trop à quoi va servir sleep et wakeup =/
      Peut-être qu’en relisant 3 fois j’arriverais à comprendre xd

      Singleton : instance unique d’une classe

      • Par Emacs
      • 5 commentaires
      • 38224 lectures
      • RSSAtom

      Dans la plupart des développements professionnels ou de grande envergure, il est nécessaire de savoir structurer correctement son application dans le but de faciliter sa conception et sa maintenance. Les « design patterns », où « patrons de conception » en français, constitue l’une des meilleures solutions à cette problématique. Avec le nouveau modèle orienté objet de PHP 5 proche de celui de Java, l’implémentation des design patterns est facilitée. Ce tutoriel s’intéresse à la présentation et à l’implémentation du motif Singleton, particulièrement employé au sein des applications web.

      Qu’est-ce qu’un patron de conception ?

      Définition extraite de Wikipedia :

      En génie logiciel, un patron de conception (design pattern en anglais) est un concept destiné à résoudre les problèmes récurrents suivant le paradigme objet. En français on utilise aussi le terme motif de conception qui est une traduction alternative de «design pattern», perçue comme incorrecte par certains.

      Les patrons de conception sont des solutions qui répondent à des problèmes récurrents d’architecture et de conception logiciels. Ce ne sont ni des algorithmes ni des fragments de code. Les desing patterns décrivent des procédés de conception généraux indépendemment du langage de programmation employé. C’est d’ailleurs pour cette raison que l’on présente les motifs de conception sous forme de diagramme de classes UML.

      En clair, les patrons de conception doivent être perçus comme un outil de conception et de structuration formelle des applications informatiques. Ce sont des solutions éprouvées et figurant parmi les bonnes pratiques de programmation à adopter.

      Introduction au patron de conception Singleton

      Présentation du design pattern Singleton

      Le Singleton, en programmation orientée objet, répond à la problématique de n’avoir qu’une seule et unique instance d’une même classe dans un programme. Par exemple, dans le cadre d’une application web dynamique, la connexion au serveur de bases de données est unique. Afin de préserver cette unicité, il est judicieux d’avoir recours à un objet qui adopte la forme d’un singleton. Il suffit donc par exemple de créer l’unique objet représentant l’accès à la base de données, et de stocker la référence à cet objet dans une variable globale du programme afin que l’on puisse y accéder de n’importe où dans le script.

      Structure d’une classe Singleton

      Concrètement, un singleton est très simple à mettre en place. Il est composé de 3 caractéristiques :

      • Un attribut privé et statique qui conservera l’instance unique de la classe.
      • Un constructeur privé afin d’empêcher la création d’objet depuis l’extérieur de la classe
      • Une méthode statique qui permet soit d’instancier la classe soit de retourner l’unique instance créée.

      Le code ci-dessous présente une classe minimaliste qui intègre le motif de conception Singleton. Nous y retrouvons le strict minimum, à savoir les trois caractéristiques présentées juste au dessus.

      Structure minimale du Singleton
      <?php
      class Singleton {
      /**
      * @var Singleton
      * @access private
      * @static
      */
      private static $_instance = null;
      /**
      * Constructeur de la classe
      *
      * @param void
      * @return void
      */
      private function __construct() {
      }
      /**
      * Méthode qui crée l'unique instance de la classe
      * si elle n'existe pas encore puis la retourne.
      *
      * @param void
      * @return Singleton
      */
      public static function getInstance() {
      if(is_null(self::$_instance)) {
      self::$_instance = new Singleton();
      }
      return self::$_instance;
      }
      }
      ?>

      La variable $_instance est déclarée comme statique. C’est une variable de classe, c’est-à-dire que sa valeur ne dépend pas de l’objet créé à partir de cette classe. Ainsi, si deux objets sont issus de la même classe, ils peuvent partager cette même variable et lire la même valeur quel que soit leur état à l’instant T. Cette variable $_instance se charge de conserver l’objet de type Singleton qui sera créé. Grâce au constructeur privé et à la méthode getInstance(), il est impossible de créer plusieurs objets de type Singleton.

      Le constructeur est déclaré privé afin de ne pas pouvoir être appelé depuis l’extérieur de la classe. Plus concrètement, il est ainsi impossible de faire un "new" en dehors de la classe. Le seul moyen d’obtenir une instance de la classe est de passer par la méthode publique et statique getInstance().

      La méthode getInstance() doit obligatoirement être publique et statique. La déclaration avec le mot-clé "public" lui permet d’être appelée en dehors de la classe. Le mot-clé "static", quant à lui, lui permet d’être appelée sans passer par l’objet (puisque nous n’avons pas d’objet lorsqu’on l’appel). Le but de cette méthode est de faire du contrôle d’accès. Elle vérifie tout d’abord que la propriété statique $_instance est NULL ou non. Si elle est NULL alors elle crée dans cette variable une instance de la même classe. Ensuite elle retourne cette instance. C’est LA seule et unique instance possible que l’on peut faire : c’est le singleton.

      Test de la classe Singleton

      Pour contrôler que notre classe fait bien son travail, nous allons tout d’abord essayer d’instancier la classe depuis l’extérieur puis nous essaierons d’obtenir deux instances différentes de la classe.

      Tentative d’instanciation depuis l’extérieur de la classe
      <?php
      // Import de la classe
      require(dirname(__FILE__).'/Singleton.class.php');
      // Tentative d'instanciation de la classe
      $oSingleton = new Singleton();
      ?>

      Du fait de la déclaration "private" du constructeur, nous ne pouvons instancier la classe directement. PHP génère donc une erreur non surprenante.

      Fatal error: Call to private Singleton::__construct() from invalid context in /Users/Emacs/Sites/Demo/MySingleton.php on line 7
      Tentative de construction de deux objets de type Singleton
      <?php
      // Import de la classe
      require(dirname(__FILE__).'/Singleton.class.php');
      // Tentative d'instanciation de la classe
      $oSingletonA = Singleton::getInstance();
      $oSingletonB = Singleton::getInstance();
      echo '<pre>';
      var_dump($oSingletonA);
      echo '</pre>';
      echo '<pre>';
      var_dump($oSingletonB);
      echo '</pre>';
      ?>

      Nous obtenons le résultat suivant :

      object(Singleton)#1 (0) {
      }
      object(Singleton)#1 (0) {
      }

      Le #1 correspond à la référence de l’instance. On constate ici que pour les deux objets $oSingletonA et $oSingletonB, cette référence est strictement identique. On en déduit donc que ces deux variables référencent (pointent) le même objet en mémoire. Nous avons donc réussi à créer une instance unique de la classe, le fameux Singleton.

      Exemple d’implémentation de Singleton

      En l’état, notre classe de Singleton ne nous permet pas d’être utilisable dans un projet concret. Elle constitue uniquement l’ossature minimaliste d’un objet adoptant la forme d’un Singleton. Nous allons donc nous appuyer sur un exemple concret tiré de la réalité pour illustrer davantage ce patron.

      Comme nous le savons tous, une seule et unique personne se trouve à la tête de notre pays. Il s’agit du Président de la République. Il ne peut y en avoir plus d’un. Nous pouvons donc très facilement représenter cette contrainte sous forme d’une classe intégrant les spécificités du singleton.

      <?php
      class PresidentDeLaRepublique {
      /**
      * L'objet unique PresidentDeLaRepublique
      *
      * @var PresidentDeLaRepublique
      * @access private
      * @static
      */
      private static $_instance = null;
      /**
      * Le nom du Président
      *
      * @var string
      * @access private
      */
      private $_nom='';
      /**
      * Le prénom du Président
      *
      * @var string
      * @access private
      */
      private $_prenom='';
      /**
      * Représentation chainée de l'objet
      *
      * @param void
      * @return string
      */
      public function __toString() {
      return $this->getPrenom() .' '. strtoupper($this->getNom());
      }
      /**
      * Constructeur de la classe
      *
      * @param string $nom Nom du Président
      * @param string $prenom Prénom du Président
      * @return void
      * @access private
      */
      private function __construct($nom, $prenom) {
      $this->_nom = $nom;
      $this->_prenom = $prenom;
      }
      /**
      * Méthode qui crée l'unique instance de la classe
      * si elle n'existe pas encore puis la retourne.
      *
      * @param string $nom Nom du Président
      * @param string $prenom Prénom du Président
      * @return PresidentDeLaRepublique
      */
      public static function getInstance($nom, $prenom) {
      if(is_null(self::$_instance)) {
      self::$_instance = new PresidentDeLaRepublique($nom, $prenom);
      }
      return self::$_instance;
      }
      /**
      * Retourne le nom du Président
      *
      * @return string
      */
      public function getNom() {
      return $this->_nom;
      }
      /**
      * Retourne le prénom du Président
      *
      * @return string
      */
      public function getPrenom() {
      return $this->_prenom;
      }
      }
      ?>

      Création de l’objet :

      <?php
      // Import de la classe
      require(dirname(__FILE__).'/PresidentDeLaRepublique.class.php');
      // Instanciation de l'objet
      $oPresident = PresidentDeLaRepublique::getInstance('Sarkozy','Nicolas');
      // Appel implicite à la méthode __toString()
      echo $oPresident;
      ?>

      Nous obtenons bien le résultat ci-après sur la sortie standard :

      Nicolas SARKOZY

      Conclusion

      Nous avons présenté ici le motif de conception Singleton qui permet de répondre à la problématique de n’avoir qu’une seule et même instance d’une classe. L’implémentation d’une telle structure est très rapide et simple à mettre en place. Il est possible par exemple de l’implémenter pour gérer la connexion au serveur de bases de données, le contexte de votre application, l’aire de jeux d’un jeu vidéo…



      Les commentaires

      1. Par Luis le 01/09/2008 21:31

      Donc un singleton c’est une class normal qui s’instancie qu’une seule fois !

      et pas une class singleton, c’était juste pour l’exemple en fait !

      2. Par Emacs le 01/09/2008 22:39

      Oui c’est une classe que l’on ne peut instancier qu’une seule et unique fois.

      3. Par Xtouch le 10/09/2008 23:23

      Article très clair et très bien rédigé, ce qui l’a rendu utile pour ma compréhension du pattern singleton. Merci !

      4. Par petitchevalroux le 16/12/2008 09:08

      Trés bel article je compl_te sur mon site avec un exemple d’utilisation du singleton dans une classe de connexion utilisateur

      5. Par cyber le 25/05/2009 19:51

      merci pour l’article c’est très clair

       

      Classe singleton d’accès aux SGBD intégrant PDO

      • Par Palleas
      • 12 commentaires
      • 26824 lectures
      • RSSAtom

      J’ai commencé à créer ma propre classe d’accès aux bases de données car j’avais véritablement envie d’implémenter des interfaces telles que Iterator ou Countable. Seulement voilà, il existe déjà une classe de ce type native à PHP : PDO.

      Présentation brève de l’extension PDO

      Cette classe est particulièrement intéressante et pratique à utiliser car elle gère directement l’abstraction (comprenez qu’elle ne dépend pas d’un Système de Gestion de Base de Données (SGBD) pour fonctionner), la protection des données, les requêtes préparées, etc. En y reflechissant, je me suis dit que j’étais en train de perdre mon temps à essayer de refaire ce qui se fait déjà, probablement en moins bien ! C’est une chose de ne pas aimer le code tout fait (i.e. les Frameworks PHP), c’en est une autre de ne pas vouloir utiliser les classes natives.

      Il y a cependant certaines choses que j’aimerai retrouver dans PDO : notamment le design pattern Singleton. C’est parti pour la conception de ma classe que j’ai nommée très sobrement SPDO (Singletoned-PDO, sympa non ?).

      Le pattern Singleton

      Le singleton c’est quoi ? C’est une manière de s’assurer de ne disposer que d’une seule instance de votre classe durant toute l’exécution du script. Il y aurait bien la solution de déclarer PDO dans une variable en tête d’une page appelée systématiquement mais pour moi, cette solution est à proscrire.

      Singleton : redéfinition du constructeur en accès private
      <?php
      class MyClass extends PDO
      {
      /**
      * Constructeur
      *
      * @param string $path
      * @param string $user
      * @param string $path
      * @return void
      * @access private
      * @see PDO::__construct()
      */
      private function __construct($path, $user, $pass)
      {
      parent::__construct($path, $user, $pass);
      }
      }
      ?>

      Vous croyez vraiment que ça allait être aussi simple? Essayez d’instancier la classe MyClass, et vous aurez droit au message suivant :

      Fatal error: Access level to MyClass::__construct() must be public (as in class PDO) in /path/to/your/script/pdo.class.php on line 6

      Vous avez cependant la possibilité de faire un presque-singleton en laissant simplement le constructeur publique, mais pour moi ce serait tout simplement une hérésie. La solution est donc la composition.

      Présentation de la classe SPDO

      Qu’est ce que la composition ? C’est une autre manière de voir la programmation orientée objet, au même titre que l’héritage. C’est à dire que de cette manière nous allons nous contenter de stocker dans la classe une instance de PDO, plutôt que d’en hériter :

      <?php
      class SPDO
      {
      /**
      * Instance de la classe SPDO
      *
      * @var SPDO
      * @access private
      */
      private $PDOInstance = null;
      /**
      * Constante: nom d'utilisateur de la bdd
      *
      * @var string
      */
      const DEFAULT_SQL_USER = 'user';
      /**
      * Constante: hôte de la bdd
      *
      * @var string
      */
      const DEFAULT_SQL_HOST = 'localhost';
      /**
      * Constante: hôte de la bdd
      *
      * @var string
      */
      const DEFAULT_SQL_PASS = 'xxxxxxxxx';
      /**
      * Constante: nom de la bdd
      *
      * @var string
      */
      const DEFAULT_SQL_DTB = 'database';
      /**
      * Constructeur
      *
      * @param void
      * @return void
      * @see PDO::__construct()
      */
      public function __construct()
      {
      $this->PDOInstance = new PDO('mysql:dbname='.self::DEFAULT_SQL_DTB.';host='.self::DEFAULT_SQL_HOST,self::DEFAULT_SQL_USER ,self::DEFAULT_SQL_PASS);
      }
      }
      ?>

      Vous avez ensuite la possibilité d’implémenter votre Singleton :

      <?php
      class SPDO
      {
      /**
      * Instance de la classe PDO
      *
      * @var PDO
      * @access private
      */
      private $PDOInstance = null;
      /**
      * Instance de la classe SPDO
      *
      * @var SPDO
      * @access private
      * @static
      */
      private static $instance = null;
      /**
      * Constante: nom d'utilisateur de la bdd
      *
      * @var string
      */
      const DEFAULT_SQL_USER = 'root';
      /**
      * Constante: hôte de la bdd
      *
      * @var string
      */
      const DEFAULT_SQL_HOST = 'localhost';
      /**
      * Constante: hôte de la bdd
      *
      * @var string
      */
      const DEFAULT_SQL_PASS = '';
      /**
      * Constante: nom de la bdd
      *
      * @var string
      */
      const DEFAULT_SQL_DTB = 'jobeet';
      /**
      * Constructeur
      *
      * @param void
      * @return void
      * @see PDO::__construct()
      * @access private
      */
      private function __construct()
      {
      $this->PDOInstance = new PDO('mysql:dbname='.self::DEFAULT_SQL_DTB.';host='.self::DEFAULT_SQL_HOST,self::DEFAULT_SQL_USER ,self::DEFAULT_SQL_PASS);
      }
      /**
      * Crée et retourne l'objet SPDO
      *
      * @access public
      * @static
      * @param void
      * @return SPDO $instance
      */
      public static function getInstance()
      {
      if(is_null(self::$instance))
      {
      self::$instance = new SPDO();
      }
      return self::$instance;
      }
      /**
      * Exécute une requête SQL avec PDO
      *
      * @param string $query La requête SQL
      * @return PDOStatement Retourne l'objet PDOStatement
      */
      public function query($query)
      {
      return $this->PDOInstance->query($query);
      }
      }

      Enfin, en redéfinissant les méthodes query() ou prepare() de PDO, vous aurez la possibilité d’ajouter un log des requêtes exécutées, etc.

      Pour l’utilisation, rien de plus simple. Admettons que nous ayons une table "membre" dans notre base de données qui contient n colonnes dont les champs suivants : id, nom et prénom.

      Comme nous avons encapsuler (ou wrapper) la méthode query() de PDO dans la méthode query() de SPDO, alors nous pouvons simplement parcourir notre table de la manière suivante :

      <?php
      foreach (SPDO::getInstance()->query('SELECT id, nom, prenom FROM membre m') as $membre)
      {
      echo '<pre>', print_r($membre) ,'</pre>';
      }

      Rien ne vous empêche à présent de redéfinir chaque méthode de PDO ou bien d’utiliser la méthode magique __call() pour faire une redéfinition dynamique de toutes les méthodes de PDO.



      Les commentaires

      1. Par x@v le 25/12/2008 15:01

      excellent cet article !
      j’ai moi même conceptualiser une classe, sans le singleton. Ne connaissant rien au design pattern ou peu.

      http://www.phpclasses.org/browse/file/23687.html

      Tu me pousse fortement à mettre ton article en pratique.
      Le seul truc qui me trouble c’est le mot clé self::
      Moi j’utulise this-> !
      Y’a t-il une difference ?
      PS: ton captcha est bien mais ont à pas le droit à l’erreur

      2. Par Piko le 26/12/2008 17:44

      "Il y aurait bien la solution de déclarer PDO dans une variable en tête d’une page appelée systématiquement mais pour moi, cette solution est à proscrire."
      Pourquoi cela ?
      Je comprends pas pourquoi ce n’est pas une bonne solution, on est au moins sûr d’avoir qu’une seule et unique instance…

      Piko.

      3. Par saturn1 le 26/12/2008 19:41

      Bon article , comme d’habitude!
      J’utilise ce script et je n’ai pas à m’en plaindre !!
      Bravo

      4. Par Tim le 30/12/2008 22:38

      En réponse a x@v il y a une différence entre $this et self, ce que chacun référence en fait $this référence l’objet courant (donc la classe doit etre instancié pour pouvoir l’utiliser). self référence la classe et donc il ne faut pas spécialement que la classe soit instancié pour pouvoir utiliser self

      5. Par gtraxx le 05/02/2009 16:33

      je suppose que l’on peut redéfinir ainsi chaque méthode de PDO avec un singleton ?
      genre :
      public function setMode(){
      return $this->PDOInstance->setFetchMode(PDO::FETCH_ASSOC);
      }
      ou encore modifier le comportement de fetch par exemple.
      Ou mieux se créer ses propres fonctions d’insertion

      6. Par Bob le 10/02/2009 17:12

      Ce script est super mais comment faire si je veux me connecter à plusieurs DB différentes ?

      7. Par Emacs le 10/02/2009 19:32

      @Bob : tu ne peux pas ! C’est bien là le principe du Singleton, c’est de n’avoir qu’une et une seule instance unique. Si tu veux pouvoir faire plusieurs connexions, tu vas devoir implémenter un pattern multiton.

      8. Par gtraxx le 11/02/2009 09:23

      J’ai essayer de définir d’autre fonction que query afin d’avoir un pattern plus complet mais j’ai quelque souçi.

      dans la class :

      /**
      * config setFetchMode
      *
      * @return bool
      */
      public function setMode($mode){
      $fetch = array(
      ‘assoc’ => PDO::FETCH_ASSOC,
      ‘class’ =>PDO::FETCH_CLASS,
      ‘column’=>PDO::FETCH_NUM
      );
      foreach ($fetch as $key){
      $fetchmode = $key[$mode];
      }
      return $fetchmode;
      }
      /**
      * function prepare
      *
      * @param request $sql
      * @return array()
      */
      public function Prepare($sql){

      return $this->PDOInstance->prepare($sql);
      }

      public function FetchAll($sql,$array=false){

      $prepare = $this->Prepare($sql);
      $result = $prepare->setFetchMode($this->setMode(‘assoc’);
      $result .= $array ? $prepare->execute($array = array()) : $prepare->execute();
      $result .= $prepare->fetchAll();
      $result .= $prepare->closeCursor();
      $result .= $prepare->null;
      return $result;
      }

      Cela me retourne un tableau vide avec :

      11Array1
      Warning: Invalid argument supplied for foreach() in C:\wamp\www\wizard\_scriptroot\index\index.php on line 31

      je l’utilise comme sa :

      function testDb(){
      $ini = new DataOjects();
      $sql = ‘SELECT * FROM test’;
      //$prepare = $ini->getInstance()->Prepare($sql);
      $result = $ini->getInstance()->FetchAll($sql);
      //var_dump($result);
      return $result;

      }
      function displayIndex(){
      $result=array();
      $result = $this->testDb();
      print_r($result);
      foreach ($result as $tabs => $key){
      $fetch = $key['nom'];
      }
      }

      9. Par Babynus le 26/02/2009 13:39

      L’idée est bonne, mais pourquoi se compliquer avec un singleton alors qu’une classe statique suffirait largement ?
      Le seul point que je vois serait de pouvoir supprimer dans le futur le caractère singleton sans rien changer dans le code … ce qui n’est pas possible si on est parti en statique.

      Donc juste ave cune classe statique contenant l’instance de PDO (statique) avec une methode de type getPDO() qui initialise l’instance si elle ne l’est pas ou la retourne simplement dans le cas contraire.
      On a juste à faire un SPDO::getPDO()->methodPDO()… (en remplacant le -> par :: pour valider l’appel statique.

      Et pour répondre à Bob, la solution est toute simple : au lieu de stocker un seul objet PDO, tu peux stocker un tableau de couple (type DB, Objet PDO)

      10. Par Kirk le 29/03/2009 15:36

      Bonjour,

      je n’ai pas vraiment compris comment fonctionnait la composition ni à quoi servait la variable $instance. Ça semble lié mais bon ça m’échappe.

      J’ai créé un presque singleton d’hérétique pour pouvoir utiliser les méthodes de la classe PDO (en gros j’ai collé getintance() dans le constructeur). Est ce vraiment cradingue?

      Merci, en tout cas pour ces tutoriels qui manquent cruellement sur le net.

      11. Par thierry le 18/05/2009 22:37

      Babynus > Pourrais-tu donner le code complet de ce à quoi tu penses ? Car je ne saisis pas exactement ce que tu proposes.

      Kirk > Le problème de ton "presque singleton" c’est le "presque" Ton constructeur est public, donc tu ne garantis pas l’unicité de l’instance dans ton programme, ce qui est contraire à la définition du singleton.

      12. Par tyty le 11/10/2009 18:51

      Bonjour,
      j’utilise ta classe et après je veux faire ça :
      $db=Connexion_serveur::getInstance();
      $sth = $db->prepare(‘INSERT INTO logs_erreurs (date, texte) VALUES (?, ?’;
      $sth->bindParam(1, "test", PDO:ARAM_STR);
      $sth->bindParam(2, "texte", PDO:ARAM_STR);
      $sth->execute();

      Mais ca marche pas sais-tu pourquoi ? Il me marque : : Call to undefined method Connexion_serveur:repare()

      Merci

       

      Méthodes magiques : __call()

      • Par Palleas
      • 6 commentaires
      • 26839 lectures
      • RSSAtom

      PHP a fait un grand pas en avant en matière de programmation orientée objet avec sa version 5. Depuis cette version, il permet d’implémenter des méthodes au comportement prédéfini par PHP. Ces méthodes sont nommées « méthodes magiques », __call() est l’une d’entre elles.

      Appeler une méthode qui n’existe pas

      Prenons l’exemple d’une classe qui modélise un Manchot, que l’on instancie pour ensuite appeler sa méthode « voler ».

      <?php
      class Manchot
      {
      }
      $georges = new Manchot();
      $georges->voler('Afrique');
      ?>

      Ce morceau de code vous lèvera une erreur. Vous ne le saviez peut-être pas mais les manchots ne peuvent pas voler :

      Fatal error: Call to undefined method Manchot::voler() in /path/to/Apprendre-php/magic_methods.php on line 4.

      Ce petit rappel morphologique vous permet surtout de voir la chose suivante : on ne peut pas appeler une méthode qui n’existe pas. Cependant PHP, grâce à la méthode magique __call(), va vous permettre de violer une loi élémentaire de la nature, à savoir faire voler un manchot ou plus généralement appeler une méthode qui n’a pas été déclarée dans votre classe.

      Implémenter la méthode __call()

      La méthode __call() prend deux paramètres. Le premier contient le nom de la méthode que vous avez cherché à appeler, le seconde contient les arguments que vous lui avez passés. Le listing ci-après présente le structure globale de cette méthode.

      <?php
      class MyObject
      {
      /**
      * Methode magique __call()
      *
      * @param string $method Nom de la méthode à appeler
      * @param array $arguments Tableau de paramètres
      * @return void
      */
      public function __call($method, $arguments)
      {
      // Code personnalisé à exécuter
      }
      }
      ?>

      Maintenant reprenons l’exemple du Manchot.

      <?php
      class Manchot
      {
      /**
      * Methode magique __call()
      *
      * @param string $method Nom de la méthode à appeler
      * @param array $arguments Tableau de paramètres
      * @return void
      * @access private
      */
      private function __call($method,$arguments)
      {
      echo 'Vous avez appelé la méthode ', $method, 'avec les arguments : ', implode(', ',$arguments);
      }
      }
      $george = new Manchot();
      $george->voler('Afrique');
      ?>

      Quelques remarques :

      Si vous avez rendu votre méthode __call() publique, vous aurez aussi la possibilité de l’appeler directement en faisant : $georges->__call(‘voler’,'Afrique’); mais il y aura une petite différence. En appelant directement la méthode voler(), la variable $arguments sera un array stockant les différents arguments. A contrario, si vous passez par la méthode __call(), le second argument sera du type que vous voudrez.

      A l’heure actuelle, il est impossible d’en faire de même avec des méthodes statiques, c’est quelque chose qui est désormais corrigée dans la version 5.3 de PHP qui vient tout juste de sortir en version alpha 1. Une méthode magique nommée « __callStatic() » permet, en PHP 5.3, d’appeler des méthodes statiques qui ne sont pas déclarées dans la classe.

      Exemple concret : création d’un moteur de recherche

      Vous vous dites que cela n’a pas grand intérêt, et pourtant avec l’exemple suivant vous devriez y voir un peu plus clair.

      Nous allons tenter de recréer un moteur de recherche. Vous remarquerez que nous utilisons la classe SPDO présentée dans un précédent tutoriel et qui permet d’accéder à la base de données via l’extension native PDO.

      <?php
      class SearchEngine
      {
      /**
      * Effectue une recherche dans la base de données à
      * partir des critères fournis en argument
      *
      * @param array $conditions Tableau de critères de recherche
      * @return array $return Tableau des résultats
      * @see SPDO
      */
      public function search($conditions = array())
      {
      $query = 'SELECT id FROM table';
      if(sizeof($conditions) > 0) {
      $query.=' WHERE '.implode(' AND ',$conditions);
      }
      // Exécution de la requête SQL avec une classe PDO
      $result = SPDO::getInstance()->query($query);
      $return = $result->fetchAll(PDO::FETCH_ASSOC);
      return $return;
      }
      }
      ?>

      Comme vous pouvez le constater, ce moteur de recherche possède une méthode search() qui prend en paramètre un tableau des différentes conditions à appliquer à la requête effectuant la recherche. Ces conditions étant de la forme suivante : nomDuChamp= "valeur".

      Vous admettrez comme moi (j’espère !) que cette syntaxe n’est pas des plus pratiques, je ne me vois pas utiliser la requête de cette manière :

      <?php
      $mySearchEngine = new SearchEngine();
      $mySearchEngine->search(array(
      'champ1' => 'apprendre-php',
      'champ2' => 'palleas'
      ));

      Ce serait vraiment sympa de pouvoir faire $mySearchEngine->searchByName(‘palleas’); par exemple, ou encore $mySearchEngine->searchByNameAndDate(‘palleas’,’25/07/1987′); pas vrai ?

      Et c’est là que l’on va pouvoir mettre en application la méthode __call().

      <?php
      class SearchEngine
      {
      /**
      * Effectue une recherche dans la base de données à
      * partir des critères fournis en argument
      *
      * @param array $conditions Tableau de critères de recherche
      * @return array $return Tableau des résultats
      * @see SPDO
      */
      public function search($conditions = array())
      {
      $query = 'SELECT id FROM table';
      if(sizeof($conditions) > 0) {
      $query.=' WHERE '.implode(' AND ',$conditions);
      }
      // Exécution de la requête SQL avec une classe PDO
      $result = SPDO::getInstance()->query($query);
      $return = $result->fetchAll(PDO::FETCH_ASSOC);
      return $return;
      }
      /**
      * Méthode magique __call() permettant d'appeller une méthode virtuelle
      * du type searchByName(), searchByAge() ou searchByNameAndAge()...
      *
      * @param string $method Nom de la méthode virtuelle appelée
      * @param array $args Tableau des critères de recherche
      * @return array|null $return Tableau des résultats ou NULL
      * @see SearchEngine::search()
      */
      public function __call($method, $args)
      {
      if(preg_match('#^searchBy#i',$method))
      {
      $searchConditions = str_replace('searchBy','',$method);
      $searchCriterias = explode('and',$searchConditions);
      $conditions = array();
      $nbCriterias = sizeof($searchCriterias);
      for($i=0; $i < $nbCriterias; $i++)
      {
      $conditions[] = strtolower($searchCriterias[$i]).'="'.$args[$i] .'"';
      }
      return $this->search($conditions);
      }
      return null;
      }
      }
      ?>

      Voilà un morceau de code assez conséquent à digérer, nous allons donc le décortiquer étape par étape :

      • Pour commencer, on vérifie que la méthode que l’on a cherché à appeler est une méthode dont le nom commence par « searchBy ». Cette étape n’est pas indispensable, nous appellerons ça une précaution : nous nous assurons ainsi de l’intuitivité du code.
      • On récupère ce qu’il y a après searchBy, dans cet exemple : name
      • Au cas ou nous aurions plusieurs Conditions, par exemple searchByNameAndDate, on récupère chacun des champs à tester, ici NameAndDate.
      • Pour chacun des paramètres, on crée la condition, dans le cas de : $google->searchByNameAndDate(‘palleas’,’25/07/1987′); on obtient un tableau avec name=«palleas » et date=«25/07/1987» que l’on va pouvoir passer en paramètre à la méthode search(), comme on en parlait plus haut.

      Inconvénients de l’utilisation de la méthode magique __call()

      Au même titre que les méthodes magiques __get() et __set(), la méthode magique __call() possède deux inconvénients non négligeables lorsque l’on développe en environnemet professionnel. En effet, l’utilisation de __call() empêche tout d’abord la génération automatique de documentation de code au moyen des APIs (PHPDocumentor par exemple) utilisant les objets d’introspection (Reflection). D’autre part, cela empêche également les IDE tels qu’Eclipse d’introspecter le code de la classe et ainsi proposer l’auto-complétion du code. A utiliser donc avec parcimonie !



      Les commentaires

      1. Par Mickaël Wolff le 11/08/2008 05:01

      J’utilise aussi cette méthode dans une classe proxy (pour accéder à mes données par des méthodes standardisées). Malheureusement, une utilisation intensive de __call rend le code illisible, et empêche de créer une documentation complète de manière aisée (puisque par définition les noms des méthodes sont dynamiques.

      Bref, à utiliser avec retenue.

      2. Par Palleas le 11/08/2008 09:18

      C’est vrai le coup de la doc, je vais le rajouter, merci

      3. Par greg le 26/11/2008 15:19

      LA POO C’est chaud sérieux…

      4. Par Tom le 10/05/2009 21:46

      Une grande utilisation aussi:

      Savoir qui appel vos fonctions:

      Ajouter "echo var_dump(debug_backtrace());"

      Dans la méthode __call()

      5. Par caccia le 28/07/2009 11:36

      On ne pourrait pas passer tout ce qu’il y a dans la méthode _call dans la méthode search?

      6. Par boulet_sensei le 25/08/2009 08:56

      difficile de faire une doc propre, mais toujours facile a mettre en place pour de grosses classes. Bref, je ne le trouve pas tres pratique, sauf pour creer des pages d erreurs plus propres quand une methodes inexistantes est apellee.
      c est bon a savoir, sans pour autant l utiliser.
      totalement hors sujet mais je conseille a tous cakephp, un framework php complet et simple !

 

Aucun commentaire

 

Laissez un commentaire