¿Cómo se supone que debe usarse la palabra clave ‘finalmente’ en PHP?

8 minutos de lectura

avatar de usuario
ʎɹnɔɹǝW

Por lo tanto, he estado leyendo sobre las excepciones hoy en el manual en línea de PHP y me doy cuenta de que todavía tengo que entender el propósito o la necesidad real de la palabra clave finalmente. He leído algunas publicaciones aquí, por lo que mi pregunta es ligeramente diferente.

Entiendo que podemos usar finalmente de esta manera:

function hi(){
    return 'Hi';
}


try {
    throw new LogicException("Throw logic \n");
} catch (InvalidArgumentException $e) {
    echo $e->getMessage(); 
}

echo hi();

producción:

Fatal error:  Uncaught LogicException: Throw Logic in C:\Users\...a.php:167
Stack trace:
#0 {main}
  thrown in C:\Users\...a.php on line 167

Entonces, en este caso la función hi(); no se está ejecutando y por una buena razón. Entiendo que si no se maneja la excepción, el intérprete de php detiene el script. bien. Lejos de lo que leí, finalmente nos permite ejecutar la función hi(); incluso si no se maneja la excepción (aunque no sé por qué)

Entonces, este lo entiendo.

try {
    throw new LogicException("Throw logic \n");
} catch (InvalidArgumentException $e) {
    echo $e->getMessage(); 
}finally{
    echo hi();
}

producción:

Hi
Fatal error:  Uncaught LogicException: Throw Logic in C:\Users\...a.php:167
Stack trace:
#0 {main}
  thrown in C:\Users\...a.php on line 167

Este debe ser el error de excepción, así como el mensaje ‘hola’ de la función, incluso aquellos que no conozco ningún uso para esto. Pero lo que no entiendo es esto, incluso si captamos el LogicException con pestillo (LogicException $e) y no se lanzaron excepciones aún veríamos que la función se está ejecutando, y veríamos el mensaje ‘hola’. como en este ejemplo

try {
    throw new LogicException("Throw logic \n");
} catch (LogicException $e) {
    echo $e->getMessage(); 
}finally{
    echo hi();
}

salidas

// Throw logic 
// Hi

Entonces, todavía vemos la función. hi() ejecutado a pesar de que no tenemos excepciones no detectadas. ¿Por qué y cuál es el uso de esto? Pensé que el bloque finalmente se usaría como último recurso en caso de que no se detectaran las excepciones, incluso si ese no fuera el caso, ¿por qué se usa para ejecutarlo?

  • ¿Qué versión de PHP estás usando? Verdadero finally la funcionalidad no se agregó hasta PHP 5.5.

    – Alec Gordon

    12 de enero de 2017 a las 15:29

  • @AlecGordon php 7.0.1

    – ʎɹnɔɹǝW

    12 de enero de 2017 a las 15:33

  • Oh, eso es extraño entonces. De acuerdo a este análisis el bloque finalmente debe imprimirse antes de la excepción no detectada.

    – Alec Gordon

    12 de enero de 2017 a las 15:36

  • Código en finally se ejecuta después de todo el código en try + catch.

    – Germán Lashevich

    12 de enero de 2017 a las 16:37

avatar de usuario
jeff puckett

finally ejecuta cada* tiempo

Independientemente de errores, excepciones o incluso return declaraciones, la finally se ejecutará un bloque de código.

*Va a no correr si el try o catch los bloques se ejecutan die/exit.

Excepción

Un uso común que veo es cerrar una conexión de base de datos en un trabajador de larga ejecución: desea que esto suceda cada vez (con o sin excepción) para que no termine con una conexión colgante que bloquee el servidor de la base de datos para que no acepte nuevas conexiones. .

Considere este pseudo-código:

try {
   $database->execute($sql);
} finally {
   $database->close();
}

Aquí siempre cerraremos la conexión a la base de datos. Si es una consulta normal, cerramos la conexión después del éxito y el script continuará ejecutándose.

Si se trata de una consulta errónea, aún cerramos después de que se haya lanzado la excepción, y la excepción no detectada hará que la secuencia de comandos se detenga.

He aquí un ejemplo con catch haciendo algo de registro.

try {
   $database->execute($sql);
} catch (Exception $exception) {
   $logger->error($exception->getMessage(), ['sql' => $sql]);
   throw $exception;
} finally {
   $database->close();
}

Esto hará que cierre la conexión con o sin excepción.

Devolver

Uno de los comportamientos más oscuros es su capacidad para ejecutar código después de una declaración de devolución.

Aquí puede establecer una variable después de que la función haya regresado:

function foo(&$x)
{
    try {
        $x = 'trying';
        return $x;
    } finally {
        $x = 'finally';
    }
}

$bar="main";
echo foo($bar) . $bar;

tratando finalmente

pero una asignación será lo que se devuelva en try:

$bar = foo($bar);
echo $bar . $bar;

tratando

y volver en el finalmente anula el regreso en el intento:

function baz()
{
    try {
        return 'trying';
    } finally {
        return 'finally';
    }
}

echo baz();

finalmente

Nota este comportamiento era diferente en php 5:

finalmentefinalmente
finalmentefinalmente
finalmente

https://3v4l.org/biO4e

Rentabilidad excepcional

Puede hacer que parezca que lanza 2 excepciones para que surjan al mismo tiempo:

try {
    throw new Exception('try');
} finally {
    throw new Exception('finally');
}
Fatal error: Uncaught Exception: try in /in/2AYmF:4
Stack trace:
#0 {main}

Next Exception: finally in /in/2AYmF:6
Stack trace:
#0 {main}
  thrown in /in/2AYmF on line 6

Process exited with code 255.

https://3v4l.org/2AYmF

Pero realmente no puede detectar la “primera” excepción que conozco para hacer algo divertido en tiempo de ejecución:

try {
    try {
        throw new Exception('try');
    } finally {
        throw new Exception('finally');
    }
} catch (Exception $exception) {
    echo 'caught ' . $exception->getMessage();
}

atrapado finalmente

https://3v4l.org/Jknpm

* Morir

Si usted exit o die entonces el finally el bloque no se ejecutará.

try {
    echo "trying";
    die;
} finally {
    echo "finally";
}

echo "end";

difícil

https://3v4l.org/pc9oc

† Fallo de hardware

Finalmente, debe comprender que el finally block no se ejecutará si alguien desconecta el enchufe de su servidor 😉 y, aunque no lo he probado, espero que el agotamiento de la memoria también lo omita.

  • Lo entiendo ahora, pero los casos de uso parecen muy raros. En su ejemplo, por ejemplo, aún podría cerrar la base de datos antes de que throw declaración, y hubiera logrado lo mismo.

    – ʎɹnɔɹǝW

    13 de enero de 2017 a las 8:54

  • @mvrht: Por supuesto, podría cerrar la conexión DB antes del throwdeclaración sin que usando finallypero el punto es en ese caso: en primer lugar, tal vez no capte cada excepción pero solo un cierto tipo, entonces no llegas al close() llame para otras excepciones. En segundo lugar, incluso si detecta todas las excepciones, necesita una línea de código duplicada en algún lugar (al final de trybloque o después catch bloque) para llamar close(). Poner esa llamada en el finally bloque, debe escribirlo solo una vez y se ejecuta en todos los casos (si ocurre una excepción o no, si se detecta o no).

    – dwytrykus

    15 de febrero de 2017 a las 10:57

  • Si alguien está preguntando, finally se ejecuta cada vez excepto cuando hay un exit()/die() dentro de probar o el captura bloque (el que se ejecuta)

    – Simo Pelle

    11 de junio de 2020 a las 21:17

  • @Foxel buena idea! Incluiré esto en mi respuesta 🙂

    –Jeff Puckett

    11 de junio de 2020 a las 21:24

  • @James 😂 debemos estar cortados por la misma tijera, porque eso además me vuelve loco dondequiera que lo vea, así que intencionalmente lo dejé para que fuera un idiota. pero ahora me doy cuenta de la inmadurez de mis caminos y he agregado dos referencias apropiadas ⛪

    –Jeff Puckett

    19 de noviembre de 2020 a las 1:34

Finalmente, debe contener cualquier código que deba ejecutarse independientemente de si hay una excepción o no.

Sin finalmente:

try {
   $handle = fopen("file.txt");
   //Do stuff
   fclose($handle);
   return something;
} catch (Exception $e) {
   // Log
   if (isset($handle) && $handle !== false) {
      fclose($handle);
   }     
}

con finalmente:

try {
   $handle = fopen("file.txt");
   return something;
} catch (Exception $e) {
   // Log
} finally {
   if (isset($handle) && $handle !== false) {
      fclose($handle);
   }     
}

Ofrece un poco de orden en el caso de que necesite liberar un recurso después de que una función haya regresado.

Esto se vuelve aún más útil en un caso como el siguiente:

 try {
     $handle = fopen("file.txt");
     if (case1) { return result1; }  
     if (case2) { return result2; }
     if (case3) { return result3; }
     if (case4) { return result4; }

 } finally {
     if (isset($handle) && $handle !== false) {
          fclose($handle);
       }    
 }

En este caso, puede reducir todos los requisitos fclose llamadas antes de cada regreso a una sola fclose llamada que se ejecutará justo antes de que el método regrese pero después de cualquier otro código.

avatar de usuario
myxaxa

try {
    throw new LogicException("Throw logic \n"); -> LogicException thrown
} catch (InvalidArgumentException $e) { -> LogicException not catched
    echo $e->getMessage(); 
}finally{
    echo hi(); -> code executed. "Hi" printed out
}

LogicException is here -> Fatal error

entonces en este caso:

try {
    throw new LogicException("Throw logic \n"); -> LogicException thrown
} catch (InvalidArgumentException $e) { -> LogicException not catched
    echo $e->getMessage(); 
}finally{
    echo hi(); -> code executed
    die();
}

no se generará ningún error fatal, debido a la declaración y la última variación:

try {
    throw new LogicException("Throw logic \n"); -> LogicException thrown
} catch (InvalidArgumentException $e) { -> LogicException not catched
    echo $e->getMessage(); 
} catch (LogicException $e) { -> LogicException catched
    echo $e->getMessage(); 
}finally{
    echo hi(); -> code executed
}

avatar de usuario
Yevgeni Afanasyev

Hice una pequeña prueba unitaria para mostrar cómo funciona

    $a="a";
    try {
        $a .= 'b';
    } catch (Exception $ex) {
        $a .= 'e';
    } finally {
        $a .= 'f';
    }
    $a .= 'x';

    $this->assertSame('abfx', $a);


    $a="a";
    try {
        $a .= 'b';
        throw new Exception();
        $a .= '1';
    } catch (Exception $ex) {
        $a .= 'e';
    } finally {
        $a .= 'f';
    }
    $a .= 'x';

    $this->assertSame('abefx', $a);


    $a="a";
    try {
        try {
            $a .= 'b';
            throw new Exception();
            $a .= '1';
        } catch (Exception $ex) {
            $a .= 'e';
            throw $ex;
            $a .= '2';
        } finally {
            $a .= 'f';
        }
        $a .= 'x';
    } catch (Exception $ex) {
        $a .= 'z';
    }

    $this->assertSame('abefz', $a);

¿Ha sido útil esta solución?