PHP 斷言(assert) 詳解

PHP 中的斷言常用於調試,檢查一個表達式或語句是否爲 FALSE。本文帶你重新認識 PHP assert() 函數的神(Qi)通(Yin)廣(Ji)大(Qiao)。

本文基於 PHP Version 7.1.28

什麼是斷言

編寫程序時,常會做出一定的假設,那斷言就是用來捕獲假設的異常,我們也可以認爲斷言是異常的一種特殊形式。

斷言一般用於程序執行結構的判斷,不可讓斷言處理業務流程。用的最多的場景就是單元測試,一般的單元測試框架都採用了斷言。

assert(1 == 2);

// 運行結果:
// Warning: assert(): assert(1 == 2) failed in /Users/shocker/Desktop/demo.php on line 25

PHP 中的斷言

在 PHP 中,採用 assert() 函數對表達式進行斷言。

// PHP 5
assert ( mixed $assertion [, string $description ] ) : bool

// PHP 7
assert ( mixed $assertion [, Throwable $exception ] ) : bool

傳統的斷言方式 (PHP 5 and 7)

參數 assertion 既支持表達式,也支持表達式字符串(某些特定的場景會用到,比如判斷某個字符串表達式是否合法)

如果 assertion 是字符串,它將會被 assert() 當做 PHP 代碼來執行。assertion 是字符串的優勢是當禁用斷言時它的開銷會更小,並且在斷言失敗時消息會包含 assertion 表達式

斷言這個功能應該只被用來調試。你應該用於完整性檢查時測試條件是否始終應該爲 TRUE,來指示某些程序錯誤,或者檢查具體功能的存在(類似擴展函數或特定的系統限制和功能)。

斷言不應該用於普通運行時操作,類似輸入參數的檢查。作爲一個經驗法則,在斷言禁用時你的代碼也應該能夠正確地運行。

使用示例:

function my_assert_handler($file, $line, $code, $desc)
{
    echo "Assertion Failed:
    File '{$file}'
    Line '{$line}'
    Code '{$code}'
    Desc '{$desc}'
";
}

// 設置回調函數
assert_options(ASSERT_CALLBACK, 'my_assert_handler');

// 讓一則斷言失敗
assert('1 == 2', '1 不可能等於 2');

運行結果:

Assertion Failed:
    File '/Users/shocker/Desktop/demo.php'
    Line '29'
    Code '1 == 2'
    Desc '1 不可能等於 2'

支持異常的斷言 (僅 PHP 7)

在 PHP 7 中,assert() 是一個語言結構,允許在不同環境中生效不同的措施,具體可見 zend.assertions 配置。

另外,還支持通過 AssertionError 捕獲錯誤。

使用示例:

assert_options(ASSERT_EXCEPTION, 1); // 在斷言失敗時產生異常

try {
    // 用 AssertionError 異常替代普通字符串
    assert(true == false, new AssertionError('True is not false!'));
} catch (Throwable $e) {
    echo $e->getMessage();
}

運行結果:

True is not false!

對斷言行爲進行控制

PHP 支持 assert_options() 函數對斷言進行配置,也可用 ini 進行設置

以下配置中,常量標誌用於 assert_options() 函數進行配置,ini 設置用於 ini_set() 函數設置,效果一樣
標誌 INI 設置 默認值 描述
ASSERT_ACTIVE assert.active "1" 啓用 assert() 斷言
ASSERT_WARNING assert.warning "1" 爲每個失敗的斷言產生一個 PHP 警告(warning)
ASSERT_BAIL assert.bail "0" 在斷言失敗時中止執行
ASSERT_QUIET_EVAL assert.quiet_eval "0" 在斷言表達式求值時禁用 error_reporting
ASSERT_CALLBACK assert.callback NULL 斷言失敗時調用該回調函數
ASSERT_EXCEPTION assert.exception "0" 在斷言失敗時產生 AssertionError 異常 (自 PHP 7.0.0 起有效)

zend.assertions 是個特殊的配置(PHP >= 7.0.0 支持),控制不同運行環境下斷言的行爲,僅可用 ini_set() 進行設置。並且,設置了1就不能再設置爲-1,反之亦然,其他不受限。

  • 1: 編譯代碼,並執行(開發模式)
  • 0: 編輯代碼,但運行時跳過
  • -1: 不編譯代碼(生產模式)

版本的不兼容

  • PHP >= 5.4.8,description 可作爲第四個參數提供給 ASSERT_CALLBACK 模式裏的回調函數
  • 在 PHP 5 中,參數 assertion 必須是可執行的字符串,或者運行結果爲布爾值的表達式
  • 在 PHP 7 中,參數 assertion 可以是任意表達式,並用其運算結果作爲斷言的依據
  • 在 PHP 7 中,參數 exception 可以是個 Throwable 對象,用於捕獲表達式運行錯誤或斷言結果爲失敗。(當然 assert.exception 需開啓)
  • PHP >= 7.0.0,支持 zend.assertionsassert.exception 相關配置及其特性
  • PHP >= 7.2 版本開始,參數 assertion 不再支持字符串

    詳見 PHP 7.2.x 中廢棄的功能
    Deprecated: assert(): Calling assert() with a string argument is deprecated

應用場景

調試輸出

先看示例:

assert('1 == 2', '1 不可能等於 2');

運行結果:

Warning: assert(): 1 不可能等於 2: "1 == 2" failed in /Users/shocker/Desktop/demo.php on line 10

類似於:

$expression = 1 == 2;
if (!($expression)) {
    echo "1 不可能等於 2\n";
    var_dump($expression);
    echo __FILE__ . "\n";
}

但是,我們無法得知 $expression 的具體表達式,也無法得知具體的執行行數。

單元測試

function arraySum(array $nums) {
    $sum = 0;
    foreach ($nums as $n) {
        $sum += $n;
    }

    return $sum;
}

assert(arraySum([1, 2, 3]) == 6, 'arraySum() 測試不通過:');
assert(is_numeric(arraySum([1, 2, 3])), 'arraySum() 測試不通過:');

是不是跟我們用 PHPUnit 寫單元測試很像😆

驗證表達式

Tip:

PHP 7 開始,新增了 Error 類用於捕獲 PHP 內置錯誤,包括語法錯誤。Error 與之前的 Exception 均繼承自 Throwable,所以從 7.0.0 開始,Throwable 可以捕獲一切錯誤和異常。

下例演示瞭如何驗證某個字符串表達式是否爲合法的 PHP 表達式:

try {
    assert('a +== 1');
} catch (Throwable $e) {
    echo $e->getMessage(), "\n";
}

運行結果:

Failure evaluating code: 
a +== 1

感謝您的閱讀,覺得內容不錯,點個贊吧 😆
原文地址: https://shockerli.net/post/php-assert/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章