2012-07-04 14 views
10

Non so perché, ma questo codice ha funzionato per me un mese fa ... forse ho aggiornato il php ma non riesco a ricordare. Ho provato questo con PHP 5.2.17 e 5.3.6Oggetto di classe non funzionante all'interno di callback ob_start

Perché non è possibile utilizzare un oggetto di classe all'interno del callback di una funzione ob_start?

<?php 
$f=new stdClass(); 
$f->title="awesome Title"; 

function callback($buffer) 
{ 
    global $f; 
    $buffer=str_replace("###TITLE###", $f->title, $buffer); 
    return $buffer; 
} 
ob_start("callback"); 
?> 

This is the ###TITLE### 

uscita è:

PHP Notice: Trying to get property of non-object in /Users/qxxx/Sites/test/test.php on line 8 
This is the 

dovrebbe essere:

This is the awesome Title

risposta

10

Questo perché il buffer di uscita viene implicitamente lavata dalla cessazione dello script.

A questo punto, PHP ha già distrutto le variabili senza riferimento, quindi quando si tratta di eseguire la funzione di callback, la variabile $f non esiste nell'ambito globale.

È possibile risolvere questo problema svuotando esplicitamente il buffer prima che lo spegnimento inizi a distruggere oggetti, posizionando la seguente riga da qualche parte nello script.

register_shutdown_function('ob_end_flush');

Edit:

vorrei aggiungere che, anche se questo è attualmente la risposta accettata che spiega il "perché", la soluzione fornita qui non affronta alla radice causa del problema; il fatto che sia in uso global.

Molte persone ti diranno che global è malvagio, senza dare una motivazione. Qui puoi vedere uno dei motivi.

La risposta fornita da Jack dà una soluzione più "best practice" (usando chiusure mantenere il riferimento variabile), e dovrebbe essere considerato come il modo corretto per evitare di usare global nelle nuove basi di codice.

+2

In effetti, per elaborare un po ': una parte importante di questo è _ "variabili senza riferimento" _ sono distrutti, quelli di riferimento non lo sono. Un bell'esempio per cui i 'global' tendono a rendere il codice più difficile, e il passaggio corretto di riferimenti e argomenti è preferito. – Wrikken

2

Dalla pagina di manuale php di ob_start e a bug report ho imparato che, dal momento che 5.2, tutti gli oggetti vengono distrutti @ob_start

This function's behaviour has been changed in php 5.2.0:

<? 
    global $AP; 
    $AP = new ap; 
    ob_start("ob_end"); 
    function ob_end() 
    { 
     global $AP; 
     $r = $AP->test(); 
     return $r; 
    } 
    class ap 
    { 
     function test() 
     { 
      return "debug"; 
     } 
    } 
?> 

In older versions it shows: "debug". But latest php version causes error: PHP Fatal error: Call to a member function test() on > a non-object. And this is not a bug: http://bugs.php.net/bug.php?id=40104

dalle pagine man

+0

+1, grazie per il riferimento alla segnalazione bug, non sapevo che fosse in realtà un comportamento modificato (a parte i commenti dell'autore della domanda) – Leigh

+0

@Leigh: NP, ha avuto qualche problema con un vecchio pezzo di codice che ho scritto un po ' indietro. Sono riuscito a girarlo usando una chiusura, anche se alcune persone lo consigliano. Per inciso: sai qual è _ meglio? O è solo una questione di preferenza/abitudine? Non posso fare a meno di pensare che abbia più a che fare con le persone che non capiscono come funzionano le chiusure, che con l'effettiva qualità del codice –

+1

Sono d'accordo con te, non è una cattiva abitudine ** capire ** perché stai facendo qualcosa. L'uso di una chiusura (IMHO) non è una soluzione alternativa, è il modo corretto per preservare la variabile di riferimento ed evitare l'uso di 'global'. Come può essere sbagliato usare una funzione linguistica nel modo in cui è stata concepita. – Leigh

6

La ragione di ciò è stata delineata bene da Leigh. Utilizzando una chiusura avrebbe funzionato meglio in questo caso:

ob_start(function($b) use ($f) { 
     return str_replace('###TITLE###', $f->title, $b); 
}); 

Questo perché la chiusura non mancherà di tenere il riferimento a $f in vita dalla fine dello script in modo che non otterrà garbage collection prima di eseguire la funzione di callback.

Problemi correlati