2008-10-25 16 views
10

Tengo una clase PHP que crea una imagen PNG sobre la marcha y la envía al navegador. El manual de PHP dice que necesito asegurarme de que la función imagedestroy se llame al final para liberar la memoria. Ahora, si yo no estuviera usando una clase, me gustaría tener un poco de código como este:PHP: destructor vs register_shutdown_function

function shutdown_func() 
{ 
    global $img; 
    if ($img) 
     imagedestroy($img); 
} 
register_shutdown_function("shutdown_func"); 

Sin embargo, creo que el lugar apropiado para mi clase sería la de realizar una llamada a imagedestroy en clase destructor .

¿No pude averiguar si los destructores se llaman de la misma manera que las funciones de apagado? Por ejemplo, si la ejecución se detiene cuando el usuario presiona el botón DETENER en el navegador.

Nota: escriba lo que escriba en su respuesta, señale a algún artículo o página de manual (URL) que lo admita.

+0

Agradecería que no editaras mis respuestas para eliminar las secciones que consideraras "irrelevantes", eso es irrespetuoso. En cuanto a la recolección de basura, fue relevante, pero se adapta a usted. – Tomalak

+0

@Tomalak: La otra parte de su respuesta fue relevante y puntual, incluso la voté. ¿Podrías devolverlo? –

Respuesta

11

Acabo de probar con Apache, PHP se utiliza como módulo de Apache. He creado un bucle sin fin como esto:

<?php 
class X 
{ 
    function __destruct() 
    { 
     $fp = fopen("/var/www/htdocs/dtor.txt", "w+"); 
     fputs($fp, "Destroyed\n"); 
     fclose($fp); 
    } 
}; 

$obj = new X(); 
while (true) { 
    // do nothing 
} 
?> 

Esto es lo que descubrí:

  • botón STOP presionando en Firefox no se detiene este script
  • Si apago Apache, destructor no recibe llama
  • se detiene cuando llega max_execution_time PHP y destuctor no consigue llamado

Sin embargo, haciendo esto:

<?php 
function shutdown_func() { 
    $fp = fopen("/var/www/htdocs/dtor.txt", "w+"); 
    fputs($fp, "Destroyed2\n"); 
    fclose($fp); 
} 
register_shutdown_function("shutdown_func"); 

while (true) { 
    // do nothing 
} 
?> 

shutdown_func se llama. Entonces esto significa que el destuctor de clase no es tan bueno como las funciones de apagado.

+1

¿Puede agregar un enlace a cualquier cosa en el manual de php o que sea compatible con su prueba? Eso sería de gran beneficio para todos nosotros. –

+1

Si tiene una configuración que funcione, solo copie y pegue los ejemplos que di y pruebe usted mismo. Fue explicado en el manual de PHP o de lo contrario no preguntaría aquí en SO. –

+0

Este comportamiento no es "por diseño", por lo que puede cambiar en versiones futuras. Si no se envía ninguna salida al navegador, el botón de detención no tiene efecto. "eco" es una función que puede causar un error fatal (tubería rota). Gracias por su investigación, pero en el caso de img GD, todos deberían usar el método destructor. –

0

Creo que una gran cosa que te has perdido es que toda la memoria que PHP ha asignado durante la ejecución del script se libera una vez que termina el script. Incluso si el usuario presiona el botón de detención, PHP procesa el script hasta que finaliza, lo devuelve al daemon HTTP para que se lo sirva al visitante (o no, dependiendo de qué tan inteligente sea el daemon).

Por lo tanto, la liberación explícita de memoria al final de la ejecución del script es un poco redundante. Algunos podrían argumentar que sería bueno hacerlo, pero sigue siendo redundante.

Pero, en el tema de los destructores de clase, se llaman siempre que el objeto se destruye, ya sea explícitamente por unset() o en la finalización/finalización del script.

La recomendación del desarrollador liberar explícitamente la memoria utilizada en la manipulación de imágenes es seguro sólo para estar absolutamente seguro de no tener una pérdida de memoria, como mapas de bits se pueden esforzaban en el lado de la memoria de las cosas (altura x anchura x profundidad de bits * 3 (+ 1 si tiene un canal alfa))

Para satisfacer las necesidades de su usuario de Wikipedia:

+0

AFAIU, la memoria no está asignada por PHP porque invoca a GD para crear imágenes y GD no tiene recolección de elementos no utilizados. –

+0

Ya leí el manual de PHP, pero no dice explícitamente si se invoca el destructor si el servidor web finaliza el script. –

+0

también AFAIK, GD es solo una biblioteca que está vinculada dinámicamente con PHP. Esto implicaría que, aunque GD sabe cómo hacer todas las cosas, sigue siendo la memoria de PHP y, por lo tanto, la recolección de basura de PHP. –

2

Basado en el principio de que debe finish what you start, diría que el destructor es el lugar correcto para la llamada gratuita.

Se llamará al destructor cuando se elimine el objeto, mientras que un shutdown function no se ejecutará hasta que finalice la ejecución del script. Como señaló Wolfie, esto no sucederá necesariamente si detiene el servidor o el script, pero en ese momento, la memoria asignada por PHP se liberará de todos modos.

También anotado por Wolfie, PHP liberará los recursos del script cuando se cierre el script, por lo que si solo está instanciando uno de estos objetos, entonces probablemente no notaría una gran diferencia. Sin embargo, si más tarde terminas instanciando estas cosas, o lo haces en un bucle, entonces probablemente no quieras tener que preocuparte por un aumento repentino en el uso de la memoria, por lo que en aras de la cordura futura, regreso a mi recomendación original; ponlo en el destructor.

1

Recientemente tuve problemas con esto ya que estaba tratando de manejar la destrucción específicamente para el caso donde el servidor experimenta un tiempo de espera y quería incluir datos de clase en el registro de errores. Recibiría un error al hacer referencia a & $ this (aunque lo he visto en algunos ejemplos, posiblemente un problema de versión o un efecto secundario de Symfony), y la solución que obtuve fue bastante clara:

class MyClass 
{ 
    protected $myVar; 

    /** 
    * constructor, registers shutdown handling 
    */ 
    public function __construct() 
    { 
     $this->myVar = array(); 

     // workaround: set $self because $this fails 
     $self = $this; 
     // register for error logging in case of timeout 
     $shutdown = function() use (&$self) { 
      $self->shutdown(); 
     }; 
     register_shutdown_function($shutdown); 
    } 

    /** 
    * handle shutdown events 
    */ 
    public function shutdown() 
    { 
     $error = error_get_last(); 
     // if shutdown in error 
     if ($error['type'] === E_ERROR) { 
      // write contents to error log 
      error_log('MyClass->myVar on shutdown' . json_encode($this->myVar), 0); 
     } 
    } 

    ... 

Espero que esto ayude a alguien!

+0

código impresionante, resolver el mío –