diff options
Diffstat (limited to 'ext/spl/php_spl.c')
-rw-r--r-- | ext/spl/php_spl.c | 716 |
1 files changed, 215 insertions, 501 deletions
diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index b147a96128..09c876b45f 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -1,7 +1,5 @@ /* +----------------------------------------------------------------------+ - | PHP Version 7 | - +----------------------------------------------------------------------+ | Copyright (c) The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | @@ -25,6 +23,7 @@ #include "php_main.h" #include "ext/standard/info.h" #include "php_spl.h" +#include "php_spl_arginfo.h" #include "spl_functions.h" #include "spl_engine.h" #include "spl_array.h" @@ -48,16 +47,11 @@ ZEND_DECLARE_MODULE_GLOBALS(spl) #define SPL_DEFAULT_FILE_EXTENSIONS ".inc,.php" -static zend_function *spl_autoload_fn = NULL; -static zend_function *spl_autoload_call_fn = NULL; - -/* {{{ PHP_GINIT_FUNCTION - */ +/* {{{ PHP_GINIT_FUNCTION */ static PHP_GINIT_FUNCTION(spl) { - spl_globals->autoload_extensions = NULL; - spl_globals->autoload_functions = NULL; - spl_globals->autoload_running = 0; + spl_globals->autoload_extensions = NULL; + spl_globals->autoload_functions = NULL; } /* }}} */ @@ -81,8 +75,7 @@ static zend_class_entry * spl_find_ce_by_name(zend_string *name, zend_bool autol return ce; } -/* {{{ proto array class_parents(object instance [, bool autoload = true]) - Return an array containing the names of all parent classes */ +/* {{{ Return an array containing the names of all parent classes */ PHP_FUNCTION(class_parents) { zval *obj; @@ -90,12 +83,12 @@ PHP_FUNCTION(class_parents) zend_bool autoload = 1; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &obj, &autoload) == FAILURE) { - RETURN_FALSE; + RETURN_THROWS(); } if (Z_TYPE_P(obj) != IS_OBJECT && Z_TYPE_P(obj) != IS_STRING) { - php_error_docref(NULL, E_WARNING, "object or string expected"); - RETURN_FALSE; + zend_argument_type_error(1, "must be of type object|string, %s given", zend_zval_type_name(obj)); + RETURN_THROWS(); } if (Z_TYPE_P(obj) == IS_STRING) { @@ -115,8 +108,7 @@ PHP_FUNCTION(class_parents) } /* }}} */ -/* {{{ proto array class_implements(mixed what [, bool autoload ]) - Return all classes and interfaces implemented by SPL */ +/* {{{ Return all classes and interfaces implemented by SPL */ PHP_FUNCTION(class_implements) { zval *obj; @@ -124,11 +116,11 @@ PHP_FUNCTION(class_implements) zend_class_entry *ce; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &obj, &autoload) == FAILURE) { - RETURN_FALSE; + RETURN_THROWS(); } if (Z_TYPE_P(obj) != IS_OBJECT && Z_TYPE_P(obj) != IS_STRING) { - php_error_docref(NULL, E_WARNING, "object or string expected"); - RETURN_FALSE; + zend_argument_type_error(1, "must be of type object|string, %s given", zend_zval_type_name(obj)); + RETURN_THROWS(); } if (Z_TYPE_P(obj) == IS_STRING) { @@ -144,8 +136,7 @@ PHP_FUNCTION(class_implements) } /* }}} */ -/* {{{ proto array class_uses(mixed what [, bool autoload ]) - Return all traits used by a class. */ +/* {{{ Return all traits used by a class. */ PHP_FUNCTION(class_uses) { zval *obj; @@ -153,11 +144,11 @@ PHP_FUNCTION(class_uses) zend_class_entry *ce; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &obj, &autoload) == FAILURE) { - RETURN_FALSE; + RETURN_THROWS(); } if (Z_TYPE_P(obj) != IS_OBJECT && Z_TYPE_P(obj) != IS_STRING) { - php_error_docref(NULL, E_WARNING, "object or string expected"); - RETURN_FALSE; + zend_argument_type_error(1, "must be of type object|string, %s given", zend_zval_type_name(obj)); + RETURN_THROWS(); } if (Z_TYPE_P(obj) == IS_STRING) { @@ -233,12 +224,11 @@ PHP_FUNCTION(class_uses) SPL_ADD_CLASS(UnderflowException, z_list, sub, allow, ce_flags); \ SPL_ADD_CLASS(UnexpectedValueException, z_list, sub, allow, ce_flags); \ -/* {{{ proto array spl_classes() - Return an array containing the names of all clsses and interfaces defined in SPL */ +/* {{{ Return an array containing the names of all clsses and interfaces defined in SPL */ PHP_FUNCTION(spl_classes) { if (zend_parse_parameters_none() == FAILURE) { - return; + RETURN_THROWS(); } array_init(return_value); @@ -305,16 +295,19 @@ static int spl_autoload(zend_string *class_name, zend_string *lc_name, const cha return 0; } /* }}} */ -/* {{{ proto void spl_autoload(string class_name [, string file_extensions]) - Default implementation for __autoload() */ +/* {{{ Default autoloader implementation */ PHP_FUNCTION(spl_autoload) { int pos_len, pos1_len; char *pos, *pos1; - zend_string *class_name, *lc_name, *file_exts = SPL_G(autoload_extensions); + zend_string *class_name, *lc_name, *file_exts = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|S!", &class_name, &file_exts) == FAILURE) { + RETURN_THROWS(); + } - if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|S", &class_name, &file_exts) == FAILURE) { - RETURN_FALSE; + if (!file_exts) { + file_exts = SPL_G(autoload_extensions); } if (file_exts == NULL) { /* autoload_extensions is not initialized, set to defaults */ @@ -342,15 +335,15 @@ PHP_FUNCTION(spl_autoload) zend_string_release(lc_name); } /* }}} */ -/* {{{ proto string spl_autoload_extensions([string file_extensions]) - Register and return default file extensions for spl_autoload */ +/* {{{ Register and return default file extensions for spl_autoload */ PHP_FUNCTION(spl_autoload_extensions) { zend_string *file_exts = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &file_exts) == FAILURE) { - return; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S!", &file_exts) == FAILURE) { + RETURN_THROWS(); } + if (file_exts) { if (SPL_G(autoload_extensions)) { zend_string_release_ex(SPL_G(autoload_extensions), 0); @@ -368,124 +361,109 @@ PHP_FUNCTION(spl_autoload_extensions) typedef struct { zend_function *func_ptr; - zval obj; - zval closure; + zend_object *obj; + zend_object *closure; zend_class_entry *ce; } autoload_func_info; -static void autoload_func_info_dtor(zval *element) -{ - autoload_func_info *alfi = (autoload_func_info*)Z_PTR_P(element); - if (!Z_ISUNDEF(alfi->obj)) { - zval_ptr_dtor(&alfi->obj); +static void autoload_func_info_destroy(autoload_func_info *alfi) { + if (alfi->obj) { + zend_object_release(alfi->obj); } if (alfi->func_ptr && UNEXPECTED(alfi->func_ptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { zend_string_release_ex(alfi->func_ptr->common.function_name, 0); zend_free_trampoline(alfi->func_ptr); } - if (!Z_ISUNDEF(alfi->closure)) { - zval_ptr_dtor(&alfi->closure); + if (alfi->closure) { + zend_object_release(alfi->closure); } efree(alfi); } -/* {{{ proto void spl_autoload_call(string class_name) - Try all registered autoload function to load the requested class */ -PHP_FUNCTION(spl_autoload_call) +static void autoload_func_info_zval_dtor(zval *element) { - zval *class_name, retval; - zend_string *lc_name, *func_name; - autoload_func_info *alfi; + autoload_func_info_destroy(Z_PTR_P(element)); +} - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &class_name) == FAILURE || Z_TYPE_P(class_name) != IS_STRING) { - return; +static autoload_func_info *autoload_func_info_from_fci( + zend_fcall_info *fci, zend_fcall_info_cache *fcc) { + autoload_func_info *alfi = emalloc(sizeof(autoload_func_info)); + alfi->ce = fcc->calling_scope; + alfi->func_ptr = fcc->function_handler; + alfi->obj = fcc->object; + if (alfi->obj) { + GC_ADDREF(alfi->obj); + } + if (Z_TYPE(fci->function_name) == IS_OBJECT) { + alfi->closure = Z_OBJ(fci->function_name); + GC_ADDREF(alfi->closure); + } else { + alfi->closure = NULL; } + return alfi; +} - if (SPL_G(autoload_functions)) { - HashPosition pos; - zend_ulong num_idx; - zend_function *func; - zend_fcall_info fci; - zend_fcall_info_cache fcic; - zend_class_entry *called_scope = zend_get_called_scope(execute_data); - int l_autoload_running = SPL_G(autoload_running); - - SPL_G(autoload_running) = 1; - lc_name = zend_string_tolower(Z_STR_P(class_name)); - - fci.size = sizeof(fci); - fci.retval = &retval; - fci.param_count = 1; - fci.params = class_name; - fci.no_separation = 1; - - ZVAL_UNDEF(&fci.function_name); /* Unused */ - - zend_hash_internal_pointer_reset_ex(SPL_G(autoload_functions), &pos); - while (zend_hash_get_current_key_ex(SPL_G(autoload_functions), &func_name, &num_idx, &pos) == HASH_KEY_IS_STRING) { - alfi = zend_hash_get_current_data_ptr_ex(SPL_G(autoload_functions), &pos); - func = alfi->func_ptr; - if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - func = emalloc(sizeof(zend_op_array)); - memcpy(func, alfi->func_ptr, sizeof(zend_op_array)); - zend_string_addref(func->op_array.function_name); - } - ZVAL_UNDEF(&retval); - fcic.function_handler = func; - if (Z_ISUNDEF(alfi->obj)) { - fci.object = NULL; - fcic.object = NULL; - if (alfi->ce && - (!called_scope || - !instanceof_function(called_scope, alfi->ce))) { - fcic.called_scope = alfi->ce; - } else { - fcic.called_scope = called_scope; - } - } else { - fci.object = Z_OBJ(alfi->obj); - fcic.object = Z_OBJ(alfi->obj); - fcic.called_scope = Z_OBJCE(alfi->obj); - } +static zend_bool autoload_func_info_equals( + const autoload_func_info *alfi1, const autoload_func_info *alfi2) { + return alfi1->func_ptr == alfi2->func_ptr + && alfi1->obj == alfi2->obj + && alfi1->ce == alfi2->ce + && alfi1->closure == alfi2->closure; +} - zend_call_function(&fci, &fcic); - zval_ptr_dtor(&retval); +static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_string *lc_name) { + if (!SPL_G(autoload_functions)) { + return NULL; + } - if (EG(exception)) { - break; - } + /* We don't use ZEND_HASH_FOREACH here, + * because autoloaders may be added/removed during autoloading. */ + HashPosition pos; + zend_hash_internal_pointer_reset_ex(SPL_G(autoload_functions), &pos); + while (1) { + autoload_func_info *alfi = + zend_hash_get_current_data_ptr_ex(SPL_G(autoload_functions), &pos); + if (!alfi) { + break; + } - if (pos + 1 == SPL_G(autoload_functions)->nNumUsed || - zend_hash_exists(EG(class_table), lc_name)) { - break; - } - zend_hash_move_forward_ex(SPL_G(autoload_functions), &pos); + zend_function *func = alfi->func_ptr; + if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + func = emalloc(sizeof(zend_op_array)); + memcpy(func, alfi->func_ptr, sizeof(zend_op_array)); + zend_string_addref(func->op_array.function_name); } - zend_string_release_ex(lc_name, 0); - SPL_G(autoload_running) = l_autoload_running; - } else { - /* do not use or overwrite &EG(autoload_func) here */ - zend_fcall_info fcall_info; - zend_fcall_info_cache fcall_cache; - - ZVAL_UNDEF(&retval); - - fcall_info.size = sizeof(fcall_info); - ZVAL_UNDEF(&fcall_info.function_name); - fcall_info.retval = &retval; - fcall_info.param_count = 1; - fcall_info.params = class_name; - fcall_info.object = NULL; - fcall_info.no_separation = 1; - - fcall_cache.function_handler = spl_autoload_fn; - fcall_cache.called_scope = NULL; - fcall_cache.object = NULL; - - zend_call_function(&fcall_info, &fcall_cache); - zval_ptr_dtor(&retval); + + zval param; + ZVAL_STR(¶m, class_name); + zend_call_known_function(func, alfi->obj, alfi->ce, NULL, 1, ¶m, NULL); + if (EG(exception)) { + break; + } + + zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name); + if (ce) { + return ce; + } + + zend_hash_move_forward_ex(SPL_G(autoload_functions), &pos); } + return NULL; +} + +/* {{{ Try all registered autoload function to load the requested class */ +PHP_FUNCTION(spl_autoload_call) +{ + zend_string *class_name; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &class_name) == FAILURE) { + RETURN_THROWS(); + } + + zend_string *lc_name = zend_string_tolower(class_name); + spl_perform_autoload(class_name, lc_name); + zend_string_release(lc_name); } /* }}} */ #define HT_MOVE_TAIL_TO_HEAD(ht) \ @@ -497,339 +475,172 @@ PHP_FUNCTION(spl_autoload_call) zend_hash_rehash(ht); \ } while (0) -/* {{{ proto bool spl_autoload_register([mixed autoload_function [, bool throw [, bool prepend]]]) - Register given function as __autoload() implementation */ +static Bucket *spl_find_registered_function(autoload_func_info *find_alfi) { + if (!SPL_G(autoload_functions)) { + return NULL; + } + + autoload_func_info *alfi; + ZEND_HASH_FOREACH_PTR(SPL_G(autoload_functions), alfi) { + if (autoload_func_info_equals(alfi, find_alfi)) { + return _p; + } + } ZEND_HASH_FOREACH_END(); + return NULL; +} + +/* {{{ Register given function as autoloader */ PHP_FUNCTION(spl_autoload_register) { - zend_string *func_name; - char *error = NULL; - zend_string *lc_name; - zval *zcallable = NULL; zend_bool do_throw = 1; zend_bool prepend = 0; - zend_function *spl_func_ptr; - autoload_func_info alfi; - zend_object *obj_ptr; + zend_fcall_info fci = {0}; zend_fcall_info_cache fcc; + autoload_func_info *alfi; - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "|zbb", &zcallable, &do_throw, &prepend) == FAILURE) { - return; - } - - if (ZEND_NUM_ARGS()) { - if (!zend_is_callable_ex(zcallable, NULL, IS_CALLABLE_STRICT, &func_name, &fcc, &error)) { - alfi.ce = fcc.calling_scope; - alfi.func_ptr = fcc.function_handler; - obj_ptr = fcc.object; - if (Z_TYPE_P(zcallable) == IS_ARRAY) { - if (!obj_ptr && alfi.func_ptr && !(alfi.func_ptr->common.fn_flags & ZEND_ACC_STATIC)) { - if (do_throw) { - zend_throw_exception_ex(spl_ce_LogicException, 0, "Passed array specifies a non static method but no object (%s)", error); - } - if (error) { - efree(error); - } - zend_string_release_ex(func_name, 0); - RETURN_FALSE; - } else if (do_throw) { - zend_throw_exception_ex(spl_ce_LogicException, 0, "Passed array does not specify %s %smethod (%s)", alfi.func_ptr ? "a callable" : "an existing", !obj_ptr ? "static " : "", error); - } - if (error) { - efree(error); - } - zend_string_release_ex(func_name, 0); - RETURN_FALSE; - } else if (Z_TYPE_P(zcallable) == IS_STRING) { - if (do_throw) { - zend_throw_exception_ex(spl_ce_LogicException, 0, "Function '%s' not %s (%s)", ZSTR_VAL(func_name), alfi.func_ptr ? "callable" : "found", error); - } - if (error) { - efree(error); - } - zend_string_release_ex(func_name, 0); - RETURN_FALSE; - } else { - if (do_throw) { - zend_throw_exception_ex(spl_ce_LogicException, 0, "Illegal value passed (%s)", error); - } - if (error) { - efree(error); - } - zend_string_release_ex(func_name, 0); - RETURN_FALSE; - } - } else if (fcc.function_handler->type == ZEND_INTERNAL_FUNCTION && - fcc.function_handler->internal_function.handler == zif_spl_autoload_call) { - if (do_throw) { - zend_throw_exception_ex(spl_ce_LogicException, 0, "Function spl_autoload_call() cannot be registered"); - } - if (error) { - efree(error); - } - zend_string_release_ex(func_name, 0); - RETURN_FALSE; - } - alfi.ce = fcc.calling_scope; - alfi.func_ptr = fcc.function_handler; - obj_ptr = fcc.object; - if (error) { - efree(error); - } - - if (Z_TYPE_P(zcallable) == IS_OBJECT) { - ZVAL_COPY(&alfi.closure, zcallable); - - lc_name = zend_string_alloc(ZSTR_LEN(func_name) + sizeof(uint32_t), 0); - zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(func_name), ZSTR_LEN(func_name)); - memcpy(ZSTR_VAL(lc_name) + ZSTR_LEN(func_name), &Z_OBJ_HANDLE_P(zcallable), sizeof(uint32_t)); - ZSTR_VAL(lc_name)[ZSTR_LEN(lc_name)] = '\0'; - } else { - ZVAL_UNDEF(&alfi.closure); - /* Skip leading \ */ - if (ZSTR_VAL(func_name)[0] == '\\') { - lc_name = zend_string_alloc(ZSTR_LEN(func_name) - 1, 0); - zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(func_name) + 1, ZSTR_LEN(func_name) - 1); - } else { - lc_name = zend_string_tolower(func_name); - } - } - zend_string_release_ex(func_name, 0); + ZEND_PARSE_PARAMETERS_START(0, 3) + Z_PARAM_OPTIONAL + Z_PARAM_FUNC_OR_NULL(fci, fcc) + Z_PARAM_BOOL(do_throw) + Z_PARAM_BOOL(prepend) + ZEND_PARSE_PARAMETERS_END(); - if (SPL_G(autoload_functions) && zend_hash_exists(SPL_G(autoload_functions), lc_name)) { - if (!Z_ISUNDEF(alfi.closure)) { - Z_DELREF_P(&alfi.closure); - } - goto skip; - } + if (!do_throw) { + php_error_docref(NULL, E_NOTICE, "Argument #2 ($do_throw) has been ignored, " + "spl_autoload_register() will always throw"); + } - if (obj_ptr && !(alfi.func_ptr->common.fn_flags & ZEND_ACC_STATIC)) { - /* add object id to the hash to ensure uniqueness, for more reference look at bug #40091 */ - lc_name = zend_string_extend(lc_name, ZSTR_LEN(lc_name) + sizeof(uint32_t), 0); - memcpy(ZSTR_VAL(lc_name) + ZSTR_LEN(lc_name) - sizeof(uint32_t), &obj_ptr->handle, sizeof(uint32_t)); - ZSTR_VAL(lc_name)[ZSTR_LEN(lc_name)] = '\0'; - ZVAL_OBJ(&alfi.obj, obj_ptr); - Z_ADDREF(alfi.obj); - } else { - ZVAL_UNDEF(&alfi.obj); - } + if (!SPL_G(autoload_functions)) { + ALLOC_HASHTABLE(SPL_G(autoload_functions)); + zend_hash_init(SPL_G(autoload_functions), 1, NULL, autoload_func_info_zval_dtor, 0); + /* Initialize as non-packed hash table for prepend functionality. */ + zend_hash_real_init_mixed(SPL_G(autoload_functions)); + } - if (!SPL_G(autoload_functions)) { - ALLOC_HASHTABLE(SPL_G(autoload_functions)); - zend_hash_init(SPL_G(autoload_functions), 1, NULL, autoload_func_info_dtor, 0); + /* If first arg is not null */ + if (ZEND_FCI_INITIALIZED(fci)) { + if (!fcc.function_handler) { + /* Call trampoline has been cleared by zpp. Refetch it, because we want to deal + * with it outselves. It is important that it is not refetched on every call, + * because calls may occur from different scopes. */ + zend_is_callable_ex(&fci.function_name, NULL, 0, NULL, &fcc, NULL); } - spl_func_ptr = spl_autoload_fn; - - if (EG(autoload_func) == spl_func_ptr) { /* registered already, so we insert that first */ - autoload_func_info spl_alfi; - - spl_alfi.func_ptr = spl_func_ptr; - ZVAL_UNDEF(&spl_alfi.obj); - ZVAL_UNDEF(&spl_alfi.closure); - spl_alfi.ce = NULL; - zend_hash_add_mem(SPL_G(autoload_functions), spl_autoload_fn->common.function_name, - &spl_alfi, sizeof(autoload_func_info)); - if (prepend && SPL_G(autoload_functions)->nNumOfElements > 1) { - /* Move the newly created element to the head of the hashtable */ - HT_MOVE_TAIL_TO_HEAD(SPL_G(autoload_functions)); - } + if (fcc.function_handler->type == ZEND_INTERNAL_FUNCTION && + fcc.function_handler->internal_function.handler == zif_spl_autoload_call) { + zend_argument_value_error(1, "must not be the spl_autoload_call() function"); + RETURN_THROWS(); } - if (UNEXPECTED(alfi.func_ptr == &EG(trampoline))) { + alfi = autoload_func_info_from_fci(&fci, &fcc); + if (UNEXPECTED(alfi->func_ptr == &EG(trampoline))) { zend_function *copy = emalloc(sizeof(zend_op_array)); - memcpy(copy, alfi.func_ptr, sizeof(zend_op_array)); - alfi.func_ptr->common.function_name = NULL; - alfi.func_ptr = copy; - } - if (zend_hash_add_mem(SPL_G(autoload_functions), lc_name, &alfi, sizeof(autoload_func_info)) == NULL) { - if (obj_ptr && !(alfi.func_ptr->common.fn_flags & ZEND_ACC_STATIC)) { - Z_DELREF(alfi.obj); - } - if (!Z_ISUNDEF(alfi.closure)) { - Z_DELREF(alfi.closure); - } - if (UNEXPECTED(alfi.func_ptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - zend_string_release_ex(alfi.func_ptr->common.function_name, 0); - zend_free_trampoline(alfi.func_ptr); - } - } - if (prepend && SPL_G(autoload_functions)->nNumOfElements > 1) { - /* Move the newly created element to the head of the hashtable */ - HT_MOVE_TAIL_TO_HEAD(SPL_G(autoload_functions)); + memcpy(copy, alfi->func_ptr, sizeof(zend_op_array)); + alfi->func_ptr->common.function_name = NULL; + alfi->func_ptr = copy; } -skip: - zend_string_release_ex(lc_name, 0); + } else { + alfi = emalloc(sizeof(autoload_func_info)); + alfi->func_ptr = zend_hash_str_find_ptr( + CG(function_table), "spl_autoload", sizeof("spl_autoload") - 1); + alfi->obj = NULL; + alfi->ce = NULL; + alfi->closure = NULL; } - if (SPL_G(autoload_functions)) { - EG(autoload_func) = spl_autoload_call_fn; - } else { - EG(autoload_func) = spl_autoload_fn; + if (spl_find_registered_function(alfi)) { + autoload_func_info_destroy(alfi); + RETURN_TRUE; + } + + zend_hash_next_index_insert_ptr(SPL_G(autoload_functions), alfi); + if (prepend && SPL_G(autoload_functions)->nNumOfElements > 1) { + /* Move the newly created element to the head of the hashtable */ + HT_MOVE_TAIL_TO_HEAD(SPL_G(autoload_functions)); } RETURN_TRUE; } /* }}} */ -/* {{{ proto bool spl_autoload_unregister(mixed autoload_function) - Unregister given function as __autoload() implementation */ +/* {{{ Unregister given function as autoloader */ PHP_FUNCTION(spl_autoload_unregister) { - zend_string *func_name = NULL; - char *error = NULL; - zend_string *lc_name; - zval *zcallable; - int success = FAILURE; - zend_function *spl_func_ptr; - zend_object *obj_ptr; + zend_fcall_info fci; zend_fcall_info_cache fcc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zcallable) == FAILURE) { - return; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "f", &fci, &fcc) == FAILURE) { + RETURN_THROWS(); } - if (!zend_is_callable_ex(zcallable, NULL, IS_CALLABLE_CHECK_SYNTAX_ONLY, &func_name, &fcc, &error)) { - zend_throw_exception_ex(spl_ce_LogicException, 0, "Unable to unregister invalid function (%s)", error); - if (error) { - efree(error); - } - if (func_name) { - zend_string_release_ex(func_name, 0); - } - RETURN_FALSE; - } - obj_ptr = fcc.object; - if (error) { - efree(error); + if (fcc.function_handler && zend_string_equals_literal( + fcc.function_handler->common.function_name, "spl_autoload_call")) { + /* Don't destroy the hash table, as we might be iterating over it right now. */ + zend_hash_clean(SPL_G(autoload_functions)); + RETURN_TRUE; } - if (Z_TYPE_P(zcallable) == IS_OBJECT) { - lc_name = zend_string_alloc(ZSTR_LEN(func_name) + sizeof(uint32_t), 0); - zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(func_name), ZSTR_LEN(func_name)); - memcpy(ZSTR_VAL(lc_name) + ZSTR_LEN(func_name), &Z_OBJ_HANDLE_P(zcallable), sizeof(uint32_t)); - ZSTR_VAL(lc_name)[ZSTR_LEN(lc_name)] = '\0'; - } else { - /* Skip leading \ */ - if (ZSTR_VAL(func_name)[0] == '\\') { - lc_name = zend_string_alloc(ZSTR_LEN(func_name) - 1, 0); - zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(func_name) + 1, ZSTR_LEN(func_name) - 1); - } else { - lc_name = zend_string_tolower(func_name); - } + autoload_func_info *alfi = autoload_func_info_from_fci(&fci, &fcc); + Bucket *p = spl_find_registered_function(alfi); + autoload_func_info_destroy(alfi); + if (p) { + zend_hash_del_bucket(SPL_G(autoload_functions), p); + RETURN_TRUE; } - zend_string_release_ex(func_name, 0); - if (SPL_G(autoload_functions)) { - if (zend_string_equals(lc_name, spl_autoload_call_fn->common.function_name)) { - /* remove all */ - if (!SPL_G(autoload_running)) { - zend_hash_destroy(SPL_G(autoload_functions)); - FREE_HASHTABLE(SPL_G(autoload_functions)); - SPL_G(autoload_functions) = NULL; - EG(autoload_func) = NULL; - } else { - zend_hash_clean(SPL_G(autoload_functions)); - } - success = SUCCESS; - } else { - /* remove specific */ - success = zend_hash_del(SPL_G(autoload_functions), lc_name); - if (success != SUCCESS && obj_ptr) { - lc_name = zend_string_extend(lc_name, ZSTR_LEN(lc_name) + sizeof(uint32_t), 0); - memcpy(ZSTR_VAL(lc_name) + ZSTR_LEN(lc_name) - sizeof(uint32_t), &obj_ptr->handle, sizeof(uint32_t)); - ZSTR_VAL(lc_name)[ZSTR_LEN(lc_name)] = '\0'; - success = zend_hash_del(SPL_G(autoload_functions), lc_name); - } - } - } else if (zend_string_equals(lc_name, spl_autoload_fn->common.function_name)) { - /* register single spl_autoload() */ - spl_func_ptr = spl_autoload_fn; - - if (EG(autoload_func) == spl_func_ptr) { - success = SUCCESS; - EG(autoload_func) = NULL; - } - } - - zend_string_release_ex(lc_name, 0); - RETURN_BOOL(success == SUCCESS); + RETURN_FALSE; } /* }}} */ -/* {{{ proto false|array spl_autoload_functions() - Return all registered __autoload() functionns */ +/* {{{ Return all registered autoloader functions */ PHP_FUNCTION(spl_autoload_functions) { - zend_function *fptr; autoload_func_info *alfi; if (zend_parse_parameters_none() == FAILURE) { - return; - } - - if (!EG(autoload_func)) { - if ((fptr = zend_hash_find_ptr(EG(function_table), ZSTR_KNOWN(ZEND_STR_MAGIC_AUTOLOAD)))) { - zval tmp; - - array_init(return_value); - ZVAL_STR_COPY(&tmp, ZSTR_KNOWN(ZEND_STR_MAGIC_AUTOLOAD)); - zend_hash_next_index_insert_new(Z_ARR_P(return_value), &tmp); - return; - } - RETURN_FALSE; + RETURN_THROWS(); } - fptr = spl_autoload_call_fn; - - if (EG(autoload_func) == fptr) { - zend_string *key; - array_init(return_value); - ZEND_HASH_FOREACH_STR_KEY_PTR(SPL_G(autoload_functions), key, alfi) { - if (!Z_ISUNDEF(alfi->closure)) { - Z_ADDREF(alfi->closure); - add_next_index_zval(return_value, &alfi->closure); + array_init(return_value); + if (SPL_G(autoload_functions)) { + ZEND_HASH_FOREACH_PTR(SPL_G(autoload_functions), alfi) { + if (alfi->closure) { + zval obj_zv; + ZVAL_OBJ_COPY(&obj_zv, alfi->closure); + add_next_index_zval(return_value, &obj_zv); } else if (alfi->func_ptr->common.scope) { zval tmp; array_init(&tmp); - if (!Z_ISUNDEF(alfi->obj)) { - Z_ADDREF(alfi->obj); - add_next_index_zval(&tmp, &alfi->obj); + if (alfi->obj) { + zval obj_zv; + ZVAL_OBJ_COPY(&obj_zv, alfi->obj); + add_next_index_zval(&tmp, &obj_zv); } else { add_next_index_str(&tmp, zend_string_copy(alfi->ce->name)); } add_next_index_str(&tmp, zend_string_copy(alfi->func_ptr->common.function_name)); add_next_index_zval(return_value, &tmp); } else { - if (strncmp(ZSTR_VAL(alfi->func_ptr->common.function_name), "__lambda_func", sizeof("__lambda_func") - 1)) { - add_next_index_str(return_value, zend_string_copy(alfi->func_ptr->common.function_name)); - } else { - add_next_index_str(return_value, zend_string_copy(key)); - } + add_next_index_str(return_value, zend_string_copy(alfi->func_ptr->common.function_name)); } } ZEND_HASH_FOREACH_END(); - return; } - - array_init(return_value); - add_next_index_str(return_value, zend_string_copy(EG(autoload_func)->common.function_name)); } /* }}} */ -/* {{{ proto string spl_object_hash(object obj) - Return hash id for given object */ +/* {{{ Return hash id for given object */ PHP_FUNCTION(spl_object_hash) { zval *obj; if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) { - return; + RETURN_THROWS(); } RETURN_NEW_STR(php_spl_object_hash(obj)); } /* }}} */ -/* {{{ proto int spl_object_id(object obj) - Returns the integer object handle for the given object */ +/* {{{ Returns the integer object handle for the given object */ PHP_FUNCTION(spl_object_id) { zval *obj; @@ -868,8 +679,7 @@ static void spl_build_class_list_string(zval *entry, char **list) /* {{{ */ *list = res; } /* }}} */ -/* {{{ PHP_MINFO(spl) - */ +/* {{{ PHP_MINFO(spl) */ PHP_MINFO_FUNCTION(spl) { zval list, *zv; @@ -902,102 +712,11 @@ PHP_MINFO_FUNCTION(spl) } /* }}} */ -/* {{{ arginfo */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_iterator_to_array, 0, 0, 1) - ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0) - ZEND_ARG_INFO(0, use_keys) -ZEND_END_ARG_INFO(); - -ZEND_BEGIN_ARG_INFO(arginfo_iterator, 0) - ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0) -ZEND_END_ARG_INFO(); - -ZEND_BEGIN_ARG_INFO_EX(arginfo_iterator_apply, 0, 0, 2) - ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0) - ZEND_ARG_INFO(0, function) - ZEND_ARG_ARRAY_INFO(0, args, 1) -ZEND_END_ARG_INFO(); - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_parents, 0, 0, 1) - ZEND_ARG_INFO(0, instance) - ZEND_ARG_INFO(0, autoload) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_implements, 0, 0, 1) - ZEND_ARG_INFO(0, what) - ZEND_ARG_INFO(0, autoload) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_uses, 0, 0, 1) - ZEND_ARG_INFO(0, what) - ZEND_ARG_INFO(0, autoload) -ZEND_END_ARG_INFO() - - -ZEND_BEGIN_ARG_INFO(arginfo_spl_classes, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_spl_autoload_functions, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload, 0, 0, 1) - ZEND_ARG_INFO(0, class_name) - ZEND_ARG_INFO(0, file_extensions) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_extensions, 0, 0, 0) - ZEND_ARG_INFO(0, file_extensions) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_call, 0, 0, 1) - ZEND_ARG_INFO(0, class_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_register, 0, 0, 0) - ZEND_ARG_INFO(0, autoload_function) - ZEND_ARG_INFO(0, throw) - ZEND_ARG_INFO(0, prepend) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_unregister, 0, 0, 1) - ZEND_ARG_INFO(0, autoload_function) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_object_hash, 0, 0, 1) - ZEND_ARG_INFO(0, obj) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_object_id, 0, 0, 1) - ZEND_ARG_INFO(0, obj) -ZEND_END_ARG_INFO() -/* }}} */ - -/* {{{ spl_functions - */ -static const zend_function_entry spl_functions[] = { - PHP_FE(spl_classes, arginfo_spl_classes) - PHP_FE(spl_autoload, arginfo_spl_autoload) - PHP_FE(spl_autoload_extensions, arginfo_spl_autoload_extensions) - PHP_FE(spl_autoload_register, arginfo_spl_autoload_register) - PHP_FE(spl_autoload_unregister, arginfo_spl_autoload_unregister) - PHP_FE(spl_autoload_functions, arginfo_spl_autoload_functions) - PHP_FE(spl_autoload_call, arginfo_spl_autoload_call) - PHP_FE(class_parents, arginfo_class_parents) - PHP_FE(class_implements, arginfo_class_implements) - PHP_FE(class_uses, arginfo_class_uses) - PHP_FE(spl_object_hash, arginfo_spl_object_hash) - PHP_FE(spl_object_id, arginfo_spl_object_id) - PHP_FE(iterator_to_array, arginfo_iterator_to_array) - PHP_FE(iterator_count, arginfo_iterator) - PHP_FE(iterator_apply, arginfo_iterator_apply) - PHP_FE_END -}; -/* }}} */ - -/* {{{ PHP_MINIT_FUNCTION(spl) - */ +/* {{{ PHP_MINIT_FUNCTION(spl) */ PHP_MINIT_FUNCTION(spl) { + zend_autoload = spl_perform_autoload; + PHP_MINIT(spl_exceptions)(INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(spl_iterators)(INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(spl_array)(INIT_FUNC_ARGS_PASSTHRU); @@ -1007,10 +726,6 @@ PHP_MINIT_FUNCTION(spl) PHP_MINIT(spl_fixedarray)(INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(spl_observer)(INIT_FUNC_ARGS_PASSTHRU); - spl_autoload_fn = zend_hash_str_find_ptr(CG(function_table), "spl_autoload", sizeof("spl_autoload") - 1); - spl_autoload_call_fn = zend_hash_str_find_ptr(CG(function_table), "spl_autoload_call", sizeof("spl_autoload_call") - 1); - ZEND_ASSERT(spl_autoload_fn != NULL && spl_autoload_call_fn != NULL); - return SUCCESS; } /* }}} */ @@ -1040,12 +755,11 @@ PHP_RSHUTDOWN_FUNCTION(spl) /* {{{ */ return SUCCESS; } /* }}} */ -/* {{{ spl_module_entry - */ +/* {{{ spl_module_entry */ zend_module_entry spl_module_entry = { STANDARD_MODULE_HEADER, "SPL", - spl_functions, + ext_functions, PHP_MINIT(spl), NULL, PHP_RINIT(spl), |