关键字'finally'在PHP中是如何使用的?

o4hqfura  于 2023-02-07  发布在  PHP
关注(0)|答案(4)|浏览(186)

所以,我今天一直在阅读PHP在线手册上的异常,并意识到我还没有理解finally关键字的用途或真正的必要性。我读了这里的一些帖子,所以我的问题略有不同。
我理解我们可以用最后这样的方式:

function hi(){
    return 'Hi';
}

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

echo 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

在这种情况下,函数hi();是没有被执行,并有一个很好的理由。我明白,如果异常是不处理php解释器停止脚本。好。到目前为止,从我读到的,终于使我们能够执行函数hi();即使未处理异常(即使我不知道原因)
所以,这个我理解。

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

输出:

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

这应该是异常错误,以及来自函数的'hi'消息,即使我不知道它的任何用法,但我不明白的是,即使我们用catch (LogicException $e)捕获LogicException并且没有抛出异常,我们仍然会看到函数正在执行,我们会看到'hi'消息,就像这个例子

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

产出

// Throw logic 
// Hi

所以,我们仍然可以看到函数hi()被执行,即使我们没有未捕获的异常,为什么这样做呢?我认为finally块是作为最后的手段,以防异常没有被捕获,即使不是这样,为什么要运行它呢?

1cosmwyk

1cosmwyk1#

finally每 * †次执行一次

无论错误、异常甚至return语句如何,finally代码块都将运行。

  • 如果trycatch块执行die / exit,则它 * 不会 * 运行。

异常

一个示例是在进程中关闭数据库连接,否则可能会留下悬空连接,从而阻止数据库服务器接受新连接。
考虑下面的伪代码:

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

这里我们总是关闭数据库连接,如果是正常查询,成功后关闭连接,脚本继续执行。
如果这是一个错误的查询,那么我们仍然在抛出异常后关闭,并且未捕获的异常将冒泡。
下面是catch执行一些日志记录的示例。

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

这将使它关闭连接,无论有无异常。

返回

比较模糊的行为之一是它在return语句之后执行代码的能力。
在这里,您可以在函数返回后设置变量:

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

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

终于
但try中返回的是赋值语句

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

尝试
并且finally中的返回将覆盖try中的返回:

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

echo baz();

终于

    • 注意**这个行为在PHP 5中是不同的:

最后
最后
终于
https://3v4l.org/biO4e

异常退货

你可以让它看起来像是同时抛出两个异常:

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
但是,您无法真正捕获我所知道的"第一个"异常来在运行时执行任何有趣的操作:

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

终于被抓住了
https://3v4l.org/Jknpm

* 死亡

如果执行exit or die,则finally块将不会执行。

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

echo "end";

尝试
https://3v4l.org/pc9oc

†硬件故障

最后,您应该理解,如果有人拔掉服务器上的电源插头,finally块将不会执行,尽管我没有测试它,但我希望内存耗尽也会跳过它。😉 and although I haven't tested it, I'd expect memory exhaustion to skip it too.

8fq7wneg

8fq7wneg2#

Finally应该包含任何需要执行的代码,无论是否存在异常。
没有最后:

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

最后:

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

在函数返回后需要释放资源的情况下,提供了一些整理功能。
这在类似以下的情况下变得更加有用:

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);
       }    
 }

在这种情况下,您可以将每次返回之前所需的所有fclose调用减少为一个fclose调用,该调用将在方法返回之前但在任何其他代码之后执行。

n9vozmp4

n9vozmp43#

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

因此在本例中:

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

由于die语句和最后一个变量,将不会引发致命错误:

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
}
odopli94

odopli944#

我做了一个小的单元测试来展示它是如何工作的

$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);

相关问题