如何在PHP中使用关键字“finally”?

时间:2017-01-12 15:20:20

标签: php oop exception try-catch-finally

所以,我今天在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捕获(LogicException $e)并且没有抛出任何异常,我们仍然会看到函数正在执行,我们会看到'hi'消息。如在这个例子中

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

输出

// Throw logic 
// Hi

因此,即使我们没有未捕获的异常,我们仍会看到函数hi()已执行。为什么以及它的用途是什么? 我认为finally块是作为最后的手段,以防异常没有被捕获,即使不是这种情况,那么为什么运行它的用途呢?

4 个答案:

答案 0 :(得分:22)

每次

finally执行

无论是异常还是返回

例外

我看到的一个常见用途是在长时间运行的工作程序中关闭数据库连接 - 您希望每次都发生这种情况(有或没有例外),因此您不会以阻止数据库的悬空连接结束服务器接受新连接。

考虑这个伪代码:

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

这将使其在有或没有例外的情况下关闭连接。

注意此行为在其他语言中有所不同。例如,在.NET中,如果从catch块抛出/重新抛出异常,则finally块将不会执行。

返回

其中一个比较模糊的行为是它在返回语句后执行代码的能力。

您可以在函数返回后设置变量:

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

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

tryingfinally

但是作业将在try:

中返回
$bar = foo($bar);
echo $bar . $bar;
  

tryingtrying

并在最后返回覆盖try中的返回:

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

echo baz();
  

最后

注意这种行为在php 5中有所不同:

  

finallyfinally
  finallyfinally
  终于

https://3v4l.org/biO4e

答案 1 :(得分:1)

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

由于死亡陈述,不会引发致命错误 和最后的变化:

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
}

答案 2 :(得分:0)

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

没有最后:

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调用,该调用将在方法返回之前执行,但在任何其他代码之后执行。

答案 3 :(得分:0)

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

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