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();
}
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
.
Gestió d'errors i excepcions
- Modifica les classes Input creades anteriorment perquè en cas que
el atribut
$name
no tinga valor llance una excepció amb el missatge "L'atribut name no pot estar buit". - Captura l'excepció.
- Crea una excepció personalitzada
ValuesNotFoundException
en cas que el paràmetre$values
del controlInputRadio
estiga buit. - Captura l'excepció.
- Crea una excepció personalitzada
MovieNotFound
que es llance en cas que l'identificador$id
sol·licitat no trobe cap pel·licula ensingle-page.php
- Captura l'excepció.
Exercicis pràctics
Activitat: Pel·lícules
Objectius
Els objectius de l'activitat són els següents: * Aprendre a crear classes * Instanciar objectes * Accedir a les seues propietats.
Enunciat
- Crea la classe Movie en la carpeta src del projecte (cal usar tipat esctricte).
- Els atributs seran:
- id (enter)
- title (string)
- tagline (string)
- releaseDate (DateTime)
- starsRating (float)
- poster (string)
- Crea els setters i els getters.
- La constant de classe
POSTER_PATH
contindrà la ruta al directori dels posters. - Modifica el projecte de forma que
$movies
siga un array d’objectesMovie
. - Modifica'l també perquè les pàgines index.php i movies.php funcionen correctament
Activitat: Pel·licules II
Objectius
Els objectius de l'activitat són els següents: * Repassar el concepte de paràmetres per referència. * Clonar objectes.
Enunciat
- Crea una pel·lícula copiant-la (=) de la darrera pel·lícula de l'array.
- Comprova en movies.php que s'ha insertat i que tenen les mateixes dades.
- Ara canvia el títol de la còpia afegint-li "(Còpia)" al títol que tenía. Per exemple si el títol era "Ava" ara serà "Ava (còpia)".
- Comprova en
movies.php
que s'ha insertat i que és diferent a l'original. Tenen les mateixes dades? Per què? - Repassa els apunts i soluciona-ho.
Activitat: Pel·lícules III
Objectius
Els objectius de l'activitat són els següents: * Seguir construint el projecte. * Treballar amb objectes.
Enunciat
- El títol de la pel·licula serà un enllaç a
single-page.php?id=[atribut id de la pel·lícula]
. - La pàgina
single-page.php
mostrarà les dades de la pel·licula que tinga l'id
que s'ha passat pel querystring. - Post usar
array_filter
per trobar l'objecte que té l'id indicat.
Activitat 4.2: Herència
Objectius
Els objectius de l'activitat són els següents: * Treballar l'herència. * Treballar amb classes abstractes
Enunciat
Anem a crear una sèrie de classes per mostrar camps de formulari.
Pots incloure'ls al projecte en src/utils
o crear-les a banda en la carpeta activitat4
en
l'arrel del projecte.
Crea la classe abstracta Input
que tindrà les propietats:
* title, serà el text que apareix com a etiqueta de camp.
* name, serà el atribut name del camp.
* value, serà el valor a mostrar si hi ha.
* type , serà el tipus de camp, el valor per defecte serà 'text' i es modificarà
internament en les subclasses ($this->type=??
).
El constructor serà
public function __construct(string $title, string $name, string $value);
Tindrà un mètode generateHTML()
que no tindrà paràmetres i tornarà un string amb
l'HTML que representa el camp.
Per exemple:
<label>Nom</label>
<input type="text" value="" name="nom" />
A continuació crea les classes InputText
, InputEmail
(type=email), InputTel
(type=tel),
que hereten de la classe abstracta i usa-les per a crear un formulari en els camps
* Nom
* Cogonms
* Correu electrònic
* Telèfon fixe
* Telèfon mòbil.
* Enviar (pots implementar la classe InputSubmit
o escriure'l directament en HTML.
La pàgina de resposta mostrarà els valors enviats pel formulari.
Activitat 4.3: Herència (i 2)
{:.no_toc .nocount}
Objectius
{:.no_toc .nocount} Els objectius de l'activitat són els següents: * Treballar l'herència. * Treballar amb classes abstractes * Treballar el polimorfisme
Enunciat
{:.no_toc .nocount}
Anem a crear una sèrie de classes per mostrar camps de formulari.
Pots incloure'ls al projecte en src/utils
o crear-les a banda en la carpeta activitat4
en
l'arrel del projecte.
Crea les classes InputRange
i InputRadio
que heretaran de la classe abstracta però que
tindran el constructor i els atributs diferents. Afig als camps ja existents els camps:
- Edat (de tipus range, 40 serà el valor per defecte)
- Sexe (de tipus radio amb el sexe)
Crea la classe Form
que representarà un formulari HTML.
El constructor serà
public function __construct(string $action, string $method ='POST');
Tindrà una propietat pública $elements
que serà un array d'objectes Input
. Un mètode Form::add(Input $input)
que afegirà elements a l'array.
Tindrà també altres dos mètodes Form::generateStartHTML()
que generarà l'etiqueta <form...>
i
Form::generaEndHTML()
que generarà l'etiqueta de tancament </form>
Crea un objecte Form
i afig mitjançant el mètode add
els elements ja existents i mostra el formulari.
La pàgina de resposta mostrarà els valors enviats pel formulari.
Activitat 4.4: Interfície IControl
Objectius
Els objectius de l'activitat són els següents: * Crear interfícies. * Treballar amb classes abstractes * Treballar el polimorfisme
Enunciat
{:.no_toc .nocount}
Crea una nova classe Textarea
semblant a la classe Input
que generarà
un camp textarea de formulari.
Podrem afegir objectes Textarea
mitjançant el mètode Form::add()
?;
Crea la interfície IControl
:
interface IControl {
public function render(): string;
}
Modifica Form::add()
perquè admeta paràmetres de tipus IControl
.
Implementa en la classe Form
la interfície Countable
.
Després mitjançant la funció count()
mostra el número total d'elements del formulari.
Bibliografia
{: .no_toc }
- 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) .