2009-07-29 8 views
27

Ecco cosa voglio fare:Genera dinamicamente le classi in fase di runtime in php?

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

Ovviamente questo non è quello che sto facendo in realtà, ma in fondo non ho nomi sconosciuti per una classe e in base al nome, voglio generare la classe con alcune proprietà ecc

ho provato con eval(), ma mi sta dando adatta sopra privato e $ this-> riferimenti ...

// modifica

Ok, ovviamente la mia breve e dolce "ecco cosa voglio fare" ha causato enorme conflitto e costernazione tra coloro che potrebbero essere in grado di fornire risposte. Nella speranza di ottenere una risposta effettiva sarò più dettagliato.

Possiedo un framework di convalida che utilizza i suggerimenti sul codice nel sito che gestisco. Ogni funzione ha due definizioni

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

Sto cercando di aggiungere un validatore per le chiavi primarie nel mio database. Non voglio creare una classe separata per OGNI tabella (203). Quindi il mio piano era di fare qualcosa di simile

function DoSomething_Validate(vPrimaryKey_Products $id){ } 

Qualora il __autoload genererebbe una sottoclasse di vPrimaryKey e impostare il parametro tabella per i prodotti.

Felice ora?

+1

Ti suggerisco di dirci esattamente cosa stai cercando di fare e chiederci come fare meglio. Quello che stai tentando non è l'approccio giusto. – hobodave

+2

L'ho upvoted questo così non ha avuto un voto negativo. Questa è una domanda valida, anche se è una cattiva idea. – MitMaro

+0

Ho aggiornato la domanda. –

risposta

4

Questa è quasi certamente una cattiva idea.

Penso che il tuo tempo sarebbe meglio spendere la creazione di uno script che creerebbe le definizioni di classe per te, e non provando a farlo in fase di runtime.

Qualcosa con una firma a linea di comando come:

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

Dovresti fornire almeno un motivo per cui. –

+14

Se non sapevi cosa fosse la morte allora sì, dovrebbe essere spiegato. L'OP potrebbe non comprendere i problemi di sicurezza con questo. – MitMaro

+1

Punto fisso hobodave. – MitMaro

2

Utilizzare eval() è davvero una cattiva idea. Si apre un grande buco di sicurezza. Basta non usarlo!

+1

+1 per dare il motivo per cui questa è una cattiva idea. – MitMaro

+13

Non posso leggere quell'ironia, ma come una risposta seria - 'eval()' non è non sicuro per definizione, se lo fosse, non ci sarebbe alcuna funzione disponibile. È il suo input ciò che apre i buchi di sicurezza quando è combinato con l'input dell'utente. Ad esempio, se si esegue un 'eval ('2 + 2')' o 'eval ('function() {return 42;}')' non vi sono problemi di sicurezza, anche se tale decisione di progettazione è discutibile. –

2

Leggere tutte le risposte su come questa sia davvero una pessima idea.

Una volta capito questo, ecco una piccola demo su come si potrebbe, ma non dovrebbe, fare questo.


<?php 
$clname = "TestClass"; 

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

var_dump($cls); 
+2

Sì, ma quando si esegue eval (...) questo non riesce: eval ("class $ clsname {funzione pubblica DoSomething() {$ this-> bob = 4;}}"); perché non è possibile utilizzare "questo" nel mezzo di una funzione non di classe. O se fossi in una funzione di classe sarebbe il torto "questo" puntatore ... –

+3

Hai un problema di fuga, ma mi rifiuto di aiutare oltre perché è come se tutti avessero detto una cattiva idea. – MitMaro

+1

MitMaro ... wow, devo esserne stato davvero fuori ieri per non aver notato la necessità di sfuggire al mio $. Grazie. –

13

la sua divertente, in realtà questo è una delle poche cose in cui eval Non sembra una cattiva idea.

fintanto che è possibile garantire che nessun utente immetterà mai l'eval.

ci sono ancora aspetti negativi come quando si utilizza una cache bytecode che il codice non sarà memorizzato nella cache ecc. Ecc. Ma i problemi di sicurezza di eval sono praticamente legati alla capacità di input dell'utente nell'eval o di finire nell'ambito sbagliato.

se sai cosa stai facendo, eval ti aiuterà con questo.

Detto questo, a mio parere sono molto meglio quando non si basano su tipo-hinting per la convalida, ma si ha una funzione

DoSomething_Validate($id) 
{ 
    // get_class($id) and other validation foo here 
} 
3
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(); 

Questo potrebbe contribuire a creare una classe a runtime. Crea anche un'esecuzione methor e un metodo catchall per metodi sconosciuti Ma è meglio creare oggetti in fase di runtime, non classi.

10

So che questa è una vecchia domanda e ci sono risposte che funzioneranno, ma volevo offrire alcuni frammenti che avrebbero risposto alla domanda originale e penso che offrire una soluzione più estesa dovrebbe qualcuno finire qui come ho fatto quando alla ricerca di una risposta a questo problema.

Creare unico dinamico Classe

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

Finché a fuggire correttamente il testo, si potrebbe anche aggiungere una funzione in là.

Ma cosa succede se si desidera creare dinamicamente classi basate su qualcosa che potrebbe essere dinamico come creare una classe per ogni tabella nel database come la domanda originale menzionata?

creare più classi dinamiche

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

?> 

Questo genererà dinamicamente una classe per ogni tabella in un database. Per ogni classe, viene creata anche una proprietà che prende il nome da ogni colonna.

Ora è stato sottolineato che non dovresti farlo. Finché controlli ciò che accade nella valutazione, il rischio di sicurezza non è un problema. MA - probabilmente c'è una soluzione che ha più senso se ci pensi abbastanza profondamente. Pensavo di avere il perfetto caso d'uso per creare dinamicamente nuove classi. L'attento esame del problema ha dimostrato il contrario.

Una soluzione potenziale: utilizzare lo stdClass per creare oggetti che sono solo contenitori di dati e non richiedono alcun metodo.

Inoltre, come accennato, è possibile utilizzare uno script per generare manualmente molte classi. Nel caso di classi che eseguono il mirroring delle tabelle del database, è possibile utilizzare la stessa logica di cui sopra e invece di eseguire un'eval, scrivere tali informazioni in un file.

+0

L'ultimo paragrafo della tua risposta è abbastanza buono - il codice è lo stesso per eval come per la classe nel file, quindi meglio scrivere quelle classi su file e ** averli nel repository VCS ** – PeterM

4

Penso che l'utilizzo di eval() non sia una soluzione affidabile, soprattutto se lo script o il software verranno distribuiti a client diversi. I provider di hosting condivisi disabilitano sempre la funzione eval().

sto pensando di un aproach meglio così:

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

Dopo aver terminato l'esecuzione del codice, è possibile eliminare il file, se si desidera, ho provato e funziona perfettamente.

+0

+1 per la risposta intelligente ma. . . i rischi per la sicurezza posti da eval sono ora maggiori, come ora hai esposto il filesystem per attaccare. Ma come hanno scritto sopra, se l'input è igienizzato, il rischio è ridotto al minimo. A proposito, mi piace testare cose come questa con '$ code =" "' – eggmatters