Gestió d'errors i excepcions
De ben segur que a mesura que has anat resolent exercicis o simplement provant codi, t'has trobat amb errors de programació. Alguns són reconeguts per l'entorn de desenvolupament i pots corregir-los abans d'executar. Altres apareixen al navegador en forma de missatge d'error a l'executar l'script.
A diferència d'altres llenguatges de programació que tenen una gestió d'excepcions més completa PHP manté una gestió d'excepcions lleugera ja que distingeix entre errors i excepcions. Així, mentre no es produisca una excepció o un error fatal l'execució del programa continuarà.
No obstant això, en PHP 7 canvia la majoria dels errors notificats per PHP. En lloc de notificar errors a través
de l'mecanisme de notificació d'errors tradicional de PHP 5, la majoria dels errors ara són
notificats llançant excepcions Error
.
A l'igual que les excepcions normals, les excepcions Error
es propagaran fins a arribar al
primer bloc catch
coincident. Si no hi ha blocs coincidents, serà invocat qualsevol
gestor d'excepcions predeterminat definit amb set_exception_handler ()
, i si no hi hagués
cap gestor d'excepcions predeterminat, l'excepció serà convertida en un error fatal i serà gestionada
com un error tradicional.
A causa de que la jerarquia d'Error
no hereta de Exception
, el codi que empre blocs catch
(Exception $e) {...}
per gestionar excepcions no capturades en PHP 5 trobareu que
aquests Errors
no són capturats per aquests blocs. Es requereix, per tant, un bloc
catch (Error $e) {...}
o un gestor set_exception_handler()
.
Errors
PHP defineix una classificació dels errors que es poden produir en
l'execució d'un programa i ofereix mètodes per ajustar el tractament
dels mateixos. Per fer referència a cada un dels nivells d'error, PHP
defineix una sèrie de constants. Cada nivell s'identifica per una
constant. Per exemple, la constant E_NOTICE
fa referència a avisos que
poden indicar un error en executar el programa, i la constant E_ERROR
engloba errors fatals que provoquen que s'interrompa forçosament l'execució.
La llista completa de constants la pots consultar al manual de PHP, on també es descriu el tipus d'errors que representa : http://es.php.net/manual/es/errorfunc.constants.php
Canviar el comportament de PHP
La configuració inicial de com va a tractar-se cada error segons el seu
nivell es realitza en php.ini
el fitxer de configuració de PHP. Entre
els principals paràmetres que pots ajustar estan:
error_reporting
. Indica quins tipus d'errors es notificaran. El seu valor es forma utilitzant els operadors a nivell de bit per combinar les constants anteriors. El seu valor per defecte ésE_ALL & ~ E_NOTICE
que indica que es notifiquin tots els errors (E_ALL
) excepte els avisos a temps d'execució (E_NOTICE
).display_errors
. En el seu valor per defecte (On
), fa que els missatges s'envien a la sortida estàndard (i per tant es mostrin al navegador). S'ha de desactivar (Off
) en els servidors que s'usen per a producció.
Hi ha altres paràmetres que podem utilitzar en php.ini
per ajustar el
comportament de PHP quan es produeix un error.
http://es.php.net/manual/es/errorfunc.configuration.php
Des codi, pots fer servir la funció error_reporting
amb les constants
anteriors per establir el nivell de notificació en un moment determinat.
Per exemple, si en algun lloc del teu codi figura una divisió en la qual
hi haja la possibilitat que el divisor siga zero, quan això passe
obtindràs un missatge d'error al navegador. Per evitar-ho, pots
desactivar la notificació de errors de nivell E_WARNING
abans de la
divisió i restaurar al seu valor normal a continuació:
error_reporting(E_ALL & ~ E_NOTICE & ~ E_WARNING);
$resultat = $dividend/$divisor;
error_reporting (E_ALL & ~ E_NOTICE);
Notificació d'error en entorns de producció
; PHP comes packaged with two INI files. One that is recommended to be used
; in production environments and one that is recommended to be used in
; development environments.
; php.ini-production contains settings which hold security, performance and
; best practices at its core. But please be aware, these settings may break
; compatibility with older or less security conscience applications. We
; recommending using the production ini in production and testing environments.
; display_errors
; Default Value: On
; Development Value: On
; Production Value: Off
; display_startup_errors
; Default Value: Off
; Development Value: On
; Production Value: Off
; error_reporting
; Default Value: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED
; Development Value: E_ALL
; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT
; html_errors
; Default Value: On
; Development Value: On
; Production value: On
; log_errors
; Default Value: Off
; Development Value: On
; Production Value: On
Gestió global dels errors
En utilitzar la funció error_reporting
només controles quin tipus
d'errors va a notificar PHP. A vegades pot ser suficient, però per
obtenir més control sobre el procés existeix també la possibilitat de
gestionar de forma personalitzada els errors. És a dir, pots programar
una funció perquè siga la que executa PHP cada vegada que es produeix un
error. El nom d'aquesta funció s'indica utilitzant set_error_handler
i
ha de tenir com a mínim dos paràmetres obligatoris (el nivell de l'error
i el missatge descriptiu) i fins tres opcionals amb informació addicional
sobre el error (el nom del fitxer en què es produeix, el nombre de línia,
i un bolcat de l'estat de les variables en aquest moment).
set_error_handler ("miGestorDeErrores");
$resultat = $dividend / $divisor;
restore_error_handler ();
function miGestorDeErrores ($nivell, $missatge)
{
switch ($nivell) {
case E_WARNING:
echo "Error de tipus WARNING: $missatge. <br />";
break;
default:
echo "Error de tipus no especificat: $ missatge. <br />";
}
}
La funció restore_error_handler
restaura el gestor d'errors original
de PHP (més concretament, el que s'estava fent servir abans de la crida
a set_error_handler
).
ErrorException
Gràcies a la combinació de la classe ErrorException
i el mètode set_error_handler
podem convertir fàcilment els errors en excepcions de tipus ErrorException
:
<?php
function exception_error_handler($severidad, $mensaje, $fichero, $línea) {
if (!(error_reporting() & $severidad)) {
// Este código de error no está incluido en error_reporting
return;
}
throw new ErrorException($mensaje, 0, $severidad, $fichero, $línea);
}
set_error_handler("exception_error_handler");
/* Desencadenar la excepción */
strpos();
?>
Excepcions
A partir de la versió 5 es va introduir en PHP un model d'excepcions similar a l'existent en altres llenguatges de programació:
- El codi susceptible de produir algun error s'introdueix en un bloc
try
. - Quan es produeix algun error, es llança una excepció utilitzant la
instrucció
throw
. - Després del bloc
try
ha d'haver com a mínim un bloccatch
encarregat de processar el error. - Si un cop acabat el bloc
try
no s'ha llançat cap excepció, es continua amb la execució en la línia següent al bloc o blocscatch
.
Per exemple, per llançar una excepció quan es produeix una divisió per zero podries fer:
try {
if ($divisor == 0)
throw new Exception ( "Divisió per zero.");
$resultat = $dividend/$divisor;
}
catch (Exception $e) {
echo "S'ha produït el següent error:". $ e-> getMessage ();
}
PHP ofereix una classe base Exception
per utilitzar com a gestor
d'excepcions. Per llançar una excepció no cal indicar cap paràmetre,
encara que de forma opcional es pot passar un missatge d'error (com en
l'exemple anterior) i també un codi d'error.
Entre els mètodes que podeu utilitzar amb els objectes de la classe
Exception
estan:
getMessage
. Retorna el missatge, en cas que s'hagi posat algun.getCode
. Retorna el codi d'error si existeix.
Les funcions internes de PHP i moltes extensions com MySQLi usen el
sistema d'errors vist anteriorment. Només les extensions més modernes
orientades a objectes, com és el cas de PDO, utilitzen aquest model
d'excepcions. En aquest cas, el més comú és que l'extensió definisca els
seues propis controladors d'errors heretant de la classe Exception
.
Classe ErrorException
Com hem dit abans, utilitzant la classe ErrorException
és possible traduir els errors a
excepcions.http://es.php.net/manual/es/class.errorexception.php
Concretament, la classe PDO permet definir la fórmula que farà servir
quan es produeixi un error, utilitzant l'atribut PDO::ATTR_ERRMODE
.
Les possibilitats són:
PDO::ERRMODE_SILENT
. No es fa res quan ocorre un error. És el comportament per defecte.PDO::ERRMODE_WARNING
. Genera un error de tipus E_WARNING quan es produeix un error.PDO::ERRMODE_EXCEPTION
. Quan es produeix un error llança una excepció utilitzant el gestor propi PDOException.
És a dir, que si vols utilitzar excepcions amb l'extensió PDO, has de configurar la connexió fent:
$dwes-> setAttribute (PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Per exemple, el següent codi:
$dwes = new PDO("mysql:host=localhost;dbname=dwes", "dwes", "abc123.");
$dwes-> setAttribute (PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$sql = "SELECT * FROM stox";
$result = $dwes->query ($sql);
...
}
catch (PDOException $p) {
echo "Error". $p-> getMessage (). "<br />";
}
Captura l'excepció que llança PDO a causa de que la taula no existeix.
El bloc catch
mostra el següent missatge:
Error SQLSTATE [42S02]: Base table or view not found: 1146 Table 'dwes.stox' does not exist
El bloc finally
En PHP 5.5 i posterior, es pot utilitzar un bloc finally
després o en
lloc dels blocs catch
. El codi de dins el bloc finally
sempre
s'executarà després dels blocs try
i catch
, independentment que
s'haja llançat una excepció o no, i abans que l'execució normal
continue.
Com que el contingut del bloc finally
sempre s'executa és ideal per a
alliberar recursos en cas d'error, com una connexió a base de dades o a
la connexió a un servei proporcionats per tercers.
Múltiples catch
És possible afegir més d'un cath
de la següent manera:
try {
...
} catch (FooException e) {
...
} catch (BarException e) {
...
} catch (Exception e) {
...
}
A partir de PHP 7.1 també és possible fer-ho així:
try {
...
} catch (FooException | BarException | Exception e) {
...
}
Ampliant les excepcions
És possible crear excepcions personalitzades heretant de la classe
Exception
.
class NegativeIdException extends Exception {
public function __construct($message = null) {
$this->message = $message ?: 'ID Negatiu.';
parent::__construct($message);
}
}
Després l'utilitzarem així:
try {
$post = new Post(-1, "Hola món", "Aquest és el primer post del blog", new DateTime(), "Vicent");
} catch (NegativeIdException $e) {
echo "No es pot posar un ID negatiu";
} catch (Exception $e) {
echo "Excepció desconeguda". $e->getMessage();
}
Les excepcions personalitzades ens permeten una gestió més senzilla dels errors i major flexibilitat a l'hora de gestionar les possibles situacions d'errors.
Interfície Throwable
Throwable és la interfície base per a qualsevol objecte que puga ser llançat
mitjançant una sentència throw
en PHP 7, incloent Error
i Exception
.
Les classes de PHP no poden implementar la interfície Throwable
directament,
de manera que han d'estendre en el seu lloc Exception
.
Aquesta nova interfície té per objectiu unificar les dos tipologies d'exceptions. Tant Exception
com
Error
implementen la interfície Throwable
.
Bibliografia
- José Luis Comesaña. Apuntes de formación a distancia del módulo «Desarrollo web en entorno servidor» del CFGS DAW, elaborados y licenciados por el Ministerio de Educación, Cultura y Deporte. [en línia]. 2012 [data de consulta: 13 de setembre de 2019]. Disponible en \<https://github.com/statickidz/TemarioDAW/tree/master/DWES>
- Antonio López. Learning PHP 7. Packt Publishing (29 de marzo de 2016) .