}
break;
case IS_OBJECT:
- if (type == BP_VAR_R) {
+ if (type == BP_VAR_R || type == BP_VAR_RW) {
if (!Z_OBJ_HT_P(container)->read_dimension) {
zend_error(E_ERROR, "Cannot use object as array");
} else {
zval *dim = get_zval_ptr(op2, Ts, &EG(free_op2), BP_VAR_R);
zval *overloaded_result = Z_OBJ_HT_P(container)->read_dimension(container, dim TSRMLS_CC);
+ if (type == BP_VAR_RW && !overloaded_result->is_ref) {
+ zend_error(E_ERROR, "Objects used as arrays in post/pre increment/decrement must return values by reference");
+ }
+
*retval = &overloaded_result;
AI_USE_PTR(T(result->u.var).var);
FREE_OP(Ts, op2, EG(free_op2));
zend_class_entry *zend_ce_traversable;
zend_class_entry *zend_ce_aggregate;
zend_class_entry *zend_ce_iterator;
+zend_class_entry *zend_ce_arrayaccess;
/* {{{ zend_call_method
Only returns the returned zval if retval_ptr != NULL */
}
/* }}} */
+/* {{{ zend_implement_arrayaccess */
+static int zend_implement_arrayaccess(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
+{
+ return SUCCESS;
+}
+/* }}}*/
+
/* {{{ function tables */
zend_function_entry zend_funcs_aggregate[] = {
ZEND_ABSTRACT_ME(iterator, getIterator, NULL)
};
zend_function_entry *zend_funcs_traversable = NULL;
+
+static
+ZEND_BEGIN_ARG_INFO(arginfo_arrayaccess_offset, 0)
+ ZEND_ARG_INFO(0, offset)
+ZEND_END_ARG_INFO();
+
+static
+ZEND_BEGIN_ARG_INFO(arginfo_arrayaccess_offset_value, 0)
+ ZEND_ARG_INFO(0, offset)
+ ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO();
+
+zend_function_entry zend_funcs_arrayaccess[] = {
+ ZEND_ABSTRACT_ME(arrayaccess, offsetExists, arginfo_arrayaccess_offset)
+ ZEND_ABSTRACT_ME(arrayaccess, offsetGet, arginfo_arrayaccess_offset)
+ ZEND_ABSTRACT_ME(arrayaccess, offsetSet, arginfo_arrayaccess_offset_value)
+ ZEND_ABSTRACT_ME(arrayaccess, offsetUnset, arginfo_arrayaccess_offset)
+ {NULL, NULL, NULL}
+};
+
/* }}} */
#define REGISTER_ITERATOR_INTERFACE(class_name, class_name_str) \
REGISTER_ITERATOR_INTERFACE(iterator, Iterator);
REGISTER_ITERATOR_IMPLEMENT(iterator, traversable);
+
+ REGISTER_ITERATOR_INTERFACE(arrayaccess, ArrayAccess);
}
/* }}} */
ZEND_API zend_class_entry *zend_ce_traversable;
ZEND_API zend_class_entry *zend_ce_aggregate;
ZEND_API zend_class_entry *zend_ce_iterator;
+ZEND_API zend_class_entry *zend_ce_arrayaccess;
ZEND_API zval* zend_call_method(zval **object_pp, zend_class_entry *obj_ce, zend_function **fn_proxy, char *function_name, int function_name_len, zval **retval_ptr_ptr, int param_count, zval* arg1, zval* arg2 TSRMLS_DC);
#include "zend_objects.h"
#include "zend_objects_API.h"
#include "zend_object_handlers.h"
+#include "zend_interfaces.h"
#define DEBUG_OBJECT_HANDLERS 0
zval *zend_std_read_dimension(zval *object, zval *offset TSRMLS_DC)
{
-#if 1
- zend_error(E_ERROR, "Cannot use object as array");
-#else
- zend_printf("Fetching from object: ");
- zend_print_zval(object, 0);
-
- zend_printf("\n the offset: ");
- zend_print_zval(offset, 0);
-
- zend_printf("\n");
-#endif
- return EG(uninitialized_zval_ptr);
+ zend_class_entry *ce = Z_OBJCE_P(object);
+ zval *retval;
+
+ if (instanceof_function_ex(ce, zend_ce_arrayaccess, 1 TSRMLS_CC)) {
+ zend_call_method_with_1_params(&object, ce, NULL, "offsetget", &retval, offset);
+ if (retval->refcount > 0) { /* Should always be the case */
+ retval->refcount--;
+ }
+ return retval;
+ } else {
+ zend_error(E_ERROR, "Cannot use object of type %s as array", ce->name);
+ return 0;
+ }
}
static void zend_std_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC)
{
-#if 1
- zend_error(E_ERROR, "Cannot use object as array");
-#else
- zend_printf("Assigning to object: ");
- zend_print_zval(object, 0);
-
- zend_printf("\n with offset: ");
- zend_print_zval(offset, 0);
-
- zend_printf("\n the value: ");
- zend_print_zval(value, 0);
-
- zend_printf("\n");
-#endif
+ zend_class_entry *ce = Z_OBJCE_P(object);
+
+ if (instanceof_function_ex(ce, zend_ce_arrayaccess, 1 TSRMLS_CC)) {
+ zend_call_method_with_2_params(&object, ce, NULL, "offsetset", NULL, offset, value);
+ } else {
+ zend_error(E_ERROR, "Cannot use object of type %s as array", ce->name);
+ }
}
static int zend_std_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC)
{
- zend_error(E_ERROR, "Cannot use object as array");
- return 0;
+ zend_class_entry *ce = Z_OBJCE_P(object);
+ zval *retval;
+ int result;
+
+ if (instanceof_function_ex(ce, zend_ce_arrayaccess, 1 TSRMLS_CC)) {
+ zend_call_method_with_1_params(&object, ce, NULL, "offsetexists", &retval, offset);
+ result = i_zend_is_true(retval);
+ zval_ptr_dtor(&retval);
+ return result;
+ } else {
+ zend_error(E_ERROR, "Cannot use object of type %s as array", ce->name);
+ return 0;
+ }
}
static void zend_std_unset_dimension(zval *object, zval *offset TSRMLS_DC)
{
- zend_error(E_ERROR, "Cannot use object as array");
+ zend_class_entry *ce = Z_OBJCE_P(object);
+ zval *retval;
+
+ if (instanceof_function_ex(ce, zend_ce_arrayaccess, 1 TSRMLS_CC)) {
+ zend_call_method_with_1_params(&object, ce, NULL, "offsetunset", &retval, offset);
+ zval_ptr_dtor(&retval);
+ } else {
+ zend_error(E_ERROR, "Cannot use object of type %s as array", ce->name);
+ }
}
}
-ZEND_API zend_bool instanceof_function(zend_class_entry *instance_ce, zend_class_entry *ce TSRMLS_DC)
+ZEND_API zend_bool instanceof_function_ex(zend_class_entry *instance_ce, zend_class_entry *ce, zend_bool interfaces_only TSRMLS_DC)
{
zend_uint i;
return 1;
}
}
- while (instance_ce) {
- if (instance_ce == ce) {
- return 1;
+ if (!interfaces_only) {
+ while (instance_ce) {
+ if (instance_ce == ce) {
+ return 1;
+ }
+ instance_ce = instance_ce->parent;
}
- instance_ce = instance_ce->parent;
}
return 0;
}
+ZEND_API zend_bool instanceof_function(zend_class_entry *instance_ce, zend_class_entry *ce TSRMLS_DC)
+{
+ return instanceof_function_ex(instance_ce, ce, 0 TSRMLS_CC);
+}
+
#define LOWER_CASE 1
#define UPPER_CASE 2
#define NUMERIC 3
ZEND_API int is_smaller_function(zval *result, zval *op1, zval *op2 TSRMLS_DC);
ZEND_API int is_smaller_or_equal_function(zval *result, zval *op1, zval *op2 TSRMLS_DC);
+ZEND_API zend_bool instanceof_function_ex(zend_class_entry *instance_ce, zend_class_entry *ce, zend_bool interfaces_only TSRMLS_DC);
ZEND_API zend_bool instanceof_function(zend_class_entry *instance_ce, zend_class_entry *ce TSRMLS_DC);
static inline zend_bool is_numeric_string(char *str, int length, long *lval, double *dval, zend_bool allow_errors)
--- /dev/null
+--TEST--
+ZE2 ArrayAccess
+--SKIPIF--
+<?php
+ if (!class_exists('ArrayAccess')) die('skip ArrayAccess not present');
+?>
+--FILE--
+<?php
+class Object implements ArrayAccess {
+
+ public $a = array('1st', 1, 2=>'3rd', '4th'=>4);
+
+ function offsetExists($index) {
+ echo __METHOD__ . "($index)\n";
+ return array_key_exists($index, $this->a);
+ }
+ function offsetGet($index) {
+ echo __METHOD__ . "($index)\n";
+ return $this->a[$index];
+ }
+ function offsetSet($index, $newval) {
+ echo __METHOD__ . "($index,$newval)\n";
+ return $this->a[$index] = $newval;
+ }
+ function offsetUnset($index) {
+ echo __METHOD__ . "($index)\n";
+ unset($this->a[$index]);
+ }
+}
+
+$obj = new Object;
+
+var_dump($obj->a);
+
+echo "===EMPTY===\n";
+var_dump(empty($obj[0]));
+var_dump(empty($obj[1]));
+var_dump(empty($obj[2]));
+var_dump(empty($obj['4th']));
+var_dump(empty($obj['5th']));
+var_dump(empty($obj[6]));
+
+echo "===isset===\n";
+var_dump(isset($obj[0]));
+var_dump(isset($obj[1]));
+var_dump(isset($obj[2]));
+var_dump(isset($obj['4th']));
+var_dump(isset($obj['5th']));
+var_dump(isset($obj[6]));
+
+echo "===offsetGet===\n";
+var_dump($obj[0]);
+var_dump($obj[1]);
+var_dump($obj[2]);
+var_dump($obj['4th']);
+var_dump($obj['5th']);
+var_dump($obj[6]);
+
+echo "===offsetSet===\n";
+echo "WRITE 1\n";
+$obj[1] = 'Changed 1';
+var_dump($obj[1]);
+echo "WRITE 2\n";
+$obj['4th'] = 'Changed 4th';
+var_dump($obj['4th']);
+echo "WRITE 3\n";
+$obj['5th'] = 'Added 5th';
+var_dump($obj['5th']);
+echo "WRITE 4\n";
+$obj[6] = 'Added 6';
+var_dump($obj[6]);
+
+var_dump($obj[0]);
+var_dump($obj[2]);
+
+$x = $obj[6] = 'changed 6';
+var_dump($obj[6]);
+var_dump($x);
+
+echo "===unset===\n";
+var_dump($obj->a);
+unset($obj[2]);
+unset($obj['4th']);
+unset($obj[7]);
+unset($obj['8th']);
+var_dump($obj->a);
+
+?>
+===DONE===
+--EXPECTF--
+array(4) {
+ [0]=>
+ string(3) "1st"
+ [1]=>
+ int(1)
+ [2]=>
+ string(3) "3rd"
+ ["4th"]=>
+ int(4)
+}
+===EMPTY===
+object::offsetExists(0)
+bool(false)
+object::offsetExists(1)
+bool(false)
+object::offsetExists(2)
+bool(false)
+object::offsetExists(4th)
+bool(false)
+object::offsetExists(5th)
+bool(true)
+object::offsetExists(6)
+bool(true)
+===isset===
+object::offsetExists(0)
+bool(true)
+object::offsetExists(1)
+bool(true)
+object::offsetExists(2)
+bool(true)
+object::offsetExists(4th)
+bool(true)
+object::offsetExists(5th)
+bool(false)
+object::offsetExists(6)
+bool(false)
+===offsetGet===
+object::offsetGet(0)
+string(3) "1st"
+object::offsetGet(1)
+int(1)
+object::offsetGet(2)
+string(3) "3rd"
+object::offsetGet(4th)
+int(4)
+object::offsetGet(5th)
+
+Notice: Undefined index: 5th in %sarray_access_001.php on line %d
+NULL
+object::offsetGet(6)
+
+Notice: Undefined offset: 6 in %sarray_access_001.php on line %d
+NULL
+===offsetSet===
+WRITE 1
+object::offsetSet(1,Changed 1)
+object::offsetGet(1)
+string(9) "Changed 1"
+WRITE 2
+object::offsetSet(4th,Changed 4th)
+object::offsetGet(4th)
+string(11) "Changed 4th"
+WRITE 3
+object::offsetSet(5th,Added 5th)
+object::offsetGet(5th)
+string(9) "Added 5th"
+WRITE 4
+object::offsetSet(6,Added 6)
+object::offsetGet(6)
+string(7) "Added 6"
+object::offsetGet(0)
+string(3) "1st"
+object::offsetGet(2)
+string(3) "3rd"
+object::offsetSet(6,changed 6)
+object::offsetGet(6)
+string(9) "changed 6"
+string(9) "changed 6"
+===unset===
+array(6) {
+ [0]=>
+ string(3) "1st"
+ [1]=>
+ string(9) "Changed 1"
+ [2]=>
+ string(3) "3rd"
+ ["4th"]=>
+ string(11) "Changed 4th"
+ ["5th"]=>
+ string(9) "Added 5th"
+ [6]=>
+ string(9) "changed 6"
+}
+object::offsetUnset(2)
+object::offsetUnset(4th)
+object::offsetUnset(7)
+object::offsetUnset(8th)
+array(4) {
+ [0]=>
+ string(3) "1st"
+ [1]=>
+ string(9) "Changed 1"
+ ["5th"]=>
+ string(9) "Added 5th"
+ [6]=>
+ string(9) "changed 6"
+}
+===DONE===
--- /dev/null
+--TEST--
+ZE2 ArrayAccess::offsetSet without return
+--SKIPIF--
+<?php
+ if (!class_exists('ArrayAccess')) die('skip ArrayAccess not present');
+?>
+--FILE--
+<?php
+class Object implements ArrayAccess {
+
+ public $a = array('1st', 1, 2=>'3rd', '4th'=>4);
+
+ function offsetExists($index) {
+ echo __METHOD__ . "($index)\n";
+ return array_key_exists($index, $this->a);
+ }
+ function offsetGet($index) {
+ echo __METHOD__ . "($index)\n";
+ return $this->a[$index];
+ }
+ function offsetSet($index, $newval) {
+ echo __METHOD__ . "($index,$newval)\n";
+ /*return*/ $this->a[$index] = $newval;
+ }
+ function offsetUnset($index) {
+ echo __METHOD__ . "($index)\n";
+ unset($this->a[$index]);
+ }
+}
+
+$obj = new Object;
+
+var_dump($obj->a);
+
+echo "===EMPTY===\n";
+var_dump(empty($obj[0]));
+var_dump(empty($obj[1]));
+var_dump(empty($obj[2]));
+var_dump(empty($obj['4th']));
+var_dump(empty($obj['5th']));
+var_dump(empty($obj[6]));
+
+echo "===isset===\n";
+var_dump(isset($obj[0]));
+var_dump(isset($obj[1]));
+var_dump(isset($obj[2]));
+var_dump(isset($obj['4th']));
+var_dump(isset($obj['5th']));
+var_dump(isset($obj[6]));
+
+echo "===offsetGet===\n";
+var_dump($obj[0]);
+var_dump($obj[1]);
+var_dump($obj[2]);
+var_dump($obj['4th']);
+var_dump($obj['5th']);
+var_dump($obj[6]);
+
+echo "===offsetSet===\n";
+echo "WRITE 1\n";
+$obj[1] = 'Changed 1';
+var_dump($obj[1]);
+echo "WRITE 2\n";
+$obj['4th'] = 'Changed 4th';
+var_dump($obj['4th']);
+echo "WRITE 3\n";
+$obj['5th'] = 'Added 5th';
+var_dump($obj['5th']);
+echo "WRITE 4\n";
+$obj[6] = 'Added 6';
+var_dump($obj[6]);
+
+var_dump($obj[0]);
+var_dump($obj[2]);
+
+$x = $obj[6] = 'changed 6';
+var_dump($obj[6]);
+var_dump($x);
+
+echo "===unset===\n";
+var_dump($obj->a);
+unset($obj[2]);
+unset($obj['4th']);
+unset($obj[7]);
+unset($obj['8th']);
+var_dump($obj->a);
+
+?>
+===DONE===
+--EXPECTF--
+array(4) {
+ [0]=>
+ string(3) "1st"
+ [1]=>
+ int(1)
+ [2]=>
+ string(3) "3rd"
+ ["4th"]=>
+ int(4)
+}
+===EMPTY===
+object::offsetExists(0)
+bool(false)
+object::offsetExists(1)
+bool(false)
+object::offsetExists(2)
+bool(false)
+object::offsetExists(4th)
+bool(false)
+object::offsetExists(5th)
+bool(true)
+object::offsetExists(6)
+bool(true)
+===isset===
+object::offsetExists(0)
+bool(true)
+object::offsetExists(1)
+bool(true)
+object::offsetExists(2)
+bool(true)
+object::offsetExists(4th)
+bool(true)
+object::offsetExists(5th)
+bool(false)
+object::offsetExists(6)
+bool(false)
+===offsetGet===
+object::offsetGet(0)
+string(3) "1st"
+object::offsetGet(1)
+int(1)
+object::offsetGet(2)
+string(3) "3rd"
+object::offsetGet(4th)
+int(4)
+object::offsetGet(5th)
+
+Notice: Undefined index: 5th in %sarray_access_002.php on line %d
+NULL
+object::offsetGet(6)
+
+Notice: Undefined offset: 6 in %sarray_access_002.php on line %d
+NULL
+===offsetSet===
+WRITE 1
+object::offsetSet(1,Changed 1)
+object::offsetGet(1)
+string(9) "Changed 1"
+WRITE 2
+object::offsetSet(4th,Changed 4th)
+object::offsetGet(4th)
+string(11) "Changed 4th"
+WRITE 3
+object::offsetSet(5th,Added 5th)
+object::offsetGet(5th)
+string(9) "Added 5th"
+WRITE 4
+object::offsetSet(6,Added 6)
+object::offsetGet(6)
+string(7) "Added 6"
+object::offsetGet(0)
+string(3) "1st"
+object::offsetGet(2)
+string(3) "3rd"
+object::offsetSet(6,changed 6)
+object::offsetGet(6)
+string(9) "changed 6"
+string(9) "changed 6"
+===unset===
+array(6) {
+ [0]=>
+ string(3) "1st"
+ [1]=>
+ string(9) "Changed 1"
+ [2]=>
+ string(3) "3rd"
+ ["4th"]=>
+ string(11) "Changed 4th"
+ ["5th"]=>
+ string(9) "Added 5th"
+ [6]=>
+ string(9) "changed 6"
+}
+object::offsetUnset(2)
+object::offsetUnset(4th)
+object::offsetUnset(7)
+object::offsetUnset(8th)
+array(4) {
+ [0]=>
+ string(3) "1st"
+ [1]=>
+ string(9) "Changed 1"
+ ["5th"]=>
+ string(9) "Added 5th"
+ [6]=>
+ string(9) "changed 6"
+}
+===DONE===
--- /dev/null
+--TEST--
+ZE2 ArrayAccess::offsetGet ambiguties
+--SKIPIF--
+<?php
+ if (!class_exists('ArrayAccess')) die('skip ArrayAccess not present');
+?>
+--FILE--
+<?php
+class Object implements ArrayAccess {
+
+ public $a = array('1st', 1, 2=>'3rd', '4th'=>4);
+
+ function offsetExists($index) {
+ echo __METHOD__ . "($index)\n";
+ return array_key_exists($index, $this->a);
+ }
+ function &offsetGet($index) {
+ echo __METHOD__ . "($index)\n";
+ switch($index) {
+ case 1:
+ $a = 'foo';
+ return $a . 'Bar';
+ case 2:
+ static $a=1;
+ return $a;
+ }
+ return $this->a[$index];
+ }
+ function offsetSet($index, $newval) {
+ echo __METHOD__ . "($index,$newval)\n";
+ if ($index==3) {
+ $this->cnt = $newval;
+ }
+ return $this->a[$index] = $newval;
+ }
+ function offsetUnset($index) {
+ echo __METHOD__ . "($index)\n";
+ unset($this->a[$index]);
+ }
+}
+
+$obj = new Object;
+
+var_dump($obj[1]);
+var_dump($obj[2]);
+$obj[2]++;
+var_dump($obj[2]);
+
+?>
+===DONE===
+--EXPECTF--
+object::offsetGet(1)
+string(6) "fooBar"
+object::offsetGet(2)
+int(1)
+object::offsetGet(2)
+object::offsetGet(2)
+int(2)
+===DONE===
--- /dev/null
+--TEST--
+ZE2 ArrayAccess::offsetGet ambiguties
+--SKIPIF--
+<?php
+ if (!class_exists('ArrayAccess')) die('skip ArrayAccess not present');
+?>
+--FILE--
+<?php
+class Object implements ArrayAccess {
+
+ public $a = array('1st', 1, 2=>'3rd', '4th'=>4);
+
+ function offsetExists($index) {
+ echo __METHOD__ . "($index)\n";
+ return array_key_exists($index, $this->a);
+ }
+ function offsetGet($index) {
+ echo __METHOD__ . "($index)\n";
+ switch($index) {
+ case 1:
+ $a = 'foo';
+ return $a . 'Bar';
+ case 2:
+ static $a=1;
+ return $a;
+ }
+ return $this->a[$index];
+ }
+ function offsetSet($index, $newval) {
+ echo __METHOD__ . "($index,$newval)\n";
+ if ($index==3) {
+ $this->cnt = $newval;
+ }
+ return $this->a[$index] = $newval;
+ }
+ function offsetUnset($index) {
+ echo __METHOD__ . "($index)\n";
+ unset($this->a[$index]);
+ }
+}
+
+$obj = new Object;
+
+var_dump($obj[1]);
+var_dump($obj[2]);
+$obj[2]++;
+var_dump($obj[2]);
+
+?>
+===DONE===
+--EXPECTF--
+object::offsetGet(1)
+string(6) "fooBar"
+object::offsetGet(2)
+int(1)
+object::offsetGet(2)
+
+Fatal error: Objects used as arrays in post/pre increment/decrement must return values by reference in %sarray_access_004.php on line %d