php-src/Zend/zend_interfaces.c
Zeev Suraski e7e0f7d4b4 - Check return-by-reference bit when implementing interface prototypes
- Add infrastructure for built-in functions to hint whether they
  return by reference or not.  It is NOT currently used for anything,
  except for interface prototypes (you can use it to request that the
  function that implements your prototype returns by reference or
  doesn't return by reference).
  For downwards compatibility - by default, interface prototypes are
  agnostic as to whether the function that implements them returns
  by reference or not.  Use ZEND_BEGIN_ARG_INFO_EX() with
  ZEND_RETURN_VALUE/ZEND_RETURN_REFERENCE to change that.
- Fix ArrayAccess::getOffset() to conduct additional checks.
  If your getOffset() should work with multidimensional arrays - it
  must return by reference.
2004-02-12 13:49:55 +00:00

441 lines
13 KiB
C
Executable File

/*
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2004 Zend Technologies Ltd. (http://www.zend.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 2.00 of the Zend 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.zend.com/license/2_00.txt. |
| If you did not receive a copy of the Zend license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@zend.com so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Marcus Boerger <helly@php.net> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#include "zend.h"
#include "zend_API.h"
#include "zend_interfaces.h"
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_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)
{
int result;
zend_fcall_info fci;
zval z_fname;
zval *retval;
zval **params[2];
params[0] = &arg1;
params[1] = &arg2;
fci.size = sizeof(fci);
/*fci.function_table = NULL; will be read form zend_class_entry of object if needed */
fci.object_pp = object_pp;
fci.function_name = &z_fname;
fci.retval_ptr_ptr = retval_ptr_ptr ? retval_ptr_ptr : &retval;
fci.param_count = param_count;
fci.params = params;
fci.no_separation = 1;
fci.symbol_table = NULL;
if (!fn_proxy && !obj_ce) {
/* no interest in caching and no information already present that is
* needed later inside zend_call_function. */
ZVAL_STRINGL(&z_fname, function_name, function_name_len, 0);
result = zend_call_function(&fci, NULL TSRMLS_CC);
} else {
zend_fcall_info_cache fcic;
fcic.initialized = 1;
if (!obj_ce) {
obj_ce = Z_OBJCE_PP(object_pp);
}
if (!fn_proxy || !*fn_proxy) {
if (zend_hash_find(&obj_ce->function_table, function_name, function_name_len+1, (void **) &fcic.function_handler) == FAILURE) {
/* error at c-level */
zend_error(E_CORE_ERROR, "Couldn't find implementation for method %s::%s", obj_ce->name, function_name);
}
if (fn_proxy) {
*fn_proxy = fcic.function_handler;
}
} else {
fcic.function_handler = *fn_proxy;
}
fcic.calling_scope = obj_ce;
fcic.object_pp = object_pp;
result = zend_call_function(&fci, &fcic TSRMLS_CC);
}
if (result == FAILURE) {
/* error at c-level */
if (!obj_ce) {
obj_ce = Z_OBJCE_PP(object_pp);
}
zend_error(E_CORE_ERROR, "Couldn't execute method %s::%s", obj_ce->name, function_name);
}
if (!retval_ptr_ptr) {
if (retval) {
zval_ptr_dtor(&retval);
}
return NULL;
}
return *retval_ptr_ptr;
}
/* }}} */
/* iterator interface, c-level functions used by engine */
/* {{{ zend_user_it_new_iterator */
static zval *zend_user_it_new_iterator(zend_class_entry *ce, zval *object TSRMLS_DC)
{
zval *retval;
return zend_call_method_with_0_params(&object, ce, &ce->iterator_funcs.zf_new_iterator, "getiterator", &retval);
}
/* }}} */
/* {{{ zend_user_it_dtor */
ZEND_API void zend_user_it_free_current(zend_object_iterator *_iter TSRMLS_DC)
{
zend_user_iterator *iter = (zend_user_iterator*)_iter;
if (iter->value) {
zval_ptr_dtor(&iter->value);
iter->value = NULL;
}
}
/* }}} */
/* {{{ zend_user_it_dtor */
static void zend_user_it_dtor(zend_object_iterator *_iter TSRMLS_DC)
{
zend_user_iterator *iter = (zend_user_iterator*)_iter;
zval *object = (zval*)iter->it.data;
if (iter->value) {
zval_ptr_dtor(&iter->value);
iter->value = NULL;
}
zval_ptr_dtor(&object);
efree(iter);
}
/* }}} */
/* {{{ zend_user_it_has_more */
static int zend_user_it_has_more(zend_object_iterator *_iter TSRMLS_DC)
{
if (_iter) {
zend_user_iterator *iter = (zend_user_iterator*)_iter;
zval *object = (zval*)iter->it.data;
zval *more;
int result;
zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_has_more, "hasmore", &more);
if (more) {
result = i_zend_is_true(more);
zval_ptr_dtor(&more);
return result ? SUCCESS : FAILURE;
}
}
return FAILURE;
}
/* }}} */
/* {{{ zend_user_it_get_current_data */
static void zend_user_it_get_current_data(zend_object_iterator *_iter, zval ***data TSRMLS_DC)
{
zend_user_iterator *iter = (zend_user_iterator*)_iter;
zval *object = (zval*)iter->it.data;
if (!iter->value) {
zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_current, "current", &iter->value);
}
*data = &iter->value;
}
/* }}} */
/* {{{ zend_user_it_get_current_key_default */
#if 0
static int zend_user_it_get_current_key_default(zend_object_iterator *_iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC)
{
*int_key = _iter->index;
return HASH_KEY_IS_LONG;
}
#endif
/* }}} */
/* {{{ zend_user_it_get_current_key */
static int zend_user_it_get_current_key(zend_object_iterator *_iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC)
{
zend_user_iterator *iter = (zend_user_iterator*)_iter;
zval *object = (zval*)iter->it.data;
zval *retval;
zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_key, "key", &retval);
switch (retval->type) {
default:
zend_error(E_WARNING, "Illegal type returned from %s::key()", iter->ce->name);
case IS_NULL:
*str_key = "";
*str_key_len = 0;
*int_key = 0;
zval_ptr_dtor(&retval);
return HASH_KEY_IS_LONG;
case IS_STRING:
*str_key = estrndup(retval->value.str.val, retval->value.str.len);
*str_key_len = retval->value.str.len+1;
*int_key = 0;
zval_ptr_dtor(&retval);
return HASH_KEY_IS_STRING;
case IS_DOUBLE:
case IS_RESOURCE:
case IS_BOOL:
case IS_LONG: {
if (retval->type == IS_DOUBLE) {
*int_key = (long)retval->value.dval;
} else {
*int_key = retval->value.lval;
}
}
zval_ptr_dtor(&retval);
return HASH_KEY_IS_LONG;
}
}
/* }}} */
/* {{{ zend_user_it_move_forward */
static void zend_user_it_move_forward(zend_object_iterator *_iter TSRMLS_DC)
{
zend_user_iterator *iter = (zend_user_iterator*)_iter;
zval *object = (zval*)iter->it.data;
if (iter->value) {
zval_ptr_dtor(&iter->value);
iter->value = NULL;
}
zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_next, "next", NULL);
}
/* }}} */
/* {{{ zend_user_it_rewind */
static void zend_user_it_rewind(zend_object_iterator *_iter TSRMLS_DC)
{
zend_user_iterator *iter = (zend_user_iterator*)_iter;
zval *object = (zval*)iter->it.data;
zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_rewind, "rewind", NULL);
}
/* }}} */
zend_object_iterator_funcs zend_interface_iterator_funcs_iterator = {
zend_user_it_dtor,
zend_user_it_has_more,
zend_user_it_get_current_data,
zend_user_it_get_current_key,
zend_user_it_move_forward,
zend_user_it_rewind
};
/* {{{ zend_user_it_get_iterator */
static zend_object_iterator *zend_user_it_get_iterator(zend_class_entry *ce, zval *object TSRMLS_DC)
{
zend_user_iterator *iterator = emalloc(sizeof(zend_user_iterator));
object->refcount++;
iterator->it.data = (void*)object;
iterator->it.funcs = ce->iterator_funcs.funcs;
iterator->ce = Z_OBJCE_P(object);
iterator->value = NULL;
return (zend_object_iterator*)iterator;
}
/* }}} */
/* {{{ zend_user_it_get_new_iterator */
static zend_object_iterator *zend_user_it_get_new_iterator(zend_class_entry *ce, zval *object TSRMLS_DC)
{
zval *iterator = zend_user_it_new_iterator(ce, object TSRMLS_CC);
zend_object_iterator *new_iterator;
zend_class_entry *ce_it = Z_TYPE_P(iterator) == IS_OBJECT ? Z_OBJCE_P(iterator) : NULL;
if (!ce || !ce_it || !ce_it->get_iterator) {
zend_error(E_WARNING, "Objects returned by %s::getIterator() must be traversable or implement interface Iterator", ce->name);
zval_ptr_dtor(&iterator);
return NULL;
}
new_iterator = ce_it->get_iterator(ce_it, iterator TSRMLS_CC);
zval_ptr_dtor(&iterator);
return new_iterator;
}
/* }}} */
/* {{{ zend_implement_traversable */
static int zend_implement_traversable(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
{
/* check that class_type is traversable at c-level or implements at least one of 'aggregate' and 'Iterator' */
int i;
if (class_type->get_iterator || (class_type->parent && class_type->parent->get_iterator)) {
return SUCCESS;
}
for (i = 0; i < class_type->num_interfaces; i++) {
if (class_type->interfaces[i] == zend_ce_aggregate || class_type->interfaces[i] == zend_ce_iterator) {
return SUCCESS;
}
}
zend_error(E_CORE_ERROR, "Class %s must implement interface %s as part of either %s or %s",
class_type->name,
zend_ce_traversable->name,
zend_ce_iterator->name,
zend_ce_aggregate->name);
return FAILURE;
}
/* }}} */
/* {{{ zend_implement_aggregate */
static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
{
if (class_type->get_iterator) {
if (class_type->type == ZEND_INTERNAL_CLASS) {
/* inheritance ensures the class has necessary userland methods */
return SUCCESS;
} else if (class_type->get_iterator != zend_user_it_get_new_iterator) {
/* c-level get_iterator cannot be changed */
return FAILURE;
}
}
class_type->iterator_funcs.zf_new_iterator = NULL;
class_type->get_iterator = zend_user_it_get_new_iterator;
return SUCCESS;
}
/* }}} */
/* {{{ zend_implement_iterator */
static int zend_implement_iterator(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
{
if (class_type->get_iterator && class_type->get_iterator != zend_user_it_get_iterator) {
if (class_type->type == ZEND_INTERNAL_CLASS) {
/* inheritance ensures the class has the necessary userland methods */
return SUCCESS;
} else if (class_type->get_iterator != zend_user_it_get_new_iterator) {
/* c-level get_iterator cannot be changed */
return FAILURE;
}
}
class_type->get_iterator = zend_user_it_get_iterator;
class_type->iterator_funcs.zf_has_more = NULL;
class_type->iterator_funcs.zf_current = NULL;
class_type->iterator_funcs.zf_key = NULL;
class_type->iterator_funcs.zf_next = NULL;
class_type->iterator_funcs.zf_rewind = NULL;
if (!class_type->iterator_funcs.funcs) {
class_type->iterator_funcs.funcs = &zend_interface_iterator_funcs_iterator;
}
return SUCCESS;
}
/* }}} */
/* {{{ zend_implement_arrayaccess */
static int zend_implement_arrayaccess(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
{
#if 0
/* get ht from ce */
if (ht->read_dimension != zend_std_read_dimension
|| ht->write_dimension != zend_std_write_dimension
|| ht->has_dimension != zend_std_has_dimension
|| ht->unset_dimension != zend_std_unset_dimension) {
return FAILURE;
}
#endif
return SUCCESS;
}
/* }}}*/
/* {{{ function tables */
zend_function_entry zend_funcs_aggregate[] = {
ZEND_ABSTRACT_ME(iterator, getIterator, NULL)
{NULL, NULL, NULL}
};
zend_function_entry zend_funcs_iterator[] = {
ZEND_ABSTRACT_ME(iterator, current, NULL)
ZEND_ABSTRACT_ME(iterator, next, NULL)
ZEND_ABSTRACT_ME(iterator, key, NULL)
ZEND_ABSTRACT_ME(iterator, hasMore, NULL)
ZEND_ABSTRACT_ME(iterator, rewind, NULL)
{NULL, NULL, 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) \
{\
zend_class_entry ce;\
INIT_CLASS_ENTRY(ce, # class_name_str, zend_funcs_ ## class_name) \
zend_ce_ ## class_name = zend_register_internal_interface(&ce TSRMLS_CC);\
zend_ce_ ## class_name->interface_gets_implemented = zend_implement_ ## class_name;\
}
#define REGISTER_ITERATOR_IMPLEMENT(class_name, interface_name) \
zend_class_implements(zend_ce_ ## class_name TSRMLS_CC, 1, zend_ce_ ## interface_name)
/* {{{ zend_register_interfaces */
ZEND_API void zend_register_interfaces(TSRMLS_D)
{
REGISTER_ITERATOR_INTERFACE(traversable, Traversable);
REGISTER_ITERATOR_INTERFACE(aggregate, IteratorAggregate);
REGISTER_ITERATOR_IMPLEMENT(aggregate, traversable);
REGISTER_ITERATOR_INTERFACE(iterator, Iterator);
REGISTER_ITERATOR_IMPLEMENT(iterator, traversable);
REGISTER_ITERATOR_INTERFACE(arrayaccess, ArrayAccess);
}
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/