tag name php exception resource-cleanup

php - get tag name wordpress



¿Cómo puedo evitar la falta de un bloqueo final en PHP? (7)

PHP antes de la versión 5.5 no tiene bloque finalmente, es decir, mientras que en la mayoría de los lenguajes sensibles, puedes hacer:

try { //do something } catch(Exception ex) { //handle an error } finally { //clean up after yourself }

PHP no tiene noción de un bloque finalmente.

¿Alguien tiene experiencia de soluciones a este agujero bastante irritante en el lenguaje?



Aquí está mi solución a la falta de finalmente bloquear. No solo proporciona una solución alternativa para el bloque finally, también extiende el try / catch para detectar errores de PHP (y también errores fatales). Mi solución se ve así (PHP 5.3):

_try( //some piece of code that will be our try block function() { //this code is expected to throw exception or produce php error }, //some (optional) piece of code that will be our catch block function($exception) { //the exception will be caught here //php errors too will come here as ErrorException }, //some (optional) piece of code that will be our finally block function() { //this code will execute after the catch block and even after fatal errors } );

Puede descargar la solución con documentación y ejemplos de git hub - https://github.com/Perennials/travelsdk-core-php/tree/master/src/sys


Como se trata de una construcción de lenguaje, no encontrarás una solución fácil para esto. Puede escribir una función y llamarla como la última línea de su bloque try y última línea antes de volver a lanzar la excepción en el bloque try.

Los buenos libros argumentan en contra del uso de los bloques finally para cualquier recurso que no sea de liberación, ya que no se puede estar seguro de que se ejecutará si sucede algo desagradable. Llamarlo un agujero irritante es una exageración. Créame, muchísimo código excepcionalmente bueno está escrito en idiomas sin bloquear. :)

El objetivo final es ejecutar sin importar si el bloque try fue exitoso o no.


El modismo RAII ofrece un sustituto a nivel de código para un bloqueo final. Crea una clase que mantenga llamable (s). En el destinatario, llame a los llamables.

class Finally { # could instead hold a single block public $blocks = array(); function __construct($block) { if (is_callable($block)) { $this->blocks = func_get_args(); } elseif (is_array($block)) { $this->blocks = $block; } else { # TODO: handle type error } } function __destruct() { foreach ($this->blocks as $block) { if (is_callable($block)) { call_user_func($block); } else { # TODO: handle type error. } } } }

Coordinación

Tenga en cuenta que PHP no tiene ámbito de bloque para las variables, por lo que Finally no se activará hasta que la función finalice o (en el alcance global) la secuencia de apagado. Por ejemplo, lo siguiente:

try { echo "Creating global Finally./n"; $finally = new Finally(function () { echo "Global Finally finally run./n"; }); throw new Exception; } catch (Exception $exc) {} class Foo { function useTry() { try { $finally = new Finally(function () { echo "Finally for method run./n"; }); throw new Exception; } catch (Exception $exc) {} echo __METHOD__, " done./n"; } } $foo = new Foo; $foo->useTry(); echo "A whole bunch more work done by the script./n";

dará como resultado la salida:

Creating global Finally. Foo::useTry done. Finally for method run. A whole bunch more work done by the script. Global Finally finally run.

$ this

Los cierres de PHP 5.3 no pueden acceder a $this (corregidos en 5.4), por lo que necesitarás una variable adicional para acceder a los miembros de la instancia dentro de algunos bloques finally.

class Foo { function useThis() { $self = $this; $finally = new Finally( # if $self is used by reference, it can be set after creating the closure function () use ($self) { $self->frob(); }, # $this not used in a closure, so no need for $self array($this, ''wibble'') ); /*...*/ } function frob() {/*...*/} function wibble() {/*...*/} }

Campos privados y protegidos

Podría decirse que el mayor problema con este enfoque en PHP 5.3 es que el cierre definitivo no puede acceder a los campos privados y protegidos de un objeto. Al igual que al acceder a $this , este problema se resuelve en PHP 5.4. Por ahora, se puede acceder a las propiedades privadas y protegidas mediante referencias, como muestra Artefacto en su answer a una pregunta sobre este tema en otro lugar de este sitio.

class Foo { private $_property=''valid''; public function method() { $this->_property = ''invalid''; $_property =& $this->_property; $finally = new Finally(function () use (&$_property) { $_property = ''valid''; }); /* ... */ } public function reportState() { return $this->_property; } } $f = new Foo; $f->method(); echo $f->reportState(), "/n";

Se puede acceder a los métodos privados y protegidos mediante la reflexión. Puede utilizar la misma técnica para acceder a propiedades no públicas, pero las referencias son más simples y livianas. En un comentario en la página del manual de PHP para funciones anónimas , Martin Partel da un ejemplo de una clase FullAccessWrapper que abre los campos no públicos al acceso público. No lo reproduciré aquí (vea los dos enlaces anteriores para eso), pero así es como lo usaría:

class Foo { private $_property=''valid''; public function method() { $this->_property = ''invalid''; $self = new FullAccessWrapper($this); $finally = new Finally(function () use (&$self) { $self->_fixState(); }); /* ... */ } public function reportState() { return $this->_property; } protected function _fixState() { $this->_property = ''valid''; } } $f = new Foo; $f->method(); echo $f->reportState(), "/n";

try/finally

try bloques de try requieren al menos una catch . Si solo quiere try/finally , agregue un bloque catch que capture una Exception (el código PHP no puede arrojar nada que no provenga de Exception ) o vuelva a lanzar la excepción detectada. En el primer caso, sugiero capturar StdClass como una expresión idiomática que significa "no atrapar nada". En los métodos, capturar la clase actual también podría usarse para decir "no atrapar nada", pero usar StdClass es más simple y más fácil de encontrar al buscar archivos.

try { $finally = new Finally(/*...*/); /* ... */ } catch (StdClass $exc) {} try { $finally = new Finally(/*...*/); /* ... */ } catch (RuntimeError $exc) { throw $exc }


Si alguien sigue haciendo un seguimiento de esta pregunta, es posible que esté interesado en consultar el (nuevo) wiki.php.net/rfc/finally en la wiki de PHP. El autor ya parece tener parches de trabajo, y estoy seguro de que la propuesta se beneficiaría de los comentarios de otros desarrolladores.


Solución, no. Irritante y engorrosa solución alternativa, sí:

$stored_exc = null; try { // Do stuff } catch (Exception $exc) { $stored_exc = $exc; // Handle an error } // "Finally" here, clean up after yourself if ($stored_exc) { throw($stored_exc); }

Yucky, pero debería funcionar.

Nota : PHP 5.5 finalmente (ejem, lo siento) agregó un bloque finally: wiki.php.net/rfc/finally (y solo tomó unos años ... disponible en el 5.5 RC casi cuatro años para el fecha desde que publiqué esta respuesta ...)


function _try(callable $try, callable $catch, callable $finally = null) { if (is_null($finally)) { $finally = $catch; $catch = null; } try { $return = $try(); } catch (Exception $rethrow) { if (isset($catch)) { try { $catch($rethrow); $rethrow = null; } catch (Exception $rethrow) { } } } $finally(); if (isset($rethrow)) { throw $rethrow; } return $return; }

Llamar usando cierres. El segundo parámetro, $catch , es opcional. Ejemplos:

_try(function () { // try }, function ($ex) { // catch ($ex) }, function () { // finally }); _try(function () { // try }, function () { // finally });

Correctamente maneja excepciones en todas partes:

  • $try : la excepción pasará a $catch . $catch se ejecutará primero, luego $finally . Si no hay $catch , la excepción se volverá a generar después de ejecutar $finally .
  • $catch : $finally se ejecutará de inmediato. La excepción se volverá a lanzar después de que $finally complete.
  • $finally : Exception romperá la pila de llamadas sin impedimentos. Cualquier otra excepción programada para volver a lanzarse será descartada.
  • Ninguno : se devolverá el valor de retorno de $try .