Fonction getInstance (singleton)

[Résolu]
Signaler
Messages postés
1123
Date d'inscription
mardi 8 janvier 2002
Statut
Modérateur
Dernière intervention
21 avril 2009
-
Messages postés
1123
Date d'inscription
mardi 8 janvier 2002
Statut
Modérateur
Dernière intervention
21 avril 2009
-
Bonjour à tous, j'ai un probleme tres étrange dans mon singleton getInstance

Voici le code :
public static function getInstance ($aCnxParams, $bAutoConnect false, $bIsPersistant false) {
        if (!isset (self::$_oInstance) || ((self::$_oInstance instanceof mysql) == false)) {            self::$_oInstance new mysql ($aCnxParams, $bAutoConnect false, $bIsPersistant = false);
        }
        return self::$_oInstance;
}

Ce code me met Fatal error : Call to undefined function  () in ... (la ligne if (!isset (self::$_oInstance) || ((self::$_oInstance instanceof mysql) == false)) {)

Le truc tres étrange, c'est que si je ne met que la partie !isset (...) OU la partie instanceof, ca marche, quand il y a les deux avec un ||, ca me met cette erreur. A croire que c'est le || qui merde.

Une idée peut-etre ?

9 réponses

Messages postés
10839
Date d'inscription
lundi 24 février 2003
Statut
Modérateur
Dernière intervention
2 mars 2010
25
Hello,

y a un truc que je ne pige pas : si ton singleton n'instancie QUE la classe mysql (c'est le cas ici), quel besoin d'avoir un moyen de connaître la classe, vu que c'est toujours mysql ?
Et si ton exemple est mauvais, tu es obligé de passre par de l'usinage (factory), et donc de préciser la classe à  instancier.

Je crois que tu te trompes dans la modélisation de ton bin's. Ton singleton ne doit PAS se trouver au niveau de chaque classe fille.
Sinon tu le répêtes chaque fois, c'est inutile.
Et il ne doit pas à mon sens se trouver au niveau de la classe parente : un objet qui se contient lui même dans une de ses propriétés?? Pourquoi faire ?

ce que tu veux faire, c'est un multiton, associé à une usine (factory) :
<?php
class factory {
    static public $aInstances;
   
    static public function getInstance($sClassName, $iParam) {
        if(!class_exists($sClassName)) {
            throw new Exception($sClassName.' does not exist');
        }
        $o = new ReflectionClass($sClassName);
        if(!$o->isSubclassOf('parentClass')) {
            throw new Exception($sClassName.' is not a subclass of parentClass');
        }
        if(!isset(self::$aInstances[$sClassName])) {
            self::$aInstances[$sClassName] = new $sClassName($iParam);
        }
        return self::$aInstances[$sClassName] ;
    }
}

abstract class parentClass {
    protected $iParam;
   
    protected function __construct($iParam) {
        $this->iParam = $iParam;
    }
   
    abstract public function say();
}

class A extends parentClass {
   
    public function __construct($iParam) {
        parent::__construct($iParam);   
    }
   
    public function say() {
        echo 'I am a A and my param is ',$this->iParam;
    }
}

class B extends parentClass {
   
    public function __construct($iParam) {
        parent::__construct($iParam);   
    }
   
    public function say() {
        echo 'I am a B and my param is ',$this->iParam;
    }
}

class C {
    protected $iParam;
   
    public function __construct($iParam) {
        parent::__construct($iParam);   
    }
   
    public function say() {
        echo 'I am a C and my param is ',$this->iParam, ' but i am NOT a subclass of parentClass!';
    }
}
try {
    $A = factory::getInstance('A', 1);
    $B = factory::getInstance('B', 2);
    var_dump(factory::$aInstances);
    $B = factory::getInstance('B', 3);
    var_dump(factory::$aInstances);
    $C = factory::getInstance('C', 4);
} catch(Exception $e) {
    echo $e->getMessage();
}
?>

J'ai mis factory::$aInstances en "public" pour pouvoir faire mon var_dump() hein. En réalité, ce sera plutôt une propriété privée.

Pour débugger ton 1er problème, il faudrait le code complet. En lisant ce que tu dis, je pense que tu essayes de faire quelque chose de parfaitement illégal :-)

Un exemple de code qui ne fonctionne pas (regardez bien les var_dump() pour comprendre ) :
<?php
abstract class X {
    protected $iParam;
    public static $_oInstance;
   
    protected function __construct($iParam) {
        $this->iParam = $iParam;
    }
   
    abstract public static function getInstance($iParam);
    abstract public function say();
}

class Z extends X {
   
    public static function getInstance ($iParam) {
        if (!isset (self::$_oInstance) || ((self::$_oInstance instanceof X) == false)) {
            self::$_oInstance = new Z ($iParam);
        }
        return self::$_oInstance;
    }
   
    public function say() {
        echo 'I am a Z and my param is '.$this->iParam;
    }
}

class W extends X {
   
    public static function getInstance ($iParam) {
        if (!isset (self::$_oInstance) || ((self::$_oInstance instanceof W) == false)) {
            self::$_oInstance = new W ($iParam);
        }
        return self::$_oInstance;
    }
   
    public function say() {
        echo 'I am a W and my param is '.$this->iParam;
    }
}

try {
    $Z = Z::getInstance(1);
    $W = W::getInstance(2);
    var_dump(X::$_oInstance);
    var_dump(Z::$_oInstance);
    var_dump(W::$_oInstance);
    $Z = Z::getInstance(3);
    $W = W::getInstance(4);
    var_dump(Z::$_oInstance);
    var_dump(W::$_oInstance);
} catch(Exception $e) {
    echo $e->getMessage();
}
?>
Messages postés
1123
Date d'inscription
mardi 8 janvier 2002
Statut
Modérateur
Dernière intervention
21 avril 2009
1
par ailleur, je me demandais s'il était possible de connaitre l'instance fille d'une classe lors d'une abstraction ?

Par exemple, j'ai une classe db abstraite, et une classe mysql qui extends db.
Dans db, j'aimerai dire "c'est mysql qui étends db"
Puisque db étant abstraite, n'importe quelle classe peut étendre db, et j'aurai besoin de savoir laquelle.

Dans l'idée, c'est de mettre le getInstance dans la classe abstraite au lieu de toutes les classes filles, mais le new mysql, je doit le changer en new "child" ... :/
Une idée de comment faire ca aussi ? :p
Messages postés
2483
Date d'inscription
jeudi 30 novembre 2006
Statut
Membre
Dernière intervention
14 janvier 2011
18
Salut,

Pour ton premier problème, j'avoue que je vois pas... C'est très étrange en effet...

Par contre :
"Dans l'idée, c'est de mettre le getInstance dans la classe abstraite au lieu de toutes les classes filles, mais le new mysql, je doit le changer en new "child" ... :/
Une idée de comment faire ca aussi ? :p"

dans ta méthode statique getInstance :
$layer = 'mysql' // admettons...
return new $layer($arg1, $arg2...);

Les variables peuvent être dynamiques, les noms des objets... aussi...

Sinon, pour connaitre l'instance fille, en fait, c'est assez simple : il faut utiliser la fonction get_class(), mais pas n'importe comment.

get_class() utilisé sans argument, retourne le nom de la classe dans laquelle cette fonction est utilisée (la classe abstraite, concrète, voire mêmel'interface).

get_class($this) retourne le nom de la classe concrète : celle qui a été instanciée avec new, et ce, quelle que soit la classe dans laquelle elle est utiliée. Appelée dans la classe abstraite, elle renvoit le nom de la classe concrète.
Messages postés
1123
Date d'inscription
mardi 8 janvier 2002
Statut
Modérateur
Dernière intervention
21 avril 2009
1
le probleme, c'est que mon get_class ($this) serait dans le getInstance, et comme c'est une méthode statique, $this ne marche pas :p

Du coup pour le moment j'ai surchargé ma classe getInstance fille (dans mysql) et j'appel getInstance mère avec comme premier parametre, le nom de la classe à instancier (en l'occurence mysql)
c'est un peu crade mais c'est la seule solution que j'ai :p
Je suis ouvert à d'autres propositions !! :)

Merci de ton aide neigedhiver :)
Messages postés
2483
Date d'inscription
jeudi 30 novembre 2006
Statut
Membre
Dernière intervention
14 janvier 2011
18
Y'a bien un moment où ta méthode statique doit connaitre le nom du layer concret à instancier, puisque là, c'est aussi une factory. T'es dans le même cas que moi avec ma dernière classe de base de données (qui date d'hier soir).

Ma méthode statique getInstance prend un paramètre optionnel : le nom du layer concret (mysql, pgsql, etc) qui est donc le nom de la classe à instancier.
Voici ma méthode getInstance, qui se trouve dans ma classe abstraite :

public final static function getInstance($sMoteur = null) {
if (!empty(self::$oInstance)) {
return self::$oInstance;
}
elseif (empty(self::$oInstance) && !empty($sMoteur)) {
$sFichierMoteur = sprintf(dirname(__FILE__) .'/%1$s/%1$s.class.php', $sMoteur);
if (is_file($sFichierMoteur)) {
require_once($sFichierMoteur);
}
if (class_exists($sMoteur)) {
self::$oInstance = new $sMoteur();
return self::$oInstance;
}
else {
throw new bddException('MOTEUR_INEXISTANT');
}
}
else {
throw new bddException('RIEN_A_RETOURNER');
}
}

Et le constructeur de la classe abstraite :

private final function __construct() {
# Connect to database
$this -> bEstConnecte = $this -> _connect();

# The layer name
$this -> sMoteur = get_class($this);
}

Comme ma classe est un singleton, je me dispense de lui passer en argument les paramètres de connexion, et je vais les chercher directement avec une classe de configuration. Si je c'était un multiton, je lui passerais les paramètres en argument (sous la forme d'un objet config, je trouve ça plus propre).

Et pareil que toi, connaitre le moteur utilisé (le nom de la classe instanciée) me permet d'instancier la bonne classe de résultat. La méthode select(), définie dans ma classe abstraite fait ça :

public final function select($sRequete) {
$sClasseResultat = $this -> sMoteur . '_result';
if (!class_exists($sClasseResultat)) {
throw new bddException('CLASSE_RESULTAT_NON_TROUVEE');
}
$rResultat = $this -> query($sRequete);
return new $sClasseResultat($rResultat);
}

Si ça t'éclaire...
Messages postés
1123
Date d'inscription
mardi 8 janvier 2002
Statut
Modérateur
Dernière intervention
21 avril 2009
1
en fait mon probleme (que je pense avoir résolu par une méthode alternative :p), c'est que get_class($this) se situe dans la méthode static getInstance, donc $this n'a pas encore été initialisé.
Du coup ca marche pas comme ca.
Le truc, c'est que j'ai un lot de plusieurs classes, et j'était parti dans un getInstance pour chaques classes.
Je vais externaliser la getInstance dans une classe singleton, comme ca ca me sera carrément plus facile, ca me facilite la tache, et ca me rends le code plus propre.
Un ptit getInstance ('nomvoulu', 'nomdelaclasse', 'parametres'); et hop ! :)

Merci de ton aide en tout cas pour m'avoir orienté sur tes astuces :) c'est gentil de ta part :)

Par contre si quelqu'un à une idée pour le premier probleme ?
Messages postés
1123
Date d'inscription
mardi 8 janvier 2002
Statut
Modérateur
Dernière intervention
21 avril 2009
1
nan mais en effet, je pense que je vais partir sur une classe factory, car j'ai d'autres classes.
En fait pour le moment au niveau bdd j'ai que mysql, mais il va arriver que j'aie d'autres classes à terme.
En plus, j'ai d'autres classes (aucun rapport avec db) qui utilisent un singleton (user, cache, etc) donc je vais partir sur un multiton. J'ai parlé d'un singleton sur une autre classe, mais je voulais dire multiton, autant pour moi :p

Merci de votre aide :)
Messages postés
10839
Date d'inscription
lundi 24 février 2003
Statut
Modérateur
Dernière intervention
2 mars 2010
25
Note bien, CodeFalse, ce que je montre dans mon 1er exemple : l'API "Reflection" t'apportera de nombreuses réponses quant à l'examen poussé d'une classe.
Messages postés
1123
Date d'inscription
mardi 8 janvier 2002
Statut
Modérateur
Dernière intervention
21 avril 2009
1
Je connaissais pas cette api ! Vraiment sympa :)
Jvais voir ca :)
De toute facon, j'ai retapé ma couche d'abstraction aux db, je vais mettre à jour mon code bientot :)
J'attends vos avis :)