(PHP 5 >= 5.5.0, PHP 7, PHP 8)
Un bloc finally peut aussi être spécifié après, ou à la place, des blocs
catch.
Le code d'un bloc finally est toujours exécuté après les blocs try et
catch, même si une instruction return ou yield est rencontrée, ou si une
erreur Throwable n'est pas attrapée ou est
relancée.
Il est exécuté avant la reprise du flux d'exécution normal.
La seule exception à cette règle est l'arrêt brutal du script, comme lors d'un
appel à exit() ou d'une erreur fatale (par exemple, lorsque
le temps d'exécution maximal est dépassé).
Exemple #1 Traitement d'une erreur Throwable avec un bloc finally
<?php
function inverse($x) {
return 1/$x;
}
try {
echo inverse(5) . "\n";
} catch (DivisionByZeroError $e) {
echo 'Erreur attrapée : ', $e->getMessage(), "\n";
} finally {
echo "Premier finally.\n";
}
try {
echo inverse(0) . "\n";
} catch (DivisionByZeroError $e) {
echo 'Erreur attrapée : ', $e->getMessage(), "\n";
} finally {
echo "Second finally.\n";
}
// Poursuite de l'exécution
echo "Bonjour le monde\n";Résultat de l'exemple ci-dessus en PHP 8 :
0.2 Premier finally. Erreur attrapée : Division by zero Second finally. Bonjour le monde
Lorsque la pile d'appels est dépilée après qu'une erreur
Throwable a été lancée, tous les blocs
finally rencontrés en chemin sont exécutés.
En d'autres termes, les blocs finally imbriqués sont exécutés même si
l'erreur Throwable n'est pas attrapée par un
bloc catch adjacent.
Exemple #2 Tous les blocs finally sont exécutés
<?php
try {
try {
throw new Exception();
} finally {
echo "Finally interne\n";
}
} catch (Exception $e) {
} finally {
echo "Finally externe\n";
}L'exemple ci-dessus va afficher :
Finally interne Finally externe
finally et une instruction return
Une interaction notable existe entre le bloc finally et une instruction
return.
Si une instruction return est rencontrée dans un bloc try ou catch, le
bloc finally est tout de même exécuté. De plus, l'instruction return est
évaluée lorsqu'elle est rencontrée, mais le résultat n'est retourné qu'après
l'exécution du bloc finally. En outre, si le bloc finally contient lui
aussi une instruction return, c'est la valeur du bloc finally qui est
retournée.
Exemple #3 Interaction entre return dans un bloc finally et un return précédent
<?php
function testReturnTry() {
try {
echo 'Dans try', "\n";
return 'try';
} finally {
echo 'Dans finally', "\n";
return 'finally';
}
}
function testReturnCatch() {
try {
throw new Exception('foo');
} catch (Exception $e) {
echo 'Dans catch', "\n";
return 'catch';
} finally {
echo 'Dans finally', "\n";
return 'finally';
}
}
echo testReturnTry(), "\n";
echo testReturnCatch(), "\n";L'exemple ci-dessus va afficher :
Dans try Dans finally finally Dans catch Dans finally finally
Exemple #4 Interaction entre le bloc finally et return
<?php
function testReturnTry() {
try {
return 'try';
} finally {
echo 'finally ';
}
}
function testReturnCatch() {
try {
throw new Exception('foo');
} catch (Exception $e) {
return 'catch';
} finally {
echo 'finally ';
}
}
echo testReturnTry(), "\n";
echo testReturnCatch(), "\n";L'exemple ci-dessus va afficher :
finally try finally catch
finally et une exception lancée dans un bloc try
Si le bloc try et le bloc finally lancent tous deux une exception,
l'exception lancée par le bloc finally est celle qui est propagée, et
l'exception lancée par le bloc try est utilisée comme son exception
précédente.
Exemple #5 Exception dans try et dans finally
<?php
try {
try {
throw new Exception(message: 'Troisième', previous: new Exception('Quatrième'));
} finally {
throw new Exception(message: 'Première', previous: new Exception('Deuxième'));
}
} catch (Exception $e) {
var_dump(
$e->getMessage(),
$e->getPrevious()->getMessage(),
$e->getPrevious()->getPrevious()->getMessage(),
$e->getPrevious()->getPrevious()->getPrevious()->getMessage(),
);
}L'exemple ci-dessus va afficher :
string(9) "Première" string(9) "Deuxième" string(10) "Troisième" string(10) "Quatrième"
finally dans un
générateur
Si une erreur Throwable est lancée pendant le
parcours d'un générateur, le bloc finally du générateur est exécuté, car
il est l'élément précédent de la pile d'appels.
Exemple #6
Bloc finally exécuté lorsqu'une Exception
a été lancée
<?php
function generate()
{
try {
yield 1;
yield 2;
} catch (Exception $e) {
echo 'catch';
} finally {
echo "finally\n";
}
}
try {
foreach (generate() as $i) {
echo $i, "\n";
throw new Exception();
}
} catch (Exception $e) {
echo 'catch externe';
}Résultat de l'exemple ci-dessus est similaire à :
1 finally catch externe
Il est possible d'utiliser yield dans un bloc finally, mais si une
erreur Throwable survient et n'est pas
attrapée dans le foreach, une erreur Error
Cannot yield from finally in a force-closed generator est
lancée.
Exemple #7
Erreur lors d'un yield dans un bloc finally lorsqu'une
Exception a été lancée
<?php
function generate()
{
try {
yield 1;
yield 2;
} catch (Exception $e) {
echo 'catch';
} finally {
echo "finally\n";
yield 3;
}
}
try {
foreach (generate() as $i) {
echo $i, "\n";
throw new Exception();
}
} catch (Exception $e) {
echo 'catch externe';
}Résultat de l'exemple ci-dessus est similaire à :
1
finally
Fatal error: Uncaught Exception in /in/GbFmu:19
Stack trace:
#0 {main}
Next Error: Cannot yield from finally in a force-closed generator in /in/GbFmu:12
Stack trace:
#0 /in/GbFmu(19): generate()
#1 {main}
thrown in /in/GbFmu on line 12
finally et un appel à exit()
Lorsque exit() est appelé dans un bloc try ou catch,
tous les blocs finally sont ignorés.
Exemple #8 Interaction entre un bloc finally et exit() appelé dans un bloc try
<?php
try {
echo 'quitter';
exit(0);
} finally {
echo 'finally';
}L'exemple ci-dessus va afficher :
quitter
Exemple #9 Interaction entre un bloc finally et exit() appelé dans un bloc catch
<?php
try {
throw new Exception('foo');
} catch (Exception $e) {
echo 'catch';
exit(0);
} finally {
echo 'finally ';
}L'exemple ci-dessus va afficher :
catch
Exemple #10 Tous les blocs finally sont ignorés lorsque exit() est appelé
<?php
try {
try {
echo 'quitter';
exit(0);
} finally {
echo 'finally interne ';
}
} finally {
echo 'finally externe';
}L'exemple ci-dessus va afficher :
quitter
Lorsque exit() est appelé dans un bloc finally, tous les
blocs finally externes sont ignorés.
Exemple #11 Ignorer un bloc finally externe en appelant exit()
<?php
try {
try {
echo 'try', "\n";
} finally {
echo 'finally interne ';
exit(0);
}
} finally {
echo 'finally externe';
}L'exemple ci-dessus va afficher :
try finally interne
foreach sur un générateurCe cas limite a un comportement fortement dépendant de la version de PHP avec laquelle le code est exécuté.
<?php
function generate()
{
try {
yield 1;
yield 2;
} catch (Exception $e) {
echo 'catch';
} finally {
echo "finally interne\n";
}
}
try {
foreach (generate() as $i) {
echo $i, "\n";
exit(0);
}
} finally {
echo "finally externe\n";
}Sortie de l'exemple ci-dessus antérieurement à PHP 7.1.14, avec PHP 7.2.0 et 7.2.1, et à partir de PHP 8.0.0
1 finally interne
Sortie de l'exemple ci-dessus à partir de PHP 7.1.14 et 7.2.2, et antérieurement à PHP 8.0.0
1