PHP Control de erros

De MediaWiki
Saltar a: navegación, buscar

Excepcións

  • Durante o desenvolvemento da aplicación imos atoparnos coa necesidade de controlar os erros que poidan darse nas chamadas ás diferentes funcións.
Isto pode levar a programar cunha estrutura de if - else moi 'anidada', que faga que o código sexa pouco lexible.
Podemos facer uso delas a partires da versión 5 de PHP.
  • A idea é moi simple: En caso de erro imos 'capturar' ou 'lanzar' unha excepción.
Unha excepción é un tipo de erro que pode darse na execución de programas. Normalmente son erros non controlados por nos nas liñas de programación, pero tamén podemos 'provocar' unha excepción enviando un texto e un código.


A sintaxe é moi sinxela:

  1. try {
  2.  
  3.   // Sentenzas a controlar
  4.   if (condicion_a_controlar)
  5.      throw new Exception('Mensaxe a enviar',1);   // Provocamos unha excepción. O control pasa a sección catch. Podemos enviar un texto e un dato numérico a sección catch (non é obrigatorio)
  6.    
  7.  
  8. }
  9. catch (Excepcion $e){
  10.   // Sentenzas a executar en caso de lanzarse a excepción
  11.  
  12. }
  • Liña 5: En base a controlar algunha condición, podemos provocar unha excepción. Poderíamos lanzar a excepción se enviar nada á sección catch (throw Exception()), ou podemos enviar soamente un texto (throw Exception('Mensaxe a enviar')).
  • Liña 9: O obxecto $e pertence á clase Excepcion e dispón entre as súas propiedades e métodos:
  • $e->getMessage(); // Devolve a mensaxe da excepción. É o texto que enviamos na orde throw new Exception('texto',número)
  • $e->getCode(); // Devolve o código da excepción. É o número que enviamos na orde throw new Exception('texto',número)
  • Cando se produce a excepción, o control do programa vai a sección catch, deixando de executarse o resto de liñas do programa dentro da sección try.



  • A partires da versión PHP 5.5 podemos incorporar outra sección: finally.
Dita sección execútase sempre, haia ou non unha excepción e ao final de todo, despois de executar a última sentenza controlada ou despois de executar a sección catch se houbo unha excepción.
  1. try {
  2.  
  3.   // Sentenzas a controlar
  4.   if (condicion_a_controlar)
  5.      throw new Exception('Mensaxe a enviar');  
  6.  
  7. }
  8. catch (Excepcion $e){
  9.   // Sentenzas a executar en caso de lanzarse a excepción
  10.  
  11. }
  12. finally{
  13.   // Sentenzas que sempre se executan
  14. }

Capturando os erros da funcións internas de PHP

  • Indicar que no caso de PHP 'tradicional' as funcións PHP non 'lanzan' excepcións, se non que 'avisan' con warnings, errors,...dependendo do nivel de erro a amosar, o cal está indicado na directiva display_error de php.ini.


  1.         <?php
  2.             set_error_handler(function () {
  3.                 throw new Exception('Houbo un erro...');
  4.             });
  5.  
  6.             try{
  7.                 $array = array('uno','dos','tres');
  8.                 echo $array[10];
  9.                
  10.             }
  11.             catch (Exception $ex) {
  12.                 echo "Outro erro non controlado";
  13.                 echo $ex->getMessage(); // Amosará o enviado dende a función --> Houbo un erro...
  14.             }
  15.          ?>
  • Liñas 2-4: Definimos unha función que vai 'capturar' os erros que poidan lanzar as funcións de PHP, lanzando unha excepción da clase Exception que será capturada polo bloque catch (Exception $ex).


Se queremos volver a deixar a xestión de erros como estaba deberemos chamar á función restore_error_handler.

Xerarquía de excepcións

  • No control de excepcións temos unha xerarquía de clases que representan diferentes tipos de excepción.
Esta xerarquía ten unha estrutura de herdanza no que o pai de todas elas é a clase Excepcion (a vista na explicación anterior).
Esta clase (a clase Excepcion) ten coma vantaxe que vai poder capturar todas as excepcións que se poidan producir, pero terá como desvantaxe que non imos saber que tipo de excepción foi capturada (podemos ter unha idea polo código da excepción).


  • Para 'afinar' máis o tipo de erro que podemos capturar existen outras clases que derivan da clase Exception e que capturan un tipo específico de excepción. Desta forma podemos ou mandar unha mensaxe ao cliente ou ter diferentes sentenzas a executar en caso de producirse diferentes tipos de excepcións.


Php Exception 1.jpg
Imaxe obtida dende https://fossies.org/dox/php-5.2.17/classException.html



  • A partires da versións 7 de PHP aparece unha nova xerarquía de clases para capturar erros:
Php Exception 2.jpg
Máis información neste enlace


  • Polo tanto podemos 'capturar' e 'lanzar' varias excepcións:
  1. try {
  2.  
  3.   // Sentenzas a controlar
  4.   if (condicion_a_controlar 1)
  5.      throw new OutOfRangeException('Mensaxe a enviar',1);   // Provocamos unha excepción. O control pasa a sección catch. Podemos enviar un texto e un dato numérico a sección catch (non é obrigatorio)
  6.  
  7.   // Sentenzas a controlar
  8.   if (condicion_a_controlar 2)
  9.      throw new Exception('Mensaxe a enviar',1);   // Provocamos unha excepción. O control pasa a sección catch. Podemos enviar un texto e un dato numérico a sección catch (non é obrigatorio)
  10.    
  11.  
  12. }
  13. catch (OutOfRangeException $e){
  14.   // Sentenzas a executar en caso de lanzarse a excepción
  15. }
  16. catch (Exception $e){
  17.   // Sentenzas a executar en caso de lanzarse a excepción
  18. }


Nota: Normalmente en todas as linguaxes a clase de excepción máis alta na xerarquía (neste caso Exception) ponse ao final de todo.


Excepcións definidas polo programador

  • Podemos crear as nosas propias excepcións creando unha clase que 'derive' da clase Exception.
Vexamos un exemplo:
  1.         <?php
  2.             class ExcepcionNumerica extends Exception
  3.             {
  4.               public function errorMessage() {
  5.                    //error message
  6.                   $errorMsg = $this->getMessage().'Soamente acéptanse valores numéricos.';
  7.                   return $errorMsg;
  8.               }        
  9.             }
  10.         ?>
  11.        
  12.        
  13.         <?php
  14.             $a='cadea';
  15.             $array = array('uno','dos','tres');
  16.             try{
  17.                
  18.                 if (!is_numeric($a)){
  19.                     throw new ExcepcionNumerica('Non é un número.');
  20.                 }
  21.                
  22.                 // Supoñamos que queremos acceder ao elemento 10 do array
  23.                 $acceso = 10;
  24.                 if (count($array)<$acceso){ // Non podemos, xa que o array ten 3 elementos
  25.                     throw new OutOfRangeException('Acceso fora de rango no array...');
  26.                 }
  27.                
  28.             }
  29.             catch (OutOfRangeException $ex) {
  30.                 echo "Outro erro non controlado1<br />";
  31.                 echo $ex->getMessage() . ':' . $ex->getCode(); // Amosará o enviado dende o thow new
  32.             }
  33.             catch (ExcepcionNumerica $ex){
  34.                 echo "Outro erro non controlado2<br />";
  35.                 echo $ex->errorMessage() . ':' . $ex->getCode(); // Amosará o enviado dende o thow new máis o posto na clase => función errorMessage
  36.             }
  37.             catch (Exception $ex) {
  38.                 echo "Outro erro non controlado3<br />";
  39.                 echo $ex->getMessage() . ':' . $ex->getCode(); // Amosará o enviado dende o thow new
  40.             }
  41.        
  42.         ?>
  • Liñas 2-10: Definimos a nosa propia excepción.
  • Liña 6: Fixarse como podemos definir unha función propia (errorMessage) para enviar á sección catch unha mensaxe personalizada. Neste caso enviamos o texto que se indica na orde 'throw new' xunto cun texto.
  • Liña 20: Neste exemplo como non cumpre a condición de que $a sexa numérica lanzará a excepción.
  • Liñas 34-37: Capturamos a excepción personalizada.
  • Liña 36: Fixarse como facemos uso da función 'errorMessage()' definida por nos na clase ExcepcionNumerica.
  • Se no mesmo exemplo cambiades a liña 15 e dádeslle un valor numérico a $a, lanzará a excepción 'OutOfRangeException' (liña 26).


  • Como podedes ver somos nos os que decidimos o tipo de excepción a lanzar en base ao noso programa e condicións.


Controlando erros no acceso a base de datos

  • En caso de estar a utilizar a 'extensión mysqli' para acceso a base de datos, teremos que controlar os erros que nos devolve o xestor para lanzar a excepción correspondente.
Non funciona poñer try {} catch{ Exception $e} na operación sobre o xestor de base de datos (alta, baixa, modificación).
Isto funcionaría se estivésemos a utilizar e extensión Obxectos de Datos de PHP (PDO por súas siglas en inglés).


  • Así, se queremos controlar, por exemplo, se produciuse un erro de clave duplicada ao facer un insert podemos:
  • Controlar no procedemento almacenado (se estamos a utilizar un proc.) que o dato a introducir non exista (con select previo) ou ben capturar a excepción e devolver á páxina php un código de erro.
  • Executar a operación (coa orde SQL INSERT, ou chamando a un proc. almacenado) e controlar o código de erro que devolve o xestor MYSQL para o caso dunha clave duplicada.
Temos neste enlace os erros que vai devolver o xestor (comprobar que a versión de Mysql sexa a mesma que tedes instalada, se non buscade os erros para a vosa versión).


Por exemplo:

  1. try {
  2.    $query=sprintf("INSERT INTO TABOA (col) VALUES ('%s')",$titulo);
  3.    if ($conex->query($query)){
  4.       // FOI TODO BEN
  5.    }
  6.    else{
  7.       throw new Exception('Houbo un erro na chamada á base de datos<br />',1);  // Enviamos un texto e un código de erro. O código de erro podería ser utilizado para saber cando estamos dentro dunha transacción e facer o rollback
  8.    }               }
  9. }
  10. catch(Exception $e){
  11.     // Amosamos o erro
  12.     $error = $e->getMessage();
  13.     $errorCode = $e->getCode();
  14. }


Neste caso non estamos controlando o tipo de erro que devolve o xestor.


  • Se queremos ser máis específico sobre o erro, podemos preguntar polo código específico do erro:
  1. try {
  2.    $query=sprintf("INSERT INTO TABOA (col) VALUES ('%s')",$titulo);
  3.    if ($conex->query($query)){
  4.       // FOI TODO BEN
  5.    }
  6.    else{
  7.       if ($conex->errno==1452) { // Clave duplicada
  8.          throw new Exception('Non podes engadir un dato que xa existe...<br />',1);  // Enviamos un texto e un código de erro. O código de erro podería ser utilizado para saber cando estamos dentro dunha transacción e facer o rollback
  9.       }
  10.       else {
  11.          throw new Exception('Houbo un erro na chamada á base de datos<br />',1);  // Enviamos un texto e un código de erro. O código de erro podería ser utilizado para saber cando estamos dentro dunha transacción e facer o rollback
  12.       }
  13.    }              
  14. }
  15. catch(Exception $e){
  16.     // Amosamos o erro
  17.     $error = $e->getMessage();
  18.     $errorCode = $e->getCode();
  19. }



-- Ángel D. Fernández González -- (2016).