finally

(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

Interaction entre un bloc 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

Interaction entre un bloc 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"

Comportement d'un bloc 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

Interaction entre un bloc 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

Cas limite d'un appel à exit() dans un foreach sur un générateur

Ce 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
+add a note

User Contributed Notes

There are no user contributed notes for this page.