九月初曾經寫過一篇文章是介紹 isset() 與 empty() 的差異,這篇來介紹延伸比較:
- 跟 isset() 幾乎一樣的
??
(Null coalescing operator)。
- 與 array_key_exists() 很像的
property_exists()
。
Null coalescing operator
Coalescing 這個單字有合併的意思,這運算子的用法有包含了 null 的判斷,故稱為 Null coalescing operator。下面是簡單的測試結果:
Psy Shell v0.10.8 (PHP 8.0.11 — cli) by Justin Hileman >>> $t = $v ?? 'foo' => "foo" >>> $v = null => null >>> $t = $v ?? 'foo' => "foo" >>> $v = 0 => 0 >>> $t = $v ?? 'foo' => 0 >>>
|
從這個測試可以知道,??
它可以判斷變數未設置,同時也判斷變數是否為 null,這個結果與 isset() 相同,可以參考先前的文章了解 isset() 的詳細說明。
來看一下原始碼為何:
<?php
$v = null;
$result = isset($v) ? $v : 'foo'; $result = is_null($v) ? 'foo' : $v; $result = null === $v ? 'foo' : $v; $result = $v ?? 'foo';
|
接著 dump opcode:
function name: (null) number of ops: 28 compiled vars: !0 = $v, !1 = $result line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 3 0 E > EXT_STMT 1 ASSIGN !0, null 5 2 EXT_STMT 3 ISSET_ISEMPTY_CV !0 4 > JMPZ ~3, ->7 5 > QM_ASSIGN ~4 !0 6 > JMP ->8 7 > QM_ASSIGN ~4 'foo' 8 > ASSIGN !1, ~4 6 9 EXT_STMT 10 TYPE_CHECK 2 !0 11 > JMPZ ~6, ->14 12 > QM_ASSIGN ~7 'foo' 13 > JMP ->15 14 > QM_ASSIGN ~7 !0 15 > ASSIGN !1, ~7 7 16 EXT_STMT 17 TYPE_CHECK 2 !0 18 > JMPZ ~9, ->21 19 > QM_ASSIGN ~10 'foo' 20 > JMP ->22 21 > QM_ASSIGN ~10 !0 22 > ASSIGN !1, ~10 8 23 EXT_STMT 24 COALESCE ~12 !0 25 QM_ASSIGN ~12 'foo' 26 ASSIGN !1, ~12 9 27 > RETURN 1
|
這裡看到 ??
(line 8)用了 COALESCE
以及跟三元運算子相同的 QM_ASSIGN
,最後再 ASSIGN
變數結束。 雖然 opcode 是少的,但它應該需要做很多事,包含型別的判斷等,因此效能可能比較差一點。
接著來實測:
+---------------------+----------------+-----------+---------+---------+---------+--------+---------+ | benchmark | subject | memory | min | max | mode | rstdev | stdev | +---------------------+----------------+-----------+---------+---------+---------+--------+---------+ | ConditionNullBench | bench () | 969.752kb | 0.466μs | 0.509μs | 0.490μs | ±2.88% | 0.014μs | | NullCoalescingBench | bench () | 969.752kb | 0.447μs | 0.567μs | 0.467μs | ±9.15% | 0.045μs | | IsNullBench | bench () | 969.784kb | 0.450μs | 0.537μs | 0.460μs | ±6.88% | 0.033μs | | IssetBench | bench () | 969.784kb | 0.440μs | 0.473μs | 0.463μs | ±2.33% | 0.011μs | +---------------------+----------------+-----------+---------+---------+---------+--------+---------+
|
實際結果發現,其實沒有什麼差異,就放心大膽的用下去吧!
測試原始碼參考
property_exists()
參考這篇文章,發現 property_exists() 應該跟 array_key_exists() 很像,因此拿來用 PHPUnit 試一下是否是一樣的:
$actual = property_exists((object)$arr, 'foo');
$this->assertSame($expected, $actual);
|
這裡的 $arr
是之前測試 array_key_exists() 的 case,用了 (object)
可以將 ['foo' => 'bar']
轉成 stdClass 物件,並包含了 foo
的屬性。
單元測試測完結果正確,因此 property_exists() 與 array_key_exists() 的真值表是一樣的沒錯!
而效能部分就不測了,因為以前曾試過 array 存取的速度跟 object 不同,因此考量的點主要在 array 與 object 的選擇,這未來有機會再另外寫一篇文章。