[php5] factory et paramètres constructeurs

Soyez le premier à donner votre avis sur cette source.

Snippet vu 6 107 fois - Téléchargée 23 fois

Contenu du snippet

Lorsque l'on utilise une gestion modulaire (par exemple un système de Factory), et que l'on charge dynamiquement les classes, il est plutôt usuel de leur passer en paramètre un tableu de configuration :
return new $className($config);
Seulement, il arrive peut-être des fois où l'on veut intégrer une classe déjà existante que, elle, prends quelques paramètres. Il n'est certes pas difficile du tout de réécrire le bout de code faisant en sorte que le tout soit géré dans un array, mais je pense qu'il est globalement mieux d'avoir une liste de paramètres. D'autant plus que celà oblige à passer le nombre exact de paramètres, sinon PHP renvoie une erreur. Avec un array, il faut nous même gêrer ça... Et comme les informaticiens programment plus pour en faire le moins possible après, je pense que ce code a sa place ici :)
Pour ceux qui ne connaissent pas, voici deux liens :
http://en.wikipedia.org/wiki/Reflection_%28computer_science%29 (ou http://fr.wikipedia.org/wiki/R%C3%A9flexion_%28informatique%29 , mais moins complet ... )
http://php.net/reflection

Pour faire simple, la réflection, c'est la capacité d'un programme à analyser son état, où à se modifier lui-même.
Il est par exemple possible, en javascript, de modifier le comportemt d'une fonction :
http://en.wikipedia.org/wiki/Self-modifying_code#Example_JavaScript_self-modifying_code

Bref, il n'est pas question ici de modifier le comportement d'une fonction ( il existe d'ailleurs runkit pour ça pour le moment [Même si ça ne vaut pas la réflexion] : http://php.net/runkit ), mais tout simplement de faire appel à une fonction de l'API de réflexion pour instancier notre classe.

Voir le code ci-dessous.

Source / Exemple :


<?php

class MySQL
{
	function __construct($sBdd, $sUser, $sPass)
	{
		echo '<strong>Paramètres :</strong> ' . $sBdd . '/' . $sUser . '/' . $sPass . '<br />';
	}
}

class Postgresql
{
    function __construct($sConnextionString)
    {
        echo '<strong>Paramètres :</strong> ' . $sConnextionString . '<br />';
    }
}

class Factory
{
	private static $_aInstances = array();

	public static function createInstance($sClassName, $mArgs)
	{
		if (!isset(self::$_aInstances[$sClassName]))
		{
			if (class_exists($sClassName))
			{
				$oClass = new ReflectionClass($sClassName);
				if (is_array($mArgs))
				{
					self::$_aInstances[$sClassName] = $oClass->newInstanceArgs($mArgs);
				}
				else if ($mArgs)
				{
					self::$_aInstances[$sClassName] = $oClass->newInstance($mArgs);
				}
				else
				{
					self::$_aInstances[$sClassName] = $oClass->newInstance();
				}
			}
			else
			{
				throw new Exception('La classe ' . $sClassName . ' n\'existe pas !');
			}
		}
		return self::$_aInstances[$sClassName];
	}
}

try
{
	$oMySQL = Factory::createInstance('MySQL', array('bdd.exemple.com', 'MyUser', 'MyPass'));
	echo '<strong>$oMySQL</strong> est une instance de la classe : ' . get_class($oMySQL) . '<br />';
	$oPgsql = Factory::createInstance('Postgresql', 'host=bdd.exemple.com dbname=test user=MyUser password=MyPass');
	echo '<strong>$oPgsql</strong> est une instance de la classe : ' . get_class($oPgsql) . '<br />';
}
catch (Exception $e)
{
	echo $e->getMessage();
}

?>

Conclusion :


Bon j'aurais aussi pu remplacer :
///
if (is_array($mArgs))
{
self::$_aInstances[$sClassName] = $oClass->newInstanceArgs($mArgs);
}
else
{
self::$_aInstances[$sClassName] = $oClass->newInstance($mArgs);
}
\\\

Par

///
$newFunction = (is_array($mArgs) ? 'newInstanceArgs' : 'newInstance');
self::$_aInstances[$sClassName] = $oClass->$newFunction($mArgs);
\\\

Ah, et le code du Factory a été très largement inspiré par cette source, merci à l'auteur :
http://www.phpcs.com/codes/PHP5-COUCHE-ABSTRACTION-SGBD_35488.aspx

Have fun :)
Mais j'ai préféré garder le code lisible pour que tout le monde puisse comprendre :)

A voir également

Ajouter un commentaire Commentaires
Messages postés
1423
Date d'inscription
mardi 14 décembre 2004
Statut
Membre
Dernière intervention
29 décembre 2012
4
Avec un peu de retard ...
Je crois que y'a une coquille ligne 34 !
Si je veux instancer un Boolean je dois passer un param disons ... false pour créer un bool qui vaut false.
A vu de nez euh
l.23 public static function createInstance($sClassName, $mArgs=null)
l.34 elseif(!is_null($mArgs)){

J'me trompe ?
Messages postés
455
Date d'inscription
mardi 17 septembre 2002
Statut
Membre
Dernière intervention
22 juillet 2007

La pratique, c'est toujours utile. Je viens donc de mettre en pratique ce code, et je viens de me rendre compte que j'avais oublié le cas où il n'y aurais pas de constructeur dans la classe ( où si le constructeur ne prends pas de paramètres )
Messages postés
1293
Date d'inscription
mardi 9 novembre 2004
Statut
Membre
Dernière intervention
21 mai 2015

Ouep c'est ttrès utile comme principe... j'utilise quelque chose de similaire en php4... à la différence près que...

1 $arg est facultatif... si vide la class est instanciée sans arguments

2 le fichier de la class est inclu par la fonction si la class n'est pas déclarée (et que le fichier est trouvé biensur)... .. .

Au final c'est une sorte d'__autoload() à la sauce php4... .. .

^_^
@ tchaOo°
Messages postés
455
Date d'inscription
mardi 17 septembre 2002
Statut
Membre
Dernière intervention
22 juillet 2007

Merci ! Et merci du rappel aussi...
Dans ma classe de test, j'avais bel et bien mis la gestion d'erreur, mais lorsque j'ai mis en forme pour poster ici, ça a disparu... Et j'ai pas pensé à vérifier. Fatigue quand tu nous tiens. :)

En tout cas, j'espère que cette API évoluera encore :p
Messages postés
10839
Date d'inscription
lundi 24 février 2003
Statut
Modérateur
Dernière intervention
2 mars 2010
24
Hello,

c'est pas mal du tout. Utiliser l'API de réflexion pour une méthode d'usinage est une très bonne idée.
Manque juste une petite gestion d'erreur si on demande une classe n'existant pas (une Exception), et un try catch sur tes instanciations, ensuite (en dehors de la classe je veux dire).

J'avais utilisé cet API en profondeur pour ma classe de documentation de classes et de fonctions, mais je n'y avais jamais pensé poru de l'usinage.

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.