php8.5
Home/ Manual/ language / control-structures/ finally

finally

(PHP 5 >= 5.5.0, PHP 7, PHP 8)

A Finally block may also be specified after or instead of Catch blocks. Code within the Finally block will always be executed after the Try and Catch blocks, even if a Return or Yield statement is encountered, or if a Throwable error is not caught or is rethrown. It will be executed prior to resuming the normal execution flow. The only exception to this rule is when the script is terminated abruptly, such as by a call to exit or by a fatal error (for example, when the maximum execution time is exceeded).

A Finally block may also be specified after or instead of Catch blocks. Code within the Finally block will always be executed after the Try and Catch blocks, even if a Return or Yield statement is encountered, or if a Throwable error is not caught or is rethrown. It will be executed prior to resuming the normal execution flow. The only exception to this rule is when the script is terminated abruptly, such as by a call to exit() or by a fatal error (for example, when the maximum execution time is exceeded).

Throwable error handling with a Finally block

php
<?php
function inverse($x) {
    return 1/$x;
}

try {
    echo inverse(5) . "\n";
} catch (DivisionByZeroError $e) {
    echo 'Caught error: ', $e->getMessage(), "\n";
} finally {
    echo "First finally.\n";
}

try {
    echo inverse(0) . "\n";
} catch (DivisionByZeroError $e) {
    echo 'Caught error: ', $e->getMessage(), "\n";
} finally {
    echo "Second finally.\n";
}

// Continue execution
echo "Hello World\n";

8

output
0.2
First finally.
Caught error: Division by zero
Second finally.
Hello World

When the call stack is unwound after a Throwable error has been thrown, all Finally blocks it encounters along the way will be executed. In other words, nested Finally blocks are executed even if the Throwable error is not caught by an adjacent Catch block.

All Finally blocks will be executed

php
<?php
try {
    try {
        throw new Exception();
    } finally {
        echo "Inner finally\n";
    }
} catch (Exception $e) {
} finally {
    echo "Outer finally\n";
}

The above example will output:

output
Inner finally
Outer finally

Interaction between a Finally block and a Return statement

One notable interaction is between the Finally block and a Return statement. If a Return statement is encountered inside either the Try or the Catch block, the Finally block will still be executed. Moreover, the Return statement is evaluated when encountered, but the result will be returned after the Finally block is executed. Additionally, if the Finally block also contains a Return statement, the value from the Finally block is returned.

Interaction between Return in Finally block and a previous Return

php
<?php
function testReturnTry() {
    try {
        echo 'Within try', "\n";
        return 'try';
    } finally {
        echo 'Within finally', "\n";
        return 'finally';
    }
}

function testReturnCatch() {
    try {
        throw new Exception('foo');
    } catch (Exception $e) {
        echo 'Within catch', "\n";
        return 'catch';
    } finally {
        echo 'Within finally', "\n";
        return 'finally';
    }
}

echo testReturnTry(), "\n";
echo testReturnCatch(), "\n";

The above example will output:

output
Within try
Within finally
finally
Within catch
Within finally
finally

Interaction between the Finally block and Return

php
<?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";

The above example will output:

output
finally try
finally catch

Interaction between a Finally block and an exception thrown in a Try block

If both the Try block and the Finally block throw an exception, the exception thrown from the Finally block is the one that is propagated, and the exception thrown from the Try block will be used as its previous exception.

Exception in try and in finally

php
<?php

try {
    try {
        throw new Exception(message: 'Third', previous: new Exception('Fourth'));
    } finally {
        throw new Exception(message: 'First', previous: new Exception('Second'));
    }
} catch (Exception $e) {
    var_dump(
        $e->getMessage(),
        $e->getPrevious()->getMessage(),
        $e->getPrevious()->getPrevious()->getMessage(),
        $e->getPrevious()->getPrevious()->getPrevious()->getMessage(),
    );
}

The above example will output:

output
string(5) "First"
string(6) "Second"
string(5) "Third"
string(6) "Fourth"

Behaviour of a Finally block in a Generator

If, during the traversal of a generator, a Throwable error is thrown, the Finally block within the generator will be executed, as it is the previous element of the call stack.

Finally block being run when an Exception has been thrown

php
<?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 'outer catch';
}

The above example will output something similar to:

output
1
finally
outer catch

It is possible to Yield within a Finally block, but if a Throwable error occurs and is not caught within the Foreach, a Cannot yield from finally in a force-closed generator Error is thrown.

Error when Yielding in a Finally block when an Exception has been thrown

php
<?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 'outer catch';
}

The above example will output something similar to:

output
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 between a Finally block and a call to exit()

When exit() is called within a Try or a Catch block, all Finally blocks are skipped.

Interaction between a Finally block and exit() called in a Try block

php
<?php
try {
    echo 'quit';
    exit(0);
} finally {
    echo 'finally';
}

The above example will output:

output
quit

Interaction between a Finally block and exit() called in a Catch block

php
<?php
try {
    throw new Exception('foo');
} catch (Exception $e) {
    echo 'catch';
    exit(0);
} finally {
    echo 'finally ';
}

The above example will output:

output
catch

All Finally blocks are skipped when exit() is called

php
<?php
try {
    try {
        echo 'quit';
        exit(0);
    } finally {
        echo 'inner finally ';
    }
} finally {
    echo 'outer finally';
}

The above example will output:

output
quit

When exit() is called within a Finally block, all outer Finally blocks are skipped.

Skipping an outer Finally block by calling exit()

php
<?php
try {
    try {
        echo 'try', "\n";
    } finally {
        echo 'inner finally ';
        exit(0);
    }
} finally {
    echo 'outer finally';
}

The above example will output:

output
try
inner finally

Edge case of calling exit() in a Foreach over a generator

This one edge case behaviour is highly dependent on which version of PHP the code is executed with.

php
<?php
function generate()
{
    try {
        yield 1;
        yield 2;
    } catch (Exception $e) {
        echo 'catch';
    } finally {
        echo "inner finally\n";
    }
}

try {
    foreach (generate() as $i) {
        echo $i, "\n";
        exit(0);
    }
} finally {
    echo "outer finally\n";
}

Output of the above example prior to PHP 7.1.14, in PHP 7.2.0 and 7.2.1, and as of PHP 8.0.0

output
1
inner finally

Output of the above example as of PHP 7.1.14 and 7.2.2, and prior to PHP 8.0.0

output
1

Source: language/control-structures/finally.xml · from the official PHP manual (php/doc-en)