1 Commits

Author SHA1 Message Date
SVN Migration
2cd8a295d0 This commit was manufactured by cvs2svn to create tag 'RELEASE_0_3'. 2006-02-08 21:22:17 +00:00
16 changed files with 908 additions and 479 deletions

31
.gitignore vendored
View File

@@ -1,31 +0,0 @@
/.deps
/.libs/
/Makefile
/Makefile.fragments
/Makefile.global
/Makefile.objects
/acinclude.m4
/aclocal.m4
/autom4te.cache/
/build/
/config.cache
/config.guess
/config.h
/config.h.in
/config.log
/config.nice
/config.status
/config.sub
/configure
/configure.ac
/configure.in
/install-sh
/libtool
/ltmain.sh
/missing
/mkinstalldirs
/modules/
/php_test_results_*.txt
/run-tests.php
/*.lo
/*.la

View File

@@ -1,2 +1,3 @@
operator
Sara Golemon

View File

@@ -1,68 +0,0 @@
# Operator overloading extension for PHP7
## Usage
Implement magic methods in a class to provide operator overloading automatically, for example:
```php
class C {
private $value = 0;
public function __add($delta) {
$this->value += $delta;
return $this;
}
public function __toString() {
return strval($this->value);
}
}
$c = new C;
var_dump($c + 5); // object(C)#1 { ["C:value:private"]=>5 }
```
The following overload methods are supported:
| Operator | Method |
|:---:| --- |
| $o + $arg | `__add($arg)` |
| $o - $arg | `__sub($arg)` |
| $o * $arg | `__mul($arg)` |
| $o / $arg | `__div($arg)` |
| $o % $arg | `__mod($arg)` |
| $o ** $arg | `__pow($arg)` |
| $o << $arg | `__sl($arg)` |
| $o >> $arg | `__sr($arg)` |
| $o . $arg | `__concat($arg)` |
| $o &#x7c; $arg | `__bw_or($arg)` |
| $o & $arg | `__bw_and($arg)` |
| $o ^ $arg | `__bw_xor($arg)` |
| ~$o | `__bw_not()` |
| $o === $arg | `__is_identical($arg)` |
| $o !== $arg | `__is_not_identical($arg)` |
| $o == $arg | `__is_equal($arg)` |
| $o != $arg | `__is_not_equal($arg)` |
| $o < $arg | `__is_smaller($arg)` |
| $o <= $arg | `__is_smaller_or_equal($arg)` |
| $o > $arg | `__is_greater($arg)` &#42; |
| $o >= $arg | `__is_greater_or_equal($arg)` &#42; |
| $o <=> $arg | `__cmp($arg)` |
| ++$o | `__pre_inc()` |
| $o++ | `__post_inc()` |
| --$o | `__pre_dec()` |
| $o-- | `__post_dec()` |
| $o = $arg | `__assign($arg)` |
| $o += $arg | `__assign_add($arg)` |
| $o -= $arg | `__assign_sub($arg)` |
| $o *= $arg | `__assign_mul($arg)` |
| $o /= $arg | `__assign_div($arg)` |
| $o %= $arg | `__assign_mod($arg)` |
| $o **= $arg | `__assign_pow($arg)` |
| $o <<= $arg | `__assign_sl($arg)` |
| $o >>= $arg | `__assign_sr($arg)` |
| $o .= $arg | `__assign_concat($arg)` |
| $o &#x7c;= $arg | `__assign_bw_or($arg)` |
| $o &= $arg | `__assign_bw_and($arg)` |
| $o ^= $arg | `__assign_bw_xor($arg)` |
&#42; - `__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.

View File

@@ -0,0 +1,66 @@
Index: Zend/zend_compile.c
===================================================================
RCS file: /repository/ZendEngine2/zend_compile.c,v
retrieving revision 1.567.2.33
diff -u -r1.567.2.33 zend_compile.c
--- Zend/zend_compile.c 24 Nov 2005 11:33:27 -0000 1.567.2.33
+++ Zend/zend_compile.c 8 Feb 2006 21:05:21 -0000
@@ -235,6 +235,20 @@
}
+void zend_do_binary_compare_op(zend_uchar op, znode *result, znode *op1, znode *op2, zend_uint extval TSRMLS_DC)
+{
+ zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
+
+ opline->opcode = op;
+ opline->result.op_type = IS_TMP_VAR;
+ opline->result.u.var = get_temporary_variable(CG(active_op_array));
+ opline->op1 = *op1;
+ opline->op2 = *op2;
+ opline->extended_value = extval;
+ *result = opline->result;
+}
+
+
void zend_do_unary_op(zend_uchar op, znode *result, znode *op1 TSRMLS_DC)
{
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
Index: Zend/zend_compile.h
===================================================================
RCS file: /repository/ZendEngine2/zend_compile.h,v
retrieving revision 1.284.2.7
diff -u -r1.284.2.7 zend_compile.h
--- Zend/zend_compile.h 20 Sep 2005 11:38:37 -0000 1.284.2.7
+++ Zend/zend_compile.h 8 Feb 2006 21:05:21 -0000
@@ -321,6 +321,8 @@
/* parser-driven code generators */
+#define ZEND_HAVE_DO_BINARY_COMPARE_OP 1
+void zend_do_binary_compare_op(zend_uchar op, znode *result, znode *op1, znode *op2, zend_uint extval TSRMLS_DC);
void zend_do_binary_op(zend_uchar op, znode *result, znode *op1, znode *op2 TSRMLS_DC);
void zend_do_unary_op(zend_uchar op, znode *result, znode *op1 TSRMLS_DC);
void zend_do_binary_assign_op(zend_uchar op, znode *result, znode *op1, znode *op2 TSRMLS_DC);
Index: Zend/zend_language_parser.y
===================================================================
RCS file: /repository/ZendEngine2/zend_language_parser.y,v
retrieving revision 1.144.2.4
diff -u -r1.144.2.4 zend_language_parser.y
--- Zend/zend_language_parser.y 21 Sep 2005 09:58:10 -0000 1.144.2.4
+++ Zend/zend_language_parser.y 8 Feb 2006 21:05:21 -0000
@@ -585,10 +585,10 @@
| expr T_IS_NOT_IDENTICAL expr { zend_do_binary_op(ZEND_IS_NOT_IDENTICAL, &$$, &$1, &$3 TSRMLS_CC); }
| expr T_IS_EQUAL expr { zend_do_binary_op(ZEND_IS_EQUAL, &$$, &$1, &$3 TSRMLS_CC); }
| expr T_IS_NOT_EQUAL expr { zend_do_binary_op(ZEND_IS_NOT_EQUAL, &$$, &$1, &$3 TSRMLS_CC); }
- | expr '<' expr { zend_do_binary_op(ZEND_IS_SMALLER, &$$, &$1, &$3 TSRMLS_CC); }
- | expr T_IS_SMALLER_OR_EQUAL expr { zend_do_binary_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$1, &$3 TSRMLS_CC); }
- | expr '>' expr { zend_do_binary_op(ZEND_IS_SMALLER, &$$, &$3, &$1 TSRMLS_CC); }
- | expr T_IS_GREATER_OR_EQUAL expr { zend_do_binary_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$3, &$1 TSRMLS_CC); }
+ | expr '<' expr { zend_do_binary_compare_op(ZEND_IS_SMALLER, &$$, &$1, &$3, 0 TSRMLS_CC); }
+ | expr T_IS_SMALLER_OR_EQUAL expr { zend_do_binary_compare_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$1, &$3, 0 TSRMLS_CC); }
+ | expr '>' expr { zend_do_binary_compare_op(ZEND_IS_SMALLER, &$$, &$3, &$1, 1 TSRMLS_CC); }
+ | expr T_IS_GREATER_OR_EQUAL expr { zend_do_binary_compare_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$3, &$1, 1 TSRMLS_CC); }
| expr T_INSTANCEOF class_name_reference { zend_do_instanceof(&$$, &$1, &$3, 0 TSRMLS_CC); }
| '(' expr ')' { $$ = $2; }
| expr '?' { zend_do_begin_qm_op(&$1, &$2 TSRMLS_CC); }

View File

@@ -0,0 +1,66 @@
Index: Zend/zend_compile.c
===================================================================
RCS file: /repository/ZendEngine2/zend_compile.c,v
retrieving revision 1.647.2.21
diff -u -r1.647.2.21 zend_compile.c
--- Zend/zend_compile.c 4 Jan 2006 23:53:04 -0000 1.647.2.21
+++ Zend/zend_compile.c 8 Feb 2006 21:06:49 -0000
@@ -267,6 +267,20 @@
}
+void zend_do_binary_compare_op(zend_uchar op, znode *result, znode *op1, znode *op2, zend_uint extval TSRMLS_DC)
+{
+ zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
+
+ opline->opcode = op;
+ opline->result.op_type = IS_TMP_VAR;
+ opline->result.u.var = get_temporary_variable(CG(active_op_array));
+ opline->op1 = *op1;
+ opline->op2 = *op2;
+ opline->extended_value = extval;
+ *result = opline->result;
+}
+
+
void zend_do_unary_op(zend_uchar op, znode *result, znode *op1 TSRMLS_DC)
{
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
Index: Zend/zend_compile.h
===================================================================
RCS file: /repository/ZendEngine2/zend_compile.h,v
retrieving revision 1.316.2.6
diff -u -r1.316.2.6 zend_compile.h
--- Zend/zend_compile.h 4 Jan 2006 23:53:04 -0000 1.316.2.6
+++ Zend/zend_compile.h 8 Feb 2006 21:06:49 -0000
@@ -336,6 +336,8 @@
/* parser-driven code generators */
+#define ZEND_HAVE_DO_BINARY_COMPARE_OP 1
+void zend_do_binary_compare_op(zend_uchar op, znode *result, znode *op1, znode *op2, zend_uint extval TSRMLS_DC);
void zend_do_binary_op(zend_uchar op, znode *result, znode *op1, znode *op2 TSRMLS_DC);
void zend_do_unary_op(zend_uchar op, znode *result, znode *op1 TSRMLS_DC);
void zend_do_binary_assign_op(zend_uchar op, znode *result, znode *op1, znode *op2 TSRMLS_DC);
Index: Zend/zend_language_parser.y
===================================================================
RCS file: /repository/ZendEngine2/zend_language_parser.y,v
retrieving revision 1.160.2.3
diff -u -r1.160.2.3 zend_language_parser.y
--- Zend/zend_language_parser.y 4 Jan 2006 23:53:04 -0000 1.160.2.3
+++ Zend/zend_language_parser.y 8 Feb 2006 21:06:49 -0000
@@ -597,10 +597,10 @@
| expr T_IS_NOT_IDENTICAL expr { zend_do_binary_op(ZEND_IS_NOT_IDENTICAL, &$$, &$1, &$3 TSRMLS_CC); }
| expr T_IS_EQUAL expr { zend_do_binary_op(ZEND_IS_EQUAL, &$$, &$1, &$3 TSRMLS_CC); }
| expr T_IS_NOT_EQUAL expr { zend_do_binary_op(ZEND_IS_NOT_EQUAL, &$$, &$1, &$3 TSRMLS_CC); }
- | expr '<' expr { zend_do_binary_op(ZEND_IS_SMALLER, &$$, &$1, &$3 TSRMLS_CC); }
- | expr T_IS_SMALLER_OR_EQUAL expr { zend_do_binary_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$1, &$3 TSRMLS_CC); }
- | expr '>' expr { zend_do_binary_op(ZEND_IS_SMALLER, &$$, &$3, &$1 TSRMLS_CC); }
- | expr T_IS_GREATER_OR_EQUAL expr { zend_do_binary_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$3, &$1 TSRMLS_CC); }
+ | expr '<' expr { zend_do_binary_compare_op(ZEND_IS_SMALLER, &$$, &$1, &$3, 0 TSRMLS_CC); }
+ | expr T_IS_SMALLER_OR_EQUAL expr { zend_do_binary_compare_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$1, &$3, 0 TSRMLS_CC); }
+ | expr '>' expr { zend_do_binary_compare_op(ZEND_IS_SMALLER, &$$, &$3, &$1, 1 TSRMLS_CC); }
+ | expr T_IS_GREATER_OR_EQUAL expr { zend_do_binary_compare_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$3, &$1, 1 TSRMLS_CC); }
| expr T_INSTANCEOF class_name_reference { zend_do_instanceof(&$$, &$1, &$3, 0 TSRMLS_CC); }
| '(' expr ')' { $$ = $2; }
| expr '?' { zend_do_begin_qm_op(&$1, &$2 TSRMLS_CC); }

View File

@@ -1,3 +1,4 @@
dnl $Id$
dnl config.m4 for extension operators
PHP_ARG_ENABLE(operator, whether to enable operator overload support,

View File

@@ -1,3 +1,4 @@
// $Id$
// vim:ft=javascript
ARG_ENABLE("operator", "enable operator overload support", "no");
@@ -6,3 +7,4 @@ if (PHP_OPERATOR != "no") {
AC_DEFINE("ZEND_VM_KIND", 1, "CFLAGS_OPERATOR");
EXTENSION("operator", "operator.c");
}

View File

@@ -1,236 +1,574 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_0.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Sara Golemon <pollita@php.net> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_operator.h"
#define USE_OPLINE const zend_op *opline = EX(opline);
#define GET_OP1_ZVAL_PTR_UNDEF(fetch) \
get_zval_ptr_undef(opline->op1_type, opline->op1, &free_op1, execute_data)
#define GET_OP2_ZVAL_PTR_UNDEF(fetch) \
get_zval_ptr_undef(opline->op2_type, opline->op2, &free_op2, execute_data)
#ifndef ZEND_VM_KIND_CALL
/* ZEND_VM_KIND gets defined to this, but this doesn't get defined... go figure... */
#define ZEND_VM_KIND_CALL 1
#endif
#define FREE_OP1 if (free_op1) { zval_ptr_dtor_nogc(free_op1); }
#define FREE_OP2 if (free_op2) { zval_ptr_dtor_nogc(free_op2); }
#if (PHP_MAJOR_VERSION > 5 || PHP_MINOR_VERSION > 0) && ZEND_VM_KIND != ZEND_VM_KIND_CALL
# error "Operator overload support requires CALL style Zend VM"
#endif
static
zval *get_zval_ptr_undef(zend_uchar op_type, znode_op op, zend_free_op *free_op,
zend_execute_data *execute_data) {
switch (op_type) {
case IS_TMP_VAR:
case IS_VAR: return (*free_op = EX_VAR(op.var));
case IS_CONST: return EX_CONSTANT(op);
case IS_CV: return EX_VAR(op.var);
default: return NULL;
}
/* ***********
* Helpers *
*********** */
#define PHP_OPERATOR_EX_T(offset) (*(temp_variable *)((char*)execute_data->Ts + offset))
static inline int php_operator_method(zval *result, zval *op1, const char *method, int method_len, zval *op2 TSRMLS_DC)
{
zval *caller;
int ret;
ALLOC_INIT_ZVAL(caller);
array_init(caller);
ZVAL_ADDREF(op1);
add_index_zval(caller, 0, op1);
add_index_stringl(caller, 1, (char*)method, method_len, 1);
ret = call_user_function(EG(function_table), NULL, caller, result, op2 ? 1 : 0, op2 ? &op2 : NULL TSRMLS_CC);
zval_ptr_dtor(&caller);
return ret;
}
/* ----------------------------------------------------------------------- */
static inline zval *php_operator_zval_ptr(znode *node, zend_free_op *should_free, zend_execute_data *execute_data TSRMLS_DC)
{
should_free->var = NULL;
#define UNARY_OPS(X) \
X(BW_NOT, __bw_not)
switch (node->op_type) {
case IS_CONST:
return &(node->u.constant);
case IS_VAR:
return PHP_OPERATOR_EX_T(node->u.var).var.ptr;
case IS_TMP_VAR:
return (should_free->var = &PHP_OPERATOR_EX_T(node->u.var).tmp_var);
#if PHP_MAJOR_VERSION > 5 || PHP_MINOR_VERSION > 0
case IS_CV:
{
zval ***ret = &execute_data->CVs[node->u.var];
#define BINARY_OPS(X) \
X(ADD, __add) \
X(SUB, __sub) \
X(MUL, __mul) \
X(DIV, __div) \
X(MOD, __mod) \
X(POW, __pow) \
X(SL, __sl) \
X(SR, __sr) \
X(CONCAT, __concat) \
X(BW_OR, __bw_or) \
X(BW_AND, __bw_and) \
X(BW_XOR, __bw_xor) \
X(IS_IDENTICAL, __is_identical) \
X(IS_NOT_IDENTICAL, __is_not_identical) \
X(IS_EQUAL, __is_equal) \
X(IS_NOT_EQUAL, __is_not_equal) \
X(IS_SMALLER, __is_smaller) \
X(IS_SMALLER_OR_EQUAL, __is_smaller_or_equal) \
X(SPACESHIP, __cmp)
#define UNARY_ASSIGN_OPS(X) \
X(PRE_INC, __pre_inc) \
X(POST_INC, __post_inc) \
X(PRE_DEC, __pre_dec) \
X(POST_DEC, __post_dec)
#define BINARY_ASSIGN_OPS(X) \
X(ASSIGN, __assign) \
X(ASSIGN_ADD, __assign_add) \
X(ASSIGN_SUB, __assign_sub) \
X(ASSIGN_MUL, __assign_mul) \
X(ASSIGN_DIV, __assign_div) \
X(ASSIGN_MOD, __assign_mod) \
X(ASSIGN_POW, __assign_pow) \
X(ASSIGN_SL, __assign_sl) \
X(ASSIGN_SR, __assign_sr) \
X(ASSIGN_CONCAT, __assign_concat) \
X(ASSIGN_BW_OR, __assign_bw_or) \
X(ASSIGN_BW_AND, __assign_bw_and) \
X(ASSIGN_BW_XOR, __assign_bw_xor)
#define ALL_OPS(X) \
UNARY_OPS(X) \
BINARY_OPS(X) \
UNARY_ASSIGN_OPS(X) \
BINARY_ASSIGN_OPS(X)
/* greater/greater-equal are encoded as smaller/smaller-equal
* so they require special handling
*/
#define GREATER_OPS(X) \
X(IS_SMALLER, __is_greater) \
X(IS_SMALLER_OR_EQUAL, __is_greater_or_equal)
#define X(op, meth) static zend_string *s_##meth;
ALL_OPS(X)
GREATER_OPS(X)
#undef X
/* {{{ operator_method_name */
static inline zend_string* operator_method_name(zend_uchar opcode) {
switch (opcode) {
#define X(op, meth) case ZEND_##op: return s_##meth;
ALL_OPS(X)
#undef X
default:
ZEND_ASSERT(0);
return NULL;
}
if (!*ret) {
zend_compiled_variable *cv = &EG(active_op_array)->vars[node->u.var];
if (zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, (void **)ret)==FAILURE) {
zend_error(E_NOTICE, "Undefined variable: %s", cv->name);
return &EG(uninitialized_zval);
}
}
return **ret;
}
#endif
case IS_UNUSED:
default:
return NULL;
}
}
/* }}} */
/* {{{ operator_get_method */
static zend_bool operator_get_method(zend_string *method, zval *obj,
zend_fcall_info *fci,
zend_fcall_info_cache *fcc) {
memset(fci, 0, sizeof(zend_fcall_info));
fci->size = sizeof(zend_fcall_info);
fci->object = Z_OBJ_P(obj);
ZVAL_STR(&(fci->function_name), method);
static inline zval **php_operator_zval_ptr_ptr(znode *node, zend_execute_data *execute_data TSRMLS_DC)
{
switch (node->op_type) {
case IS_VAR:
return PHP_OPERATOR_EX_T(node->u.var).var.ptr_ptr;
#if PHP_MAJOR_VERSION > 5 || PHP_MINOR_VERSION > 0
case IS_CV:
{
zval ***ret = &execute_data->CVs[node->u.var];
if (!zend_is_callable_ex(&(fci->function_name), fci->object,
IS_CALLABLE_CHECK_SILENT | IS_CALLABLE_STRICT,
NULL, fcc, NULL)) {
return 0;
}
/* Disallow dispatch via __call */
if (fcc->function_handler == Z_OBJCE_P(obj)->__call) { return 0; }
if (fcc->function_handler->type == ZEND_USER_FUNCTION) {
zend_op_array *oparray = (zend_op_array*)(fcc->function_handler);
if (oparray->fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
return 0;
}
}
return 1;
if (!*ret) {
zend_compiled_variable *cv = &EG(active_op_array)->vars[node->u.var];
if (zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, (void **)ret)==FAILURE) {
zend_error(E_NOTICE, "Undefined variable: %s", cv->name);
return &EG(uninitialized_zval_ptr);
}
}
return *ret;
}
#endif
case IS_CONST:
case IS_TMP_VAR:
case IS_UNUSED:
default:
return NULL;
}
}
/* }}} */
/* {{{ operator_is_greater_op */
static inline zend_bool operator_is_greater_op(const zend_op *opline, zend_string **pmethod) {
if (opline->extended_value == 1) {
switch (opline->opcode) {
#define X(op, meth) \
case ZEND_##op: *pmethod = s_##meth; return 1;
GREATER_OPS(X)
#undef X
}
}
return 0;
static inline zval *php_operator_get_result_ptr(zend_op *opline, zend_execute_data *execute_data)
{
zval *tmp;
if (opline->result.op_type == IS_TMP_VAR) {
return &PHP_OPERATOR_EX_T(opline->result.u.var).tmp_var;
}
ALLOC_INIT_ZVAL(tmp);
return tmp;
}
/* }}} */
/* {{{ op_handler */
static int op_handler(zend_execute_data *execute_data) {
USE_OPLINE
zend_free_op free_op1 = NULL, free_op2 = NULL;
zval *op1, *op2 = NULL;
zend_fcall_info fci;
zend_fcall_info_cache fcc;
zend_string *method = operator_method_name(opline->opcode);
if (opline->op1_type == IS_UNUSED) {
/* Assign op */
op1 = EX_VAR(opline->result.var);
} else {
op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
}
ZVAL_DEREF(op1);
switch (opline->opcode) {
#define X(op, meth) \
case ZEND_##op:
BINARY_OPS(X)
BINARY_ASSIGN_OPS(X)
#undef X
op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
}
if (operator_is_greater_op(opline, &method)) {
zval *tmp = op1;
zend_free_op free_tmp = free_op1;
op1 = op2; op2 = tmp;
free_op1 = free_op2; free_op2 = free_tmp;
}
if ((Z_TYPE_P(op1) != IS_OBJECT) ||
!operator_get_method(method, op1, &fci, &fcc)) {
/* Not an overloaded call */
return ZEND_USER_OPCODE_DISPATCH;
}
fci.retval = EX_VAR(opline->result.var);
fci.params = op2;
fci.param_count = op2 ? 1 : 0;
if (FAILURE == zend_call_function(&fci, &fcc)) {
php_error(E_WARNING, "Failed calling %s::%s()", Z_OBJCE_P(op1)->name, Z_STRVAL(fci.function_name));
ZVAL_NULL(fci.retval);
}
FREE_OP2
FREE_OP1
EX(opline) = opline + 1;
return ZEND_USER_OPCODE_CONTINUE;
static inline void php_operator_set_result_ptr(zval *result, zend_op *opline, zend_execute_data *execute_data)
{
switch (opline->result.op_type) {
case IS_TMP_VAR:
/* Nothing to do */
return;
case IS_VAR:
PHP_OPERATOR_EX_T(opline->result.u.var).var.ptr = result;
PHP_OPERATOR_EX_T(opline->result.u.var).var.ptr_ptr = &PHP_OPERATOR_EX_T(opline->result.u.var).var.ptr;
return;
default:
zval_ptr_dtor(&result);
}
}
/* }}} */
/* {{{ MINIT */
static PHP_MINIT_FUNCTION(operator) {
#define X(op, meth) \
s_##meth = zend_string_init(#meth, strlen(#meth), 1);
GREATER_OPS(X)
#undef X
#define X(op, meth) \
s_##meth = zend_string_init(#meth, strlen(#meth), 1); \
zend_set_user_opcode_handler(ZEND_##op, op_handler);
ALL_OPS(X)
#undef X
return SUCCESS;
#if PHP_MAJOR_VERSION > 5 || PHP_MINOR_VERSION > 0
static inline int _php_operator_decode(zend_op *opline)
{
int ret = opline->opcode * 25;
switch (opline->op1.op_type) {
case IS_CONST: break;
case IS_TMP_VAR: ret += 5; break;
case IS_VAR: ret += 10; break;
case IS_UNUSED: ret += 15; break;
case IS_CV: ret += 20; break;
}
switch (opline->op2.op_type) {
case IS_CONST: break;
case IS_TMP_VAR: ret += 1; break;
case IS_VAR: ret += 2; break;
case IS_UNUSED: ret += 3; break;
case IS_CV: ret += 4; break;
}
return ret;
}
/* }}} */
#define PHP_OPERATOR_OPHANDLER_COUNT ((25 * 151) + 1)
#define PHP_OPERATOR_REPLACE_OPCODE(opname) { int i; for(i = 5; i < 25; i++) if (php_operator_opcode_handlers[(opname*25) + i]) php_operator_opcode_handlers[(opname*25) + i] = php_operator_op_##opname; }
#define PHP_OPERATOR_REPLACE_ALL_OPCODE(opname) { int i; for(i = 0; i < 25; i++) if (php_operator_opcode_handlers[(opname*25) + i]) php_operator_opcode_handlers[(opname*25) + i] = php_operator_op_##opname; }
#define PHP_OPERATOR_DECODE(opline) _php_operator_decode(opline)
#define PHP_OPERATOR_GET_OPLINE zend_op *opline = (execute_data->opline);
#else
#define PHP_OPERATOR_OPHANDLER_COUNT 512
#define PHP_OPERATOR_REPLACE_OPCODE(opname) zend_opcode_handlers[opname] = php_operator_op_##opname
#define PHP_OPERATOR_REPLACE_ALL_OPCODE(opname) zend_opcode_handlers[opname] = php_operator_op_##opname
#define PHP_OPERATOR_DECODE(opline) (opline->code)
#define PHP_OPERATOR_GET_OPLINE
#endif
/* {{{ operator_module_entry
*/
static zend_module_entry operator_module_entry = {
STANDARD_MODULE_HEADER,
"operator",
NULL, /* functions */
PHP_MINIT(operator),
NULL, /* MSHUTDOWN */
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
NULL, /* MINFO */
"7.2.0",
STANDARD_MODULE_PROPERTIES
static opcode_handler_t *php_operator_original_opcode_handlers;
static opcode_handler_t php_operator_opcode_handlers[PHP_OPERATOR_OPHANDLER_COUNT];
/* *******************
* Op Replacements *
******************* */
#define PHP_OPERATOR_BINARY_OP(opname,methodname) static int php_operator_op_##opname (ZEND_OPCODE_HANDLER_ARGS) { return _php_operator_binary_op(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU, methodname, sizeof(methodname) - 1); }
static inline int _php_operator_binary_op(ZEND_OPCODE_HANDLER_ARGS, const char *methodname, int methodname_len)
{
PHP_OPERATOR_GET_OPLINE
zend_free_op free_op1, free_op2;
zval *result;
zval *op1 = php_operator_zval_ptr(&(opline->op1), &free_op1, execute_data TSRMLS_CC);
zval *op2 = php_operator_zval_ptr(&(opline->op2), &free_op2, execute_data TSRMLS_CC);
#ifdef ZEND_HAVE_DO_BINARY_COMPARE_OP
if (opline->extended_value &&
opline->opcode == ZEND_IS_SMALLER) {
zval *swap = op1; op1 = op2; op2 = swap;
methodname = "__is_greater";
methodname_len = sizeof("__is_greater") - 1;
} else if (opline->extended_value &&
opline->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
zval *swap = op1; op1 = op2; op2 = swap;
methodname = "__is_greater_or_equal";
methodname_len = sizeof("__is_greater_or_equal") - 1;
}
#endif
if (!op1 || op1->type != IS_OBJECT ||
!zend_hash_exists(&Z_OBJCE_P(op1)->function_table, (char*)methodname, methodname_len + 1)) {
/* Rely on primary handler */
return php_operator_original_opcode_handlers[PHP_OPERATOR_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}
result = php_operator_get_result_ptr(opline, execute_data);
if (php_operator_method(result, op1, methodname, methodname_len, op2 TSRMLS_CC) == FAILURE) {
/* Fallback on original handler */
if (opline->result.op_type != IS_TMP_VAR) zval_ptr_dtor(&result);
return php_operator_original_opcode_handlers[PHP_OPERATOR_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}
php_operator_set_result_ptr(result, opline, execute_data);
if (free_op1.var) zval_dtor(free_op1.var);
if (free_op2.var) zval_dtor(free_op2.var);
execute_data->opline++;
return 0;
}
#define PHP_OPERATOR_UNARY_OP(opname,methodname) static int php_operator_op_##opname (ZEND_OPCODE_HANDLER_ARGS) { return _php_operator_unary_op(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU, methodname, sizeof(methodname) - 1); }
static inline int _php_operator_unary_op(ZEND_OPCODE_HANDLER_ARGS, const char *methodname, int methodname_len)
{
PHP_OPERATOR_GET_OPLINE
zend_free_op free_op1;
zval *result;
zval *op1 = php_operator_zval_ptr(&(opline->op1), &free_op1, execute_data TSRMLS_CC);
if (!op1 ||
op1->type != IS_OBJECT ||
!zend_hash_exists(&Z_OBJCE_P(op1)->function_table, (char*)methodname, methodname_len + 1)) {
/* Rely on primary handler */
return php_operator_original_opcode_handlers[PHP_OPERATOR_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}
result = php_operator_get_result_ptr(opline, execute_data);
if (php_operator_method(result, op1, methodname, methodname_len, NULL TSRMLS_CC) == FAILURE) {
/* Fallback on original handler */
if (opline->result.op_type != IS_TMP_VAR) zval_ptr_dtor(&result);
return php_operator_original_opcode_handlers[PHP_OPERATOR_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}
php_operator_set_result_ptr(result, opline, execute_data);
if (free_op1.var) zval_dtor(free_op1.var);
execute_data->opline++;
return 0;
}
#define PHP_OPERATOR_BINARY_ASSIGN_OP(opname,methodname) static int php_operator_op_##opname (ZEND_OPCODE_HANDLER_ARGS) { return _php_operator_binary_assign_op(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU, methodname, sizeof(methodname) - 1); }
static int _php_operator_binary_assign_op(ZEND_OPCODE_HANDLER_ARGS, const char *methodname, int methodname_len)
{
PHP_OPERATOR_GET_OPLINE
zend_free_op free_value, free_prop, free_obj;
zval *var = NULL, *value, *result;
zend_bool increment_opline = 0;
free_prop.var = free_obj.var = NULL;
switch (opline->extended_value) {
case ZEND_ASSIGN_OBJ:
case ZEND_ASSIGN_DIM:
{
zend_op *opdata = opline + 1;
zval *object = php_operator_zval_ptr(&(opline->op1), &free_obj, execute_data TSRMLS_CC);
zval *prop = php_operator_zval_ptr(&(opline->op2), &free_prop, execute_data TSRMLS_CC);
if (!object || Z_TYPE_P(object) != IS_OBJECT) {
/* Let orignal handler throw error */
return php_operator_original_opcode_handlers[PHP_OPERATOR_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}
increment_opline = 1;
value = php_operator_zval_ptr(&(opdata->op1), &free_value, execute_data TSRMLS_CC);
if (!value) {
/* Shouldn't happen */
return php_operator_original_opcode_handlers[PHP_OPERATOR_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}
/* Plan A */
if (opline->extended_value == ZEND_ASSIGN_OBJ &&
Z_OBJ_HT_P(object)->get_property_ptr_ptr) {
zval **varpp = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, prop TSRMLS_CC);
if (varpp) {
var = *varpp;
break;
}
}
/* Plan B */
if (opline->extended_value == ZEND_ASSIGN_OBJ &&
Z_OBJ_HT_P(object)->read_property) {
var = Z_OBJ_HT_P(object)->read_property(object, prop, BP_VAR_RW TSRMLS_CC);
} else if (opline->extended_value == ZEND_ASSIGN_DIM &&
Z_OBJ_HT_P(object)->read_dimension) {
var = Z_OBJ_HT_P(object)->read_dimension(object, prop, BP_VAR_RW TSRMLS_CC);
}
break;
}
default:
var = php_operator_zval_ptr(&(opline->op1), &free_obj, execute_data TSRMLS_CC);
value = php_operator_zval_ptr(&(opline->op2), &free_value, execute_data TSRMLS_CC);
}
if (!var || Z_TYPE_P(var) != IS_OBJECT ||
!zend_hash_exists(&Z_OBJCE_P(var)->function_table, (char*)methodname, methodname_len + 1)) {
/* Rely on primary handler */
return php_operator_original_opcode_handlers[PHP_OPERATOR_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}
result = php_operator_get_result_ptr(opline, execute_data);
if (php_operator_method(result, var, methodname, methodname_len, value TSRMLS_CC) == FAILURE) {
/* Fallback on original handler */
if (opline->result.op_type != IS_TMP_VAR) zval_ptr_dtor(&result);
return php_operator_original_opcode_handlers[PHP_OPERATOR_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}
if (free_prop.var) zval_dtor(free_prop.var);
if (free_value.var) zval_dtor(free_value.var);
php_operator_set_result_ptr(result, opline, execute_data);
execute_data->opline += increment_opline ? 2 : 1;
return 0;
}
#define PHP_OPERATOR_UNARY_ASSIGN_OP(opname,methodname) static int php_operator_op_##opname (ZEND_OPCODE_HANDLER_ARGS) { return _php_operator_unary_assign_op(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU, methodname, sizeof(methodname) - 1); }
static inline int _php_operator_unary_assign_op(ZEND_OPCODE_HANDLER_ARGS, const char *methodname, int methodname_len)
{
PHP_OPERATOR_GET_OPLINE
zval *result;
zval **op1 = php_operator_zval_ptr_ptr(&(opline->op1), execute_data TSRMLS_CC);
if (!op1 || Z_TYPE_PP(op1) != IS_OBJECT ||
!zend_hash_exists(&Z_OBJCE_PP(op1)->function_table, (char*)methodname, methodname_len + 1)) {
/* Rely on primary handler */
return php_operator_original_opcode_handlers[PHP_OPERATOR_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}
result = php_operator_get_result_ptr(opline, execute_data);
if (php_operator_method(result, *op1, methodname, methodname_len, NULL TSRMLS_CC) == FAILURE) {
/* Fallback on original handler */
if (opline->result.op_type != IS_TMP_VAR) zval_ptr_dtor(&result);
return php_operator_original_opcode_handlers[PHP_OPERATOR_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}
php_operator_set_result_ptr(result, opline, execute_data);
execute_data->opline++;
return 0;
}
#define PHP_OPERATOR_UNARY_ASSIGN_OBJ_OP(opname,methodname) static int php_operator_op_##opname (ZEND_OPCODE_HANDLER_ARGS) { return _php_operator_unary_assign_obj_op(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU, methodname, sizeof(methodname) - 1); }
static inline int _php_operator_unary_assign_obj_op(ZEND_OPCODE_HANDLER_ARGS, const char *methodname, int methodname_len)
{
PHP_OPERATOR_GET_OPLINE
zend_free_op free_obj, free_prop;
zval *result;
zval *obj = php_operator_zval_ptr(&(opline->op1), &free_obj, execute_data TSRMLS_CC);
zval *prop = php_operator_zval_ptr(&(opline->op2), &free_prop, execute_data TSRMLS_CC);
zval *var = NULL;
if (!obj || Z_TYPE_P(obj) != IS_OBJECT || !prop) {
/* Rely on primary handler */
return php_operator_original_opcode_handlers[PHP_OPERATOR_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}
if (Z_OBJ_HT_P(obj)->get_property_ptr_ptr) {
zval **varpp = Z_OBJ_HT_P(obj)->get_property_ptr_ptr(obj, prop TSRMLS_CC);
if (varpp) {
var = *varpp;
}
}
if (!var && Z_OBJ_HT_P(obj)->read_property) {
var = Z_OBJ_HT_P(obj)->read_property(obj, prop, BP_VAR_RW TSRMLS_CC);
}
if (!var || Z_TYPE_P(var) != IS_OBJECT ||
!zend_hash_exists(&Z_OBJCE_P(var)->function_table, (char*)methodname, methodname_len + 1)) {
/* Rely on primary handler */
return php_operator_original_opcode_handlers[PHP_OPERATOR_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}
result = php_operator_get_result_ptr(opline, execute_data);
if (php_operator_method(result, var, methodname, methodname_len, NULL TSRMLS_CC) == FAILURE) {
/* Fallback on original handler */
if (opline->result.op_type != IS_TMP_VAR) zval_ptr_dtor(&result);
return php_operator_original_opcode_handlers[PHP_OPERATOR_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}
if (free_obj.var) { zval_dtor(free_obj.var); }
if (free_prop.var) { zval_dtor(free_prop.var); }
php_operator_set_result_ptr(result, opline, execute_data);
execute_data->opline++;
return 0;
}
PHP_OPERATOR_BINARY_OP(ZEND_ADD, "__add")
PHP_OPERATOR_BINARY_OP(ZEND_SUB, "__sub")
PHP_OPERATOR_BINARY_OP(ZEND_MUL, "__mul")
PHP_OPERATOR_BINARY_OP(ZEND_DIV, "__div")
PHP_OPERATOR_BINARY_OP(ZEND_MOD, "__mod")
PHP_OPERATOR_BINARY_OP(ZEND_SL, "__sl")
PHP_OPERATOR_BINARY_OP(ZEND_SR, "__sr")
PHP_OPERATOR_BINARY_OP(ZEND_CONCAT, "__concat")
PHP_OPERATOR_BINARY_OP(ZEND_BW_OR, "__bw_or")
PHP_OPERATOR_BINARY_OP(ZEND_BW_AND, "__bw_and")
PHP_OPERATOR_BINARY_OP(ZEND_BW_XOR, "__bw_xor")
PHP_OPERATOR_BINARY_OP(ZEND_IS_IDENTICAL, "__is_identical")
PHP_OPERATOR_BINARY_OP(ZEND_IS_NOT_IDENTICAL, "__is_not_identical")
PHP_OPERATOR_BINARY_OP(ZEND_IS_EQUAL, "__is_equal")
PHP_OPERATOR_BINARY_OP(ZEND_IS_NOT_EQUAL, "__is_not_equal")
PHP_OPERATOR_BINARY_OP(ZEND_IS_SMALLER, "__is_smaller") /* includes __is_greater when patch applied */
PHP_OPERATOR_BINARY_OP(ZEND_IS_SMALLER_OR_EQUAL, "__is_smaller_or_equal") /* includes __is_greater_or_equal ... */
PHP_OPERATOR_UNARY_OP(ZEND_BW_NOT, "__bw_not")
PHP_OPERATOR_UNARY_OP(ZEND_BOOL, "__bool")
PHP_OPERATOR_UNARY_OP(ZEND_BOOL_NOT, "__bool_not")
PHP_OPERATOR_BINARY_ASSIGN_OP(ZEND_ASSIGN_ADD, "__assign_add")
PHP_OPERATOR_BINARY_ASSIGN_OP(ZEND_ASSIGN_SUB, "__assign_sub")
PHP_OPERATOR_BINARY_ASSIGN_OP(ZEND_ASSIGN_MUL, "__assign_mul")
PHP_OPERATOR_BINARY_ASSIGN_OP(ZEND_ASSIGN_DIV, "__assign_div")
PHP_OPERATOR_BINARY_ASSIGN_OP(ZEND_ASSIGN_MOD, "__assign_mod")
PHP_OPERATOR_BINARY_ASSIGN_OP(ZEND_ASSIGN_SL, "__assign_sl")
PHP_OPERATOR_BINARY_ASSIGN_OP(ZEND_ASSIGN_SR, "__assign_sr")
PHP_OPERATOR_BINARY_ASSIGN_OP(ZEND_ASSIGN_CONCAT, "__assign_concat")
PHP_OPERATOR_BINARY_ASSIGN_OP(ZEND_ASSIGN_BW_OR, "__assign_bw_or")
PHP_OPERATOR_BINARY_ASSIGN_OP(ZEND_ASSIGN_BW_AND, "__assign_bw_and")
PHP_OPERATOR_BINARY_ASSIGN_OP(ZEND_ASSIGN_BW_XOR, "__assign_bw_xor")
PHP_OPERATOR_UNARY_ASSIGN_OP(ZEND_PRE_INC, "__pre_inc")
PHP_OPERATOR_UNARY_ASSIGN_OP(ZEND_PRE_DEC, "__pre_dec")
PHP_OPERATOR_UNARY_ASSIGN_OP(ZEND_POST_INC, "__post_inc")
PHP_OPERATOR_UNARY_ASSIGN_OP(ZEND_POST_DEC, "__post_dec")
PHP_OPERATOR_UNARY_ASSIGN_OBJ_OP(ZEND_PRE_INC_OBJ, "__pre_inc")
PHP_OPERATOR_UNARY_ASSIGN_OBJ_OP(ZEND_PRE_DEC_OBJ, "__pre_dec")
PHP_OPERATOR_UNARY_ASSIGN_OBJ_OP(ZEND_POST_INC_OBJ, "__post_inc")
PHP_OPERATOR_UNARY_ASSIGN_OBJ_OP(ZEND_POST_DEC_OBJ, "__post_dec")
/* ***********************
* Module Housekeeping *
*********************** */
PHP_MINIT_FUNCTION(operator)
{
memcpy(php_operator_opcode_handlers, zend_opcode_handlers, sizeof(php_operator_opcode_handlers));
#if PHP_MAJOR_VERSION > 5 || PHP_MINOR_VERSION > 0
php_operator_original_opcode_handlers = zend_opcode_handlers;
zend_opcode_handlers = php_operator_opcode_handlers;
#else
php_operator_original_opcode_handlers = php_operator_opcode_handlers;
#endif
/* Binaries */
PHP_OPERATOR_REPLACE_OPCODE(ZEND_ADD);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_SUB);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_MUL);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_DIV);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_MOD);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_SL);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_SR);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_CONCAT);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_BW_OR);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_BW_AND);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_BW_XOR);
/* Comparators (Binaries in disguise) */
PHP_OPERATOR_REPLACE_OPCODE(ZEND_IS_IDENTICAL);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_IS_NOT_IDENTICAL);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_IS_EQUAL);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_IS_NOT_EQUAL);
#ifdef ZEND_HAVE_DO_BINARY_COMPARE_OP
/* __is_greater and __is_greater_or_equal support requires patching parser: compare-greater-VERSION.diff */
REGISTER_LONG_CONSTANT("OPERATOR_COMPARE_PATCH", 1, CONST_CS | CONST_PERSISTENT);
PHP_OPERATOR_REPLACE_ALL_OPCODE(ZEND_IS_SMALLER);
PHP_OPERATOR_REPLACE_ALL_OPCODE(ZEND_IS_SMALLER_OR_EQUAL);
#else
REGISTER_LONG_CONSTANT("OPERATOR_COMPARE_PATCH", 0, CONST_CS | CONST_PERSISTENT);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_IS_SMALLER);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_IS_SMALLER_OR_EQUAL);
#endif
/* Unaries */
PHP_OPERATOR_REPLACE_OPCODE(ZEND_BW_NOT);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_BOOL);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_BOOL_NOT);
/* Binary Assign */
PHP_OPERATOR_REPLACE_OPCODE(ZEND_ASSIGN_ADD);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_ASSIGN_SUB);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_ASSIGN_MUL);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_ASSIGN_DIV);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_ASSIGN_MOD);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_ASSIGN_SL);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_ASSIGN_SR);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_ASSIGN_CONCAT);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_ASSIGN_BW_OR);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_ASSIGN_BW_AND);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_ASSIGN_BW_XOR);
/* Unary Assign */
PHP_OPERATOR_REPLACE_OPCODE(ZEND_PRE_INC);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_PRE_DEC);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_POST_INC);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_POST_DEC);
/* Unary Assign Obj */
PHP_OPERATOR_REPLACE_OPCODE(ZEND_PRE_INC_OBJ);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_PRE_DEC_OBJ);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_POST_INC_OBJ);
PHP_OPERATOR_REPLACE_OPCODE(ZEND_POST_DEC_OBJ);
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(operator)
{
#if PHP_MAJOR_VERSION > 5 || PHP_MINOR_VERSION > 0
zend_opcode_handlers = php_operator_original_opcode_handlers;
#else
memcpy(zend_opcode_handlers, php_operator_original_opcode_handlers, sizeof(php_operator_opcode_handlers));
#endif
return SUCCESS;
}
PHP_MINFO_FUNCTION(operator)
{
php_info_print_table_start();
php_info_print_table_header(2, "operator overloading support", "+ - * / % << >> . | & ^ ~ ! ++ -- "
"+= -= *= /= %= <<= >>= .= |= &= ^= ~= === !== == != < <= "
#ifdef ZEND_HAVE_DO_BINARY_COMPARE_OP
"> >= "
#endif
);
php_info_print_table_end();
}
zend_module_entry operator_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
PHP_OPERATOR_EXTNAME,
NULL,
PHP_MINIT(operator),
PHP_MSHUTDOWN(operator),
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
PHP_MINFO(operator),
#if ZEND_MODULE_API_NO >= 20010901
PHP_OPERATOR_EXTVER,
#endif
STANDARD_MODULE_PROPERTIES
};
/* }}} */
#ifdef COMPILE_DL_OPERATOR
ZEND_GET_MODULE(operator)
#endif

View File

@@ -1,119 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.9.0" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
http://pear.php.net/dtd/tasks-1.0.xsd
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd">
<name>operator</name>
<channel>pecl.php.net</channel>
<summary>Operator overloading for Objects</summary>
<description>Operator overloading for: +, -, *, /, %, **, &lt;&lt;, &gt;&gt;, ., |, &amp;, ^, ~, !, ++, --,
+=, -=, *=, /=, %=, **=, &lt;&lt;=, &gt;&gt;=, .=, |=, &amp;=, ^=, ~=,
==, !=, ===, !==, &lt;, &lt;=, and &lt;=&gt; operators.
<package version="1.0">
<name>operator</name>
<summary>Operator overloading for Objects</summary>
<description>Operator overloading for: +, -, *, /, %, &lt;&lt;, &gt;&gt;, ., |, &amp;, ^, ~, !, ++, --,
+=, -=, *=, /=, %=, &lt;&lt;=, &gt;&gt;=, .=, |=, &amp;=, ^=, ~=,
==, !=, ===, !==, &lt;, and &lt;= operators.
Conditional support for &gt; and &gt;= available with application of a patch.
</description>
<license>PHP</license>
<maintainers>
<maintainer>
<user>pollita</user>
<name>Sara Golemon</name>
<email>pollita@php.net</email>
<role>lead</role>
</maintainer>
</maintainers>
</description>
<lead>
<name>Sara Golemon</name>
<user>pollita</user>
<email>pollita@php.net</email>
<active>yes</active>
</lead>
<date>2017-03-08</date>
<time>20:54:00</time>
<version>
<release>1.0</release>
<api>1.0</api>
</version>
<stability>
<release>beta</release>
<api>beta</api>
</stability>
<license uri="http://www.php.net/license">PHP</license>
<notes>
* Rewrote for PHP7, dropped PHP5 support
* Removed pre/post inc/dec support for objects in object properties (this will come back eventually)
* Added pow, assign-pow, and spaceship operators
</notes>
<contents>
<dir name="/">
<dir name="tests">
<file name="binary.phpt" role="test" />
<file name="binary_assign.phpt" role="test" />
<file name="bug56904.phpt" role="test" />
<file name="compare.phpt" role="test" />
<file name="incdec.phpt" role="test" />
<file name="unary.phpt" role="test" />
</dir> <!-- //tests -->
<file name="config.m4" role="src" />
<file name="config.w32" role="src" />
<file name="CREDITS" role="doc" />
<file name="EXPERIMENTAL" role="doc" />
<file name="operator.c" role="src" />
</dir> <!-- / -->
</contents>
<dependencies>
<required>
<php>
<min>4.0.0</min>
</php>
<pearinstaller>
<min>1.4.0b1</min>
</pearinstaller>
</required>
</dependencies>
<providesextension>operator</providesextension>
<extsrcrelease />
<changelog>
<release>
<date>2009-11-05</date>
<time>17:54:16</time>
<version>
<release>0.3</release>
<api>0.3</api>
</version>
<stability>
<release>beta</release>
<api>beta</api>
</stability>
<license uri="http://www.php.net/license">PHP</license>
<notes>
* Added support for comparator ops
<release>
<version>0.3</version>
<state>beta</state>
<date>2006-02-03</date>
<notes>* Added support for comparator ops
Note: ZE treats &gt; style comparisons as backwards &lt; comparisons, so what looks like a left-associative greater than becomes a right-associative less-than.
Because of this, overloading the &gt; and &gt;= operators for left-association requires application of a parser patch.
</notes>
</release>
<release>
<version>
<release>0.2</release>
<api>0.2</api>
</version>
<stability>
<release>beta</release>
<api>beta</api>
</stability>
<date>2006-01-25</date>
<license uri="http://www.php.net/license">PHP</license>
<notes>
* Added support for pre/post inc/dec operators
</notes>
</release>
<changelog>
<release>
<version>0.2</version>
<state>beta</state>
<date>2006-01-25</date>
<notes>* Added support for pre/post inc/dec operators
* Added support for assignment operators
* Refactored opline-&gt;result handling
* Refactored opcode handlers
</notes>
</release>
<release>
<version>
<release>0.1</release>
<api>0.1</api>
</version>
<stability>
<release>beta</release>
<api>beta</api>
</stability>
<date>2006-01-12</date>
<license uri="http://www.php.net/license">PHP</license>
<notes>
Initial Release
</notes>
</release>
</changelog>
* Refactored opline->result handling
* Refactored opcode handlers</notes>
</release>
<release>
<version>0.1</version>
<state>beta</state>
<date>2006-01-12</date>
<notes>Initial Release</notes>
</release>
</changelog>
<filelist>
<file role="doc" name="CREDITS"/>
<file role="doc" name="EXPERIMENTAL"/>
<file role="src" name="config.m4"/>
<file role="src" name="config.w32"/>
<file role="src" name="operator.c"/>
<file role="src" name="php_operator.h"/>
<file role="src" name="compare-greater-5.0.5.diff"/>
<file role="src" name="compare-greater-5.1.2.diff"/>
<dir role="test" name="tests">
<file role="test" name="unary.phpt"/>
<file role="test" name="binary.phpt"/>
<file role="test" name="incdec.phpt"/>
<file role="test" name="binary_assign.phpt"/>
<file role="test" name="binary_assign_objdim.phpt"/>
<file role="test" name="compare.phpt"/>
<file role="test" name="compare2.phpt"/>
</dir>
</filelist>
</package>
<!--
vim: et ts=1 sw=1
-->

View File

@@ -1,13 +0,0 @@
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 88d7192..b797939 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -6716,7 +6716,7 @@ void zend_compile_greater(znode *result, zend_ast *ast) /* {{{ */
zend_emit_op_tmp(result,
ast->kind == ZEND_AST_GREATER ? ZEND_IS_SMALLER : ZEND_IS_SMALLER_OR_EQUAL,
- &right_node, &left_node);
+ &right_node, &left_node)->extended_value = 1;
}
/* }}} */

45
php_operator.h Normal file
View File

@@ -0,0 +1,45 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_0.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Sara Golemon <pollita@php.net> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#ifndef PHP_OPERATOR_H
#define PHP_OPERATOR_H
#define PHP_OPERATOR_EXTNAME "operator"
#define PHP_OPERATOR_EXTVER "0.3"
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 0
typedef struct {
zval *var;
} zend_free_op;
#endif
extern zend_module_entry operator_module_entry;
#define phpext_operator_ptr &operator_module_entry
#endif
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

View File

@@ -7,10 +7,6 @@ Basic binary assign ops
class foo {
private $value;
function __assign($val) {
return $this->value = $val;
}
function __assign_add($val) {
return $this->value += $val;
}
@@ -60,11 +56,6 @@ class foo {
}
}
$a = new foo(1);
var_dump($a = 2);
var_dump($a += 2);
var_dump(is_object($a));
$c = new foo(4);
var_dump($c += 3);
@@ -86,9 +77,6 @@ var_dump($f |= 0xAA);
var_dump($f &= 0xAA);
var_dump($f ^= 0xAA);
--EXPECT--
int(2)
int(4)
bool(true)
int(7)
int(4)
int(12)

View File

@@ -0,0 +1,43 @@
--TEST--
binary assign obj/dim ops
--SKIPIF--
<?php if(!extension_loaded("operator")) print "skip"; ?>
--FILE--
<?php
class foo {
private $value;
function __assign_add($val) {
return $this->value += $val;
}
function __construct($init) {
$this->value = $init;
}
}
class bar implements arrayaccess {
public $baz;
function offsetget($ofs) {
return $this->{$ofs};
}
function offsetset($ofs,$val) { echo "argh"; }
function offsetunset($ofs) { echo "oof"; }
function offsetexists($ofs) { echo "ick"; return true; }
}
$a = new foo(4);
$b = new bar;
$b->baz = $a;
var_dump($b->baz += 10);
var_dump($b->baz += 5);
var_dump($b['baz'] += 10);
var_dump($b['baz'] += 5);
--EXPECT--
int(14)
int(19)
int(29)
int(34)

View File

@@ -1,33 +0,0 @@
--TEST--
Bug#56904 Mixing direct calls and implicit overloads
--FILE--
<?php
class a {
public $v = 'initial';
public function __assign_sl($val) {
$this->v = $val;
}
}
$bob = new a;
var_dump($bob);
$bob->__assign_sl('abc');
var_dump($bob);
$bob <<= 'def';
var_dump($bob);
?>
--EXPECTF--
object(a)#%d (1) {
["v"]=>
string(7) "initial"
}
object(a)#%d (1) {
["v"]=>
string(3) "abc"
}
object(a)#%d (1) {
["v"]=>
string(3) "def"
}

View File

@@ -2,21 +2,27 @@
Extended comparison ops
--SKIPIF--
<?php if(!extension_loaded("operator")) print "skip";
if(!OPERATOR_COMPARE_PATCH) print "skip"; ?>
--FILE--
<?php
class foo {
private $value;
function __is_greater($val) {
return $this->value > $val;
}
function __is_greater_or_equal($val) {
return $this->value >= $val;
}
function __construct($init) {
$this->value = $init;
}
}
$c = new foo(5);
var_dump($c > 5);
var_dump($c > 4);
var_dump($c >= 5);

65
tests/incdec_obj.phpt Normal file
View File

@@ -0,0 +1,65 @@
--TEST--
Inc/Dec obj ops
--SKIPIF--
<?php if(!extension_loaded("operator")) print "skip"; ?>
--FILE--
<?php
class foo {
private $value;
function __post_inc() {
return $this->value++;
}
function __post_dec() {
return $this->value--;
}
function __pre_inc() {
$this->value++;
return $this->value;
}
function __pre_dec() {
$this->value--;
return $this->value;
}
function __construct($init) {
$this->value = $init;
}
}
class bar {
public $baz;
}
$c = new foo(7);
$d = new bar;
$d->baz = $c;
for($i = 0; $i < 3; $i++) {
var_dump($d->baz++);
}
for($i = 0; $i < 3; $i++) {
var_dump($d->baz--);
}
for($i = 0; $i < 3; $i++) {
var_dump(++$d->baz);
}
for($i = 0; $i < 3; $i++) {
var_dump(--$d->baz);
}
--EXPECT--
int(7)
int(8)
int(9)
int(10)
int(9)
int(8)
int(8)
int(9)
int(10)
int(9)
int(8)
int(7)