web-dev-qa-db-fra.com

Générer dynamiquement des classes lors de l'exécution en php?

Voici ce que je veux faire:

$clsName = substr(md5(Rand()),0,10); //generate a random name
$cls = new $clsName(); //create a new instance

function __autoload($class_name)
{
  //define that instance dynamically
}

Évidemment, ce n’est pas ce que je suis en train de faire, mais au fond, j’ai des noms inconnus pour une classe et sur la base de ce nom, je veux générer la classe avec certaines propriétés, etc.

J'ai essayé d'utiliser eval () mais cela me donne des crises sur les noms privés et $ this-> références ...

//modifier

Ok, évidemment, mon court et doux "voici ce que je veux faire" a provoqué des conflits et une consternation énormes parmi ceux qui pourraient être en mesure de fournir des réponses. Dans l'espoir d'obtenir une réponse concrète, je serai plus détaillé.

J'ai un cadre de validation utilisant des indicateurs de code sur le site que je gère. Chaque fonction a deux définitions

function DoSomething($param, $param2){
   //code
}
function DoSomething_Validate(vInteger $param, vFloat $param2){
   //return what to do if validation fails
}

Je cherche à ajouter un validateur pour les clés primaires dans ma base de données. Je ne veux pas créer une classe séparée pour chaque table (203). Donc, mon plan était de faire quelque chose comme

function DoSomething_Validate(vPrimaryKey_Products $id){ }

Où __autoload générerait une sous-classe de vPrimaryKey et définirait le paramètre de table sur Products.

Heureux maintenant?

27
Will Shaver

C'est presque certainement une mauvaise idée.

Je pense que votre temps serait mieux dépensé pour créer un script qui créerait vos définitions de classe pour vous, et n'essayer pas de le faire au moment de l'exécution. 

Quelque chose avec une signature en ligne de commande comme:

./generate_classes_from_db <Host> <database> [tables] [output dir]
3
hobodave

c'est drôle, en fait c'est l'une des rares choses où eval ne semble pas être une si mauvaise idée.

tant que vous pouvez vous assurer qu'aucune entrée d'utilisateur n'entrera jamais dans l'eval.

vous avez toujours des inconvénients, par exemple, lorsque vous utilisez un cache de code-octet, ce code ne sera pas mis en cache, etc., mais les problèmes de sécurité d'eval sont plutôt liés à la saisie de l'utilisateur dans l'eval, ou au fait de se retrouver dans une mauvaise portée.

si vous savez ce que vous faites, eval vous aidera avec ceci.

Cela dit, à mon avis, vous valez beaucoup mieux si vous ne vous fiez pas à l'indication de type pour votre validation, mais que vous avez une fonction. 

DoSomething_Validate($id)
{ 
   // get_class($id) and other validation foo here
}
12
foobaer

Je sais que c’est une vieille question et que les réponses fonctionneront, mais je voulais offrir quelques extraits qui répondraient à la question initiale et je pense proposer une solution plus étendue si quelqu'un devait se retrouver ici comme je le faisais lorsque je cherchais une réponse. à ce problème.

Créer une classe dynamique unique

<?php
// Without properties
$myclassname = "anewclassname";
eval("class {$myclassname} { }";
// With a property
$myclassname = "anewclassname";
$myproperty = "newproperty";
eval("class {$myclassname} { protected \${$myproperty}; }";
?>

Tant que vous échappez correctement à votre texte, vous pouvez également y ajouter une fonction.

Mais qu'en est-il si vous souhaitez créer de manière dynamique des classes en fonction de quelque chose qui pourrait être dynamique, par exemple créer une classe pour chaque table de votre base de données, comme indiqué dans la question initiale?

Créer plusieurs classes dynamiques

<?php

// Assumes $dbh is a pdo connection handle to your MySQL database
$stmt=$dbh->prepare("show tables");
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
$handle = null;
$classcode = '';
foreach ($result as $key => $value) {
    foreach ($value as $key => $value) {
        $classcode = "class {$value} { ";
        $stmt2=$dbh->prepare("DESC $value");
        $stmt2->execute();
        $result2 = $stmt2->fetchAll(PDO::FETCH_ASSOC);
        foreach ($result2 as $key => $value) {
            $classcode .= "public \${$value['Field']}; ";
        }
        $classcode .=  "}";
        eval($classcode);
    }
}

?>

Cela générera dynamiquement une classe pour chaque table d'une base de données. Pour chaque classe, une propriété nommée après chaque colonne sera également créée.

Maintenant, il a été souligné que vous ne devriez pas faire cela. Tant que vous contrôlez ce qui se passe dans eval, le risque de sécurité ne pose pas de problème. MAIS - il y a probablement une solution qui a plus de sens si vous y réfléchissez suffisamment. Je pensais avoir le cas d'utilisation idéal pour créer dynamiquement de nouvelles classes. Un examen attentif du problème a prouvé le contraire.

Une solution potentielle - utilisez la classe stdClass pour créer des objets qui ne sont que des conteneurs de données et ne nécessitent aucune méthode.

En outre, comme mentionné, vous pouvez utiliser un script pour générer manuellement de nombreuses classes. Dans le cas de classes reflétant vos tables de base de données, vous pouvez utiliser la même logique que ci-dessus et au lieu de faire une évaluation, écrivez ces informations dans un fichier.

10
akincer

Je pense que l'utilisation de eval() n'est pas une solution fiable, en particulier si votre script ou votre logiciel sera distribué à différents clients. Les fournisseurs d'hébergement partagé désactivent toujours la fonction eval().

Je pense à une meilleure approche comme celle-ci:

<?php 

 function __autoload( $class ) {
      require 'classes/'.$class.'.php';

}

$class = 'App';
$code = "<?php class $class {
    public function run() {
        echo '$class<br>';  
    }
    ".'
    public function __call($name,$args) {
        $args=implode(",",$args);
        echo "$name ($args)<br>";
    }
}';

file_put_contents('classes/App.php' ,$code);

$a = new $class();
$a->run();

Après avoir fini d’exécuter le code, vous pouvez supprimer le fichier si vous le souhaitez, je l’ai testé et cela fonctionne parfaitement.

4
Waseem Abu Senjer
function __autoload($class)  {
    $code = "class $class {`
        public function run() {
            echo '$class<br>';  
        }
        ".'
        public function __call($name,$args) {
            $args=implode(",",$args);
            echo "$name ($args)<br>";
        }
    }';

    eval($code);
}

$app=new Klasse();
$app->run();
$app->HelloWorld();

Cela pourrait aider à créer une classe au moment de l'exécution. Il crée également une exécution méthoraire et une méthode catchall pour les méthodes inconnues Mais il vaut mieux créer des objets au moment de l'exécution, pas des classes.

3
Matthias

Utiliser eval () est vraiment une mauvaise idée. Il ouvre un grand trou de sécurité. Ne l'utilisez pas!

2
Henrik P. Hessel

S'il vous plaît lire les réponses de tous les autres sur comment c'est vraiment une très très mauvaise idée.

Une fois que vous avez compris cela, voici une petite démonstration sur la façon dont vous pourriez, mais ne devriez pas, le faire.


<?php
$clname = "TestClass";

eval("class $clname{}; \$cls = new $clname();");

var_dump($cls);
2
MitMaro

À partir de PHP 7.0, avec un peu de créativité et une connaissance de fonctionnalités moins connues de PHP, vous pouvez absolument le faire sans recourir à eval ou à la création dynamique de fichiers de script. Vous devez juste utiliser classes anonymes et class_alias (), comme ceci:

spl_autoload_register(function ($unfoundClassName) {
{
    $newClass = new class{}; //create an anonymous class
    $newClassName = get_class($newClass); //get the name PHP assigns the anonymous class
    class_alias($newClassName, $unfoundClassName); //alias the anonymous class with your class name
}

Cela fonctionne car les classes anonymes se voient toujours attribuer un nom en coulisse et sont placées dans la portée globale. Vous êtes donc libre de saisir ce nom de classe et de l'alias. Consultez le deuxième commentaire sous le lien des classes anonymes ci-dessus pour plus d'informations.

Cela dit, je me sens comme tous les participants à cette question qui se disent "Eval est toujours une très mauvaise idée. Ne l'utilisez jamais!" ne font que répéter ce qu’ils ont entendu de l’esprit de la ruche et ne pensent pas par eux-mêmes. Eval est dans la langue pour une raison et il y a des situations où il peut être utilisé efficacement. Si vous utilisez une ancienne version de PHP, eval pourrait être une bonne solution ici.

Cependant, ils sont corrects en ce que peut ouvrir de très grandes failles de sécurité et vous devez faire attention à la façon dont vous l'utilisez et comprendre comment éliminer les risques. . L'important est que, tout comme l'injection SQL, vous devez effacer toutes les entrées que vous avez entrées dans l'instruction eval. 

Par exemple, si votre chargeur automatique ressemblait à ceci:

spl_autoload_register(function ($unfoundClassName) {
{
    eval("class $unfoundClassName {}");
}

Un pirate informatique pourrait faire quelque chose comme ça:

$injectionCode = "bogusClass1{} /*insert malicious code to run on the server here */ bogusClass2";

new $injectionCode();

Voyez comment cela peut potentiellement constituer une faille de sécurité? Anything le pirate informatique insère entre les deux noms de bogusClass sera exécuté sur votre serveur par l'instruction eval.

Si vous ajustez l'autoloader pour vérifier le nom de classe transmis (par exemple, en effectuant un preg_match pour vous assurer qu'il n'y a pas d'espace ou de caractères spéciaux, en le comparant à une liste de noms acceptables, etc.), vous pouvez éliminer ces risques et ensuite totalement bien à utiliser dans cette situation. Si vous êtes sur PHP 7 ou supérieur, je vous recommande la méthode d'alias de classe anonyme ci-dessus.

1
dallin

J'ai créé un paquet qui crée dynamiquement des classes/interfaces/traits ... et les stocke dans un fichier . Vous pouvez ensuite simplement inclure le fichier créé pour pouvoir utiliser votre classe.

package: https://packagist.org/packages/kanel/enuma

0
Adil El Kanabi