php-src/ext/spl/spl_array.c
2003-06-12 19:30:54 +00:00

347 lines
11 KiB
C
Executable File

/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
| 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. |
+----------------------------------------------------------------------+
| Authors: Marcus Boerger <helly@php.net> |
+----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "zend_compile.h"
#include "zend_execute_locks.h"
#include "php_spl.h"
#include "spl_functions.h"
#include "spl_engine.h"
#include "spl_array.h"
#define DELETE_ZVAL(z) \
if ((z)->refcount < 2) { \
zval_dtor(z); \
FREE_ZVAL(z); /* maybe safe_free_zval_ptr is needed for the uninitialised things */ \
}
#define DELETE_RET_ZVAL(z) \
if ((z)->refcount < 3) { \
zval_dtor(z); \
FREE_ZVAL(z); /* maybe safe_free_zval_ptr is needed for the uninitialised things */ \
}
#define AI_PTR_2_PTR_PTR(ai) \
(ai).ptr_ptr = &((ai).ptr)
/* {{{ spl_array_writer_default stuff */
typedef struct {
zval *obj;
zval *idx;
} 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)
{
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_array_writer_object *writer;
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);
}
/* }}} */
/* {{{ 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;
}
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;
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)) {
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);
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));
DELETE_ZVAL(dim);
}
if (type == BP_VAR_R || type == BP_VAR_IS) {
DELETE_RET_ZVAL(exists);
*retval = &EG(error_zval);
(*retval)->refcount++;
FREE_OP(Ts, op2, EG(free_op2));
SELECTIVE_PZVAL_LOCK(*retval, result);
return 0;
}
}
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; writer->idx->refcount++;
SELECTIVE_PZVAL_LOCK(*retval, result);
} else {
zend_error(E_ERROR, "Object must implement spl_array_access for write access");
retval = &EG(error_zval_ptr);
}
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;
}
return 1;
}
/* }}} */
/* {{{ ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_R) */
#ifdef SPL_ARRAY_READ
ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_R)
{
if (!spl_fetch_dimension_address(&EX(opline)->result, &EX(opline)->op1, &EX(opline)->op2, EX(Ts), BP_VAR_R TSRMLS_CC))
{
if (EX(opline)->extended_value == ZEND_FETCH_ADD_LOCK) {
PZVAL_LOCK(*EX_T(EX(opline)->op1.u.var).var.ptr_ptr);
}
spl_unlock_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC);
AI_PTR_2_PTR_PTR(EX_T(EX(opline)->result.u.var).var);
NEXT_OPCODE();
}
ZEND_EXECUTE_HOOK_ORIGINAL(ZEND_FETCH_DIM_R);
}
#endif
/* }}} */
/* {{{ ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_W) */
#ifdef SPL_ARRAY_READ
ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_W)
{
if (!spl_fetch_dimension_address(&EX(opline)->result, &EX(opline)->op1, &EX(opline)->op2, EX(Ts), BP_VAR_W TSRMLS_CC))
{
spl_unlock_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC);
NEXT_OPCODE();
}
ZEND_EXECUTE_HOOK_ORIGINAL(ZEND_FETCH_DIM_W);
}
#endif
/* }}} */
/* {{{ ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_RW) */
#ifdef SPL_ARRAY_READ
ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_RW)
{
if (!spl_fetch_dimension_address(&EX(opline)->result, &EX(opline)->op1, &EX(opline)->op2, EX(Ts), BP_VAR_RW TSRMLS_CC))
{
spl_unlock_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC);
NEXT_OPCODE();
}
ZEND_EXECUTE_HOOK_ORIGINAL(ZEND_FETCH_DIM_RW);
}
#endif
/* }}} */
/* {{{ ZEND_EXECUTE_HOOK_FUNCTION(ZEND_ASSIGN) */
#ifdef SPL_ARRAY_WRITE
ZEND_EXECUTE_HOOK_FUNCTION(ZEND_ASSIGN)
{
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);
} else {
ZEND_EXECUTE_HOOK_ORIGINAL(ZEND_ASSIGN);
}
} 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);
}
(*writer)->refcount = 1;
DELETE_ZVAL(*writer);
FREE_OP(EX(Ts), &EX(opline)->op2, EG(free_op2));
NEXT_OPCODE();
}
#endif
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: fdm=marker
* vim: noet sw=4 ts=4
*/