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 & 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.assertions
、assert.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 寫單元測試很像