转载自我的博客:http://70.io/2014/03/php-5_6-internal-operator-overloading
在众多php 5.6的新特性中,我觉得这是最神奇甚至是诡异的一个,如果有不理解这个概念的朋友,可能连它的说明都看不懂 https://wiki.php.net/rfc/operator_overloading_gmp。
首先要说明的是,这个重载不影响到userland
,也就是我们不用关心它是怎么重载的,只用关心怎么使用,它的实现是在php内核代码中。
在上面的php说明网址中,它举了一个对gmp_*
模块重载后的例子
// 重载前的代码
$result = gmp_mod(
gmp_add(
gmp_mul($c0, gmp_mul($ms0, gmp_invert($ms0, $n0))),
gmp_add(
gmp_mul($c1, gmp_mul($ms1, gmp_invert($ms1, $n1))),
gmp_mul($c2, gmp_mul($ms2, gmp_invert($ms2, $n2)))
)
),
gmp_mul($n0, gmp_mul($n1, $n2))
);
// 重载后的代码
$result = (
$c0 * $ms0 * gmp_invert($ms0, $n0)
+ $c1 * $ms1 * gmp_invert($ms1, $n1)
+ $c2 * $ms2 * gmp_invert($ms2, $n2)
) % ($n0 * $n1 * $n2);
如果你会一点scala
类似的语言,就能很好地理解了。在这个例子中+
操作符被重载为gmp_add
,*
操作符被重载为gmp_mull
。以前的基于函数式的代码让很多算法上的细节无法展现出来,改成基于操作符的就很好理解了。
它是怎么实现的呢?让我们来跟踪一下这个patch修改的源代码,https://github.com/php/php-src/pull/342/files。最好先关注Zend/zend_operators.c
这个文件的修改,基本上逻辑就很清晰了,比如ZEND_API int sub_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
这个函数,它处理了php中的加号+
运算(前面带+
的两行是这个patch增加的内容)
default:
if (!converted) {
+ ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_SUB);
+
zendi_convert_scalar_to_number(op1, op1_copy, result);
zendi_convert_scalar_to_number(op2, op2_copy, result);
converted = 1;
if(!converted)
判断了左右操作数还没有转换为number
时候的处理,在以前是调用zendi_convert_scalar_to_number
直接将其转换为number
然后再SUB
,现在在前面加了个宏ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_SUB)
,这个宏的实现细节是
#define ZEND_TRY_BINARY_OBJECT_OPERATION(opcode) \
if (Z_TYPE_P(op1) == IS_OBJECT && Z_OBJ_HANDLER_P(op1, do_operation)) { \
if (SUCCESS == Z_OBJ_HANDLER_P(op1, do_operation)(opcode, result, op1, op2 TSRMLS_CC)) { \
return SUCCESS; \
} \
} else if (Z_TYPE_P(op2) == IS_OBJECT && Z_OBJ_HANDLER_P(op2, do_operation)) { \
if (SUCCESS == Z_OBJ_HANDLER_P(op2, do_operation)(opcode, result, op1, op2 TSRMLS_CC)) { \
return SUCCESS; \
} \
}
它先判断左操作数是否为一个OBJECT
,并且这个OBJECT
内部定义了运算符操作的重载实现,如果是那么就调用这个handler
来处理这次操作符运算,如果左操作数不符合这些条件,再对右操作数做一次这些判断,基本上$a + $b
你就可以理解为以下伪代码
if ($a instanceof GMP && method_exists($a, 'add')) {
$a->add($b);
} else if ($b instanceof GMP && method_exists($b, 'add')) {
$b->add($a);
}
好吧,最后我来谈谈我的看法,之所以我对这个改进感到诡异是因为,它对一般的web开发基本没啥用,其影响基本是很少用到的数学运算模块。而且由于这个改进跟userland
没啥关系,所以我们在php代码中也用不了,别指望你能像scala
那样在class
里定义操作符的实现,这完全是两码事。
你只能指望这些模块或者扩展的作者实现了,而且最后实现也没有一个统一的标准,本来php
的函数命名就够混乱了。。。再重载成操作符,以后咋跟踪代码。。。