2011-02-08 12 views
9

La maggior parte delle risorse su PHP non toccano mai la gestione della memoria perché il linguaggio stesso è abbastanza buono per farlo. Tuttavia, in PHP si finisce spesso per gestire risorse esterne che non sono memoria: handle di database, sessioni, transazioni di database, ecc. Queste risorse esterne possono essere gestite in modo più pulito utilizzando una qualche forma di oggetto RAII.PHP supporta il pattern RAII? Come?

Inizialmente pensavo che PHP usasse uno schema di garbage collection simile alla JVM o al CLR, dove il concetto di un distruttore non esiste. (Ricorda: Everyone thinks about garbage collection the wrong way - i finalizzatori non sono distruttori!) C'è il metodo speciale __destruct, ma ho pensato che fosse un "finalizzatore" simile a un finalizzatore Java o C#. Per questo motivo, non puoi utilizzare RAII su JVM o CLR (i blocchi using di C# ti danno circa il 95% del tempo, ma è un po 'diverso ...).

Tuttavia, Google seems to indicate that PHP supports the RAII pattern, anche se non riesco a trovare la verifica di questo nei documenti PHP. La lingua supporta questo e sta mettendo la logica di pulizia in __destruct sufficiente per eseguire le attività RAII?

risposta

9

Questa è quasi la stessa domanda di Is destructor in PHP predictable? e la risposta è la stessa. PHP usa il refcounting e promette che il distruttore verrà chiamato immediatamente non appena il conto si azzererà (di solito quando l'oggetto esce dallo scope). Quindi, se crei un oggetto e stai attento a non farlo fuoriuscire dall'ambito di applicazione, RAII è fattibile.

+3

Un altro avvertimento: quando più voci lasciano portata, allo stesso tempo, l'ordine di loro i distruttori sono chiamati è ufficialmente definito, e di solito in ordine FIFO (esattamente la opposto di ciò che è necessario per il RAII corretto). Questo è un dealbreaker per il mio particolare caso d'uso. – Brilliand

+0

@Brilliand potresti aggiungere artificialmente delle parentesi per far rispettare l'ordine? :) – hobbs

+0

Le parentesi non lo faranno - solo una funzione può introdurre un nuovo ambito. Ancora possibile, suppongo, ma quello potrebbe ammontare a molto standard. – Brilliand

4

PHP utilizza il conteggio dei riferimenti, quindi quando hai finito con una variabile viene immediatamente ripulito. (A meno che non si creino cicli.) Ciò consente di liberare risorse prontamente in modo da non dover generalmente preoccuparsi della gestione esplicita delle risorse oltre a prestare attenzione a non creare cicli di memoria.

Se si desidera implementare una strategia particolare, è possibile farlo assicurandosi che la risorsa sia utilizzata solo da una variabile. Ogniqualvolta quella variabile viene puntata lontano dalla risorsa, la risorsa dovrebbe essere immediatamente liberata.

2

La seguente classe ReturnHandler fornisce il richiamo automatico di un gestore quando l'istanza di ReturnHandler non rientra nell'ambito. È possibile avere diversi return s nella funzione (myfunc) senza la necessità di pensare di liberare la risorsa prima di ciascuno di essi.

/** 
* Automatically calls a handler before returning from a function. Usage: 
* 
* function myfunc() 
* { 
* $resource = new Resource(); 
* $rh = new ReturnHandler(function() use ($resource) { $resource->release(); }); 
* // ... 
* if(...) { 
* return; // look, ma, automatic clean up! 
* } 
* } 
*/ 
class ReturnHandler 
{ 
    private $return_handler; 

    public function __construct($return_handler) 
    { 
    $this->return_handler = $return_handler; 
    } 

    public function __destruct() 
    { 
    $handler = $this->return_handler; 
    $handler(); 
    } 
} 

Ecco un test per esso:

class ReturnHandlerTest extends PHPUnit_Framework_TestCase 
{ 

    private static function trigger_return_handler(&$var) 
    { 
    $rh = new ReturnHandler(function() use (&$var) { $var++; }); 
    } 

    public function test() 
    { 
    $a = 0; 
    $this->assertEquals(0, $a); 
    self::trigger_return_handler($a); 
    $this->assertEquals(1, $a); 
    } 
} 
+0

Preferirei avere un tipo che avvolge la risorsa in questione per la maggior parte degli usi. Ma questo funzionerà come una soluzione rapida e sporca, ad es. se hai solo un'istanza di una determinata risorsa utilizzata nel tuo programma. –