2011-02-08 8 views
9

La mayoría de los recursos en PHP nunca tocan la gestión de memoria porque el lenguaje en sí es bastante bueno para hacer esto por usted. Sin embargo, en PHP a menudo terminas ocupándote de recursos externos que no son memoria: identificadores de bases de datos, sesiones, transacciones de bases de datos, etc. Estos recursos externos podrían manejarse más limpiamente utilizando algún tipo de objeto RAII.¿Admite PHP el patrón RAII? ¿Cómo?

Inicialmente pensé que PHP utilizaba un esquema de recolección de basura similar a la JVM o al CLR, donde el concepto de destructor no existe. (Recuerde: Everyone thinks about garbage collection the wrong way - ¡los finalizadores no son destructores!) Existe el método especial __destruct, pero pensé que era un "finalizador" similar a un finalizador de Java o C#. Por esta razón, no puede usar RAII en la JVM o en la CLR (los bloques using de C# le dan aproximadamente el 95% del camino hasta allí, pero eso es un poco diferente ...).

Sin embargo, Google seems to indicate that PHP supports the RAII pattern, aunque no puedo encontrar la verificación de esto en los documentos PHP. ¿El lenguaje lo admite y está poniendo la lógica de limpieza en __destruct suficiente para realizar tareas de RAII?

Respuesta

9

Esta es casi la misma pregunta que Is destructor in PHP predictable? y la respuesta es la misma. PHP utiliza refcounting, y promete que se llamará al destructor inmediatamente tan pronto como el refcount se ponga a cero (generalmente cuando el objeto sale del alcance). Por lo tanto, si crea un objeto y se cuida de no perderlo, RAII es viable.

+3

Otra advertencia: cuando hay varios objetos dejan alcance, al mismo tiempo, el orden de sus destructores son llamados oficialmente es indefinido, y por lo general en orden FIFO (exactamente el lo contrario de lo que se necesita para un RAII adecuado). Eso es un obstáculo para mi caso de uso particular. – Brilliand

+0

@Brilliand, ¿podrías agregar llaves artificialmente para forzar el orden? :) – hobbs

+0

Las llaves no lo harán; solo una función puede introducir un nuevo alcance. Todavía es posible, supongo, pero eso podría equivaler a una gran repetición. – Brilliand

4

PHP utiliza el recuento de referencias, por lo que cuando haya terminado con una variable, se limpia inmediatamente. (A menos que cree ciclos). Esto libera recursos rápidamente, por lo que generalmente no tiene que preocuparse por la administración explícita de recursos más allá de tener cuidado de no crear ciclos de memoria.

Si desea implementar cualquier estrategia en particular, puede hacerlo asegurándose de que el recurso solo sea utilizado por una variable. Siempre que esa variable se aleje del recurso, el recurso debería liberarse inmediatamente.

2

La siguiente clase ReturnHandler proporciona la invocación automática de un controlador cuando la instancia ReturnHandler queda fuera del alcance. Puede tener varios return s en su función (myfunc) sin la necesidad de pensar en liberar el recurso antes de cada uno de ellos.

/** 
* 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(); 
    } 
} 

Aquí está una prueba para él:

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

Preferiría tener un tipo que envuelva el recurso en cuestión para la mayoría de los usos. Pero esto funcionará como una solución rápida y sucia, p. si solo tiene una instancia de un recurso dado utilizado en su programa. –