instancier une classe à partir d'une variable en PHP?

146

Je sais que cette question semble assez vague, je vais donc la clarifier avec un exemple:

$var = 'bar';
$bar = new {$var}Class('var for __construct()'); //$bar = new barClass('var for __construct()');

C'est ce que je veux faire. Comment feriez-vous cela? Je pourrais bien sûr utiliser eval () comme ceci:

$var = 'bar';
eval('$bar = new '.$var.'Class(\'var for __construct()\');');

Mais je préfère rester à l'écart de eval (). Y a-t-il un moyen de faire cela sans eval ()?

Pim Jager
la source

Réponses:

215

Mettez d'abord le nom de la classe dans une variable:

$classname=$var.'Class';

$bar=new $classname("xyz");

C'est souvent le genre de chose que vous verrez enveloppée dans un modèle Factory.

Voir Espaces de noms et fonctionnalités de langage dynamique pour plus de détails.

Paul Dixon
la source
2
Voilà comment je fais. Notez qu'à partir des classes, vous pouvez utiliser parent et soi.
Ross
1
Sur une note similaire, vous pouvez également faire $ var = 'Name'; $ obj -> {'obtenir'. $ var} ();
Mario
1
Bon point, bien que cela ne fonctionne que pour les appels de méthode et non pour les constructeurs
Paul Dixon
12
Si vous travaillez avec un espace de noms, placez l'espace de noms actuel dans la chaîne:$var = __NAMESPACE__ . '\\' . $var . 'Class';
bastey
@bastey - mais pourquoi? Je viens de passer trop de temps à comprendre pourquoi cela ne fonctionnerait pas sans l'espace de noms précédant le nom de la classe ...
jkulak
72

Si vous utilisez des espaces de noms

Dans mes propres conclusions, je pense qu'il est bon de mentionner que vous (pour autant que je sache) devez déclarer le chemin complet de l'espace de noms d'une classe.

MyClass.php

namespace com\company\lib;
class MyClass {
}

index.php

namespace com\company\lib;

//Works fine
$i = new MyClass();

$cname = 'MyClass';

//Errors
//$i = new $cname;

//Works fine
$cname = "com\\company\\lib\\".$cname;
$i = new $cname;
csga5000
la source
6
La seule solution qui fonctionne lorsque vous avez besoin de créer avec un espace de noms est la vôtre. Merci pour le partage!
Tiago Gouvêa
Mais c'est juste bizarre, pourquoi n'utiliserait-il pas l'espace de noms déclaré?
Yisrael Dov
@YisraelDov Oui, c'est certainement contre-intuitif. Mais pour une raison quelconque, il se comporte de manière étrange dans ce contexte. Merci php
csga5000
@YisraelDov Je suppose que c'est parce que dans ce cas, instancier une classe à partir d'un autre espace de noms serait un énorme casse-tête. Considérez également que les variables peuvent être transmises. Lorsque le nom de la classe est écrit sous forme de texte dans un fichier php, celui qui l'écrit sait exactement dans quel espace de nom il est écrit. Mais quand une variable est passée entre les fonctions, ce serait un cauchemar d'en faire le suivi. De plus, si vous utilisez get_class (), il restituera le nom de classe FQ, afin qu'il puisse être stocké dans une variable immédiatement et utilisé n'importe où. Si cela dépendait de l'espace de noms du fichier, cela ne fonctionnerait pas très bien.
sbnc.eu
61

Comment transmettre également des paramètres de constructeur dynamiques

Si vous souhaitez transmettre des paramètres de constructeur dynamiques à la classe, vous pouvez utiliser ce code:

$reflectionClass = new ReflectionClass($className);

$module = $reflectionClass->newInstanceArgs($arrayOfConstructorParameters);

Plus d'informations sur les classes et paramètres dynamiques

PHP> = 5,6

Depuis PHP 5.6, vous pouvez encore plus simplifier cela en utilisant Argument Unpacking :

// The "..." is part of the language and indicates an argument array to unpack.
$module = new $className(...$arrayOfConstructorParameters);

Merci à DisgruntledGoat pour l'avoir signalé.

grippe
la source
7
À partir de PHP 5.6, vous devriez pouvoir utiliser la décompression des arguments:new $className(...$params)
DisgruntledGoat
29
class Test {
    public function yo() {
        return 'yoes';
    }
}

$var = 'Test';

$obj = new $var();
echo $obj->yo(); //yoes
Zalew
la source
Non, ce n'est pas un bon exemple. Ne fonctionne que si tout est dans le même fichier php.
fralbo
2
2ndGAB devrait supprimer ce commentaire. Le chargement automatique n'a rien à voir avec cette question.
Jim Maguire
-1

Je recommanderais les méthodes call_user_func()ou call_user_func_arrayphp. Vous pouvez les consulter ici ( call_user_func_array , call_user_func ).

exemple

class Foo {
static public function test() {
    print "Hello world!\n";
}
}

 call_user_func('Foo::test');//FOO is the class, test is the method both separated by ::
 //or
 call_user_func(array('Foo', 'test'));//alternatively you can pass the class and method as an array

Si vous avez des arguments que vous passez à la méthode, utilisez la call_user_func_array()fonction.

exemple.

class foo {
function bar($arg, $arg2) {
    echo __METHOD__, " got $arg and $arg2\n";
}
}

// Call the $foo->bar() method with 2 arguments
call_user_func_array(array("foo", "bar"), array("three", "four"));
//or
//FOO is the class, bar is the method both separated by ::
call_user_func_array("foo::bar"), array("three", "four"));
pickman murimi
la source
3
Cette réponse ne s'applique pas à la question. OP demande comment INSTANTIER une classe (en déclenchant dynamiquement la méthode de classe __construct) à partir d'une chaîne variable et en récupérant l'OBJET DE CLASSE dans une nouvelle variable - votre conseil renverra la VALEUR de la classe-> méthode ([]) vous ' re appelant, pas l'objet de classe donc ne s'applique pas à ce cas. Voir la valeur de retour php.net/manual/en/function.call-user-func-array.php
ChrisN