assertの使いどころと本番での無効化

assertは、コード内部の前提条件(不変条件)を開発中に検証するための機能です。本番では設定で無効化でき、入力検証ではなくプログラマの思い込みの確認に使います。

assertは前提条件のチェック用

assert() は「ここでは必ずこうなっているはず」という前提(不変条件)を、開発中に確認するための道具です。ユーザー入力のバリデーションとは役割が別で、あくまで「プログラマの思い込みが正しいか」を検証するものだと考えると使い分けがはっきりします。PHP 7 以降は式や説明文を直接渡せます。

function average(array $nums): float
{
    // 呼び出し側が空配列を渡さない前提を明示
    assert($nums !== [], '空配列は想定していない');

    return array_sum($nums) / count($nums);
}

assert が失敗するのは「バグ」であって「想定内のエラー」ではない、という線引きが大事だと思います。

本番では無効化してコストゼロにする

assert の良いところは、設定で完全に無効化できる点です。zend.assertions-1 にすると、assert の式そのものがコンパイル時に消えて実行コストがゼロになります。開発環境では 1(有効)、本番では -1(無効化)にするのが定番の設定ですね。

// php.ini での設定イメージ
// 開発: zend.assertions = 1
// 本番: zend.assertions = -1  (assert が完全に消える)

// 有効時の挙動は assert.exception で制御
// assert.exception = 1 なら失敗時に AssertionError を投げる
ini_set('assert.exception', '1');

本番で消えるということは、逆に言えば assert に副作用のある式を書いてはいけないということです。値を書き換えるような処理を assert 内に入れると、本番で挙動が変わってしまいます。

入力検証には使わない

本番で消える性質上、ユーザー入力や外部データの検証を assert に任せるのは危険です。攻撃者が不正な値を送ってきても、本番では assert が無効なので素通りしてしまいます。外部由来のチェックは通常の if と例外で、内部の前提確認は assert で、と役割を分けるのが安全ですね。

function withdraw(int $amount): void
{
    // 外部入力: 本番でも必ず効くべき → 通常の検証
    if ($amount <= 0) {
        throw new InvalidArgumentException('金額は正の数');
    }

    // 内部前提: 開発時だけ確認できれば十分 → assert
    assert($this->balance >= 0, '残高が負になることはない');
}

まとめ

assert は開発時に「プログラマの前提」を検証するもので、本番では無効化して消せる点が特徴です。だからこそ副作用のある式を入れてはいけないし、外部入力の検証を任せてもいけません。想定外の入力は if と例外で防ぎ、コード内部の不変条件は assert で守る。この分担ができていると、開発中はバグを早く捕まえつつ本番は軽い、という良いとこ取りができると思います。

よくある質問

Q. assertでユーザー入力を検証してもいいですか。
A. いけません。本番では無効化されて素通りするので、外部入力はifと例外で検証してください。

Q. 本番でassertを無効にするには。
A. php.iniで zend.assertions を -1 にすると、assertの式がコンパイル時に消えて実行コストがゼロになります。

Q. assertの中に副作用のある処理を書いてよいですか。
A. だめです。本番では消えるため、値を書き換えるような式を入れると本番で挙動が変わってしまいます。

類似投稿

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です