finally

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

También puede especificarse un bloque finally después de los bloques catch, o en lugar de ellos. El código dentro del bloque finally siempre se ejecutará después de los bloques try y catch, incluso si se encuentra una sentencia return o yield, o si un error Throwable no es capturado o es relanzado. Se ejecutará antes de reanudar el flujo de ejecución normal. La única excepción a esta regla es cuando el script se termina de forma abrupta, por ejemplo mediante una llamada a exit() o por un error fatal (por ejemplo, cuando se supera el tiempo máximo de ejecución).

Ejemplo #1 Manejo de un error Throwable con un bloque finally

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

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

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

// Continuar la ejecución
echo "Hola mundo\n";

Resultado del ejemplo anterior en PHP 8:

0.2
Primer finally.
Error capturado: Division by zero
Segundo finally.
Hola mundo

Cuando la pila de llamadas se desenrolla tras lanzarse un error Throwable, todos los bloques finally que encuentra por el camino se ejecutarán. En otras palabras, los bloques finally anidados se ejecutan incluso si el error Throwable no es capturado por un bloque catch adyacente.

Ejemplo #2 Se ejecutarán todos los bloques finally

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

El ejemplo anterior mostrará:

Finally interno
Finally externo

Interacción entre un bloque finally y una sentencia return

Una interacción notable se produce entre el bloque finally y una sentencia return. Si se encuentra una sentencia return dentro del bloque try o del bloque catch, el bloque finally se ejecutará igualmente. Además, la sentencia return se evalúa cuando se encuentra, pero el resultado se devolverá después de ejecutar el bloque finally. Adicionalmente, si el bloque finally también contiene una sentencia return, se devuelve el valor del bloque finally.

Ejemplo #3 Interacción entre return en un bloque finally y un return anterior

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

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

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

El ejemplo anterior mostrará:

Dentro de try
Dentro de finally
finally
Dentro de catch
Dentro de finally
finally

Ejemplo #4 Interacción entre el bloque finally y 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";

El ejemplo anterior mostrará:

finally try
finally catch

Interacción entre un bloque finally y una excepción lanzada en un bloque try

Si tanto el bloque try como el bloque finally lanzan una excepción, la excepción lanzada desde el bloque finally es la que se propaga, y la excepción lanzada desde el bloque try se utilizará como su excepción previa.

Ejemplo #5 Excepción en try y en finally

<?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(),
    );
}

El ejemplo anterior mostrará:

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

Comportamiento de un bloque finally en un generador

Si, durante el recorrido de un generador, se lanza un error Throwable, el bloque finally dentro del generador se ejecutará, ya que es el elemento previo de la pila de llamadas.

Ejemplo #6 Bloque finally ejecutado cuando se ha lanzado una Exception

<?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 externo';
}

Resultado del ejemplo anterior es similar a:

1
finally
catch externo

Es posible utilizar yield dentro de un bloque finally, pero si se produce un error Throwable y no es capturado dentro del foreach, se lanza un Error Cannot yield from finally in a force-closed generator.

Ejemplo #7 Error al utilizar yield en un bloque finally cuando se ha lanzado una Exception

<?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 externo';
}

Resultado del ejemplo anterior es similar a:

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

Interacción entre un bloque finally y una llamada a exit()

Cuando se llama a exit() dentro de un bloque try o catch, todos los bloques finally se omiten.

Ejemplo #8 Interacción entre un bloque finally y exit() llamado en un bloque try

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

El ejemplo anterior mostrará:

salir

Ejemplo #9 Interacción entre un bloque finally y exit() llamado en un bloque catch

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

El ejemplo anterior mostrará:

catch

Ejemplo #10 Todos los bloques finally se omiten cuando se llama a exit()

<?php
try {
    try {
        echo 'salir';
        exit(0);
    } finally {
        echo 'finally interno ';
    }
} finally {
    echo 'finally externo';
}

El ejemplo anterior mostrará:

salir

Cuando se llama a exit() dentro de un bloque finally, se omiten todos los bloques finally externos.

Ejemplo #11 Omisión de un bloque finally externo mediante una llamada a exit()

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

El ejemplo anterior mostrará:

try
finally interno

Caso límite de llamar a exit() en un foreach sobre un generador

Este caso límite depende en gran medida de la versión de PHP con la que se ejecute el código.

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

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

Salida del ejemplo anterior antes de PHP 7.1.14, en PHP 7.2.0 y 7.2.1, y a partir de PHP 8.0.0

1
finally interno

Salida del ejemplo anterior a partir de PHP 7.1.14 y 7.2.2, y antes de PHP 8.0.0

1
+add a note

User Contributed Notes

There are no user contributed notes for this page.