導入
「0で割ろうとした」「必須の値が空だった」――プログラムの実行中に想定外の事態が起きたとき、どう処理を止めて呼び出し元に伝えるか。これを扱うのが例外処理です。あわせて、大きなプロジェクトで名前の衝突を防ぐ名前空間、文字列のパターン照合に使う正規表現も、実務で頻繁に登場するので一緒に押さえておきましょう。
説明
想定外の事態が起きたら throw new Exception("メッセージ") で例外を投げます。呼び出し側は try { ... } catch (Exception $e) { ... } で受け止めます。
<?php
function divide(int $a, int $b): float {
if ($b === 0) {
throw new Exception("0で割ることはできません");
}
return $a / $b;
}
try {
echo divide(10, 2), "\n"; // 5
echo divide(10, 0), "\n"; // ここで例外が投げられ、以降は実行されない
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
} finally {
echo "処理を終了します\n"; // 例外の有無にかかわらず必ず実行される
}
try の中で例外が起きると、その時点で try ブロックの残りは実行されず、すぐに catch に飛びます。finally ブロックは、例外が起きても起きなくても必ず実行され、後片付け(ファイルを閉じる、接続を切るなど)に使います。
独自のエラーの種類を作りたいときは、Exception を継承して独自の例外クラスを定義できます。
<?php
class ValidationException extends Exception {}
function checkAge(int $age): void {
if ($age < 0) {
throw new ValidationException("年齢は0以上で入力してください");
}
}
try {
checkAge(-5);
} catch (ValidationException $e) {
echo "入力エラー: " . $e->getMessage() . "\n";
}
種類ごとに catch を分ければ、エラーの原因ごとに違う対応ができます(例:ValidationException は画面にメッセージを出す、PDOException はログに記録する、など)。
名前空間(namespace) は、クラスや関数の名前を「グループ分け」して衝突を防ぐ仕組みです。大きなプロジェクトや、複数のライブラリを組み合わせるときに欠かせません。
<?php
namespace App\Utils;
function double(int $n): int {
return $n * 2;
}
echo double(21), "\n"; // 42(同じ名前空間内なのでそのまま呼べる)
echo \App\Utils\double(21), "\n"; // 42(完全修飾名で呼んでも同じ)
namespace App\Utils; はファイルの先頭(<?php の直後)に書き、以降のクラスや関数は App\Utils というグループに属します。他のファイルから使うときは use App\Utils\double; と書けば、そのファイルの中では短い名前で呼べます。
正規表現 は、文字列が特定の「パターン」に一致するかを調べたり、パターンに一致した部分を置き換えたりする道具です。PHP では preg_match()(一致確認・抽出)と preg_replace()(置き換え)をよく使います。
<?php
$email = "tanaka@example.com";
if (preg_match('/^[\w.+-]+@[\w-]+\.[a-z]{2,}$/i', $email)) {
echo "正しい形式です\n";
} else {
echo "形式が正しくありません\n";
}
$text = "電話番号は 090-1234-5678 です";
$masked = preg_replace('/\d{2,4}-\d{4}-\d{4}/', '***-****-****', $text);
echo $masked, "\n"; // 電話番号は ***-****-**** です
パターンは / / で囲み、末尾の i は「大文字小文字を区別しない」というフラグです。\d は数字1文字、{4} は「直前の要素が4回」を表します。正規表現は奥が深い分野ですが、preg_match で「一致するか調べる」・preg_replace で「置き換える」の2つを知っていれば、実務のバリデーションやテキスト加工の多くに対応できます。
やってみよう
divide(10, 0) を divide(20, 4) に変えて、例外が起きない場合の流れを確認しましょう。preg_match の $email をわざと @ を含まない文字列にして、判定が変わることも試してみてください。
演習
関数 checkZip(string $zip): void を作ってください。$zip が5桁の数字(preg_match('/^\d{5}$/', $zip))でなければ、throw new Exception("郵便番号は5桁の数字で入力してください"); するようにします。try の中で checkZip("123") を呼び出し、catch (Exception $e) で $e->getMessage() を表示してください。
ヒント1を見る
function checkZip(string $zip): void { if (!preg_match('/^\d{5}$/', $zip)) { throw new Exception("郵便番号は5桁の数字で入力してください"); } }
ヒント2を見る
try { checkZip("123"); } catch (Exception $e) { echo $e->getMessage(), "\n"; }