本文へスキップ
BecomeCoder

PHPコース · 第8章 モダンPHPと道具 · レッスン27

例外処理・名前空間・正規表現

ブラウザで完結

導入

「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"; }

実際に動かしてみよう

下のエディタにPHPを書いて「実行」を押すと、ブラウザ内で動く本物のPHP(php-wasm)で出力が表示されます。コードは必ず <?php で書き始めます。本文の例をそのまま試したり、書き換えたりしてみましょう(初回だけPHPの読み込みに少し時間がかかります)。

PHP — ブラウザ内で実行

ブラウザ内で動く本物のPHP(php-wasm / PHP 8.4)を読み込みます(初回のみ読み込みに少し時間がかかります)。
スクロールして表示された時点でも自動で読み込まれます。