ふと、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;
}