No debe atrapar la excepción a menos que tenga la intención de hacer algo significativo .
"Algo significativo" podría ser uno de estos:
Manejando la excepción
La acción significativa más obvia es manejar la excepción, por ejemplo, mostrando un mensaje de error y abortando la operación:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
echo "Error while connecting to database!";
die;
}
Registro o limpieza parcial
A veces no sabes cómo manejar adecuadamente una excepción dentro de un contexto específico; quizás le falta información sobre el "panorama general", pero desea registrar la falla lo más cerca posible del punto en el que ocurrió. En este caso, es posible que desee atrapar, iniciar sesión y volver a lanzar:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
logException($e); // does something
throw $e;
}
Un escenario relacionado es donde se encuentra en el lugar correcto para realizar una limpieza de la operación fallida, pero no para decidir cómo se debe manejar la falla en el nivel superior. En versiones anteriores de PHP esto se implementaría como
$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
catch (Exception $e) {
$connect->disconnect(); // we don't want to keep the connection open anymore
throw $e; // but we also don't know how to respond to the failure
}
PHP 5.5 ha introducido la finally
palabra clave, por lo que para los escenarios de limpieza ahora hay otra forma de abordar esto. Si el código de limpieza necesita ejecutarse sin importar lo que sucedió (es decir, tanto por error como por éxito), ahora es posible hacerlo mientras se permite la propagación transparente de las excepciones lanzadas:
$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
finally {
$connect->disconnect(); // no matter what
}
Abstracción de error (con encadenamiento de excepción)
Un tercer caso es donde desea agrupar lógicamente muchas posibles fallas bajo un paraguas más grande. Un ejemplo para la agrupación lógica:
class ComponentInitException extends Exception {
// public constructors etc as in Exception
}
class Component {
public function __construct() {
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
throw new ComponentInitException($e->getMessage(), $e->getCode(), $e);
}
}
}
En este caso, no desea que los usuarios Component
sepan que se implementa utilizando una conexión de base de datos (tal vez desee mantener abiertas sus opciones y utilizar el almacenamiento basado en archivos en el futuro). Por lo tanto, su especificación para Component
diría que "en el caso de un error de inicialización, ComponentInitException
será arrojado". Esto permite a los consumidores Component
detectar excepciones del tipo esperado, al tiempo que permite que el código de depuración acceda a todos los detalles (dependientes de la implementación) .
Proporcionando un contexto más rico (con excepción de encadenamiento)
Finalmente, hay casos en los que es posible que desee proporcionar más contexto para la excepción. En este caso, tiene sentido incluir la excepción en otra que contenga más información sobre lo que estaba tratando de hacer cuando ocurrió el error. Por ejemplo:
class FileOperation {
public static function copyFiles() {
try {
$copier = new FileCopier(); // the constructor may throw
// this may throw if the files do no not exist
$copier->ensureSourceFilesExist();
// this may throw if the directory cannot be created
$copier->createTargetDirectory();
// this may throw if copying a file fails
$copier->performCopy();
}
catch (Exception $e) {
throw new Exception("Could not perform copy operation.", 0, $e);
}
}
}
Este caso es similar al anterior (y el ejemplo probablemente no sea el mejor), pero ilustra el punto de proporcionar más contexto: si se produce una excepción, nos dice que la copia del archivo falló. ¿ Pero por qué falló? Esta información se proporciona en las excepciones envueltas (de las cuales podría haber más de un nivel si el ejemplo fuera mucho más complicado).
El valor de hacerlo se ilustra si piensa en un escenario en el que, por ejemplo, la creación de un UserProfile
objeto hace que los archivos se copien porque el perfil de usuario se almacena en archivos y admite la semántica de transacciones: puede "deshacer" los cambios porque solo se realizan en un copia del perfil hasta que te comprometas.
En este caso, si lo hiciste
try {
$profile = UserProfile::getInstance();
}
y como resultado se detectó un error de excepción "No se pudo crear el directorio de destino", tendría derecho a ser confundido. Ajustar esta excepción "principal" en capas de otras excepciones que proporcionan contexto hará que el error sea mucho más fácil de manejar ("Error al crear copia de perfil" -> "Error en operación de copia de archivo" -> "No se pudo crear el directorio de destino").
finally
construcción (al menos no todavía) ... Así que eso está fuera, lo que significa que debemos recurrir a cosas sucias como esta ...finally
en PHP.error_code
propiedad que se puede verificar para obtener el error subyacente código. Si solo puede manejar significativamente algunos de esos errores, entonces probablemente quiera atrapar, inspeccionar, y si no puede manejar el error, vuelva a lanzar.Bueno, se trata de mantener la abstracción. Por lo tanto, sugeriría usar el encadenamiento de excepciones para lanzar directamente. En cuanto a por qué, permítanme explicar el concepto de abstracciones con fugas.
Digamos que estás construyendo un modelo. Se supone que el modelo abstrae toda la persistencia y validación de datos del resto de la aplicación. Entonces, ¿qué sucede cuando obtienes un error en la base de datos? Si vuelves a lanzar
DatabaseQueryException
, estás filtrando la abstracción. Para entender por qué, piensa en la abstracción por un segundo. No le importa cómo el modelo almacena los datos, solo que lo hace. Del mismo modo, no le importa exactamente qué salió mal en los sistemas subyacentes del modelo, solo sabe que algo salió mal y aproximadamente qué salió mal.Entonces, al volver a lanzar DatabaseQueryException, está filtrando la abstracción y necesitando el código de llamada para comprender la semántica de lo que está sucediendo bajo el modelo. En cambio, cree un genérico
ModelStorageException
y envuelva el atrapadoDatabaseQueryException
dentro de eso. De esa manera, su código de llamada aún puede tratar de manejar el error semánticamente, pero no importa la tecnología subyacente del Modelo, ya que solo está exponiendo los errores de esa capa de abstracción. Aún mejor, ya que envolvió la excepción, si esta burbujea y necesita registrarse, puede rastrear hasta la excepción raíz lanzada (recorrer la cadena) para que todavía tenga toda la información de depuración que necesita.No se limite a capturar y volver a lanzar la misma excepción a menos que necesite realizar un procesamiento posterior. Pero un bloque como no
} catch (Exception $e) { throw $e; }
tiene sentido. Pero puede volver a ajustar las excepciones para obtener una ganancia de abstracción significativa.fuente
En mi humilde opinión, capturar una excepción para volver a lanzarlo es inútil . En este caso, simplemente no lo atrape y deje que los métodos llamados anteriormente lo manejen (también conocido como métodos que son 'superiores' en la pila de llamadas) .
Si lo vuelves a lanzar, encadenar la excepción atrapada en la nueva que lanzarás definitivamente es una buena práctica, ya que mantendrá la información que contiene la excepción atrapada. Sin embargo, volver a lanzarlo solo es útil si agrega alguna información o maneja algo a la excepción capturada, puede ser algún contexto, valores, registro, liberación de recursos, lo que sea.
Una manera de añadir un poco de información es extender la
Exception
clase, tener excepciones comoNullParameterException
,DatabaseException
, etc. Más encima, esto permite que el revelador sólo para coger algunas excepciones que puede manejar. Por ejemplo, uno solo puede atraparDatabaseException
e intentar resolver lo que causó elException
, como volver a conectarse al databse.fuente
Debe echar un vistazo a las mejores prácticas de excepción en PHP 5.3
El manejo de excepciones en PHP no es una característica nueva de ninguna manera. En el siguiente enlace, verá dos nuevas características en PHP 5.3 basadas en excepciones. El primero son las excepciones anidadas y el segundo es un nuevo conjunto de tipos de excepciones que ofrece la extensión SPL (que ahora es una extensión central del tiempo de ejecución de PHP). Ambas características nuevas han llegado al libro de las mejores prácticas y merecen ser examinadas en detalle.
http://ralphschindler.com/2010/09/15/exception-best-practices-in-php-5-3
fuente
Usualmente lo piensas de esta manera.
Una clase puede arrojar muchos tipos de excepciones que no coinciden. Entonces crea una clase de excepción para esa clase o tipo de clase y la arroja.
Entonces, el código que usa la clase solo tiene que atrapar un tipo de excepción.
fuente