Klassen dynamisch zur Laufzeit in PHP generieren?

Lesezeit: 10 Minuten

Benutzer-Avatar
Will Rasierer

Folgendes möchte ich tun:

$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
}

Offensichtlich ist das nicht das, was ich eigentlich mache, aber im Grunde habe ich unbekannte Namen für eine Klasse und basierend auf dem Namen möchte ich die Klasse mit bestimmten Eigenschaften usw. generieren.

Ich habe versucht, eval() zu verwenden, aber es gibt mir Anfälle über private und $this-> Referenzen …

//bearbeiten

Ok, offensichtlich hat mein kurzes und süßes „hier ist, was ich tun möchte“ massiven Streit und Bestürzung bei denen ausgelöst, die möglicherweise Antworten geben können. In der Hoffnung auf eine konkrete Antwort werde ich ausführlicher.

Ich habe ein Validierungsframework, das Codehinweise auf der von mir gepflegten Website verwendet. Jede Funktion hat zwei Definitionen

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

Ich möchte meiner Datenbank einen Validator für Primärschlüssel hinzufügen. Ich möchte nicht für JEDE Tabelle (203) eine separate Klasse erstellen. Also mein Plan war, sowas zu machen

function DoSomething_Validate(vPrimaryKey_Products $id){ }

Wobei __autoload eine Unterklasse von vPrimaryKey generieren und den Tabellenparameter auf Products setzen würde.

Jetzt glücklich?

  • Ich schlage vor, Sie sagen uns, was genau Sie zu tun versuchen, und fragen uns, wie Sie es besser machen können. Was Sie versuchen, ist nicht der richtige Ansatz.

    – hobodave

    29. Juli 2009 um 23:30 Uhr

  • Ich habe dieses positiv bewertet, damit es keine negative Bewertung gab. Das ist eine berechtigte Frage, auch wenn es eine schlechte Idee ist.

    – MitMaro

    29. Juli 2009 um 23:38 Uhr

  • Abwertung entfernt. Danke fürs klarstellen.

    – hobodave

    30. Juli 2009 um 0:16 Uhr

  • Diese Frage haben viel bessere Antworten.

    – Thiago Macedo

    4. Januar 2013 um 15:26 Uhr

  • Eine Alternative in einigen Szenarien ist die Verwendung von Überladung: Sie beginnen mit einer generischen Klasse, mit drei __call __get und __set Methoden, die dynamisch zur Laufzeit das Klassenverhalten implementieren — php.net/manual/en/language.oop5.overloading.php

    – Dereckson

    7. Februar 2014 um 16:41 Uhr

Ab PHP 7.0 können Sie dies mit ein wenig Kreativität und Kenntnissen einiger weniger bekannter PHP-Funktionen absolut tun, ohne auf Eval zurückzugreifen oder Skriptdateien dynamisch zu erstellen. Sie müssen nur verwenden Anonymer Unterricht und class_alias(), so:

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
}

Dies funktioniert, weil anonymen Klassen hinter den Kulissen immer noch ein Name zugewiesen und in den globalen Bereich gestellt wird, sodass Sie diesen Klassennamen frei greifen und ihm einen Alias ​​geben können. Weitere Informationen finden Sie im zweiten Kommentar unter dem Link zu den anonymen Klassen oben.

Trotzdem fühle ich mich wie alle Leute in dieser Frage, die sagen: “Eval ist immer eine sehr schlechte Idee. Verwenden Sie es einfach nie!” wiederholen nur, was sie aus dem Schwarmgeist gehört haben, und denken nicht für sich selbst. Eval ist aus einem bestimmten Grund in der Sprache, und es gibt Situationen, in denen es effektiv eingesetzt werden kann. Wenn Sie eine ältere PHP-Version verwenden, ist eval hier möglicherweise eine gute Lösung.

Jedochsie haben damit Recht kann öffnen sehr große Sicherheitslücken und Sie müssen vorsichtig sein, wie Sie es verwenden und verstehen, wie Sie die Risiken eliminieren können. Wichtig ist, dass Sie, ähnlich wie bei der SQL-Injection, alle Eingaben bereinigen müssen, die Sie in die eval-Anweisung eingeben.

Wenn Ihr Autoloader beispielsweise so aussah:

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

Ein Hacker könnte so etwas tun:

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

new $injectionCode();

Sehen Sie, wie dies das Potenzial hat, eine Sicherheitslücke zu sein? Irgendetwas der Hacker zwischen die beiden falschen Klassennamen setzt, wird auf Ihrem Server durch die eval-Anweisung ausgeführt.

Wenn Sie Ihren Autoloader so einstellen, dass er den übergebenen Klassennamen überprüft (dh ein preg_match durchführen, um sicherzustellen, dass keine Leerzeichen oder Sonderzeichen vorhanden sind, ihn mit einer Liste akzeptabler Namen vergleichen usw.), können Sie diese Risiken beseitigen, und dann könnte eval sein völlig in Ordnung in dieser Situation zu verwenden. Wenn Sie jedoch mit PHP 7 oder höher arbeiten, empfehle ich die anonyme Klassen-Alias-Methode oben.

Benutzer-Avatar
foobaer

Es ist lustig, eigentlich ist dies eines der wenigen Dinge, bei denen eval keine so schlechte Idee zu sein scheint.

solange Sie sicherstellen können, dass keine Benutzereingaben jemals in die Bewertung eingehen.

Sie haben immer noch Nachteile, wie wenn Sie einen Bytecode-Cache verwenden, der Code nicht zwischengespeichert wird usw. usw., aber die Sicherheitsprobleme von eval hängen ziemlich stark mit Benutzereingaben in der eval zusammen oder damit, dass sie im falschen Bereich landen.

Wenn Sie wissen, was Sie tun, hilft Ihnen eval dabei.

Meiner Meinung nach sind Sie jedoch viel besser dran, wenn Sie sich bei Ihrer Validierung nicht auf Typhinweise verlassen, sondern eine Funktion haben

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

Benutzer-Avatar
ähnlich

Ich weiß, dass dies eine alte Frage ist und es Antworten gibt, die funktionieren WERDEN, aber ich wollte ein paar Ausschnitte anbieten, die die ursprüngliche Frage beantworten würden, und ich denke, eine erweiterte Lösung anbieten, sollte jemand hier enden, wie ich es bei der Suche nach einer Antwort getan habe zu diesem Problem.

Erstellen Sie eine einzelne dynamische Klasse

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

Solange Sie Ihren Text richtig maskieren, könnten Sie dort auch eine Funktion hinzufügen.

Aber was ist, wenn Sie Klassen dynamisch erstellen möchten, die auf etwas basieren, das selbst dynamisch sein könnte, z. B. das Erstellen einer Klasse für jede Tabelle in Ihrer Datenbank, wie in der ursprünglichen Frage erwähnt?

Erstellen Sie mehrere dynamische Klassen

<?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);
    }
}

?>

Dadurch wird dynamisch eine Klasse für jede Tabelle in einer Datenbank generiert. Für jede Klasse wird AUCH eine Eigenschaft erstellt, die nach jeder Spalte benannt ist.

Jetzt wurde darauf hingewiesen, dass Sie dies nicht tun sollten. Solange Sie kontrollieren, was in der Evaluierung vor sich geht, ist das Sicherheitsrisiko kein Problem. ABER – es gibt höchstwahrscheinlich eine Lösung, die sinnvoller ist, wenn Sie gründlich genug darüber nachdenken. Ich dachte, ich hätte den perfekten Anwendungsfall für die dynamische Erstellung neuer Klassen. Eine sorgfältige Untersuchung des Problems bewies das Gegenteil.

Eine mögliche Lösung: Verwenden Sie die stdClass zum Erstellen von Objekten, die nur Datencontainer sind und keine Methoden benötigen.

Außerdem könnten Sie, wie bereits erwähnt, ein Skript verwenden, um viele Klassen manuell zu generieren. Im Fall von Klassen, die Ihre Datenbanktabellen spiegeln, könnten Sie die gleiche Logik verwenden, die ich oben habe, und anstatt eine Bewertung durchzuführen, diese Informationen in eine Datei schreiben.

  • Der letzte Absatz Ihrer Antwort ist ziemlich gut – der Code ist für eval derselbe wie für die Klasse in der Datei, also schreiben Sie diese Klassen besser in die Datei und haben sie im VCS-Repository

    – PeterM

    3. Oktober 2016 um 10:45 Uhr

Ich denke, mit eval() Dies ist keine zuverlässige Lösung, insbesondere wenn Ihr Skript oder Ihre Software an verschiedene Clients verteilt wird. Shared-Hosting-Anbieter immer deaktivieren eval() Funktion.

Ich denke an einen besseren Ansatz wie diesen:

<?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();

Nachdem Sie den Code ausgeführt haben, können Sie die Datei löschen, wenn Sie möchten, ich habe es getestet und es funktioniert perfekt.

Die Verwendung von eval() ist wirklich eine schlechte Idee. Es öffnet eine große Sicherheitslücke. Nur nicht verwenden!

  • Ich verstehe diese Ironie vielleicht nicht, aber als ernsthafte Antwort – eval() ist per Definition nicht unsicher, wenn es so wäre, gäbe es keine solche Funktion. Es ist seine Eingabe, die die Sicherheitslücken öffnet, wenn sie mit Benutzereingaben kombiniert wird. Wenn Sie zum Beispiel eine eval('2+2') oder eval('function() { return 42; }') es gibt kein sicherheitsbezogenes Problem, selbst wenn eine solche Entwurfsentscheidung fragwürdig ist.

    – Tomasz Kowalczyk

    23. Juni 2014 um 18:02 Uhr

  • @TomaszKowalczyk Niemand hat gesagt eval() ist per definitionem nicht sicher. Aber es ist besser, solche fehleranfälligen Konstrukte genauso zu vermeiden, wie Sie es vermeiden sollten, SQL-Strings und -Werte miteinander zu verketten. eval('2+2') ist genauso sicher SELECT name FROM user ist sicher, aber das sind nur Spielzeugbeispiele. Echter Code sieht anders aus. Echter Code muss komplexe Dinge tun, die unsichere Benutzereingaben beinhalten, und genau dort passieren die Fehler.

    – jlh

    21. März 2019 um 12:43 Uhr

Benutzer-Avatar
Sgöttschkes

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();

Dies kann helfen, eine Klasse zur Laufzeit zu erstellen. Es erstellt auch eine Methor-Ausführung und eine Catchall-Methode für unbekannte Methoden. Erstellen Sie jedoch besser Objekte zur Laufzeit, keine Klassen.

  • Ich verstehe diese Ironie vielleicht nicht, aber als ernsthafte Antwort – eval() ist per Definition nicht unsicher, wenn es so wäre, gäbe es keine solche Funktion. Es ist seine Eingabe, die die Sicherheitslücken öffnet, wenn sie mit Benutzereingaben kombiniert wird. Wenn Sie zum Beispiel eine eval('2+2') oder eval('function() { return 42; }') es gibt kein sicherheitsbezogenes Problem, selbst wenn eine solche Entwurfsentscheidung fragwürdig ist.

    – Tomasz Kowalczyk

    23. Juni 2014 um 18:02 Uhr

  • @TomaszKowalczyk Niemand hat gesagt eval() ist per definitionem nicht sicher. Aber es ist besser, solche fehleranfälligen Konstrukte genauso zu vermeiden, wie Sie es vermeiden sollten, SQL-Strings und -Werte miteinander zu verketten. eval('2+2') ist genauso sicher SELECT name FROM user ist sicher, aber das sind nur Spielzeugbeispiele. Echter Code sieht anders aus. Echter Code muss komplexe Dinge tun, die unsichere Benutzereingaben beinhalten, und genau dort passieren die Fehler.

    – jlh

    21. März 2019 um 12:43 Uhr

Das ist mit ziemlicher Sicherheit eine schlechte Idee.

Ich denke, Ihre Zeit wäre besser damit verbracht, ein Skript zu erstellen, das Ihre Klassendefinitionen für Sie erstellt, und nicht zu versuchen, dies zur Laufzeit zu tun.

Etwas mit einer Befehlszeilensignatur wie:

./generate_classes_from_db <host> <database> [tables] [output dir]

  • Dave – Ich mag Ihre Idee, die Klassen mit einem Skript zu generieren. Daran hätte ich denken sollen. Ich weiß, dass Eval böse ist, deshalb habe ich die Frage gestellt … 🙂

    – Will Shaver

    30. Juli 2009 um 15:20 Uhr

1219290cookie-checkKlassen dynamisch zur Laufzeit in PHP generieren?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy