8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【PHP】try-catch-finallyの挙動について

Last updated at Posted at 2022-08-03

ふと、tryやcatchのブロックでreturnしている場合finallyのブロックって実行されるのかな?と疑問に思ったので詳しく調べてみることにしました。

そもそもtry-catch-finallyとは

tryブロックの中の処理に例外的なエラーが発生したときにcatchブロックの処理がされます。エラーが発生しなかった場合はcatchブロックの処理はされません。エラーハンドリングをする際によく使われます。
そしてfinallyとは例外は発生したかどうかは関係なくtryおよびcatchブロックの後で常に実行されます。

早速挙動確認していきます。

try {
    echo 'try';
} catch (Exception $e) {
    echo 'catch';
    echo $e->getMessage();
} finally {
    echo 'finally';
}

//実行結果
try
finally

例外が発生していないのでcatchブロックの処理はされません。

次はあえて例外を発生させてみます。

try {
    throw new Exception('例外を投げる');
    echo 'try';
} catch (Exception $e) {
    echo 'catch';
    echo $e->getMessage();
} finally {
    echo 'finally';
}

//実行結果
catch
例外を投げる
finally

例外が発生したときにcatchブロックへ行きその後finallyブロックが実行されます。
このようにfinallyは必ず実行されます。

ではこの場合はどうでしょうか?

try {
    throw new Exception('例外を投げる');
    echo 'try';
} catch (Exception $e) {
    echo 'catch';
    echo $e->getMessage();
    return 'catchブロック終了';
} finally {
    echo 'finally';
}

//実行結果
catch
例外を投げる
finally
catchブロック終了

私は最初catchブロックでreturnしているのでfinallyブロックに行かずに終了するのでは?と思いました。ただ実行してみるとfinallyが実行された後にreturnが実行されていることが分かります。

公式にはこのように記述されています。

finally ブロックと return 文の間には注意すべき相互作用があります。 return 文が try や catch ブロックの内部に存在した場合でも、 finally ブロックは実行されます。 さらに、return 文は出現した時に評価されますが、 結果は finally ブロックが実行された後に返されます。

ここで1つ注意しないといけないのが、finallyブロックにもreturnがある場合です。

finallyブロックにreturnを追記して実行してみます。

try {
    throw new Exception('例外を投げる');
    echo 'try';
} catch (Exception $e) {
    echo 'catch';
    echo $e->getMessage();
    return 'catchブロック終了';
} finally {
    echo 'finally';
    return 'finallyブロック終了';
}

//実行結果
catch
例外を投げる
finally
finallyブロック終了

finallyブロックのreturnが優先されています。
公式にはこうあります。

finally ブロックにも return 文が存在した場合は、 finally ブロックから値が返されます。

これは1つ勉強になりました。

DB接続時のtry-catchについて

少し話は変わりますが、PHPではDB接続時によくtry-catchを使うと思います。

try {
    $pdo = new PDO($dsn, $user, $pass);//変数は定義してある程です
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $sql = 'SELECT * FROM users';
    $users = $pdo->query($sql);
    $rows = $users->fetchAll();
    var_dump($rows);
    // 接続を閉じる
    $pdo = null;
} catch (PDOException $e) {
    echo $e->getMessage();
}

上記のようにtryブロックの中に$pdo = null(接続を閉じる処理)が書かれているコードをたまに見ます。このコードでは例外が発生した時に、接続が閉じられることなく処理が終了してしまいます。catchブロックの中にも$pdo = nullを書いてもいいですが、finallyブロックを追記してその中で$pdo = nullを書いてあげれば1回で済みますよって話でした!

try {
    $pdo = new PDO($dsn, $user, $pass);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $sql = 'SELECT * FROM users';
    $users = $pdo->query($sql);
    $rows = $users->fetchAll();
    var_dump($rows);
} catch (PDOException $e) {
    echo $e->getMessage();
} finally {
    // 接続を閉じる
    $pdo = null;
}
8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?