Manejo de errores en PHP 7. Clase Exception


Manejo de errores en PHP 7. Clase Exception

Repasamos la clase Exception de PHP y su jerarquía. Revisamos algunos ejemplos básicos y vemos un par de formas de manejar estas excepciones.



Breve repaso

Árbol de jerarquías de la clase Exception en PHP
Árbol de jerarquías de la clase Exception en PHP
Muchos errores fatales y errores fatales recuperables han sido convertidos en excepciones en PHP 7.
En el articulo anterior se dijo que a un objeto Throwable le podían pasar una de tres cosas y como Exception hereda de esta, el proceso no cambia.

  • O ser capturado en un catch()
  • O ser manejado a través de set_exception_handler()
  • O convertirse en un error fatal

Nuestra clase MiExcepcion

No hay mucho misterio y menos algo muy diferente con relación a la clase MiError del articulo anterior.
/* MiExcepcion.php */
namespace Blockpc\Errores;
class MiExcepcion extends \Excepcion implements MiThrowable {
public function __construct($message = "", $code = 0, \Throwable $previous = null) {
parent::__construct($message, $code, $previous);
}
public function metodoPersonalizado() {
// implementamos el código de error personalizado
echo '<pre><b>Excepción personalizado:</b><br>';
echo '<b>Clase</b>: '; print_r(get_class($this)); echo '</br>';
echo '<b>Linea</b>: '; print_r(parent::getLine()); echo '</br>';
echo '<b>Archivo</b>: '; print_r(parent::getFile()); echo '</br>';
echo '<b>Mensaje</b>: '; print_r(parent::getMessage()); echo '</pre>';
}
}
La interface MiThrowable nos permite extender la interface Throwable de PHP 7 y usaremos la misma del articulo anterior,

Una clase Usuario

Crearemos una clase Usuario para trabajar en este articulo y por medio del método bienvenido() lanzaremos algunas excepciones.
/* Clase Usuario */
namespace Blockpc\Clases;
class Usuario {
public function __construct() {
echo "Usuario constructor...<br>";
}
public function bienvenido(string $usuario) {
echo "Bienvenido {$usuario}";
}
public function __destruct() {
echo "... Usuario destructor.<br>";
}
}
Al método bienvenido() le haremos un par de cambios durante el articulo para la practica de las excepciones.
Solo nos queda decir que nuestro index.php sera muy similar al del articulo anterior con un solo cambio en el bloque try... catch(). El código fuente lo podrás descargar al final del articulo.
 /* index.php */
try {
$usuario = new Usuario;
$usuario->bienvenido('Jose Perez'); // Correcto
$usuario->bienvenido(null); // TypeError
} catch(MiExcepcion | Throwable $e) {
if ( $e instanceof MiExcepcion ) {
$e->metodoPersonalizado();
} else {
echo '<pre><b>Clase</b>: '; print_r(get_class($e)); echo '</br>';
echo '<b>Linea</b>: '; print_r($e->getLine()); echo '</br>';
echo '<b>Archivo</b>: '; print_r($e->getFile()); echo '</br>';
echo '<b>Mensaje</b>: '; print_r($e->getMessage()); echo '</pre>';
}
exit;
}
¿Por que en la linea del catch() atrapamos MiExcepcion y Throwable?
Pues como sabemos y entendemos, MiExcepcion y Throwable llegan a ser los mismos objetos (por herencia), pero no así si ocurre un Error, que si bien es Throwable va por el otro lado del árbol de jerarquías de PHP y que puedes ver en la imagen del lado.
Posición de nuestra clase MiExcepcion en la jerarquía de PHP
Posición de nuestra clase MiExcepcion en la jerarquía de PHP

Así si ocurre un error que no tengamos previsto, este sera atrapado por el catch(Throwable $e) y que puedes comprobar en la linea del index.php en la que se hace una llamada al método bienvenido() con un parámetro null provocando un TypeError que es un Throwable pero que desciende de la clase Error en la jerarquía de PHP como se vio en el articulo pasado.
En estricto rigor podríamos solo poner un catch(Throwable $e) y no cambiaría nada.

Uso de la clase MiExcepcion

Para un primer ejemplo, lanzamos un par de excepciones throwables que son atrapadas en el catch() de nuestro archivo index.php
 /* Primer ejemplo */
public function bienvenido(string $usuario) {
echo "Bienvenido {$usuario}<br>";
throw new \Exception("A ocurrido una excepción.");
throw new MiExcepcion("A ocurrido una excepción personalizada.");
}
Para un segundo ejemplo haremos un cambio en el método bienvenido() aplicando un bloque try... catch() y lanzando un objeto MiExcepcion que sera atrapado en el catch() y relanzado hacia index.php
 /* Segundo ejemplo */
public function bienvenido(string $usuario) {
try {
echo "Bienvenido {$usuario}<br>";
throw new MiExcepcion("A ocurrido una excepción personalizada.");
} catch(\Throwable $e) {
throw $e;
}
}
Al relanzar la excepción como un objeto MiExcepcion este sera atrapado en el siguiente catch() que lo soporte, en este caso en nuestro archivo index.php. No hay mucho mas que explicar acá.
Para un tercer ejemplo, realizaremos una validación del parámetro $usuario mediante una simple expresión regular llamando a un método privado de la clase.
/* Tercer ejemplo */
public function bienvenido(string $usuario) {
try {
if ( !$this->validarNombre($usuario) !== false ) {
throw new MiExcepcion("<b>{$usuario}</b> No es un nombre correcto.<br>");
}
echo "Bienvenido {$usuario}<br>";
} catch(\Throwable $e) {
throw $e;
}
}
private function validarNombre($nombre) {
$expresionRegular = "/^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖßÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\s])+$/i";
return preg_match($expresionRegular, $nombre);
}
La expresión regular valida que el parámetro $nombre tenga solo letras y espacios. Si no coincide el patrón retornara un cero y nosotros lanzaremos una excepción.

Buscando su utilidad

Al igual que en el articulo anterior crearemos una clase para manejar los errores de esta clase llamada UsuarioExcepcion Y nos servirá para administrar mejor el flujo de datos entre la aplicación y el usuario e incluso poder darle un formato diferente al error.
/* Clase UsuarioExcepcion */
namespace Blockpc\Errores;
final class UsuarioExcepcion extends MiExcepcion {}
Así, como un cuarto ejemplo podemos hacer esto en nuestro método bienvenido()
/* Cuarto ejemplo */
public function bienvenido(string $usuario) {
try {
if ( !$this->validarNombre($usuario) !== false ) {
throw new UsuarioExcepcion("<b>{$usuario}</b> No es un nombre correcto.<br>");
}
echo "Bienvenido {$usuario}<br>";
} catch(\Throwable $e) {
if ( $e instanceof UsuarioExcepcion )
echo $e->getMessage();
else
throw $e;
}
}
Si el objeto de error pertenece a la clase UsuarioExcepcion mostramos el mensaje, de lo contrario tenemos que relanzarlo para que sea atrapado en el bloque catch() del archivo index.php
El mensaje que se muestra puede ser como respuesta a un dato mal ingresado por un usuario a través de un formulario.

Ultimas consideraciones

Un catch(Throwable $e) nos permitirá atrapar cualquier objeto Error o Exception dentro de la jerarquía de errores en PHP 7 como lo hemos visto en las figuras. Lo que hagamos con esos errores dependerá de nosotros.

Según mi opinión todo método publico debe tener su bloque try... catch() y así poder manejar los errores según nosotros queramos. Los métodos privados no deberían llevar estos bloques, sino lanzar errores a través de throw en caso de ser necesario. Lo anterior se explica debido a que los métodos privados deben y son llamados desde métodos públicos y estos últimos deben tener su bloque try... catch() implementado.

En un próximo articulo hablaremos sobre las funciones set_exception_handler() y set_error_handler() de PHP para gestionar excepciones y errores respectivamente.

Los archivos utilizados en este articulo los puedes descargar desde acá

Saludos y nos leemos en un próximo articulo.

Etiquetas php errores

Ultima actualización Viernes 15 de Junio, 2018




Agregar Comentario