IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

[POO] Factorisation des méthodes d'un singleton [PHP 5.2]


Sujet :

Langage PHP

  1. #1
    Nouveau membre du Club
    Inscrit en
    Novembre 2008
    Messages
    43
    Détails du profil
    Informations forums :
    Inscription : Novembre 2008
    Messages : 43
    Points : 38
    Points
    38
    Par défaut [POO] Factorisation des méthodes d'un singleton
    Bonjour à tous !

    Ceci étant mon premier post ici [edit: j'ai déjà posté une fois, m'en rappelais plus... :d ], je tiens tout d'abord à remercier Développez pour la masse incroyable de cours et d'infos disponibles (marre des bouquins aux prix exorbitants pour un contenu plus que médiocre...)

    Venons-en au fait, je cherche à créer une classe abstraite Singleton qui pourrait être étendue par d'autres classes afin de factoriser le code de getInstance() et les diverses exceptions levées (lors d'un clonage, par ex.).

    J'ai donc le code suivant pour ma classe Singleton :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?php
     
    require("getCalledClassFix.php"); // http://fr.php.net/manual/en/function.get-called-class.php#92845 en attendant PHP 5.3 sur Ubuntu...
     
    abstract class Singleton {
    	private static $instance;
     
    	public static function getInstance()
    	{	
    		$cls = get_called_class();
    		if (!isset(self::$instance)) self::$instance = new $cls();
    		return self::$instance;
    	}
     
    	// Prevent cloning
    	public function __clone()
    	{
    		throw new SingletonException(__CLASS__ . ' : cloning not allowed');
    	}
    }
     
    class SingletonException extends Exception
    {
     
    }
     
    ?>
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?php
     
    final class Config extends Singleton {
    	protected static $instance;
    	private $params = array();
     
    	protected function __construct()
    	{
    		include("config.php");
    		$this->params['server'] = $server;
    		$this->params['username'] = $username;
    		$this->params['password'] = $password;
    		$this->params['db'] = $db;
    	}
     
    }
     
    class ConfigException extends Exception
    {
     
    }
     
    ?>
    Le problème est que je n'arrive pas à dériver cette classe sur une deuxième classe en même temps. Si je crée une classe DBLink (avec disons la même construction que Config...),et que je récupère une instance de chaque classe, j'obtiens un object Config et non DBLink...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    $bar = Config::getInstance();
    $foo = DBLink::getInstance();
     
    var_dump($bar);
    var_dump($foo);
     
    // Output:
    // object(Config)[1]
    //  private 'params' => 
    //    array
    //      empty
    //
    // object(Config)[1]
    //   private 'params' => 
    //     array
    //       empty
    Il me semble bien évident que le problème vient de "self::$instance" dans ma classe Singleton, qui se réfère à Singleton::$instance et non à Config::$instance ou DBLink::$instance...

    Existe-t-il une méthode pour que "self" dans la classe mère fasse en fait référence aux propriétés de la classe dérivée ? Ou suis-je lancé dans une impasse, et je dois me résigner à redéclarer getInstance dans chaque classe de type Singleton ?

    Merci d'avance pour vos lumières

  2. #2
    Membre actif
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Janvier 2008
    Messages
    227
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux

    Informations forums :
    Inscription : Janvier 2008
    Messages : 227
    Points : 273
    Points
    273
    Par défaut
    Bonjour,

    Et a la place de get_called_class, tu ne pourrais pas utiliser get_class ($this).

    http://fr.php.net/manual/fr/function.get-class.php

    Voici le code que tu obtiens alors :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?php
     
    require("getCalledClassFix.php"); // http://fr.php.net/manual/en/function.get-called-class.php#92845 en attendant PHP 5.3 sur Ubuntu...
     
    abstract class Singleton {
    	private static $instance;
     
    	public static function getInstance()
    	{	
    		$cls = get_class($this);
    		if (!isset(self::$instance)) self::$instance = new $cls();
    		return self::$instance;
    	}
     
    	// Prevent cloning
    	public function __clone()
    	{
    		throw new SingletonException(__CLASS__ . ' : cloning not allowed');
    	}
    }
     
    class SingletonException extends Exception
    {
     
    }
     
    ?>
    Cordialement,
    Patouche

  3. #3
    Nouveau membre du Club
    Inscrit en
    Novembre 2008
    Messages
    43
    Détails du profil
    Informations forums :
    Inscription : Novembre 2008
    Messages : 43
    Points : 38
    Points
    38
    Par défaut
    Hélas non Ma méthode est statique... donc pas de $this...

    J'ai trouvé une solution par intérim, mais j'aurais aimé trouver plus propre...
    Au lieu de chercher à stocker chaque instance dans la classe dérivée respective, on garde un tableau dans la classe abstraite, avec comme clé le nom de la classe, et comme valeur l'instance.

    En gros un multiton (enfin je crois)...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
     
    require("getCalledClassFix.php");
     
    abstract class Singleton {
    	// Use an array to hold instances of each class derived from Singleton.
    	private static $instances = array();
     
    	public static function getInstance()
    	{
    		$cls = get_called_class();
    		if (!isset(self::$instances[$cls])) self::$instances[$cls] = new $cls();
    		return self::$instances[$cls];
    	}
     
    	// Edit : suppression de lignes inutiles
     
    	// Prevent cloning, serialization.
    	public function __clone()
    	{
    		throw new SingletonException(__CLASS__ . ' objects cannot be cloned');
    	}
     
            // Serialization is disabled here because of PHP's mechanism... Unserializing will create a second instance of the object.
    	public function __wakeup()
    	{
    		throw new SingletonException(__CLASS__ . ' objects cannot be (un)serialized');
    	}
     
    	public function __sleep()
    	{
    		throw new SingletonException(__CLASS__ . ' objects cannot be (un)serialized');
    	}
    }
     
    class SingletonException extends Exception
    {
     
    }

  4. #4
    Membre actif
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Janvier 2008
    Messages
    227
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux

    Informations forums :
    Inscription : Janvier 2008
    Messages : 227
    Points : 273
    Points
    273
    Par défaut
    Pour le $this, en effet, tu as raison, cela n'a pas beaucoup de sens dans une méthode static.

    Mais, suite à ton message, je me pose une question...

    Dans ce que tu as fais, tu appelles get_called_class(). Or d'après le manuel php, cette fonction n'est disponible que depuis php 5.3 donc es-tu sur que tu tournes sur le php 5.2

    Sinon, il y a en bas du manuel sur la page de get_called_class, une manière de faire une classe singleton. Peut-être que cela pourra t'inspirer.

    http://fr2.php.net/get-called-class

  5. #5
    Membre actif

    Profil pro
    Inscrit en
    Décembre 2006
    Messages
    191
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 191
    Points : 275
    Points
    275
    Par défaut
    et avec __CLASS__ pour avoir le nom de la class?

    exemple tiré de la doc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    <?php
    class Example
    {
        // Hold an instance of the class
        private static $instance;
     
        // A private constructor; prevents direct creation of object
        private function __construct() 
        {
            echo 'I am constructed';
        }
     
        // The singleton method
        public static function singleton() 
        {
            if (!isset(self::$instance)) {
                $c = __CLASS__;
                self::$instance = new $c;
            }
     
            return self::$instance;
        }
     
        // Example method
        public function bark()
        {
            echo 'Woof!';
        }
     
        // Prevent users to clone the instance
        public function __clone()
        {
            trigger_error('Clone is not allowed.', E_USER_ERROR);
        }
     
    }
     
    ?>

  6. #6
    Nouveau membre du Club
    Inscrit en
    Novembre 2008
    Messages
    43
    Détails du profil
    Informations forums :
    Inscription : Novembre 2008
    Messages : 43
    Points : 38
    Points
    38
    Par défaut
    @patouche : J'utilise une émulation de get_called_class (le require au début de mon code appelle le fichier la contenant) trouvée dans les commentaires de la page que tu m'as donnée, car je suis bel et bien sous PHP 5.2, et n'ayant pas 5.3, impossible de vérifier si cette émulation fonctionne comme l'originale... J'ai tenté d'installer PHP 5.3 sur Ubuntu, mais sans succès pour le moment, et j'ai pas envie d'y passer des jours

    Et au final, j'en suis arrivé à quasi la même classe que celle présentée dans les commentaires après avoir lu un article sur les multitons, puisqu'on passe par un array();

    Après réflexion, je pense ne pas avoir d'autres solutions pour le moment... PHP 5.3 introduit le mot clé static:: et qui permet d'effectuer (à priori) ce que je recherche... Donc je vais attendre et je changerais ma classe quand j'aurais mon 5.3

    @Helfima : hélas non, __CLASS__ renvoie le nom de la classe dans laquelle il se trouve.. donc Singleton dans mon cas...

    En tout cas merci pour vos réponses... dommage que y'ait pas de bouton "semi-résolu"

  7. #7
    Membre actif
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Janvier 2008
    Messages
    227
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux

    Informations forums :
    Inscription : Janvier 2008
    Messages : 227
    Points : 273
    Points
    273
    Par défaut
    Mhhh, je ne connaissais pas ce pattern. Il peut être assez interessant. Mais après, il faut trouver en trouver l'utilité dans la pratique.

    Par contre, je serais bien curieux de voir comment tu émules get_called_class()...

    Merci d'avance

    PS : Un ubuntu user

  8. #8
    Nouveau membre du Club
    Inscrit en
    Novembre 2008
    Messages
    43
    Détails du profil
    Informations forums :
    Inscription : Novembre 2008
    Messages : 43
    Points : 38
    Points
    38
    Par défaut
    patouche : et bien j'émule comme ça . Quant à l'utilité, c'était pour gagner du temps pour factoriser les méthodes communes à tout les singletons (getInstance et le stockage de l'instance), et donc éviter d'implémenter ces méthodes dans chaque classe singleton...
    C'est vrai que l'utilité est marginale, puisque le singleton n'est pas un pattern voué à une utilisation massive dans une appli (lire les divers posts contre l'utilisation des singletons en cherchant un peu sur google), et qui doit être utilisé à bonne escient !

    C'est surtout un exercice de style pour ma part, vu que je me suis lancé corps et âme dans l'apprentissage de l'OOP !

    PS. Erreur cher ami, Windows 7 user, Ubuntu pour mon serveur privé J'ai grandi avec Windows, difficile de se défaire de certaines habitudes ! Et puis quand j'ai tenté Ubuntu en desktop, il voulait rien savoir pour mon WiFi MIMO ! Enfin, ça, c'est une autre histoire qui n'a rien à faire ici

  9. #9
    Nouveau membre du Club
    Inscrit en
    Novembre 2008
    Messages
    43
    Détails du profil
    Informations forums :
    Inscription : Novembre 2008
    Messages : 43
    Points : 38
    Points
    38
    Par défaut
    Bon, j'ai fait une install WAMP avec PHP 5.3 quand même pour voir de quoi il en retournait avec la vraie version de get_called_class et surtout les LSB (mot clé static:: )

    Résultat : ça marche, mais donc la solution "propre" n'est possible qu'à partir de PHP 5.3.

    Il suffit de créer une classe abstraite Singleton, comme un Singleton normal, et de remplacer self:: par static:: qui fait référence à la classe appelante.

    Ensuite dans les classes dérivées, on déclare une variable protected static $instance, et un constructeur protected, et le tour est joué.

  10. #10
    Membre actif
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Janvier 2008
    Messages
    227
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux

    Informations forums :
    Inscription : Janvier 2008
    Messages : 227
    Points : 273
    Points
    273
    Par défaut
    Il ne te reste plus qu'a te faire une installe LAMP (ou tout à partir des dépots, à toi de voir) sur ton serveur privé et le tour sera joué...

    Pour l'astuce d'utiliser debug_backtrace, c'est pas mal !! C'est au final une fonction bien plus pratique qu'il n'y parait. Je l'utilise d'ailleurs depuis peu dans ma toute nouvelle classe Debug pour suivre l'execution du script (ça aide bien).

    Sinon, j'utilise aussi assez souvent Windows mais après m'y être mis, Linux, c'est sympa. Galère au début mais sympa à la fin... Courage et tu verras, le cap dur passer, c'est le pied

  11. #11
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    1
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 1
    Points : 1
    Points
    1
    Par défaut
    Intéressant, petit problème par contre avec le code suivant :

    Citation Envoyé par mistertbo Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    $bar = Config::getInstance();
    $foo = DBLink::getInstance();
     
    var_dump($bar);
    var_dump($foo);
     
    // Output:
    // object(Config)[1]
    //  private 'params' => 
    //    array
    //      empty
    //
    // object(Config)[1]
    //   private 'params' => 
    //     array
    //       empty
    Si tu créés une classe "DBLink", ta méthode getInstance() ne pourra pas te retourner une ressource 'link' qui contiendrai le lien de connexion à la bdd, à moins de redéfinir la méthode getInstance(), ce qui limiterai pas mal l'utilisation de ta classe abstraite...

    Sans étendre ta sous-classe, l'utilisation de "DBLink::getInstance()" ne pourras te retourner qu'un objet "DBLink" mais tu seras obligé de mettre ton lien de connexion à la bdd dans une des propriétés de la classe...

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Organisation des méthodes de classe en POO
    Par vince-nantes dans le forum Langage
    Réponses: 4
    Dernier message: 30/10/2013, 15h59
  2. Réponses: 4
    Dernier message: 15/07/2011, 15h08
  3. [POO] Gestion des Singleton
    Par Bisûnûrs dans le forum Langage
    Réponses: 5
    Dernier message: 05/09/2008, 22h49
  4. [POO] Paramètres optionnels des méthodes PHP
    Par sliderman dans le forum Langage
    Réponses: 2
    Dernier message: 20/12/2007, 15h14
  5. [POO] Comment ajouter des méthodes à un objet DIV ?
    Par Murasame dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 09/10/2007, 00h01

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo