(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
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
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"
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
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
foreach sobre un generadorEste 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