?? ??? 20??, PHP 5.3.0
- Added support for namespaces. (Dmitry, Stas)
- Added support for Late Static Binding. (Dmitry, Etienne Kneuss)
+- Added support for __callstatic() magic method. (Sara)
- Added support for dynamic access of static members using $foo::myFunc().
(Etienne Kneuss)
--- /dev/null
+--TEST--
+__callStatic() Magic method
+--FILE--
+<?php
+class Test
+{
+ static function __callStatic($fname, $args)
+ {
+ echo $fname, '() called with ', count($args), " arguments\n";
+ }
+}
+
+call_user_func("Test::Two", 'A', 'B');
+call_user_func(array("Test", "Three"), NULL, 0, false);
+Test::Four(5, 6, 7, 8);
+
+--EXPECT--
+two() called with 2 arguments
+three() called with 3 arguments
+four() called with 4 arguments
function __call($func, $args) {
$GLOBALS["y"] = $func;
}
+ static function __callStatic($func, $args) {
+ $GLOBALS["y"] = $func;
+ }
function offsetGet($index) {
$GLOBALS["y"] = $index;
}
echo $y,"\n";
$x->const_call();
echo $y,"\n";
+foo::const_callstatic();
+echo $y,"\n";
$z = $x["const_dim_get"];
echo $y,"\n";
$x["const_dim_set"] = 1;
const_get
const_set
const_call
+const_callstatic
const_dim_get
const_dim_set
const_dim_isset
union _zend_function *__unset;
union _zend_function *__isset;
union _zend_function *__call;
+ union _zend_function *__callstatic;
union _zend_function *__tostring;
union _zend_function *serialize_func;
union _zend_function *unserialize_func;
} else if (ARG_SHOULD_BE_SENT_BY_REF(fptr, 1) || ARG_SHOULD_BE_SENT_BY_REF(fptr, 2)) {
zend_error(error_type, "Method %s::%s() cannot take arguments by reference", ce->name, ZEND_CALL_FUNC_NAME);
}
+ } else if (name_len == sizeof(ZEND_CALLSTATIC_FUNC_NAME) - 1 &&
+ !memcmp(lcname, ZEND_CALLSTATIC_FUNC_NAME, sizeof(ZEND_CALLSTATIC_FUNC_NAME)-1)) {
+ if (fptr->common.num_args != 2) {
+ zend_error(error_type, "Method %s::%s() must take exactly 2 arguments", ce->name, ZEND_CALLSTATIC_FUNC_NAME);
+ } else if (ARG_SHOULD_BE_SENT_BY_REF(fptr, 1) || ARG_SHOULD_BE_SENT_BY_REF(fptr, 2)) {
+ zend_error(error_type, "Method %s::%s() cannot take arguments by reference", ce->name, ZEND_CALLSTATIC_FUNC_NAME);
+ }
+ } else if (name_len == sizeof(ZEND_TOSTRING_FUNC_NAME) - 1 &&
+ !memcmp(lcname, ZEND_TOSTRING_FUNC_NAME, sizeof(ZEND_TOSTRING_FUNC_NAME)-1) && fptr->common.num_args != 0) {
+ zend_error(error_type, "Method %s::%s() cannot take arguments", ce->name, ZEND_TOSTRING_FUNC_NAME);
}
}
int count=0, unload=0;
HashTable *target_function_table = function_table;
int error_type;
- zend_function *ctor = NULL, *dtor = NULL, *clone = NULL, *__get = NULL, *__set = NULL, *__unset = NULL, *__isset = NULL, *__call = NULL, *__tostring = NULL;
+ zend_function *ctor = NULL, *dtor = NULL, *clone = NULL, *__get = NULL, *__set = NULL, *__unset = NULL, *__isset = NULL, *__call = NULL, *__callstatic = NULL, *__tostring = NULL;
char *lowercase_name;
int fname_len;
char *lc_class_name = NULL;
clone = reg_function;
} else if ((fname_len == sizeof(ZEND_CALL_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME))) {
__call = reg_function;
+ } else if ((fname_len == sizeof(ZEND_CALLSTATIC_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_CALLSTATIC_FUNC_NAME, sizeof(ZEND_CALLSTATIC_FUNC_NAME))) {
+ __callstatic = reg_function;
} else if ((fname_len == sizeof(ZEND_TOSTRING_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_TOSTRING_FUNC_NAME, sizeof(ZEND_TOSTRING_FUNC_NAME))) {
__tostring = reg_function;
} else if ((fname_len == sizeof(ZEND_GET_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_GET_FUNC_NAME, sizeof(ZEND_GET_FUNC_NAME))) {
scope->destructor = dtor;
scope->clone = clone;
scope->__call = __call;
+ scope->__callstatic = __callstatic;
scope->__tostring = __tostring;
scope->__get = __get;
scope->__set = __set;
}
__call->common.fn_flags &= ~ZEND_ACC_ALLOW_STATIC;
}
+ if (__callstatic) {
+ if (!(__callstatic->common.fn_flags & ZEND_ACC_STATIC)) {
+ zend_error(error_type, "Method %s::%s() must be static", scope->name, __callstatic->common.function_name);
+ }
+ __callstatic->common.fn_flags |= ZEND_ACC_STATIC;
+ }
if (__tostring) {
if (__tostring->common.fn_flags & ZEND_ACC_STATIC) {
zend_error(error_type, "Method %s::%s() cannot be static", scope->name, __tostring->common.function_name);
if (*zobj_ptr_ptr && *ce_ptr && (*ce_ptr)->__call != 0) {
retval = (*ce_ptr)->__call != NULL;
*fptr_ptr = (*ce_ptr)->__call;
+ } else if (!*zobj_ptr_ptr && *ce_ptr && (*ce_ptr)->__callstatic) {
+ retval = 1;
+ *fptr_ptr = (*ce_ptr)->__callstatic;
}
} else {
*fptr_ptr = fptr;
ZEND_API int zend_fcall_info_init(zval *callable, zend_fcall_info *fci, zend_fcall_info_cache *fcc TSRMLS_DC)
{
+ int len;
zend_class_entry *ce;
zend_function *func;
zval **obj;
fci->no_separation = 1;
fci->symbol_table = NULL;
- if (strlen(func->common.function_name) == sizeof(ZEND_CALL_FUNC_NAME) - 1 && !memcmp(func->common.function_name, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME))) {
+ len = strlen(func->common.function_name);
+ if ((len == sizeof(ZEND_CALL_FUNC_NAME) - 1 &&
+ !memcmp(func->common.function_name, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME)-1)) ||
+ (len == sizeof(ZEND_CALLSTATIC_FUNC_NAME) - 1 &&
+ !memcmp(func->common.function_name, ZEND_CALLSTATIC_FUNC_NAME, sizeof(ZEND_CALLSTATIC_FUNC_NAME)-1))) {
fcc->initialized = 0;
fcc->function_handler = NULL;
fcc->calling_scope = NULL;
class_container.create_object = NULL; \
class_container.interface_gets_implemented = NULL; \
class_container.__call = handle_fcall; \
+ class_container.__callstatic = handle_fcall; \
class_container.__tostring = NULL; \
class_container.__get = handle_propget; \
class_container.__set = handle_propset; \
zend_error(E_STRICT, "Redefining already defined constructor for class %s", CG(active_class_entry)->name);
}
CG(active_class_entry)->constructor = (zend_function *) CG(active_op_array);
- } else if ((name_len == sizeof(ZEND_DESTRUCTOR_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_DESTRUCTOR_FUNC_NAME, sizeof(ZEND_DESTRUCTOR_FUNC_NAME)))) {
+ } else if ((name_len == sizeof(ZEND_DESTRUCTOR_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_DESTRUCTOR_FUNC_NAME, sizeof(ZEND_DESTRUCTOR_FUNC_NAME)-1))) {
CG(active_class_entry)->destructor = (zend_function *) CG(active_op_array);
- } else if ((name_len == sizeof(ZEND_CLONE_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_CLONE_FUNC_NAME, sizeof(ZEND_CLONE_FUNC_NAME)))) {
+ } else if ((name_len == sizeof(ZEND_CLONE_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_CLONE_FUNC_NAME, sizeof(ZEND_CLONE_FUNC_NAME)-1))) {
CG(active_class_entry)->clone = (zend_function *) CG(active_op_array);
- } else if ((name_len == sizeof(ZEND_CALL_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME)))) {
+ } else if ((name_len == sizeof(ZEND_CALL_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME)-1))) {
CG(active_class_entry)->__call = (zend_function *) CG(active_op_array);
- } else if ((name_len == sizeof(ZEND_GET_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_GET_FUNC_NAME, sizeof(ZEND_GET_FUNC_NAME)))) {
+ } else if ((name_len == sizeof(ZEND_CALLSTATIC_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_CALLSTATIC_FUNC_NAME, sizeof(ZEND_CALLSTATIC_FUNC_NAME)-1))) {
+ CG(active_class_entry)->__callstatic = (zend_function *) CG(active_op_array);
+ } else if ((name_len == sizeof(ZEND_GET_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_GET_FUNC_NAME, sizeof(ZEND_GET_FUNC_NAME)-1))) {
CG(active_class_entry)->__get = (zend_function *) CG(active_op_array);
- } else if ((name_len == sizeof(ZEND_SET_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_SET_FUNC_NAME, sizeof(ZEND_SET_FUNC_NAME)))) {
+ } else if ((name_len == sizeof(ZEND_SET_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_SET_FUNC_NAME, sizeof(ZEND_SET_FUNC_NAME)-1))) {
CG(active_class_entry)->__set = (zend_function *) CG(active_op_array);
- } else if ((name_len == sizeof(ZEND_UNSET_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_UNSET_FUNC_NAME, sizeof(ZEND_UNSET_FUNC_NAME)))) {
+ } else if ((name_len == sizeof(ZEND_UNSET_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_UNSET_FUNC_NAME, sizeof(ZEND_UNSET_FUNC_NAME)-1))) {
CG(active_class_entry)->__unset = (zend_function *) CG(active_op_array);
- } else if ((name_len == sizeof(ZEND_ISSET_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_ISSET_FUNC_NAME, sizeof(ZEND_ISSET_FUNC_NAME)))) {
+ } else if ((name_len == sizeof(ZEND_ISSET_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_ISSET_FUNC_NAME, sizeof(ZEND_ISSET_FUNC_NAME)-1))) {
CG(active_class_entry)->__isset = (zend_function *) CG(active_op_array);
- } else if ((name_len == sizeof(ZEND_TOSTRING_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_TOSTRING_FUNC_NAME, sizeof(ZEND_TOSTRING_FUNC_NAME)))) {
+ } else if ((name_len == sizeof(ZEND_TOSTRING_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_TOSTRING_FUNC_NAME, sizeof(ZEND_TOSTRING_FUNC_NAME)-1))) {
CG(active_class_entry)->__tostring = (zend_function *) CG(active_op_array);
} else if (!(fn_flags & ZEND_ACC_STATIC)) {
CG(active_op_array)->fn_flags |= ZEND_ACC_ALLOW_STATIC;
if (!ce->__call) {
ce->__call = ce->parent->__call;
}
+ if (!ce->__callstatic) {
+ ce->__callstatic = ce->parent->__callstatic;
+ }
if (!ce->__tostring) {
ce->__tostring = ce->parent->__tostring;
}
ce->__unset = NULL;
ce->__isset = NULL;
ce->__call = NULL;
+ ce->__callstatic = NULL;
ce->__tostring = NULL;
ce->create_object = NULL;
ce->get_iterator = NULL;
#define ZEND_UNSET_FUNC_NAME "__unset"
#define ZEND_ISSET_FUNC_NAME "__isset"
#define ZEND_CALL_FUNC_NAME "__call"
+#define ZEND_CALLSTATIC_FUNC_NAME "__callstatic"
#define ZEND_TOSTRING_FUNC_NAME "__tostring"
#define ZEND_AUTOLOAD_FUNC_NAME "__autoload"
return fbc;
}
+ZEND_API void zend_std_callstatic_user_call(INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
+{
+ zend_internal_function *func = (zend_internal_function *)EG(function_state_ptr)->function;
+ zval *method_name_ptr, *method_args_ptr;
+ zval *method_result_ptr = NULL;
+ zend_class_entry *ce = EG(scope);
+
+ ALLOC_ZVAL(method_args_ptr);
+ INIT_PZVAL(method_args_ptr);
+ array_init(method_args_ptr);
+
+ if (zend_copy_parameters_array(ZEND_NUM_ARGS(), method_args_ptr TSRMLS_CC) == FAILURE) {
+ zval_dtor(method_args_ptr);
+ zend_error(E_ERROR, "Cannot get arguments for " ZEND_CALLSTATIC_FUNC_NAME);
+ RETURN_FALSE;
+ }
+
+ ALLOC_ZVAL(method_name_ptr);
+ INIT_PZVAL(method_name_ptr);
+ ZVAL_STRING(method_name_ptr, func->function_name, 0); /* no dup - it's a copy */
+
+ /* __callStatic handler is called with two arguments:
+ method name
+ array of method parameters
+ */
+ zend_call_method_with_2_params(NULL, ce, &ce->__callstatic, ZEND_CALLSTATIC_FUNC_NAME, &method_result_ptr, method_name_ptr, method_args_ptr);
+
+ if (method_result_ptr) {
+ if (method_result_ptr->is_ref || method_result_ptr->refcount > 1) {
+ RETVAL_ZVAL(method_result_ptr, 1, 1);
+ } else {
+ RETVAL_ZVAL(method_result_ptr, 0, 1);
+ }
+ }
+
+ /* now destruct all auxiliaries */
+ zval_ptr_dtor(&method_args_ptr);
+ zval_ptr_dtor(&method_name_ptr);
+
+ /* destruct the function also, then - we have allocated it in get_method */
+ efree(func);
+}
+/* }}} */
/* This is not (yet?) in the API, but it belongs in the built-in objects callbacks */
ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, char *function_name_strval, int function_name_strlen TSRMLS_DC)
{
zend_function *fbc;
- if (zend_hash_find(&ce->function_table, function_name_strval, function_name_strlen+1, (void **) &fbc)==FAILURE) {
- char *class_name = ce->name;
-
- if (!class_name) {
- class_name = "";
+ if (zend_hash_find(&ce->function_table, function_name_strval, function_name_strlen + 1, (void **) &fbc)==FAILURE) {
+ if (ce->__callstatic) {
+ zend_internal_function *callstatic_user_call = emalloc(sizeof(zend_internal_function));
+
+ callstatic_user_call->type = ZEND_INTERNAL_FUNCTION;
+ callstatic_user_call->module = ce->module;
+ callstatic_user_call->handler = zend_std_callstatic_user_call;
+ callstatic_user_call->arg_info = NULL;
+ callstatic_user_call->num_args = 0;
+ callstatic_user_call->scope = ce;
+ callstatic_user_call->fn_flags = ZEND_ACC_STATIC | ZEND_ACC_PUBLIC;
+ callstatic_user_call->function_name = estrndup(function_name_strval, function_name_strlen);
+ callstatic_user_call->pass_rest_by_reference = 0;
+ callstatic_user_call->return_reference = ZEND_RETURN_VALUE;
+
+ return (zend_function *)callstatic_user_call;
+ } else {
+ zend_error(E_ERROR, "Call to undefined method %s::%s()", ce->name ? ce->name : "", function_name_strval);
}
- zend_error(E_ERROR, "Call to undefined method %s::%s()", class_name, function_name_strval);
}
if (fbc->op_array.fn_flags & ZEND_ACC_PUBLIC) {
/* No further checks necessary, most common case */