はじめに
今回は演算子に焦点をあて,同様の動きをする演算子や関数と比較検証を行います。
‘==’と‘===’
‘==’と‘===’とを比較します。‘===’は値だけではなく型も含めて比較を行う演算子です。
まずはベンチマークを取ってみます。
benchmark_equal.php
<?php
$t = microtime(true);
$i = 0;
while($i < 1000) {
if ('a' == 'b') {}
++$i;
}
$tmp = microtime(true) - $t;
var_dump($tmp);
?>
benchmark_identical.php
<?php
$t = microtime(true);
$i = 0;
while($i < 1000) {
if ('a' === 'b') {}
++$i;
}
$tmp = microtime(true) - $t;
var_dump($tmp);
?>
実行結果
$ php benchmark_equal.php float(0.00020694732666) $ php benchmark_identical.php float(0.000159978866577)
‘===’のほうが‘==’よりも僅かに速い結果となりました。
それでは実装を見ていきましょう。まずは‘==’です。
‘==’の実体はZend/zend_operators.c内のis_equal_function()にあります。is_equal_function()の中をみると,compare_functionで左辺と右辺の比較を行っていることが分かります。
1539 ZEND_API int is_equal_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
1540 {
1541 if (compare_function(result, op1, op2 TSRMLS_CC) == FAILURE) {
1542 return FAILURE;
1543 }
1544 convert_to_boolean(result);
1545 if (result->value.lval == 0) {
1546 result->value.lval = 1;
1547 } else {
1548 result->value.lval = 0;
1549 }
1550 return SUCCESS;
1551 }
次にcompare_function()を見ていきます。同じくzend_operators.cに書かれてます。
1323 ZEND_API int compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
文字列と数値との比較について見ていきます。
1390 if (op1->type == IS_STRING && op2->type == IS_STRING) {
1391 zendi_smart_strcmp(result, op1, op2);
1392 COMPARE_RETURN_AND_FREE(SUCCESS);
1393 }
文字列の場合,zendi_smart_strcmp()で文字列比較が行われます。この関数では文字列が数字のみでできた文字列かを変換しチェックを行い,その結果によって比較処理が異なります。
1417 if (Z_TYPE_P(op1) == IS_LONG && Z_TYPE_P(op2) == IS_LONG) {
1418 ZVAL_LONG(result, Z_LVAL_P(op1)>Z_LVAL_P(op2)?1:(Z_LVAL_P(op1)<Z_LVAL_P(op2)?-1:0));
1419 COMPARE_RETURN_AND_FREE(SUCCESS);
1420 }
数値の場合,型がlong同士であればそのまま比較します。
1421 if ((Z_TYPE_P(op1) == IS_DOUBLE || Z_TYPE_P(op1) == IS_LONG)
1422 && (Z_TYPE_P(op2) == IS_DOUBLE || Z_TYPE_P(op2) == IS_LONG)) {
1423 Z_DVAL_P(result) = (Z_TYPE_P(op1) == IS_LONG ? (double) Z_LVAL_P(op1) : Z_DVAL_P(op1)) - (Z_TYPE_P(op2) == IS_LONG ? (double) Z_LVAL_P(op2) : Z_DVAL_P(op2));
1424 ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_DVAL_P(result)));
1425 COMPARE_RETURN_AND_FREE(SUCCESS);
1426 }
数値の型が違うときはlongなほうの値をdoubleに変換して比較が行われます。
一方,‘===’の実体はZend/zend_operators.c内のis_identical_function()にあります。文字列と数値の場合についての関数の動きを見ていきます。
1471 ZEND_API int is_identical_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
1472 {
1473 result->type = IS_BOOL;
1474 if (op1->type != op2->type) {
1475 result->value.lval = 0;
1476 return SUCCESS;
1477 }
1474から1477行目でまず型のチェックを行い,型が違えばfalseを返して終了,同じなら値のチェックに入ります。
1478行目から型によってそれぞれの値のチェックが行われます。
1478 switch (op1->type) {
1479 case IS_NULL:
1480 result->value.lval = (op2->type==IS_NULL);
1481 break;
型がnullの場合はもう片方の値の型もnullかどうか比較します。
1482 case IS_BOOL:
1483 case IS_LONG:
1484 case IS_RESOURCE:
1485 result->value.lval = (op1->value.lval == op2->value.lval);
1486 break;
1487 case IS_DOUBLE:
1488 result->value.lval = (op1->value.dval == op2->value.dval);
1489 break;
1482から1489行目ではbool, long, resource, double型についてのチェックが行われ,値はC言語の‘==’演算子で比較が行われます。
1490 case IS_STRING:
1491 if ((op1->value.str.len == op2->value.str.len)
1492 && (!memcmp(op1->value.str.val, op2->value.str.val, op1->value.str.len))) {
1493 result->value.lval = 1;
1494 } else {
1495 result->value.lval = 0;
1496 }
1497 break;
1490行目からはstringの比較をしていて,文字列長の比較とmemcmpによる文字列の比較が行われます。
ここまででis_identical_function()の解説は終わりです。
‘==’では型を考慮しないので,違う型での比較をすることも考えた凝った構造に対して,‘===’では同じ型かを最初にチェックして同じ型の値同士で比較を行うため,シンプルな構造で余計な型の変換もありません。そのため,‘===’のほうが速い結果になったと考えられます。

