Files
doc-fr/language/exceptions.xml
2024-08-22 18:49:28 +01:00

565 lines
16 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!-- EN-Revision: 7a75b854c8c52226d38397e7e8177e339fdb273f Maintainer: yannick Status: ready -->
<!-- Reviewed: no -->
<chapter xml:id="language.exceptions">
<!-- we use our own DTD instead of xmlns="http://docbook.org/ns/docbook" so that we can use
<sect1 annotations="chunk:false"> -->
<title>Les exceptions</title>
<para>
PHP a une gestion des exceptions similaire à ce qu'offrent les autres
langages de programmation.
Une exception peut être lancée ("&throw;") et attrapée
("&catch;") dans PHP. Le code devra être entouré
d'un bloc &try; pour faciliter la saisie d'une exception
potentielle. Chaque &try; doit avoir au moins
un bloc &catch; ou &finally; correspondant.
</para>
<para>
Si une exception est lancée et que la portée courante de la fonction n'a pas
de block &catch;, l'exception "remontera" la pile d'appel de la fonction appelante
jusqu'à trouver un bloc &catch; correspondant. Tous les blocs &finally; rencontrés
seront exécutés. Si la pile d'appel est déroulée jusqu'à la portée globale sans
rencontrer de bloc &catch; correspondant, le programme sera terminé avec
une erreur fatale sauf si un gestionnaire global d'exception a été défini.
</para>
<para>
L'objet lancé doit être une &instanceof; <interfacename>Throwable</interfacename>.
Tenter de lancer un objet qui ne l'est pas résultera en une erreur fatale émise par PHP.
</para>
<para>
À partir de PHP 8.0.0, le mot clé &throw; est une expression et peut être
utilisé dans n'importe quel contexte d'expressions. Dans les versions
antérieures c'était une déclaration qui devait être sur sa propre ligne.
</para>
<sect1 annotations="chunk:false" xml:id="language.exceptions.catch">
<title><literal>catch</literal></title>
<para>
Un bloc &catch; définit comment réagir à une exception qui a été lancée.
Un bloc &catch; définit un ou plusieurs types d'exceptions ou erreurs qu'il peut
gérer, et optionnellement une variable dans laquelle assigner l'exception.
(Cette variable était requise dans les versions antérieures à PHP 8.0.0)
Le premier bloc &catch; qu'une exception ou erreur lancée rencontre et qui correspond au
type de l'objet lancé gérera l'objet.
</para>
<para>
Plusieurs blocs &catch; peuvent être utilisés pour attraper différentes
classes d'exceptions. L'exécution normale (lorsqu'aucune exception n'est lancée
dans le bloc &try;) continue après le dernier
bloc &catch; défini dans la séquence. Les exceptions
peuvent être lancées (&throw;) ou relancées dans un bloc &catch;. Sinon,
l'exécution continuera après le bloc &catch; qui a été déclenché.
</para>
<para>
Lorsqu'une exception est lancée, le code suivant le traitement ne sera pas
exécuté et PHP tentera de trouver le premier bloc &catch; correspondant.
Si une exception n'est pas attrapée, une erreur fatale issue de PHP sera
envoyée avec un message "<literal>Uncaught Exception ...</literal>"
indiquant que l'exception n'a pu être attrapée à moins qu'un gestionnaire
d'exceptions ne soit défini avec la fonction
<function>set_exception_handler</function>.
</para>
<para>
À partir de PHP 7.1, un block &catch; peut spécifier plusieurs exceptions à
l'aide du caratère pipe (<literal>|</literal>). Ceci est utile lorsque
différentes exceptions de hiérarchies de classes différentes sont traitées
de la même manière.
</para>
<para>
À partir de PHP 8.0.0, le nom de variable pour l'exception attrapée est
optionnel. Si non spécifié, le bloc &catch; sera toujours exécuté mais
n'aura pas accès à l'objet lancé.
</para>
</sect1>
<sect1 annotations="chunk:false" xml:id="language.exceptions.finally">
<title><literal>finally</literal></title>
<para>
Un bloc &finally; peut aussi être
spécifié après des blocs &catch;. Le code à l'intérieur
du bloc &finally; sera toujours exécuté après les blocs
&try; et &catch;, indépendamment du fait qu'une
exception a été lancée, avant de continuer l'exécution normale.
</para>
<para>
Une interaction notable est entre un bloc &finally; et une déclaration
&return;.
Si une déclaration &return; est rencontrée à l'intérieur des blocs &try;
ou &catch;, le bloc &finally; sera quand même exécuté. De plus,
la déclaration &return; est évaluée quand elle est rencontrée, mais le
résultat sera retourné après que le bloc &finally; soit exécuté.
Additionnellement, si le bloc &finally; contient aussi une déclaration
&return; la valeur du bloc &finally; est retournée.
</para>
</sect1>
<sect1 annotations="chunk:false" xml:id="language.exceptions.exception-handler">
<title>Global exception handler</title>
<para>
Si une exception est autorisée à remonter jusqu'à la portée globale, elle peut
être attrapée par un gestionnaire d'exceptions global si il a été défini. La fonction
<function>set_exception_handler</function> peut définir une fonction qui sera
appelée à la place d'un block &catch; si aucun autre block n'est invoqué.
L'effet est essentiellement identique à entourer le programme entier dans un
block &try;-&catch; avec cette fonction en tant que &catch;.
</para>
</sect1>
<sect1 annotations="chunk:false" xml:id="language.exceptions.notes">
&reftitle.notes;
<note>
<para>
Les fonctions internes de PHP utilisent principalement l'
<link linkend="ini.error-reporting">Error reporting</link>, seules les extensions
<link linkend="language.oop5">orientées objet</link>
utilisent les exceptions. Quoiqu'il en soit, des erreurs peuvent facilement être traduites en
exceptions avec <link linkend="class.errorexception">ErrorException</link>.
Cependant, cette technique ne fonctionne que pour les erreurs non fatales.
</para>
<example>
<title>Convertir l'error reporting en exceptions</title>
<programlisting role="php">
<![CDATA[
<?php
function exceptions_error_handler($severity, $message, $filename, $lineno) {
throw new ErrorException($message, 0, $severity, $filename, $lineno);
}
set_error_handler('exceptions_error_handler');
?>
]]>
</programlisting>
</example>
</note>
<tip>
<para>
La <link linkend="intro.spl">bibliothèque standard PHP (SPL)</link> fournit
un bon nombre <link linkend="spl.exceptions">d'exceptions additionnelles</link>.
</para>
</tip>
</sect1>
<sect1 annotations="chunk:false" xml:id="language.exceptions.examples">
&reftitle.examples;
<example>
<title>Lancer une exception</title>
<programlisting role="php">
<![CDATA[
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Division par zéro.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Exception reçue : ', $e->getMessage(), "\n";
}
// Continue execution
echo "Bonjour le monde !\n";
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
0.2
Exception reçue : Division par zéro.
Bonjour le monde !
]]>
</screen>
</example>
<example>
<title>Gestion de l'exception avec un bloc &finally;</title>
<programlisting role="php">
<![CDATA[
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Division par zéro.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
} catch (Exception $e) {
echo 'Exception reçue : ', $e->getMessage(), "\n";
} finally {
echo "Première fin.\n";
}
try {
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Exception reçue : ', $e->getMessage(), "\n";
} finally {
echo "Seconde fin.\n";
}
// On continue l'exécution
echo "Bonjour le monde !\n";
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
0.2
Première fin.
Exception reçue : Division par zéro.
Seconde fin.
Bonjour le monde !
]]>
</screen>
</example>
<example>
<title>Interaction entre le bloc &finally; et &return;</title>
<programlisting role="php">
<![CDATA[
<?php
function test() {
try {
throw new Exception('foo');
} catch (Exception $e) {
return 'catch';
} finally {
return 'finally';
}
}
echo test();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
finally
]]>
</screen>
</example>
<example>
<title>Héritage d'une exception</title>
<programlisting role="php">
<![CDATA[
<?php
class MyException extends Exception { }
class Test {
public function testing() {
try {
try {
throw new MyException('foo!');
} catch (MyException $e) {
// on la relance
throw $e;
}
} catch (Exception $e) {
var_dump($e->getMessage());
}
}
}
$foo = new Test;
$foo->testing();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
string(4) "foo!"
]]>
</screen>
</example>
<example>
<title>Gestion des exceptions de capture multiple</title>
<programlisting role="php">
<![CDATA[
<?php
class MyException extends Exception { }
class MyOtherException extends Exception { }
class Test {
public function testing() {
try {
throw new MyException();
} catch (MyException | MyOtherException $e) {
var_dump(get_class($e));
}
}
}
$foo = new Test;
$foo->testing();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
string(11) "MyException"
]]>
</screen>
</example>
<example>
<title>Omettre la variable attrapé</title>
<para>Seulement permis dans PHP 8.0.0 et ultérieur.</para>
<programlisting role="php">
<![CDATA[
<?php
function test() {
throw new SpecificException('Oopsie');
}
try {
test();
} catch (SpecificException) {
print "A SpecificException was thrown, but we don't care about the details.";
}
?>
]]>
</programlisting>
</example>
<example>
<title>Throw en tant qu'expression</title>
<para>Seulement permis dans PHP 8.0.0 et ultérieur.</para>
<programlisting role="php">
<![CDATA[
<?php
class SpecificException extends Exception {}
function test() {
do_something_risky() or throw new Exception('It did not work');
}
try {
test();
} catch (Exception $e) {
print $e->getMessage();
}
?>
]]>
</programlisting>
</example>
</sect1>
<sect1 xml:id="language.exceptions.extending">
<title>Etendre les Exceptions</title>
<para>
Une classe d'exception définie par l'utilisateur peut être définie en étendant
la classe Exception intégrée. Les membres et les propriétés ci-dessous montrent
ce qui est accessible dans la classe enfant qui dérive de la classe Exception
intégrée.
</para>
<example>
<title>La classe d'exception intégrée</title>
<programlisting role="php">
<![CDATA[
<?php
class Exception implements Throwable
{
protected $message = 'Unknown exception'; // Message d'exception
private $string; // cache de __toString
protected $code = 0; // code d'exception défini par l'utilisateur
protected $file; // nom de fichier source de l'exception
protected $line; // ligne source de l'exception
private $trace; // trace de la pile d'exécution
private $previous; // exception précédente si exception imbriquée
public function __construct($message = '', $code = 0, Throwable $previous = null);
final private function __clone(); // Inhibe la duplication des exceptions.
final public function getMessage(); // message de l'exception
final public function getCode(); // code de l'exception
final public function getFile(); // nom du fichier source
final public function getLine(); // ligne source
final public function getTrace(); // tableau de la trace de la pile d'exécution
final public function getPrevious(); // l'exception précédente
final public function getTraceAsString(); // trace sous forme de chaîne de caractères
// Peut être redéfinie
public function __toString(); // chaîne formatée pour l'affichage
}
?>
]]>
</programlisting>
</example>
<para>
Si une classe étend la classe Exception intégrée et redéfinit le <link
linkend="language.oop5.decon">constructeur</link>, il est fortement recommandé
qu'elle appelle également <link
linkend="language.oop5.paamayim-nekudotayim">parent::__construct()</link>
pour s'assurer que toutes les données disponibles ont été correctement assignées.
La méthode <link linkend="language.oop5.magic">__toString()</link> peut être
redéfinie pour fournir une sortie personnalisée lorsque l'objet est présenté
sous forme de chaîne de caractères.
</para>
<note>
<para>
Les exceptions ne peuvent pas être clonées. Tenter de <link
linkend="language.oop5.cloning">cloner</link> une Exception entraînera une
erreur fatale <constant>E_ERROR</constant>.
</para>
</note>
<example>
<title>Étendre la classe Exception</title>
<programlisting role="php">
<![CDATA[
<?php
/**
* Défini une classe d'exception personnalisée.
*/
class MyException extends Exception
{
// Redéfini l'exception pour que le message ne soit pas facultatif.
public function __construct($message, $code = 0, Throwable $previous = null) {
// du code
// s'assurer que tout est correctement assigné
parent::__construct($message, $code, $previous);
}
// Représentation personnalisée de l'objet sous forme de chaîne de caractères.
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
public function customFunction() {
echo "Une fonction personnalisée pour ce type d'exception\n";
}
}
/**
* Créer une classe pour tester l'exception
*/
class TestException
{
public $var;
const THROW_NONE = 0;
const THROW_CUSTOM = 1;
const THROW_DEFAULT = 2;
function __construct($avalue = self::THROW_NONE) {
switch ($avalue) {
case self::THROW_CUSTOM:
// Lance une exception personnalisée
throw new MyException('1 n\'est pas un paramètre valide', 5);
break;
case self::THROW_DEFAULT:
// Lance celle par défaut.
throw new Exception('2 n\'est pas autorisé comme paramètre', 6);
break;
default:
// Aucune exception, l'object sera créé.
$this->var = $avalue;
break;
}
}
}
// Exemple 1
try {
$o = new TestException(TestException::THROW_CUSTOM);
} catch (MyException $e) { // Sera capturée
echo "MyException capturée\n", $e;
$e->customFunction();
} catch (Exception $e) { // Ignoré
echo "Exception par défaut capturée\n", $e;
}
// Poursuite de l'exécution
var_dump($o); // Null
echo "\n\n";
// Exemple 2
try {
$o = new TestException(TestException::THROW_DEFAULT);
} catch (MyException $e) { // Ne correspond pas à ce type
echo "MyException capturée\n", $e;
$e->customFunction();
} catch (Exception $e) { // Sera capturée
echo "Exception par défaut capturée\n", $e;
}
// Poursuite de l'exécution
var_dump($o); // Null
echo "\n\n";
// Exemple 3
try {
$o = new TestException(TestException::THROW_CUSTOM);
} catch (Exception $e) { // Sera capturée
echo "Exception par défaut capturée\n", $e;
}
// Poursuite de l'exécution
var_dump($o); // Null
echo "\n\n";
// Exemple 4
try {
$o = new TestException();
} catch (Exception $e) { // Sauté, aucune exception
echo "Exception par défaut capturée\n", $e;
}
// Poursuite de l'exécution
var_dump($o); // TestException
echo "\n\n";
?>
]]>
</programlisting>
</example>
</sect1>
</chapter>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->