簡體   English   中英

關鍵字“finally”是如何在 PHP 中使用的?

[英]How is the keyword 'finally' meant to be used in PHP?

所以,我今天一直在閱讀 PHP 在線手冊上的異常,並意識到我還沒有理解 finally 關鍵字的目的或真正必要性。 我在這里閱讀了一些帖子,所以我的問題略有不同。

我知道我們可以這樣使用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)捕獲 LogicException 並且沒有拋出異常,我們仍然會看到正在執行的函數,並且會看到“hi”消息。 如本例所示

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

輸出

// Throw logic 
// Hi

因此,即使我們沒有未捕獲的異常,我們仍然可以看到函數hi()被執行。 為什么以及這樣做有什么用? 我認為 finally 塊將被用作萬一沒有捕獲異常的最后手段,即使情況並非如此,那為什么還要使用它來運行它呢?

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;

終於嘗試

但是分配將是嘗試返回的內容:

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

嘗試嘗試

並在 finally 中返回會覆蓋 try 中的返回:

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

echo baz();

最后

請注意,這種行為在 php 5 中有所不同:

終於終於
終於終於
最后

https://3v4l.org/biO4e

超值回報

你可以讓它看起來像拋出 2 個異常同時冒泡:

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

* 死

如果你exitdie ,那么finally塊將不會執行。

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

echo "end";

https://3v4l.org/pc9oc

† 硬件故障

最后,你應該明白,如果有人拔掉你服務器上的電源插頭, finally塊將不會執行😉,雖然我沒有測試過,但我希望內存耗盡也會跳過它。

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調用,該調用將在方法返回之前但在任何其他代碼之后執行。

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
}

我做了一個小單元測試來展示它是如何工作的

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM