|
|
|
@ -46,139 +46,32 @@ |
|
|
|
#define AI_PTR_2_PTR_PTR(ai) \ |
|
|
|
(ai).ptr_ptr = &((ai).ptr) |
|
|
|
|
|
|
|
/* {{{ spl_array_writer_default stuff */ |
|
|
|
typedef struct { |
|
|
|
zval *obj; |
|
|
|
zval *idx; |
|
|
|
int locked; |
|
|
|
} spl_array_writer_object; |
|
|
|
|
|
|
|
static zend_class_entry *spl_array_writer_default_get_class(zval *object TSRMLS_DC) |
|
|
|
{ |
|
|
|
#ifdef SPL_ARRAY_WRITE |
|
|
|
return spl_ce_array_writer_default; |
|
|
|
#else |
|
|
|
return (zend_class_entry *)1; /* force an error here: this ensures not equal */ |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
static zend_object_handlers spl_array_writer_default_handlers = { |
|
|
|
ZEND_OBJECTS_STORE_HANDLERS, |
|
|
|
|
|
|
|
NULL, /* read_property */ |
|
|
|
NULL, /* write_property */ |
|
|
|
NULL, /* get_property_ptr */ |
|
|
|
NULL, /* get_property_zval_ptr */ |
|
|
|
NULL, /* get */ |
|
|
|
NULL, /* set */ |
|
|
|
NULL, /* has_property */ |
|
|
|
NULL, /* unset_property */ |
|
|
|
NULL, /* get_properties */ |
|
|
|
NULL, /* get_method */ |
|
|
|
NULL, /* call_method */ |
|
|
|
NULL, /* get_constructor */ |
|
|
|
spl_array_writer_default_get_class, /* get_class_entry */ |
|
|
|
NULL, /* get_class_name */ |
|
|
|
NULL /* compare_objects */ |
|
|
|
}; |
|
|
|
/* }}} */ |
|
|
|
|
|
|
|
/* {{{ spl_array_writer_dtor */ |
|
|
|
void spl_array_writer_default_dtor(void *object, zend_object_handle handle TSRMLS_DC) |
|
|
|
{ |
|
|
|
spl_array_writer_object *writer = (spl_array_writer_object*) object; |
|
|
|
|
|
|
|
if (writer->obj) |
|
|
|
{ |
|
|
|
writer->obj->refcount--; |
|
|
|
/* DELETE_ZVAL(writer->obj); */ |
|
|
|
} |
|
|
|
if (writer->idx) |
|
|
|
{ |
|
|
|
if (writer->locked) { |
|
|
|
PZVAL_UNLOCK(writer->idx); |
|
|
|
} else { |
|
|
|
writer->idx->refcount--; |
|
|
|
DELETE_ZVAL(writer->idx); |
|
|
|
} |
|
|
|
} |
|
|
|
efree(writer); |
|
|
|
} |
|
|
|
/* }}} */ |
|
|
|
|
|
|
|
/* {{{ spl_array_writer_default_create */ |
|
|
|
zend_object_value spl_array_writer_default_create(zend_class_entry *class_type TSRMLS_DC) |
|
|
|
{ |
|
|
|
zend_object_value retval; |
|
|
|
spl_array_writer_object *intern; |
|
|
|
|
|
|
|
intern = ecalloc(sizeof(spl_array_writer_object), 1); |
|
|
|
|
|
|
|
retval.handle = zend_objects_store_put(intern, spl_array_writer_default_dtor, NULL TSRMLS_CC); |
|
|
|
retval.handlers = &spl_array_writer_default_handlers; |
|
|
|
|
|
|
|
return retval; |
|
|
|
} |
|
|
|
/* }}} */ |
|
|
|
|
|
|
|
/* {{{ spl_array_writer_default_set */ |
|
|
|
void spl_array_writer_default_set(zval *object, zval *newval, zval **retval TSRMLS_DC) |
|
|
|
/* {{{ spl_fetch_dimension_address */ |
|
|
|
int spl_fetch_dimension_address(znode *result, znode *op1, znode *op2, temp_variable *Ts, int type TSRMLS_DC) |
|
|
|
{ |
|
|
|
spl_array_writer_object *writer; |
|
|
|
zval **obj; |
|
|
|
zend_class_entry *obj_ce; |
|
|
|
spl_is_a is_a; |
|
|
|
|
|
|
|
writer = (spl_array_writer_object *) zend_object_store_get_object(object TSRMLS_CC); |
|
|
|
spl_begin_method_call_arg_ex2(&writer->obj, NULL, NULL, "set", sizeof("set")-1, retval, writer->idx, newval TSRMLS_CC); |
|
|
|
} |
|
|
|
/* }}} */ |
|
|
|
obj = spl_get_zval_ptr_ptr(op1, Ts TSRMLS_CC); |
|
|
|
|
|
|
|
/* {{{ SPL_CLASS_FUNCTION(array_writer_default, __construct) */ |
|
|
|
SPL_CLASS_FUNCTION(array_writer_default, __construct) |
|
|
|
{ |
|
|
|
zval *object = getThis(); |
|
|
|
zval *obj, *idx; |
|
|
|
spl_array_writer_object *writer; |
|
|
|
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &obj, &idx) == FAILURE) { |
|
|
|
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to parse parameters"); |
|
|
|
return; |
|
|
|
if (!obj || (obj_ce = spl_get_class_entry(*obj TSRMLS_CC)) == NULL) { |
|
|
|
return 1; |
|
|
|
} |
|
|
|
writer = (spl_array_writer_object *) zend_object_store_get_object(object TSRMLS_CC); |
|
|
|
writer->obj = obj; obj->refcount++; |
|
|
|
writer->idx = idx; idx->refcount++; |
|
|
|
} |
|
|
|
/* }}} */ |
|
|
|
|
|
|
|
/* {{{ SPL_CLASS_FUNCTION(array_writer_default, set) */ |
|
|
|
SPL_CLASS_FUNCTION(array_writer_default, set) |
|
|
|
{ |
|
|
|
zval *object = getThis(); |
|
|
|
zval *newval; |
|
|
|
is_a = spl_implements(obj_ce); |
|
|
|
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &newval) == FAILURE) { |
|
|
|
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to parse parameters"); |
|
|
|
return; |
|
|
|
} |
|
|
|
spl_array_writer_default_set(object, newval, &return_value TSRMLS_CC); |
|
|
|
} |
|
|
|
/* }}} */ |
|
|
|
|
|
|
|
/* {{{ spl_fetch_dimension_address */ |
|
|
|
int spl_fetch_dimension_address(znode *result, znode *op1, znode *op2, temp_variable *Ts, int type TSRMLS_DC) |
|
|
|
{ |
|
|
|
zval **container_ptr = spl_get_zval_ptr_ptr(op1, Ts TSRMLS_CC); |
|
|
|
|
|
|
|
if (spl_is_instance_of(container_ptr, spl_ce_array_read TSRMLS_CC)) { |
|
|
|
if (is_a & SPL_IS_A_ARRAY_READ) { |
|
|
|
zval **retval = &(T(result->u.var).var.ptr); |
|
|
|
zval *dim = spl_get_zval_ptr(op2, Ts, &EG(free_op2) TSRMLS_CC); |
|
|
|
zval *exists; |
|
|
|
|
|
|
|
/*ALLOC_ZVAL(exists); not needed */ |
|
|
|
spl_begin_method_call_arg_ex1(container_ptr, NULL, NULL, "exists", sizeof("exists")-1, &exists, dim TSRMLS_CC); |
|
|
|
spl_begin_method_call_arg_ex1(obj, obj_ce, NULL, "exists", sizeof("exists")-1, &exists, dim TSRMLS_CC); |
|
|
|
if (!i_zend_is_true(exists)) { |
|
|
|
if (type == BP_VAR_R || type == BP_VAR_RW) { |
|
|
|
SEPARATE_ZVAL(&dim); |
|
|
|
convert_to_string_ex(&dim); |
|
|
|
zend_error(E_NOTICE,"Undefined index: %s", Z_STRVAL_P(dim)); |
|
|
|
zend_error(E_NOTICE, "Undefined index: %s", Z_STRVAL_P(dim)); |
|
|
|
DELETE_ZVAL(dim); |
|
|
|
} |
|
|
|
if (type == BP_VAR_R || type == BP_VAR_IS) { |
|
|
|
@ -192,37 +85,8 @@ int spl_fetch_dimension_address(znode *result, znode *op1, znode *op2, temp_vari |
|
|
|
} |
|
|
|
DELETE_RET_ZVAL(exists); |
|
|
|
if (type == BP_VAR_R || type == BP_VAR_IS) { |
|
|
|
spl_begin_method_call_arg_ex1(container_ptr, NULL, NULL, "get", sizeof("get")-1, retval, dim TSRMLS_CC); |
|
|
|
(*retval)->refcount--; |
|
|
|
} else |
|
|
|
#ifdef SPL_ARRAY_WRITE |
|
|
|
if (spl_is_instance_of(container_ptr, spl_ce_array_access_ex TSRMLS_CC)) { |
|
|
|
/* array_access_ex instaces have their own way of creating an access_writer */ |
|
|
|
spl_begin_method_call_arg_ex1(container_ptr, NULL, NULL, "new_writer", sizeof("new_writer")-1, retval, dim TSRMLS_CC); |
|
|
|
T(result->u.var).var.ptr = *retval; |
|
|
|
AI_PTR_2_PTR_PTR(T(result->u.var).var); |
|
|
|
SELECTIVE_PZVAL_LOCK(*retval, result); |
|
|
|
} else if (spl_is_instance_of(container_ptr, spl_ce_array_access TSRMLS_CC)) { |
|
|
|
/* array_access instances create the default array_writer: array_write */ |
|
|
|
spl_array_writer_object *writer; |
|
|
|
spl_instanciate(spl_ce_array_writer_default, retval TSRMLS_CC); |
|
|
|
T(result->u.var).var.ptr = *retval; |
|
|
|
AI_PTR_2_PTR_PTR(T(result->u.var).var); |
|
|
|
writer = (spl_array_writer_object *) zend_object_store_get_object(*retval TSRMLS_CC); |
|
|
|
writer->obj = *container_ptr; |
|
|
|
writer->obj->refcount++; |
|
|
|
writer->idx = dim; |
|
|
|
PZVAL_LOCK(writer->idx); |
|
|
|
writer->locked = 1; |
|
|
|
SELECTIVE_PZVAL_LOCK(*retval, result); |
|
|
|
} else { |
|
|
|
zend_error(E_ERROR, "Object must implement spl_array_access for write access"); |
|
|
|
retval = &EG(error_zval_ptr); |
|
|
|
spl_begin_method_call_arg_ex1(obj, obj_ce, NULL, "get", sizeof("get")-1, retval, dim TSRMLS_CC); |
|
|
|
} |
|
|
|
SELECTIVE_PZVAL_LOCK(*retval, result); |
|
|
|
#else |
|
|
|
zend_error(E_ERROR, "SPL compiled without array write hook"); |
|
|
|
#endif |
|
|
|
FREE_OP(Ts, op2, EG(free_op2)); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
@ -279,66 +143,90 @@ ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_RW) |
|
|
|
#endif |
|
|
|
/* }}} */ |
|
|
|
|
|
|
|
/* {{{ ZEND_EXECUTE_HOOK_FUNCTION(ZEND_ASSIGN) */ |
|
|
|
#ifdef SPL_ARRAY_WRITE |
|
|
|
ZEND_EXECUTE_HOOK_FUNCTION(ZEND_ASSIGN) |
|
|
|
static inline zval **spl_get_obj_zval_ptr_ptr(znode *op, temp_variable *Ts, int type TSRMLS_DC) |
|
|
|
{ |
|
|
|
zval **writer = spl_get_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC); |
|
|
|
zval *newval, *retval, *target; |
|
|
|
znode *result; |
|
|
|
|
|
|
|
if (writer && *writer && Z_TYPE_PP(writer) == IS_OBJECT) { |
|
|
|
/* optimization: do pre checks and only test for handlers in case of |
|
|
|
* spl_array_writer_default, for spl_array_writer we must use the |
|
|
|
* long way of calling spl_instance |
|
|
|
* if (spl_is_instance_of(writer, spl_ce_array_writer_default TSRMLS_CC)) |
|
|
|
*/ |
|
|
|
if ((*writer)->value.obj.handlers == &spl_array_writer_default_handlers) { |
|
|
|
newval = spl_get_zval_ptr(&EX(opline)->op2, EX(Ts), &EG(free_op2) TSRMLS_CC); |
|
|
|
spl_array_writer_default_set(*writer, newval, &retval TSRMLS_CC); |
|
|
|
} else if (spl_is_instance_of(writer, spl_ce_array_writer TSRMLS_CC)) { |
|
|
|
newval = spl_get_zval_ptr(&EX(opline)->op2, EX(Ts), &EG(free_op2) TSRMLS_CC); |
|
|
|
spl_begin_method_call_arg_ex1(writer, NULL, NULL, "set", sizeof("set")-1, &retval, newval TSRMLS_CC); |
|
|
|
if (op->op_type == IS_UNUSED) { |
|
|
|
if (EG(This)) { |
|
|
|
/* this should actually never be modified, _ptr_ptr is modified only when |
|
|
|
the object is empty */ |
|
|
|
return &EG(This); |
|
|
|
} else { |
|
|
|
ZEND_EXECUTE_HOOK_ORIGINAL(ZEND_ASSIGN); |
|
|
|
zend_error(E_ERROR, "Using $this when not in object context"); |
|
|
|
} |
|
|
|
} else { |
|
|
|
ZEND_EXECUTE_HOOK_ORIGINAL(ZEND_ASSIGN); |
|
|
|
} |
|
|
|
spl_unlock_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC); |
|
|
|
|
|
|
|
result = &EX(opline)->result; |
|
|
|
if (result) { |
|
|
|
if (retval->refcount<2) { |
|
|
|
if ((*writer)->value.obj.handlers == &spl_array_writer_default_handlers) { |
|
|
|
spl_array_writer_object *object = (spl_array_writer_object *) zend_object_store_get_object(*writer TSRMLS_CC); |
|
|
|
target = object->obj; |
|
|
|
} else { |
|
|
|
target = *writer; |
|
|
|
} |
|
|
|
zend_error(E_WARNING, "Method %s::set() did not return a value, using NULL", Z_OBJCE_P(target)->name); |
|
|
|
DELETE_ZVAL(retval); |
|
|
|
DELETE_ZVAL(newval); |
|
|
|
/* Unfortunately it doesn't work when trying to return newval. |
|
|
|
* But anyhow it wouldn't make sense...and confuse reference counting and such. |
|
|
|
*/ |
|
|
|
retval = &EG(uninitialized_zval); |
|
|
|
} else { |
|
|
|
retval->refcount--; |
|
|
|
} |
|
|
|
EX_T(EX(opline)->result.u.var).var.ptr = retval; |
|
|
|
AI_PTR_2_PTR_PTR(EX_T(EX(opline)->result.u.var).var); |
|
|
|
SELECTIVE_PZVAL_LOCK(retval, result); |
|
|
|
} else { |
|
|
|
retval->refcount = 1; |
|
|
|
DELETE_ZVAL(retval); |
|
|
|
return spl_get_zval_ptr_ptr(op, Ts TSRMLS_CC); |
|
|
|
} |
|
|
|
|
|
|
|
/* {{{ ZEND_EXECUTE_HOOK_FUNCTION(ZEND_ASSIGN_DIM) */ |
|
|
|
#ifdef SPL_ARRAY_WRITE |
|
|
|
ZEND_EXECUTE_HOOK_FUNCTION(ZEND_ASSIGN_DIM) |
|
|
|
{ |
|
|
|
zval **obj; |
|
|
|
zend_class_entry *obj_ce; |
|
|
|
spl_is_a is_a; |
|
|
|
|
|
|
|
obj = spl_get_obj_zval_ptr_ptr(&EX(opline)->op1, EX(Ts), 0 TSRMLS_CC); |
|
|
|
|
|
|
|
if (!obj || (obj_ce = spl_get_class_entry(*obj TSRMLS_CC)) == NULL) { |
|
|
|
ZEND_EXECUTE_HOOK_ORIGINAL(ZEND_ASSIGN_DIM); |
|
|
|
} |
|
|
|
|
|
|
|
(*writer)->refcount = 1; |
|
|
|
DELETE_ZVAL(*writer); |
|
|
|
FREE_OP(EX(Ts), &EX(opline)->op2, EG(free_op2)); |
|
|
|
is_a = spl_implements(obj_ce); |
|
|
|
|
|
|
|
if (is_a & SPL_IS_A_ARRAY_ACCESS) { |
|
|
|
znode *op2 = &EX(opline)->op2; |
|
|
|
zval *index = spl_get_zval_ptr(op2, EX(Ts), &EG(free_op2), BP_VAR_R); |
|
|
|
zval *free_value; |
|
|
|
zend_op *value_op = EX(opline)+1; |
|
|
|
zval *value = spl_get_zval_ptr(&value_op->op1, EX(Ts), &free_value, BP_VAR_R); |
|
|
|
zval tmp; |
|
|
|
zval *retval; |
|
|
|
|
|
|
|
spl_unlock_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC); |
|
|
|
|
|
|
|
/* here we are sure we are dealing with an object */ |
|
|
|
switch (op2->op_type) { |
|
|
|
case IS_CONST: |
|
|
|
/* already a constant string */ |
|
|
|
break; |
|
|
|
case IS_VAR: |
|
|
|
tmp = *index; |
|
|
|
zval_copy_ctor(&tmp); |
|
|
|
convert_to_string(&tmp); |
|
|
|
index = &tmp; |
|
|
|
break; |
|
|
|
case IS_TMP_VAR: |
|
|
|
convert_to_string(index); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
/* separate our value if necessary */ |
|
|
|
if (value_op->op1.op_type == IS_TMP_VAR) { |
|
|
|
zval *orig_value = value; |
|
|
|
|
|
|
|
ALLOC_ZVAL(value); |
|
|
|
*value = *orig_value; |
|
|
|
value->is_ref = 0; |
|
|
|
value->refcount = 0; |
|
|
|
} |
|
|
|
|
|
|
|
spl_begin_method_call_arg_ex2(obj, obj_ce, NULL, "set", sizeof("set")-1, &retval, index, value TSRMLS_CC); |
|
|
|
|
|
|
|
NEXT_OPCODE(); |
|
|
|
if (index == &tmp) { |
|
|
|
zval_dtor(index); |
|
|
|
} |
|
|
|
|
|
|
|
FREE_OP(Ts, op2, EG(free_op2)); |
|
|
|
if (&EX(opline)->result) { |
|
|
|
EX_T(EX(opline)->result.u.var).var.ptr = retval; |
|
|
|
EX_T(EX(opline)->result.u.var).var.ptr_ptr = NULL;/*&EX_T(EX(opline)->result.u.var).var.ptr;*/ |
|
|
|
SELECTIVE_PZVAL_LOCK(retval, &EX(opline)->result); |
|
|
|
} |
|
|
|
|
|
|
|
EX(opline)++; |
|
|
|
NEXT_OPCODE(); |
|
|
|
} |
|
|
|
ZEND_EXECUTE_HOOK_ORIGINAL(ZEND_ASSIGN_DIM); |
|
|
|
} |
|
|
|
#endif |
|
|
|
/* }}} */ |
|
|
|
|