├── EXPERIMENTAL ├── CREDITS ├── config.w32 ├── config.m4 ├── php7-is_greater.diff ├── .gitignore ├── tests ├── bug56904.phpt ├── compare2.phpt ├── unary.phpt ├── incdec.phpt ├── compare.phpt ├── binary.phpt └── binary_assign.phpt ├── README.md ├── package.xml └── operator.c /EXPERIMENTAL: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | operator 2 | Sara Golemon 3 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // vim:ft=javascript 2 | 3 | ARG_ENABLE("operator", "enable operator overload support", "no"); 4 | 5 | if (PHP_OPERATOR != "no") { 6 | AC_DEFINE("ZEND_VM_KIND", 1, "CFLAGS_OPERATOR"); 7 | EXTENSION("operator", "operator.c"); 8 | } 9 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl config.m4 for extension operators 2 | 3 | PHP_ARG_ENABLE(operator, whether to enable operator overload support, 4 | [ --enable-operator Enable operator overload support]) 5 | 6 | if test "$PHP_OPERATOR" != "no"; then 7 | PHP_NEW_EXTENSION(operator, operator.c, $ext_shared) 8 | fi 9 | -------------------------------------------------------------------------------- /php7-is_greater.diff: -------------------------------------------------------------------------------- 1 | diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c 2 | index 88d7192..b797939 100644 3 | --- a/Zend/zend_compile.c 4 | +++ b/Zend/zend_compile.c 5 | @@ -6716,7 +6716,7 @@ void zend_compile_greater(znode *result, zend_ast *ast) /* {{{ */ 6 | 7 | zend_emit_op_tmp(result, 8 | ast->kind == ZEND_AST_GREATER ? ZEND_IS_SMALLER : ZEND_IS_SMALLER_OR_EQUAL, 9 | - &right_node, &left_node); 10 | + &right_node, &left_node)->extended_value = 1; 11 | } 12 | /* }}} */ 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.deps 2 | /.libs/ 3 | /Makefile 4 | /Makefile.fragments 5 | /Makefile.global 6 | /Makefile.objects 7 | /acinclude.m4 8 | /aclocal.m4 9 | /autom4te.cache/ 10 | /build/ 11 | /config.cache 12 | /config.guess 13 | /config.h 14 | /config.h.in 15 | /config.log 16 | /config.nice 17 | /config.status 18 | /config.sub 19 | /configure 20 | /configure.ac 21 | /configure.in 22 | /install-sh 23 | /libtool 24 | /ltmain.sh 25 | /missing 26 | /mkinstalldirs 27 | /modules/ 28 | /php_test_results_*.txt 29 | /run-tests.php 30 | /*.lo 31 | /*.la 32 | -------------------------------------------------------------------------------- /tests/bug56904.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Bug#56904 Mixing direct calls and implicit overloads 3 | --FILE-- 4 | v = $val; 11 | } 12 | } 13 | 14 | $bob = new a; 15 | var_dump($bob); 16 | $bob->__assign_sl('abc'); 17 | var_dump($bob); 18 | $bob <<= 'def'; 19 | var_dump($bob); 20 | ?> 21 | --EXPECTF-- 22 | object(a)#%d (1) { 23 | ["v"]=> 24 | string(7) "initial" 25 | } 26 | object(a)#%d (1) { 27 | ["v"]=> 28 | string(3) "abc" 29 | } 30 | object(a)#%d (1) { 31 | ["v"]=> 32 | string(3) "def" 33 | } 34 | -------------------------------------------------------------------------------- /tests/compare2.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Extended comparison ops 3 | --SKIPIF-- 4 | value > $val; 11 | } 12 | function __is_greater_or_equal($val) { 13 | return $this->value >= $val; 14 | } 15 | function __construct($init) { 16 | $this->value = $init; 17 | } 18 | } 19 | $c = new foo(5); 20 | var_dump($c > 5); 21 | var_dump($c > 4); 22 | var_dump($c >= 5); 23 | var_dump($c >= 6); 24 | --EXPECT-- 25 | bool(false) 26 | bool(true) 27 | bool(true) 28 | bool(false) 29 | -------------------------------------------------------------------------------- /tests/unary.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Basic unary ops 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | value; 12 | } 13 | 14 | function __bool() { 15 | return (bool)$this->value; 16 | } 17 | 18 | function __bool_not() { 19 | return !(bool)$this->value; 20 | } 21 | 22 | function __construct($init) { 23 | $this->value = $init; 24 | } 25 | } 26 | 27 | $c = new foo(7); 28 | var_dump(~$c); 29 | var_dump((bool)$c); 30 | var_dump(!$c); 31 | --EXPECT-- 32 | int(-8) 33 | bool(true) 34 | bool(false) 35 | -------------------------------------------------------------------------------- /tests/incdec.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Inc/Dec ops 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | value++; 12 | } 13 | 14 | function __post_dec() { 15 | return $this->value--; 16 | } 17 | 18 | function __pre_inc() { 19 | $this->value++; 20 | return $this->value; 21 | } 22 | 23 | function __pre_dec() { 24 | $this->value--; 25 | return $this->value; 26 | } 27 | 28 | function __construct($init) { 29 | $this->value = $init; 30 | } 31 | } 32 | 33 | $c = new foo(7); 34 | for($i = 0; $i < 3; $i++) { 35 | var_dump($c++); 36 | } 37 | for($i = 0; $i < 3; $i++) { 38 | var_dump($c--); 39 | } 40 | for($i = 0; $i < 3; $i++) { 41 | var_dump(++$c); 42 | } 43 | for($i = 0; $i < 3; $i++) { 44 | var_dump(--$c); 45 | } 46 | --EXPECT-- 47 | int(7) 48 | int(8) 49 | int(9) 50 | int(10) 51 | int(9) 52 | int(8) 53 | int(8) 54 | int(9) 55 | int(10) 56 | int(9) 57 | int(8) 58 | int(7) 59 | -------------------------------------------------------------------------------- /tests/compare.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Basic comparison ops 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | value === $val; 12 | } 13 | 14 | function __is_not_identical($val) { 15 | return $this->value !== $val; 16 | } 17 | 18 | function __is_equal($val) { 19 | return $this->value == $val; 20 | } 21 | 22 | function __is_not_equal($val) { 23 | return $this->value != $val; 24 | } 25 | 26 | function __is_smaller($val) { 27 | return $this->value < $val; 28 | } 29 | 30 | function __is_smaller_or_equal($val) { 31 | return $this->value <= $val; 32 | } 33 | 34 | function __construct($init) { 35 | $this->value = $init; 36 | } 37 | } 38 | 39 | $c = new foo(5); 40 | 41 | var_dump($c === 5); 42 | var_dump($c === '5'); 43 | var_dump($c !== 5); 44 | var_dump($c !== '5'); 45 | var_dump($c == 5); 46 | var_dump($c == '5'); 47 | var_dump($c == 6); 48 | var_dump($c != 5); 49 | var_dump($c != '5'); 50 | var_dump($c != 6); 51 | var_dump($c < 5); 52 | var_dump($c < 6); 53 | var_dump($c <= 5); 54 | var_dump($c <= 4); 55 | --EXPECT-- 56 | bool(true) 57 | bool(false) 58 | bool(false) 59 | bool(true) 60 | bool(true) 61 | bool(true) 62 | bool(false) 63 | bool(false) 64 | bool(false) 65 | bool(true) 66 | bool(false) 67 | bool(true) 68 | bool(true) 69 | bool(false) 70 | -------------------------------------------------------------------------------- /tests/binary.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Basic binary ops 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | value + $val; 12 | } 13 | 14 | function __sub($val) { 15 | return $this->value - $val; 16 | } 17 | 18 | function __mul($val) { 19 | return $this->value * $val; 20 | } 21 | 22 | function __div($val) { 23 | return $this->value / $val; 24 | } 25 | 26 | function __mod($val) { 27 | return $this->value % $val; 28 | } 29 | 30 | function __sl($val) { 31 | return $this->value << $val; 32 | } 33 | 34 | function __sr($val) { 35 | return $this->value >> $val; 36 | } 37 | 38 | function __concat($val) { 39 | return $this->value . $val; 40 | } 41 | 42 | function __bw_or($val) { 43 | return $this->value | $val; 44 | } 45 | 46 | function __bw_and($val) { 47 | return $this->value & $val; 48 | } 49 | 50 | function __bw_xor($val) { 51 | return $this->value ^ $val; 52 | } 53 | 54 | function __construct($init) { 55 | $this->value = $init; 56 | } 57 | } 58 | 59 | $c = new foo(7); 60 | 61 | var_dump($c + 3); 62 | var_dump($c - 3); 63 | var_dump($c * 3); 64 | var_dump($c / 2); 65 | var_dump($c % 3); 66 | 67 | $d = new foo(14); 68 | var_dump($d << 2); 69 | var_dump($d >> 2); 70 | 71 | $e = new foo('PHP'); 72 | var_dump($e . ' with overloading'); 73 | 74 | $f = new foo(0x5A); 75 | var_dump($f | 0xAA); 76 | var_dump($f & 0xAA); 77 | var_dump($f ^ 0xAA); 78 | --EXPECT-- 79 | int(10) 80 | int(4) 81 | int(21) 82 | float(3.5) 83 | int(1) 84 | int(56) 85 | int(3) 86 | string(20) "PHP with overloading" 87 | int(250) 88 | int(10) 89 | int(240) 90 | -------------------------------------------------------------------------------- /tests/binary_assign.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Basic binary assign ops 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | value = $val; 12 | } 13 | 14 | function __assign_add($val) { 15 | return $this->value += $val; 16 | } 17 | 18 | function __assign_sub($val) { 19 | return $this->value -= $val; 20 | } 21 | 22 | function __assign_mul($val) { 23 | return $this->value *= $val; 24 | } 25 | 26 | function __assign_div($val) { 27 | return $this->value /= $val; 28 | } 29 | 30 | function __assign_mod($val) { 31 | return $this->value %= $val; 32 | } 33 | 34 | function __assign_sl($val) { 35 | return $this->value <<= $val; 36 | } 37 | 38 | function __assign_sr($val) { 39 | return $this->value >>= $val; 40 | } 41 | 42 | function __assign_concat($val) { 43 | return $this->value .= $val; 44 | } 45 | 46 | function __assign_bw_or($val) { 47 | return $this->value |= $val; 48 | } 49 | 50 | function __assign_bw_and($val) { 51 | return $this->value &= $val; 52 | } 53 | 54 | function __assign_bw_xor($val) { 55 | return $this->value ^= $val; 56 | } 57 | 58 | function __construct($init) { 59 | $this->value = $init; 60 | } 61 | } 62 | 63 | $a = new foo(1); 64 | var_dump($a = 2); 65 | var_dump($a += 2); 66 | var_dump(is_object($a)); 67 | 68 | $c = new foo(4); 69 | 70 | var_dump($c += 3); 71 | var_dump($c -= 3); 72 | var_dump($c *= 3); 73 | var_dump($c /= 2); 74 | var_dump($c %= 3); 75 | 76 | $d = new foo(14); 77 | var_dump($d <<= 2); 78 | var_dump($d >>= 2); 79 | 80 | $e = new foo('PHP'); 81 | var_dump($e .= ' with'); 82 | var_dump($e .= ' overloading'); 83 | 84 | $f = new foo(0x5A); 85 | var_dump($f |= 0xAA); 86 | var_dump($f &= 0xAA); 87 | var_dump($f ^= 0xAA); 88 | --EXPECT-- 89 | int(2) 90 | int(4) 91 | bool(true) 92 | int(7) 93 | int(4) 94 | int(12) 95 | int(6) 96 | int(0) 97 | int(56) 98 | int(14) 99 | string(8) "PHP with" 100 | string(20) "PHP with overloading" 101 | int(250) 102 | int(170) 103 | int(0) 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Operator overloading extension for PHP7 2 | 3 | ## Usage 4 | 5 | Implement magic methods in a class to provide operator overloading automatically, for example: 6 | 7 | ```php 8 | class C { 9 | private $value = 0; 10 | public function __add($delta) { 11 | $this->value += $delta; 12 | return $this; 13 | } 14 | 15 | public function __toString() { 16 | return strval($this->value); 17 | } 18 | } 19 | 20 | $c = new C; 21 | var_dump($c + 5); // object(C)#1 { ["C:value:private"]=>5 } 22 | ``` 23 | 24 | The following overload methods are supported: 25 | 26 | | Operator | Method | 27 | |:---:| --- | 28 | | $o + $arg | `__add($arg)` | 29 | | $o - $arg | `__sub($arg)` | 30 | | $o * $arg | `__mul($arg)` | 31 | | $o / $arg | `__div($arg)` | 32 | | $o % $arg | `__mod($arg)` | 33 | | $o ** $arg | `__pow($arg)` | 34 | | $o << $arg | `__sl($arg)` | 35 | | $o >> $arg | `__sr($arg)` | 36 | | $o . $arg | `__concat($arg)` | 37 | | $o | $arg | `__bw_or($arg)` | 38 | | $o & $arg | `__bw_and($arg)` | 39 | | $o ^ $arg | `__bw_xor($arg)` | 40 | | ~$o | `__bw_not()` | 41 | | $o === $arg | `__is_identical($arg)` | 42 | | $o !== $arg | `__is_not_identical($arg)` | 43 | | $o == $arg | `__is_equal($arg)` | 44 | | $o != $arg | `__is_not_equal($arg)` | 45 | | $o < $arg | `__is_smaller($arg)` | 46 | | $o <= $arg | `__is_smaller_or_equal($arg)` | 47 | | $o > $arg | `__is_greater($arg)` * | 48 | | $o >= $arg | `__is_greater_or_equal($arg)` * | 49 | | $o <=> $arg | `__cmp($arg)` | 50 | | ++$o | `__pre_inc()` | 51 | | $o++ | `__post_inc()` | 52 | | --$o | `__pre_dec()` | 53 | | $o-- | `__post_dec()` | 54 | | $o = $arg | `__assign($arg)` | 55 | | $o += $arg | `__assign_add($arg)` | 56 | | $o -= $arg | `__assign_sub($arg)` | 57 | | $o *= $arg | `__assign_mul($arg)` | 58 | | $o /= $arg | `__assign_div($arg)` | 59 | | $o %= $arg | `__assign_mod($arg)` | 60 | | $o **= $arg | `__assign_pow($arg)` | 61 | | $o <<= $arg | `__assign_sl($arg)` | 62 | | $o >>= $arg | `__assign_sr($arg)` | 63 | | $o .= $arg | `__assign_concat($arg)` | 64 | | $o |= $arg | `__assign_bw_or($arg)` | 65 | | $o &= $arg | `__assign_bw_and($arg)` | 66 | | $o ^= $arg | `__assign_bw_xor($arg)` | 67 | 68 | * - `__is_greater()` and `__is_greater_or_equal()` require a rebuild of the main PHP runtime using the [included patch](php7-is_greater.diff). Withtout this patch, `$a > $b` is automatically remapped to `$b < $a` by the engine. 69 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | operator 7 | pecl.php.net 8 | Operator overloading for Objects 9 | Operator overloading for: +, -, *, /, %, **, <<, >>, ., |, &, ^, ~, !, ++, --, 10 | +=, -=, *=, /=, %=, **=, <<=, >>=, .=, |=, &=, ^=, ~=, 11 | ==, !=, ===, !==, <, <=, and <=> operators. 12 | 13 | 14 | 15 | Sara Golemon 16 | pollita 17 | pollita@php.net 18 | yes 19 | 20 | 2017-03-08 21 | 22 | 23 | 1.0 24 | 1.0 25 | 26 | 27 | beta 28 | beta 29 | 30 | PHP 31 | 32 | * Rewrote for PHP7, dropped PHP5 support 33 | * Removed pre/post inc/dec support for objects in object properties (this will come back eventually) 34 | * Added pow, assign-pow, and spaceship operators 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 4.0.0 57 | 58 | 59 | 1.4.0b1 60 | 61 | 62 | 63 | operator 64 | 65 | 66 | 67 | 2009-11-05 68 | 69 | 70 | 0.3 71 | 0.3 72 | 73 | 74 | beta 75 | beta 76 | 77 | PHP 78 | 79 | * Added support for comparator ops 80 | 81 | Note: ZE treats > style comparisons as backwards < comparisons, so what looks like a left-associative greater than becomes a right-associative less-than. 82 | Because of this, overloading the > and >= operators for left-association requires application of a parser patch. 83 | 84 | 85 | 86 | 87 | 0.2 88 | 0.2 89 | 90 | 91 | beta 92 | beta 93 | 94 | 2006-01-25 95 | PHP 96 | 97 | * Added support for pre/post inc/dec operators 98 | * Added support for assignment operators 99 | * Refactored opline->result handling 100 | * Refactored opcode handlers 101 | 102 | 103 | 104 | 105 | 0.1 106 | 0.1 107 | 108 | 109 | beta 110 | beta 111 | 112 | 2006-01-12 113 | PHP 114 | 115 | Initial Release 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /operator.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | # include "config.h" 3 | #endif 4 | 5 | #include "php.h" 6 | 7 | #define USE_OPLINE const zend_op *opline = EX(opline); 8 | #define GET_OP1_ZVAL_PTR_UNDEF(fetch) \ 9 | get_zval_ptr_undef(opline->op1_type, opline->op1, &free_op1, execute_data) 10 | #define GET_OP2_ZVAL_PTR_UNDEF(fetch) \ 11 | get_zval_ptr_undef(opline->op2_type, opline->op2, &free_op2, execute_data) 12 | 13 | #define FREE_OP1 if (free_op1) { zval_ptr_dtor_nogc(free_op1); } 14 | #define FREE_OP2 if (free_op2) { zval_ptr_dtor_nogc(free_op2); } 15 | 16 | static 17 | zval *get_zval_ptr_undef(zend_uchar op_type, znode_op op, zend_free_op *free_op, 18 | zend_execute_data *execute_data) { 19 | switch (op_type) { 20 | case IS_TMP_VAR: 21 | case IS_VAR: return (*free_op = EX_VAR(op.var)); 22 | case IS_CONST: return EX_CONSTANT(op); 23 | case IS_CV: return EX_VAR(op.var); 24 | default: return NULL; 25 | } 26 | } 27 | 28 | /* ----------------------------------------------------------------------- */ 29 | 30 | #define UNARY_OPS(X) \ 31 | X(BW_NOT, __bw_not) 32 | 33 | #define BINARY_OPS(X) \ 34 | X(ADD, __add) \ 35 | X(SUB, __sub) \ 36 | X(MUL, __mul) \ 37 | X(DIV, __div) \ 38 | X(MOD, __mod) \ 39 | X(POW, __pow) \ 40 | X(SL, __sl) \ 41 | X(SR, __sr) \ 42 | X(CONCAT, __concat) \ 43 | X(BW_OR, __bw_or) \ 44 | X(BW_AND, __bw_and) \ 45 | X(BW_XOR, __bw_xor) \ 46 | X(IS_IDENTICAL, __is_identical) \ 47 | X(IS_NOT_IDENTICAL, __is_not_identical) \ 48 | X(IS_EQUAL, __is_equal) \ 49 | X(IS_NOT_EQUAL, __is_not_equal) \ 50 | X(IS_SMALLER, __is_smaller) \ 51 | X(IS_SMALLER_OR_EQUAL, __is_smaller_or_equal) \ 52 | X(SPACESHIP, __cmp) 53 | 54 | #define UNARY_ASSIGN_OPS(X) \ 55 | X(PRE_INC, __pre_inc) \ 56 | X(POST_INC, __post_inc) \ 57 | X(PRE_DEC, __pre_dec) \ 58 | X(POST_DEC, __post_dec) 59 | 60 | #define BINARY_ASSIGN_OPS(X) \ 61 | X(ASSIGN, __assign) \ 62 | X(ASSIGN_ADD, __assign_add) \ 63 | X(ASSIGN_SUB, __assign_sub) \ 64 | X(ASSIGN_MUL, __assign_mul) \ 65 | X(ASSIGN_DIV, __assign_div) \ 66 | X(ASSIGN_MOD, __assign_mod) \ 67 | X(ASSIGN_POW, __assign_pow) \ 68 | X(ASSIGN_SL, __assign_sl) \ 69 | X(ASSIGN_SR, __assign_sr) \ 70 | X(ASSIGN_CONCAT, __assign_concat) \ 71 | X(ASSIGN_BW_OR, __assign_bw_or) \ 72 | X(ASSIGN_BW_AND, __assign_bw_and) \ 73 | X(ASSIGN_BW_XOR, __assign_bw_xor) 74 | 75 | #define ALL_OPS(X) \ 76 | UNARY_OPS(X) \ 77 | BINARY_OPS(X) \ 78 | UNARY_ASSIGN_OPS(X) \ 79 | BINARY_ASSIGN_OPS(X) 80 | 81 | /* greater/greater-equal are encoded as smaller/smaller-equal 82 | * so they require special handling 83 | */ 84 | #define GREATER_OPS(X) \ 85 | X(IS_SMALLER, __is_greater) \ 86 | X(IS_SMALLER_OR_EQUAL, __is_greater_or_equal) 87 | 88 | #define X(op, meth) static zend_string *s_##meth; 89 | ALL_OPS(X) 90 | GREATER_OPS(X) 91 | #undef X 92 | 93 | /* {{{ operator_method_name */ 94 | static inline zend_string* operator_method_name(zend_uchar opcode) { 95 | switch (opcode) { 96 | #define X(op, meth) case ZEND_##op: return s_##meth; 97 | ALL_OPS(X) 98 | #undef X 99 | default: 100 | ZEND_ASSERT(0); 101 | return NULL; 102 | } 103 | } 104 | /* }}} */ 105 | 106 | /* {{{ operator_get_method */ 107 | static zend_bool operator_get_method(zend_string *method, zval *obj, 108 | zend_fcall_info *fci, 109 | zend_fcall_info_cache *fcc) { 110 | memset(fci, 0, sizeof(zend_fcall_info)); 111 | fci->size = sizeof(zend_fcall_info); 112 | fci->object = Z_OBJ_P(obj); 113 | ZVAL_STR(&(fci->function_name), method); 114 | 115 | if (!zend_is_callable_ex(&(fci->function_name), fci->object, 116 | IS_CALLABLE_CHECK_SILENT | IS_CALLABLE_STRICT, 117 | NULL, fcc, NULL)) { 118 | return 0; 119 | } 120 | /* Disallow dispatch via __call */ 121 | if (fcc->function_handler == Z_OBJCE_P(obj)->__call) { return 0; } 122 | if (fcc->function_handler->type == ZEND_USER_FUNCTION) { 123 | zend_op_array *oparray = (zend_op_array*)(fcc->function_handler); 124 | if (oparray->fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { 125 | return 0; 126 | } 127 | } 128 | 129 | return 1; 130 | } 131 | /* }}} */ 132 | 133 | /* {{{ operator_is_greater_op */ 134 | static inline zend_bool operator_is_greater_op(const zend_op *opline, zend_string **pmethod) { 135 | if (opline->extended_value == 1) { 136 | switch (opline->opcode) { 137 | #define X(op, meth) \ 138 | case ZEND_##op: *pmethod = s_##meth; return 1; 139 | GREATER_OPS(X) 140 | #undef X 141 | } 142 | } 143 | return 0; 144 | } 145 | /* }}} */ 146 | 147 | /* {{{ op_handler */ 148 | static int op_handler(zend_execute_data *execute_data) { 149 | USE_OPLINE 150 | zend_free_op free_op1 = NULL, free_op2 = NULL; 151 | zval *op1, *op2 = NULL; 152 | zend_fcall_info fci; 153 | zend_fcall_info_cache fcc; 154 | zend_string *method = operator_method_name(opline->opcode); 155 | 156 | if (opline->op1_type == IS_UNUSED) { 157 | /* Assign op */ 158 | op1 = EX_VAR(opline->result.var); 159 | } else { 160 | op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); 161 | } 162 | ZVAL_DEREF(op1); 163 | 164 | switch (opline->opcode) { 165 | #define X(op, meth) \ 166 | case ZEND_##op: 167 | BINARY_OPS(X) 168 | BINARY_ASSIGN_OPS(X) 169 | #undef X 170 | op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); 171 | } 172 | 173 | if (operator_is_greater_op(opline, &method)) { 174 | zval *tmp = op1; 175 | zend_free_op free_tmp = free_op1; 176 | op1 = op2; op2 = tmp; 177 | free_op1 = free_op2; free_op2 = free_tmp; 178 | } 179 | 180 | if ((Z_TYPE_P(op1) != IS_OBJECT) || 181 | !operator_get_method(method, op1, &fci, &fcc)) { 182 | /* Not an overloaded call */ 183 | return ZEND_USER_OPCODE_DISPATCH; 184 | } 185 | 186 | fci.retval = EX_VAR(opline->result.var); 187 | fci.params = op2; 188 | fci.param_count = op2 ? 1 : 0; 189 | if (FAILURE == zend_call_function(&fci, &fcc)) { 190 | php_error(E_WARNING, "Failed calling %s::%s()", Z_OBJCE_P(op1)->name, Z_STRVAL(fci.function_name)); 191 | ZVAL_NULL(fci.retval); 192 | } 193 | 194 | FREE_OP2 195 | FREE_OP1 196 | EX(opline) = opline + 1; 197 | return ZEND_USER_OPCODE_CONTINUE; 198 | } 199 | /* }}} */ 200 | 201 | /* {{{ MINIT */ 202 | static PHP_MINIT_FUNCTION(operator) { 203 | #define X(op, meth) \ 204 | s_##meth = zend_string_init(#meth, strlen(#meth), 1); 205 | GREATER_OPS(X) 206 | #undef X 207 | 208 | #define X(op, meth) \ 209 | s_##meth = zend_string_init(#meth, strlen(#meth), 1); \ 210 | zend_set_user_opcode_handler(ZEND_##op, op_handler); 211 | ALL_OPS(X) 212 | #undef X 213 | 214 | return SUCCESS; 215 | } 216 | /* }}} */ 217 | 218 | /* {{{ operator_module_entry 219 | */ 220 | static zend_module_entry operator_module_entry = { 221 | STANDARD_MODULE_HEADER, 222 | "operator", 223 | NULL, /* functions */ 224 | PHP_MINIT(operator), 225 | NULL, /* MSHUTDOWN */ 226 | NULL, /* RINIT */ 227 | NULL, /* RSHUTDOWN */ 228 | NULL, /* MINFO */ 229 | "7.2.0", 230 | STANDARD_MODULE_PROPERTIES 231 | }; 232 | /* }}} */ 233 | 234 | #ifdef COMPILE_DL_OPERATOR 235 | ZEND_GET_MODULE(operator) 236 | #endif 237 | --------------------------------------------------------------------------------