diff --git a/Zend/Makefile.am b/Zend/Makefile.am index 6417f3eb141ed..6fe5080d68697 100644 --- a/Zend/Makefile.am +++ b/Zend/Makefile.am @@ -18,7 +18,7 @@ libZend_la_SOURCES=\ zend_default_classes.c \ zend_iterators.c zend_interfaces.c zend_exceptions.c \ zend_strtod.c zend_closures.c zend_float.c zend_string.c zend_signal.c \ - zend_generators.c + zend_generators.c zend_autoload.c libZend_la_LDFLAGS = libZend_la_LIBADD = @ZEND_EXTRA_LIBS@ diff --git a/Zend/tests/use_const/alias.phpt b/Zend/tests/use_const/alias.phpt new file mode 100644 index 0000000000000..f179393006bef --- /dev/null +++ b/Zend/tests/use_const/alias.phpt @@ -0,0 +1,26 @@ +--TEST-- +aliasing imported constants to resolve naming conflicts +--FILE-- + +--EXPECT-- +int(42) +int(43) +Done diff --git a/Zend/tests/use_const/basic.phpt b/Zend/tests/use_const/basic.phpt new file mode 100644 index 0000000000000..6eaed7f27d379 --- /dev/null +++ b/Zend/tests/use_const/basic.phpt @@ -0,0 +1,22 @@ +--TEST-- +import namespaced constant +--FILE-- + +--EXPECT-- +int(42) +int(43) +Done diff --git a/Zend/tests/use_const/case_sensivity.phpt b/Zend/tests/use_const/case_sensivity.phpt new file mode 100644 index 0000000000000..1977daa93bb9a --- /dev/null +++ b/Zend/tests/use_const/case_sensivity.phpt @@ -0,0 +1,12 @@ +--TEST-- +importing const with same name but different case +--FILE-- + +--EXPECT-- diff --git a/Zend/tests/use_const/conflicting_use.phpt b/Zend/tests/use_const/conflicting_use.phpt new file mode 100644 index 0000000000000..f873fdcc65304 --- /dev/null +++ b/Zend/tests/use_const/conflicting_use.phpt @@ -0,0 +1,21 @@ +--TEST-- +use const statements with conflicting names +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use bar\baz as baz because the name is already in use in %s on line %d diff --git a/Zend/tests/use_const/conflicting_use_alias.phpt b/Zend/tests/use_const/conflicting_use_alias.phpt new file mode 100644 index 0000000000000..8b563a4ca975e --- /dev/null +++ b/Zend/tests/use_const/conflicting_use_alias.phpt @@ -0,0 +1,18 @@ +--TEST-- +use and use const with the same alias +--FILE-- + +--EXPECT-- +string(3) "foo" diff --git a/Zend/tests/use_const/define_imported.phpt b/Zend/tests/use_const/define_imported.phpt new file mode 100644 index 0000000000000..5eb44be64a058 --- /dev/null +++ b/Zend/tests/use_const/define_imported.phpt @@ -0,0 +1,14 @@ +--TEST-- +defining const with same name as imported should fail +--FILE-- + +--EXPECTF-- +Fatal error: Cannot declare const bar because the name is already in use in %s on line %d diff --git a/Zend/tests/use_const/define_imported_before.phpt b/Zend/tests/use_const/define_imported_before.phpt new file mode 100644 index 0000000000000..19374a2c5868b --- /dev/null +++ b/Zend/tests/use_const/define_imported_before.phpt @@ -0,0 +1,14 @@ +--TEST-- +using const with same name as defined should fail +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use const foo\bar as bar because the name is already in use in %s on line %d diff --git a/Zend/tests/use_const/includes/foo_bar.php b/Zend/tests/use_const/includes/foo_bar.php new file mode 100644 index 0000000000000..90ed451f364d0 --- /dev/null +++ b/Zend/tests/use_const/includes/foo_bar.php @@ -0,0 +1,5 @@ + +--EXPECTF-- +Notice: Use of undefined constant baz - assumed 'baz' in %s on line %d +string(3) "baz" diff --git a/Zend/tests/use_const/self_parent.phpt b/Zend/tests/use_const/self_parent.phpt new file mode 100644 index 0000000000000..b71f2ecc81fee --- /dev/null +++ b/Zend/tests/use_const/self_parent.phpt @@ -0,0 +1,12 @@ +--TEST-- +Allow self and parent in use const statement +--FILE-- + +--EXPECT-- diff --git a/Zend/tests/use_const/shadow_core.phpt b/Zend/tests/use_const/shadow_core.phpt new file mode 100644 index 0000000000000..7d8bcbd1892f4 --- /dev/null +++ b/Zend/tests/use_const/shadow_core.phpt @@ -0,0 +1,16 @@ +--TEST-- +shadowing a global core constant with a local version +--FILE-- + +--EXPECTF-- +int(42) +Done diff --git a/Zend/tests/use_const/shadow_global.phpt b/Zend/tests/use_const/shadow_global.phpt new file mode 100644 index 0000000000000..930cc9f0b8778 --- /dev/null +++ b/Zend/tests/use_const/shadow_global.phpt @@ -0,0 +1,25 @@ +--TEST-- +shadowing a global constant with a local version +--FILE-- + +--EXPECT-- +string(10) "global bar" +string(9) "local bar" +Done diff --git a/Zend/tests/use_function/alias.phpt b/Zend/tests/use_function/alias.phpt new file mode 100644 index 0000000000000..5f7e97fff87f0 --- /dev/null +++ b/Zend/tests/use_function/alias.phpt @@ -0,0 +1,30 @@ +--TEST-- +aliasing imported functions to resolve naming conflicts +--FILE-- + +--EXPECT-- +string(7) "foo.baz" +string(7) "bar.baz" +Done diff --git a/Zend/tests/use_function/basic.phpt b/Zend/tests/use_function/basic.phpt new file mode 100644 index 0000000000000..513a96620c17b --- /dev/null +++ b/Zend/tests/use_function/basic.phpt @@ -0,0 +1,26 @@ +--TEST-- +import namespaced function +--FILE-- + +--EXPECT-- +string(11) "foo.bar.baz" +string(11) "foo.bar.baz" +Done diff --git a/Zend/tests/use_function/case_insensivity.phpt b/Zend/tests/use_function/case_insensivity.phpt new file mode 100644 index 0000000000000..53ae3658a9771 --- /dev/null +++ b/Zend/tests/use_function/case_insensivity.phpt @@ -0,0 +1,13 @@ +--TEST-- +importing function with same name but different case should fail +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use foo\BAR as BAR because the name is already in use in %s on line %d diff --git a/Zend/tests/use_function/conflicting_use.phpt b/Zend/tests/use_function/conflicting_use.phpt new file mode 100644 index 0000000000000..ed531e4febe0d --- /dev/null +++ b/Zend/tests/use_function/conflicting_use.phpt @@ -0,0 +1,25 @@ +--TEST-- +use function statements with conflicting names +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use bar\baz as baz because the name is already in use in %s on line %d diff --git a/Zend/tests/use_function/conflicting_use_alias.phpt b/Zend/tests/use_function/conflicting_use_alias.phpt new file mode 100644 index 0000000000000..2870512014de0 --- /dev/null +++ b/Zend/tests/use_function/conflicting_use_alias.phpt @@ -0,0 +1,20 @@ +--TEST-- +use and use function with the same alias +--FILE-- + +--EXPECT-- +string(3) "foo" diff --git a/Zend/tests/use_function/conflicting_use_const_alias.phpt b/Zend/tests/use_function/conflicting_use_const_alias.phpt new file mode 100644 index 0000000000000..2e0faf0da21a6 --- /dev/null +++ b/Zend/tests/use_function/conflicting_use_const_alias.phpt @@ -0,0 +1,23 @@ +--TEST-- +use const and use function with the same alias +--FILE-- + +--EXPECT-- +string(9) "foo.const" +string(12) "foo.function" diff --git a/Zend/tests/use_function/define_imported.phpt b/Zend/tests/use_function/define_imported.phpt new file mode 100644 index 0000000000000..c542a4d5494b9 --- /dev/null +++ b/Zend/tests/use_function/define_imported.phpt @@ -0,0 +1,14 @@ +--TEST-- +defining function with same name as imported should fail +--FILE-- + +--EXPECTF-- +Fatal error: Cannot declare function bar because the name is already in use in %s on line %d diff --git a/Zend/tests/use_function/define_imported_before.phpt b/Zend/tests/use_function/define_imported_before.phpt new file mode 100644 index 0000000000000..ff5d5ca28d364 --- /dev/null +++ b/Zend/tests/use_function/define_imported_before.phpt @@ -0,0 +1,14 @@ +--TEST-- +using function with same name as defined should fail +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use function foo\bar as bar because the name is already in use in %s on line %d diff --git a/Zend/tests/use_function/ignore_constants.phpt b/Zend/tests/use_function/ignore_constants.phpt new file mode 100644 index 0000000000000..c50ff7357af17 --- /dev/null +++ b/Zend/tests/use_function/ignore_constants.phpt @@ -0,0 +1,23 @@ +--TEST-- +use function should ignore namespaced constants +--FILE-- + +--EXPECT-- +int(43) +Done diff --git a/Zend/tests/use_function/includes/foo_bar.php b/Zend/tests/use_function/includes/foo_bar.php new file mode 100644 index 0000000000000..6d2f8cab45fb4 --- /dev/null +++ b/Zend/tests/use_function/includes/foo_bar.php @@ -0,0 +1,7 @@ + +--EXPECTF-- +Fatal error: Call to undefined function foo\bar\baz() in %s on line %d diff --git a/Zend/tests/use_function/self_parent.phpt b/Zend/tests/use_function/self_parent.phpt new file mode 100644 index 0000000000000..f1e1fa84f1de1 --- /dev/null +++ b/Zend/tests/use_function/self_parent.phpt @@ -0,0 +1,12 @@ +--TEST-- +Allow self and parent in use function statement +--FILE-- + +--EXPECT-- diff --git a/Zend/tests/use_function/shadow_core.phpt b/Zend/tests/use_function/shadow_core.phpt new file mode 100644 index 0000000000000..8f92ff1e1be86 --- /dev/null +++ b/Zend/tests/use_function/shadow_core.phpt @@ -0,0 +1,16 @@ +--TEST-- +shadowing a global core function with a local version +--FILE-- + +--EXPECT-- +int(4) +Done diff --git a/Zend/tests/use_function/shadow_global.phpt b/Zend/tests/use_function/shadow_global.phpt new file mode 100644 index 0000000000000..791bcdf4d50d6 --- /dev/null +++ b/Zend/tests/use_function/shadow_global.phpt @@ -0,0 +1,25 @@ +--TEST-- +shadowing a global function with a local version +--FILE-- + +--EXPECT-- +string(10) "global bar" +string(9) "local bar" +Done diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 23ad158b17d44..c657c20b6687e 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2764,7 +2764,7 @@ static int zend_is_callable_check_func(int check_flags, zval *callable, zend_fca } /* Check if function with given name exists. * This may be a compound name that includes namespace name */ - if (zend_hash_find(EG(function_table), lmname, mlen+1, (void**)&fcc->function_handler) == SUCCESS) { + if (zend_lookup_function(lmname, mlen, (void**)&fcc->function_handler) == SUCCESS) { efree(lmname); return 1; } diff --git a/Zend/zend_autoload.c b/Zend/zend_autoload.c new file mode 100644 index 0000000000000..8f3abb29c172b --- /dev/null +++ b/Zend/zend_autoload.c @@ -0,0 +1,249 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 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: Anthony Ferrara | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "zend.h" +#include "zend_API.h" +#include "zend_execute.h" +#include "zend_globals.h" +#include "zend_globals_macros.h" +#include "zend_autoload.h" +#include "zend_hash.h" +#include "zend_execute.h" +#include "zend_interfaces.h" +#include "zend_exceptions.h" + +static char* zend_autoload_get_name_key(zend_fcall_info *fci, int *length, zend_bool *do_free TSRMLS_DC); + +int zend_autoload_call(zval* name, long type TSRMLS_DC) +{ + zval *ztype, *retval = NULL; + char *lc_name; + int lc_length; + HashTable *symbol_table; + HashPosition function_pos; + zend_autoload_func *func_info; + char dummy = 1; + + if (Z_TYPE_P(name) != IS_STRING) { + return FAILURE; + } + + switch (type) { + case ZEND_AUTOLOAD_CLASS: + symbol_table = EG(class_table); + break; + case ZEND_AUTOLOAD_FUNCTION: + symbol_table = EG(function_table); + break; + default: + return FAILURE; + } + + lc_length = Z_STRLEN_P(name); + lc_name = zend_str_tolower_dup(Z_STRVAL_P(name), lc_length); + + if (EG(autoload_funcs) == NULL || EG(autoload_funcs)->nNumOfElements == 0) { + if (type == ZEND_AUTOLOAD_CLASS + && ( + EG(autoload_legacy) != NULL + || zend_lookup_function_ex(ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME) - 1, NULL, 0, &EG(autoload_legacy) TSRMLS_CC) == SUCCESS + ) + ) { + zend_call_method_with_1_params(NULL, NULL, &EG(autoload_legacy), ZEND_AUTOLOAD_FUNC_NAME, &retval, name); + if (zend_hash_exists(symbol_table, lc_name, lc_length + 1)) { + efree(lc_name); + return SUCCESS; + } + } + efree(lc_name); + return FAILURE; + } + + if (EG(autoload_stack) == NULL) { + ALLOC_HASHTABLE(EG(autoload_stack)); + zend_hash_init(EG(autoload_stack), 0, NULL, NULL, 0); + } + + if (zend_hash_add(EG(autoload_stack), lc_name, lc_length, (void**)&dummy, sizeof(char), NULL) == FAILURE) { + efree(lc_name); + return FAILURE; + } + + MAKE_STD_ZVAL(ztype); + ZVAL_LONG(ztype, type); + + zend_hash_internal_pointer_reset_ex(EG(autoload_funcs), &function_pos); + while(zend_hash_has_more_elements_ex(EG(autoload_funcs), &function_pos) == SUCCESS) { + zend_hash_get_current_data_ex(EG(autoload_funcs), (void **) &func_info, &function_pos); + if (func_info->type & type) { + func_info->fci.retval_ptr_ptr = &retval; + zend_fcall_info_argn(&func_info->fci, 2, &name, &ztype); + zend_call_function(&func_info->fci, &func_info->fcc TSRMLS_CC); + zend_exception_save(TSRMLS_C); + if (retval) { + zval_ptr_dtor(&retval); + retval = NULL; + } + if (zend_hash_exists(symbol_table, lc_name, lc_length + 1)) { + break; + } + } + zend_hash_move_forward_ex(EG(autoload_funcs), &function_pos); + } + zend_exception_restore(TSRMLS_C); + + Z_DELREF_P(ztype); + zend_hash_del(EG(autoload_stack), lc_name, lc_length); + efree(lc_name); + return SUCCESS; +} + +#define HT_MOVE_TAIL_TO_HEAD(ht) \ + (ht)->pListTail->pListNext = (ht)->pListHead; \ + (ht)->pListHead = (ht)->pListTail; \ + (ht)->pListTail = (ht)->pListHead->pListLast; \ + (ht)->pListHead->pListNext->pListLast = (ht)->pListHead;\ + (ht)->pListTail->pListNext = NULL; \ + (ht)->pListHead->pListLast = NULL; + +static char* zend_autoload_get_name_key(zend_fcall_info *fci, int *length, zend_bool *do_free TSRMLS_DC) { + char *name; + switch (Z_TYPE_P(fci->function_name)) { + case IS_STRING: + *length = Z_STRLEN_P(fci->function_name); + return Z_STRVAL_P(fci->function_name); + break; + case IS_OBJECT: + *length = sizeof(zend_object_handle); + name = emalloc(*length + 1); + *do_free = 1; + memcpy(name, &Z_OBJ_HANDLE_P(fci->function_name), *length); + name[*length] = '\0'; + return name; + break; + default: + return 0; + } +} + +int zend_autoload_register(zend_autoload_func *func, zend_bool prepend TSRMLS_DC) +{ + char *lc_name; + zend_bool do_free = 0; + int lc_length, status = SUCCESS; + + lc_name = zend_autoload_get_name_key(&func->fci, &lc_length, &do_free); + if (lc_name == 0) { + zend_error_noreturn(E_ERROR, "Unknown Function Name Type Provided"); + } else if (do_free) { + Z_ADDREF_P(func->callable); + } + + if (!EG(autoload_funcs)) { + ALLOC_HASHTABLE(EG(autoload_funcs)); + zend_hash_init(EG(autoload_funcs), 0, NULL, NULL, 0); + } else if (zend_hash_exists(EG(autoload_funcs), lc_name, lc_length + 1)) { + if (do_free) { + efree(lc_name); + Z_DELREF_P(func->callable); + } + return FAILURE; + } + + if (zend_hash_add(EG(autoload_funcs), lc_name, lc_length + 1, (void**) func, sizeof(zend_autoload_func), NULL) == FAILURE) { + status = FAILURE; + } else if (prepend) { + HT_MOVE_TAIL_TO_HEAD(EG(autoload_funcs)); + } + if (do_free) { + efree(lc_name); + Z_DELREF_P(func->callable); + } + return status; +} + +int zend_autoload_unregister(zval *callable TSRMLS_DC) +{ + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *lc_name; + zend_bool do_free = 0; + int lc_length; + + if (zend_fcall_info_init(callable, 0, &fci, &fcc, NULL, NULL) == FAILURE) { + return FAILURE; + } + + lc_name = zend_autoload_get_name_key(&fci, &lc_length, &do_free); + if (lc_name == 0) { + return FAILURE; + } + + zend_hash_del(EG(autoload_funcs), lc_name, lc_length); + if (do_free) { + efree(lc_name); + } + + return SUCCESS; +} + +ZEND_FUNCTION(autoload_register) +{ + zval *callable; + zend_autoload_func *func; + zend_bool prepend = 0; + long type = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_DC, "z|lb", &callable, &type, &prepend) == FAILURE) { + return; + } + + func = emalloc(sizeof(zend_autoload_func)); + + if (zend_fcall_info_init(callable, 0, &func->fci, &func->fcc, NULL, NULL) == FAILURE) { + efree(func); + zend_error_noreturn(E_ERROR, "Expecting a valid callback"); + } + + func->callable = callable; + Z_ADDREF_P(callable); + + if (!type) { + func->type = ZEND_AUTOLOAD_FUNCTION | ZEND_AUTOLOAD_CLASS; + } else { + func->type = type; + } + + if (zend_autoload_register(func, prepend TSRMLS_CC) == FAILURE) { + Z_DELREF_P(callable); + efree(func); + } +} + +ZEND_FUNCTION(autoload_unregister) +{ + zval *callable; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_DC, "z", &callable) == FAILURE) { + return; + } + RETVAL_BOOL(zend_autoload_unregister(callable TSRMLS_CC) == SUCCESS); +} + diff --git a/Zend/zend_autoload.h b/Zend/zend_autoload.h new file mode 100644 index 0000000000000..88f6e12fd3a20 --- /dev/null +++ b/Zend/zend_autoload.h @@ -0,0 +1,40 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 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: Anthony Ferrara | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ +#include "zend.h" +#include "zend_API.h" + +ZEND_FUNCTION(autoload_register); +ZEND_FUNCTION(autoload_unregister); + +typedef struct { + zend_fcall_info fci; + zend_fcall_info_cache fcc; + zval *callable; + long type; +} zend_autoload_func; + +int zend_autoload_call(zval* name, long type TSRMLS_DC); +int zend_autoload_register(zend_autoload_func* func, zend_bool prepend TSRMLS_DC); +int zend_autoload_unregister(zval *callable TSRMLS_DC); + +#define ZEND_AUTOLOAD_CLASS 1 +#define ZEND_AUTOLOAD_FUNCTION 2 + + diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 44a480f2a12f4..d6de0037bb091 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -22,6 +22,7 @@ #include "zend.h" #include "zend_API.h" #include "zend_builtin_functions.h" +#include "zend_autoload.h" #include "zend_constants.h" #include "zend_ini.h" #include "zend_exceptions.h" @@ -238,6 +239,16 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_extension_loaded, 0, 0, 1) ZEND_ARG_INFO(0, extension_name) ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_autoload_register, 0, 0, 1) + ZEND_ARG_INFO(0, callback) + ZEND_ARG_INFO(0, type) + ZEND_ARG_INFO(0, prepend) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_autoload_unregister, 0, 0, 1) + ZEND_ARG_INFO(0, callback) +ZEND_END_ARG_INFO() /* }}} */ static const zend_function_entry builtin_functions[] = { /* {{{ */ @@ -307,6 +318,8 @@ static const zend_function_entry builtin_functions[] = { /* {{{ */ ZEND_FE(gc_enabled, arginfo_zend__void) ZEND_FE(gc_enable, arginfo_zend__void) ZEND_FE(gc_disable, arginfo_zend__void) + ZEND_NS_FE("php", autoload_register, arginfo_autoload_register) + ZEND_NS_FE("php", autoload_unregister, arginfo_autoload_unregister) ZEND_FE_END }; /* }}} */ @@ -316,7 +329,6 @@ ZEND_MINIT_FUNCTION(core) { /* {{{ */ INIT_CLASS_ENTRY(class_entry, "stdClass", NULL); zend_standard_class_def = zend_register_internal_class(&class_entry TSRMLS_CC); - zend_register_default_classes(TSRMLS_C); return SUCCESS; @@ -1353,8 +1365,9 @@ ZEND_FUNCTION(function_exists) zend_function *func; char *lcname; zend_bool retval; + zend_bool autoload = 1; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &name, &name_len, &autoload) == FAILURE) { return; } @@ -1367,7 +1380,7 @@ ZEND_FUNCTION(function_exists) name_len--; } - retval = (zend_hash_find(EG(function_table), name, name_len+1, (void **)&func) == SUCCESS); + retval = (zend_lookup_function_ex(name, name_len+1, NULL, (int) autoload, &func) == SUCCESS); efree(lcname); @@ -1831,7 +1844,7 @@ ZEND_FUNCTION(create_function) if (retval==SUCCESS) { zend_function new_function, *func; - if (zend_hash_find(EG(function_table), LAMBDA_TEMP_FUNCNAME, sizeof(LAMBDA_TEMP_FUNCNAME), (void **) &func)==FAILURE) { + if (zend_lookup_function_ex(LAMBDA_TEMP_FUNCNAME, sizeof(LAMBDA_TEMP_FUNCNAME), NULL, 0, &func)==FAILURE) { zend_error(E_ERROR, "Unexpected inconsistency in create_function()"); RETURN_FALSE; } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index f250b2be20879..623fe15fed4db 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -204,6 +204,10 @@ void zend_init_compiler_data_structures(TSRMLS_D) /* {{{ */ CG(in_namespace) = 0; CG(has_bracketed_namespaces) = 0; CG(current_import) = NULL; + CG(current_import_function) = NULL; + CG(current_import_const) = NULL; + zend_hash_init(&CG(function_filenames), 0, NULL, NULL, 0); + zend_hash_init(&CG(const_filenames), 0, NULL, NULL, 0); init_compiler_declarables(TSRMLS_C); zend_stack_init(&CG(context_stack)); @@ -242,6 +246,8 @@ void shutdown_compiler(TSRMLS_D) /* {{{ */ zend_stack_destroy(&CG(list_stack)); zend_hash_destroy(&CG(filenames_table)); zend_llist_destroy(&CG(open_files)); + zend_hash_destroy(&CG(function_filenames)); + zend_hash_destroy(&CG(const_filenames)); zend_stack_destroy(&CG(context_stack)); } /* }}} */ @@ -424,12 +430,16 @@ int zend_add_ns_func_name_literal(zend_op_array *op_array, const zval *zv TSRMLS lc_literal = zend_add_literal(CG(active_op_array), &c TSRMLS_CC); CALCULATE_LITERAL_HASH(lc_literal); - ns_separator = (const char*)zend_memrchr(Z_STRVAL_P(zv), '\\', Z_STRLEN_P(zv)) + 1; - lc_len = Z_STRLEN_P(zv) - (ns_separator - Z_STRVAL_P(zv)); - lc_name = zend_str_tolower_dup(ns_separator, lc_len); - ZVAL_STRINGL(&c, lc_name, lc_len, 0); - lc_literal = zend_add_literal(CG(active_op_array), &c TSRMLS_CC); - CALCULATE_LITERAL_HASH(lc_literal); + ns_separator = (const char*)zend_memrchr(Z_STRVAL_P(zv), '\\', Z_STRLEN_P(zv)); + + if (ns_separator != NULL) { + ns_separator += 1; + lc_len = Z_STRLEN_P(zv) - (ns_separator - Z_STRVAL_P(zv)); + lc_name = zend_str_tolower_dup(ns_separator, lc_len); + ZVAL_STRINGL(&c, lc_name, lc_len, 0); + lc_literal = zend_add_literal(CG(active_op_array), &c TSRMLS_CC); + CALCULATE_LITERAL_HASH(lc_literal); + } return ret; } @@ -1695,6 +1705,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n } else { zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); zval key; + zval **ns_name; if (CG(current_namespace)) { /* Prefix function name with current namespace name */ @@ -1710,6 +1721,19 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n lcname = zend_str_tolower_dup(name, name_len); } + /* Function name must not conflict with import names */ + if (CG(current_import_function) && + zend_hash_find(CG(current_import_function), lcname, Z_STRLEN(function_name->u.constant)+1, (void**)&ns_name) == SUCCESS) { + + char *tmp = zend_str_tolower_dup(Z_STRVAL_PP(ns_name), Z_STRLEN_PP(ns_name)); + + if (Z_STRLEN_PP(ns_name) != Z_STRLEN(function_name->u.constant) || + memcmp(tmp, lcname, Z_STRLEN(function_name->u.constant))) { + zend_error(E_COMPILE_ERROR, "Cannot declare function %s because the name is already in use", Z_STRVAL(function_name->u.constant)); + } + efree(tmp); + } + opline->opcode = ZEND_DECLARE_FUNCTION; opline->op1_type = IS_CONST; build_runtime_defined_function_key(&key, lcname, name_len TSRMLS_CC); @@ -1720,6 +1744,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n CALCULATE_LITERAL_HASH(opline->op2.constant); opline->extended_value = ZEND_DECLARE_FUNCTION; zend_hash_quick_update(CG(function_table), Z_STRVAL(key), Z_STRLEN(key), Z_HASH_P(&CONSTANT(opline->op1.constant)), &op_array, sizeof(zend_op_array), (void **) &CG(active_op_array)); + zend_hash_add(&CG(function_filenames), lcname, strlen(lcname)+1, CG(compiled_filename), strlen(CG(compiled_filename))+1, NULL); zend_stack_push(&CG(context_stack), (void *) &CG(context), sizeof(CG(context))); zend_init_compiler_context(TSRMLS_C); } @@ -1937,7 +1962,7 @@ int zend_do_begin_function_call(znode *function_name, zend_bool check_namespace char *lcname; char *is_compound = memchr(Z_STRVAL(function_name->u.constant), '\\', Z_STRLEN(function_name->u.constant)); - zend_resolve_non_class_name(function_name, check_namespace TSRMLS_CC); + zend_resolve_function_name(function_name, check_namespace TSRMLS_CC); if (check_namespace && CG(current_namespace) && !is_compound) { /* We assume we call function from the current namespace @@ -2076,12 +2101,12 @@ void zend_do_begin_dynamic_function_call(znode *function_name, int ns_call TSRML } /* }}} */ -void zend_resolve_non_class_name(znode *element_name, zend_bool check_namespace TSRMLS_DC) /* {{{ */ +void zend_resolve_non_class_name(znode *element_name, zend_bool check_namespace, zend_bool case_sensitive, HashTable *current_import_sub TSRMLS_DC) /* {{{ */ { znode tmp; int len; zval **ns; - char *lcname, *compound = memchr(Z_STRVAL(element_name->u.constant), '\\', Z_STRLEN(element_name->u.constant)); + char *lookup_name, *compound = memchr(Z_STRVAL(element_name->u.constant), '\\', Z_STRLEN(element_name->u.constant)); if (Z_STRVAL(element_name->u.constant)[0] == '\\') { /* name starts with \ so it is known and unambiguos, nothing to do here but shorten it */ @@ -2094,11 +2119,33 @@ void zend_resolve_non_class_name(znode *element_name, zend_bool check_namespace return; } + if (current_import_sub) { + len = Z_STRLEN(element_name->u.constant)+1; + if (case_sensitive) { + lookup_name = estrndup(Z_STRVAL(element_name->u.constant), len); + } else { + lookup_name = zend_str_tolower_dup(Z_STRVAL(element_name->u.constant), len); + } + /* Check if function/const matches imported name */ + if (zend_hash_find(current_import_sub, lookup_name, len, (void**)&ns) == SUCCESS) { + zval_dtor(&element_name->u.constant); + element_name->u.constant = **ns; + zval_copy_ctor(&element_name->u.constant); + efree(lookup_name); + return; + } + efree(lookup_name); + } + if (compound && CG(current_import)) { len = compound - Z_STRVAL(element_name->u.constant); - lcname = zend_str_tolower_dup(Z_STRVAL(element_name->u.constant), len); + if (case_sensitive) { + lookup_name = estrndup(Z_STRVAL(element_name->u.constant), len); + } else { + lookup_name = zend_str_tolower_dup(Z_STRVAL(element_name->u.constant), len); + } /* Check if first part of compound name is an import name */ - if (zend_hash_find(CG(current_import), lcname, len+1, (void**)&ns) == SUCCESS) { + if (zend_hash_find(CG(current_import), lookup_name, len+1, (void**)&ns) == SUCCESS) { /* Substitute import name */ tmp.op_type = IS_CONST; tmp.u.constant = **ns; @@ -2108,10 +2155,10 @@ void zend_resolve_non_class_name(znode *element_name, zend_bool check_namespace memmove(Z_STRVAL(element_name->u.constant), Z_STRVAL(element_name->u.constant)+len, Z_STRLEN(element_name->u.constant)+1); zend_do_build_namespace_name(&tmp, &tmp, element_name TSRMLS_CC); *element_name = tmp; - efree(lcname); + efree(lookup_name); return; } - efree(lcname); + efree(lookup_name); } if (CG(current_namespace)) { @@ -2127,6 +2174,18 @@ void zend_resolve_non_class_name(znode *element_name, zend_bool check_namespace } /* }}} */ +void zend_resolve_function_name(znode *element_name, zend_bool check_namespace TSRMLS_DC) /* {{{ */ +{ + zend_resolve_non_class_name(element_name, check_namespace, 0, CG(current_import_function) TSRMLS_CC); +} +/* }}} */ + +void zend_resolve_const_name(znode *element_name, zend_bool check_namespace TSRMLS_DC) /* {{{ */ +{ + zend_resolve_non_class_name(element_name, check_namespace, 1, CG(current_import_const) TSRMLS_CC); +} +/* }}} */ + void zend_do_resolve_class_name(znode *result, znode *class_name, int is_static TSRMLS_DC) /* {{{ */ { char *lcname; @@ -5629,7 +5688,7 @@ void zend_do_fetch_constant(znode *result, znode *constant_container, znode *con break; } - zend_resolve_non_class_name(constant_name, check_namespace TSRMLS_CC); + zend_resolve_const_name(constant_name, check_namespace TSRMLS_CC); if(!compound) { fetch_type |= IS_CONSTANT_UNQUALIFIED; @@ -5641,7 +5700,7 @@ void zend_do_fetch_constant(znode *result, znode *constant_container, znode *con case ZEND_RT: compound = memchr(Z_STRVAL(constant_name->u.constant), '\\', Z_STRLEN(constant_name->u.constant)); - zend_resolve_non_class_name(constant_name, check_namespace TSRMLS_CC); + zend_resolve_const_name(constant_name, check_namespace TSRMLS_CC); if(zend_constant_ct_subst(result, &constant_name->u.constant, 1 TSRMLS_CC)) { break; @@ -6997,6 +7056,18 @@ void zend_do_begin_namespace(const znode *name, zend_bool with_bracket TSRMLS_DC CG(current_import) = NULL; } + if (CG(current_import_function)) { + zend_hash_destroy(CG(current_import_function)); + efree(CG(current_import_function)); + CG(current_import_function) = NULL; + } + + if (CG(current_import_const)) { + zend_hash_destroy(CG(current_import_const)); + efree(CG(current_import_const)); + CG(current_import_const) = NULL; + } + if (CG(doc_comment)) { efree(CG(doc_comment)); CG(doc_comment) = NULL; @@ -7089,9 +7160,103 @@ void zend_do_use(znode *ns_name, znode *new_name, int is_global TSRMLS_DC) /* {{ } /* }}} */ +void zend_do_use_non_class(znode *ns_name, znode *new_name, int is_global, const char *type, zend_bool case_sensitive, HashTable *current_import_sub, HashTable *lookup_table TSRMLS_DC) /* {{{ */ +{ + char *lookup_name; + char *filename; + zval *name, *ns, tmp; + zend_bool warn = 0; + + ALLOC_ZVAL(ns); + *ns = ns_name->u.constant; + if (new_name) { + name = &new_name->u.constant; + } else { + const char *p; + + /* The form "use A\B" is eqivalent to "use A\B as B". + So we extract the last part of compound name to use as a new_name */ + name = &tmp; + p = zend_memrchr(Z_STRVAL_P(ns), '\\', Z_STRLEN_P(ns)); + if (p) { + ZVAL_STRING(name, p+1, 1); + } else { + *name = *ns; + zval_copy_ctor(name); + warn = !is_global && !CG(current_namespace); + } + } + + if (case_sensitive) { + lookup_name = estrndup(Z_STRVAL_P(name), Z_STRLEN_P(name)); + } else { + lookup_name = zend_str_tolower_dup(Z_STRVAL_P(name), Z_STRLEN_P(name)); + } + + if (CG(current_namespace)) { + /* Prefix import name with current namespace name to avoid conflicts with functions/consts */ + char *c_ns_name = emalloc(Z_STRLEN_P(CG(current_namespace)) + 1 + Z_STRLEN_P(name) + 1); + + zend_str_tolower_copy(c_ns_name, Z_STRVAL_P(CG(current_namespace)), Z_STRLEN_P(CG(current_namespace))); + c_ns_name[Z_STRLEN_P(CG(current_namespace))] = '\\'; + memcpy(c_ns_name+Z_STRLEN_P(CG(current_namespace))+1, lookup_name, Z_STRLEN_P(name)+1); + if (zend_hash_exists(lookup_table, c_ns_name, Z_STRLEN_P(CG(current_namespace)) + 1 + Z_STRLEN_P(name)+1)) { + char *tmp2 = zend_str_tolower_dup(Z_STRVAL_P(ns), Z_STRLEN_P(ns)); + + if (Z_STRLEN_P(ns) != Z_STRLEN_P(CG(current_namespace)) + 1 + Z_STRLEN_P(name) || + memcmp(tmp2, c_ns_name, Z_STRLEN_P(ns))) { + zend_error(E_COMPILE_ERROR, "Cannot use %s %s as %s because the name is already in use", type, Z_STRVAL_P(ns), Z_STRVAL_P(name)); + } + efree(tmp2); + } + efree(c_ns_name); + } else if (zend_hash_find(lookup_table, lookup_name, Z_STRLEN_P(name)+1, (void **)&filename) == SUCCESS && strcmp(filename, CG(compiled_filename)) == 0) { + char *c_tmp = zend_str_tolower_dup(Z_STRVAL_P(ns), Z_STRLEN_P(ns)); + + if (Z_STRLEN_P(ns) != Z_STRLEN_P(name) || + memcmp(c_tmp, lookup_name, Z_STRLEN_P(ns))) { + zend_error(E_COMPILE_ERROR, "Cannot use %s %s as %s because the name is already in use", type, Z_STRVAL_P(ns), Z_STRVAL_P(name)); + } + efree(c_tmp); + } + + if (zend_hash_add(current_import_sub, lookup_name, Z_STRLEN_P(name)+1, &ns, sizeof(zval*), NULL) != SUCCESS) { + zend_error(E_COMPILE_ERROR, "Cannot use %s as %s because the name is already in use", Z_STRVAL_P(ns), Z_STRVAL_P(name)); + } + if (warn) { + zend_error(E_WARNING, "The use statement with non-compound name '%s' has no effect", Z_STRVAL_P(name)); + } + efree(lookup_name); + zval_dtor(name); +} +/* }}} */ + +void zend_do_use_function(znode *ns_name, znode *new_name, int is_global TSRMLS_DC) /* {{{ */ +{ + if (!CG(current_import_function)) { + CG(current_import_function) = emalloc(sizeof(HashTable)); + zend_hash_init(CG(current_import_function), 0, NULL, ZVAL_PTR_DTOR, 0); + } + + zend_do_use_non_class(ns_name, new_name, is_global, "function", 0, CG(current_import_function), &CG(function_filenames) TSRMLS_CC); +} +/* }}} */ + +void zend_do_use_const(znode *ns_name, znode *new_name, int is_global TSRMLS_DC) /* {{{ */ +{ + if (!CG(current_import_const)) { + CG(current_import_const) = emalloc(sizeof(HashTable)); + zend_hash_init(CG(current_import_const), 0, NULL, ZVAL_PTR_DTOR, 0); + } + + zend_do_use_non_class(ns_name, new_name, is_global, "const", 1, CG(current_import_const), &CG(const_filenames) TSRMLS_CC); +} +/* }}} */ + void zend_do_declare_constant(znode *name, znode *value TSRMLS_DC) /* {{{ */ { zend_op *opline; + zval **ns_name; if(Z_TYPE(value->u.constant) == IS_CONSTANT_ARRAY) { zend_error(E_COMPILE_ERROR, "Arrays are not allowed as constants"); @@ -7112,11 +7277,26 @@ void zend_do_declare_constant(znode *name, znode *value TSRMLS_DC) /* {{{ */ *name = tmp; } + /* Constant name must not conflict with import names */ + if (CG(current_import_const) && + zend_hash_find(CG(current_import_const), Z_STRVAL(name->u.constant), Z_STRLEN(name->u.constant)+1, (void**)&ns_name) == SUCCESS) { + + char *tmp = estrndup(Z_STRVAL_PP(ns_name), Z_STRLEN_PP(ns_name)); + + if (Z_STRLEN_PP(ns_name) != Z_STRLEN(name->u.constant) || + memcmp(tmp, Z_STRVAL(name->u.constant), Z_STRLEN(name->u.constant))) { + zend_error(E_COMPILE_ERROR, "Cannot declare const %s because the name is already in use", Z_STRVAL(name->u.constant)); + } + efree(tmp); + } + opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_DECLARE_CONST; SET_UNUSED(opline->result); SET_NODE(opline->op1, name); SET_NODE(opline->op2, value); + + zend_hash_add(&CG(const_filenames), Z_STRVAL(name->u.constant), Z_STRLEN(name->u.constant)+1, CG(compiled_filename), strlen(CG(compiled_filename))+1, NULL); } /* }}} */ @@ -7141,6 +7321,16 @@ void zend_do_end_namespace(TSRMLS_D) /* {{{ */ efree(CG(current_import)); CG(current_import) = NULL; } + if (CG(current_import_function)) { + zend_hash_destroy(CG(current_import_function)); + efree(CG(current_import_function)); + CG(current_import_function) = NULL; + } + if (CG(current_import_const)) { + zend_hash_destroy(CG(current_import_const)); + efree(CG(current_import_const)); + CG(current_import_const) = NULL; + } } /* }}} */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 9c55b5ebe8812..e8c0309319de4 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -437,7 +437,9 @@ ZEND_API char *zend_get_compiled_filename(TSRMLS_D); ZEND_API int zend_get_compiled_lineno(TSRMLS_D); ZEND_API size_t zend_get_scanned_file_offset(TSRMLS_D); -void zend_resolve_non_class_name(znode *element_name, zend_bool check_namespace TSRMLS_DC); +void zend_resolve_non_class_name(znode *element_name, zend_bool check_namespace, zend_bool case_sensitive, HashTable *current_import_sub TSRMLS_DC); +void zend_resolve_function_name(znode *element_name, zend_bool check_namespace TSRMLS_DC); +void zend_resolve_const_name(znode *element_name, zend_bool check_namespace TSRMLS_DC); void zend_resolve_class_name(znode *class_name, ulong fetch_type, int check_ns_name TSRMLS_DC); ZEND_API const char* zend_get_compiled_variable_name(const zend_op_array *op_array, zend_uint var, int* name_len); @@ -636,6 +638,9 @@ void zend_do_begin_namespace(const znode *name, zend_bool with_brackets TSRMLS_D void zend_do_end_namespace(TSRMLS_D); void zend_verify_namespace(TSRMLS_D); void zend_do_use(znode *name, znode *new_name, int is_global TSRMLS_DC); +void zend_do_use_non_class(znode *ns_name, znode *new_name, int is_global, const char *type, zend_bool case_sensitive, HashTable *current_import_sub, HashTable *lookup_table TSRMLS_DC); +void zend_do_use_function(znode *name, znode *new_name, int is_global TSRMLS_DC); +void zend_do_use_const(znode *name, znode *new_name, int is_global TSRMLS_DC); void zend_do_end_compilation(TSRMLS_D); void zend_do_resolve_class_name(znode *result, znode *class_name, int is_static TSRMLS_DC); diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 594559d58bf06..7718e9de28a39 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -20,6 +20,7 @@ /* $Id$ */ #include "zend.h" +#include "zend_autoload.h" #include "zend_constants.h" #include "zend_execute.h" #include "zend_variables.h" @@ -125,6 +126,9 @@ void zend_register_standard_constants(TSRMLS_D) REGISTER_MAIN_BOOL_CONSTANT("ZEND_DEBUG_BUILD", ZEND_DEBUG, CONST_PERSISTENT | CONST_CS); } REGISTER_MAIN_NULL_CONSTANT("NULL", CONST_PERSISTENT | CONST_CT_SUBST); + + REGISTER_MAIN_LONG_CONSTANT(ZEND_NS_NAME("php", "AUTOLOAD_CLASS"), ZEND_AUTOLOAD_CLASS, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT(ZEND_NS_NAME("php", "AUTOLOAD_FUNCTION"), ZEND_AUTOLOAD_FUNCTION, CONST_CS | CONST_PERSISTENT); } diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index ff0758772e48b..6e470060d5b9b 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -63,6 +63,9 @@ ZEND_API void execute_internal(zend_execute_data *execute_data_ptr, struct _zend ZEND_API int zend_is_true(zval *op); ZEND_API int zend_lookup_class(const char *name, int name_length, zend_class_entry ***ce TSRMLS_DC); ZEND_API int zend_lookup_class_ex(const char *name, int name_length, const zend_literal *key, int use_autoload, zend_class_entry ***ce TSRMLS_DC); +ZEND_API int zend_lookup_function(const char *name, int name_length, zend_function **fbc TSRMLS_DC); +ZEND_API int zend_lookup_function_ex(const char *name, int name_length, const zend_literal *key, int use_autoload, zend_function **fbc TSRMLS_DC); + ZEND_API int zend_eval_string(char *str, zval *retval_ptr, char *string_name TSRMLS_DC); ZEND_API int zend_eval_stringl(char *str, int str_len, zval *retval_ptr, char *string_name TSRMLS_DC); ZEND_API int zend_eval_string_ex(char *str, zval *retval_ptr, char *string_name, int handle_exceptions TSRMLS_DC); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 83c221798416f..80048ecb325fe 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -32,6 +32,7 @@ #include "zend_exceptions.h" #include "zend_closures.h" #include "zend_generators.h" +#include "zend_autoload.h" #include "zend_vm.h" #include "zend_float.h" #ifdef HAVE_SYS_TIME_H @@ -151,8 +152,8 @@ void init_executor(TSRMLS_D) /* {{{ */ EG(class_table) = CG(class_table); EG(in_execution) = 0; - EG(in_autoload) = NULL; - EG(autoload_func) = NULL; + EG(autoload_funcs) = NULL; + EG(autoload_stack) = NULL; EG(error_handling) = EH_NORMAL; zend_vm_stack_init(TSRMLS_C); @@ -325,9 +326,13 @@ void shutdown_executor(TSRMLS_D) /* {{{ */ zend_ptr_stack_destroy(&EG(user_error_handlers)); zend_ptr_stack_destroy(&EG(user_exception_handlers)); zend_objects_store_destroy(&EG(objects_store)); - if (EG(in_autoload)) { - zend_hash_destroy(EG(in_autoload)); - FREE_HASHTABLE(EG(in_autoload)); + if (EG(autoload_stack)) { + zend_hash_destroy(EG(autoload_stack)); + FREE_HASHTABLE(EG(autoload_stack)); + } + if (EG(autoload_funcs)) { + zend_hash_destroy(EG(autoload_funcs)); + FREE_HASHTABLE(EG(autoload_funcs)); } } zend_end_try(); @@ -1010,18 +1015,81 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TS } /* }}} */ +ZEND_API int zend_lookup_function(const char *name, int name_length, zend_function **fbc TSRMLS_DC) /* {{{ */ +{ + return zend_lookup_function_ex(name, name_length, NULL, 1, fbc TSRMLS_CC); +} + +ZEND_API int zend_lookup_function_ex(const char *name, int name_length, const zend_literal *key, int use_autoload, zend_function **fbc TSRMLS_DC) +{ + char *lc_name, *lc_free; + int lc_length, retval = FAILURE; + zend_ulong hash; + ALLOCA_FLAG(use_heap) + zval *function_name_ptr; + + if (key) { + lc_name = Z_STRVAL(key->constant); + lc_length = Z_STRLEN(key->constant) + 1; + hash = key->hash_value; + } else { + if (name == NULL || !name_length) { + return FAILURE; + } + + lc_free = lc_name = do_alloca(name_length + 1, use_heap); + zend_str_tolower_copy(lc_name, name, name_length); + lc_length = name_length + 1; + + if (lc_name[0] == '\\') { + lc_name += 1; + lc_length -= 1; + } + + hash = zend_inline_hash_func(lc_name, lc_length); + } + if (zend_hash_quick_find(EG(function_table), lc_name, lc_length, hash, (void **) fbc) == SUCCESS) { + if (!key) { + free_alloca(lc_free, use_heap); + } + return SUCCESS; + } + + /* The compiler is not-reentrant. Make sure we __autoload_function() only during run-time + * (doesn't impact functionality of __autoload_function() + */ + if (!use_autoload || zend_is_compiling(TSRMLS_C)) { + if (!key) { + free_alloca(lc_free, use_heap); + } + return FAILURE; + } + + ALLOC_ZVAL(function_name_ptr); + INIT_PZVAL(function_name_ptr); + if (name[0] == '\\') { + ZVAL_STRINGL(function_name_ptr, name+1, name_length-1, 1); + } else { + ZVAL_STRINGL(function_name_ptr, name, name_length, 1); + } + if (zend_autoload_call(function_name_ptr, ZEND_AUTOLOAD_FUNCTION TSRMLS_CC) == SUCCESS && + zend_hash_quick_find(EG(function_table), lc_name, lc_length, hash, (void **) fbc) == SUCCESS) { + retval = SUCCESS; + } + + if (!key) { + free_alloca(lc_free, use_heap); + } + return retval; + +} + ZEND_API int zend_lookup_class_ex(const char *name, int name_length, const zend_literal *key, int use_autoload, zend_class_entry ***ce TSRMLS_DC) /* {{{ */ { - zval **args[1]; - zval autoload_function; zval *class_name_ptr; - zval *retval_ptr = NULL; - int retval, lc_length; + int lc_length, retval = FAILURE; char *lc_name; char *lc_free; - zend_fcall_info fcall_info; - zend_fcall_info_cache fcall_cache; - char dummy = 1; ulong hash; ALLOCA_FLAG(use_heap) @@ -1063,20 +1131,6 @@ ZEND_API int zend_lookup_class_ex(const char *name, int name_length, const zend_ return FAILURE; } - if (EG(in_autoload) == NULL) { - ALLOC_HASHTABLE(EG(in_autoload)); - zend_hash_init(EG(in_autoload), 0, NULL, NULL, 0); - } - - if (zend_hash_quick_add(EG(in_autoload), lc_name, lc_length, hash, (void**)&dummy, sizeof(char), NULL) == FAILURE) { - if (!key) { - free_alloca(lc_free, use_heap); - } - return FAILURE; - } - - ZVAL_STRINGL(&autoload_function, ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME) - 1, 0); - ALLOC_ZVAL(class_name_ptr); INIT_PZVAL(class_name_ptr); if (name[0] == '\\') { @@ -1084,42 +1138,11 @@ ZEND_API int zend_lookup_class_ex(const char *name, int name_length, const zend_ } else { ZVAL_STRINGL(class_name_ptr, name, name_length, 1); } - - args[0] = &class_name_ptr; - - fcall_info.size = sizeof(fcall_info); - fcall_info.function_table = EG(function_table); - fcall_info.function_name = &autoload_function; - fcall_info.symbol_table = NULL; - fcall_info.retval_ptr_ptr = &retval_ptr; - fcall_info.param_count = 1; - fcall_info.params = args; - fcall_info.object_ptr = NULL; - fcall_info.no_separation = 1; - - fcall_cache.initialized = EG(autoload_func) ? 1 : 0; - fcall_cache.function_handler = EG(autoload_func); - fcall_cache.calling_scope = NULL; - fcall_cache.called_scope = NULL; - fcall_cache.object_ptr = NULL; - - zend_exception_save(TSRMLS_C); - retval = zend_call_function(&fcall_info, &fcall_cache TSRMLS_CC); - zend_exception_restore(TSRMLS_C); - - EG(autoload_func) = fcall_cache.function_handler; - - zval_ptr_dtor(&class_name_ptr); - - zend_hash_quick_del(EG(in_autoload), lc_name, lc_length, hash); - - if (retval_ptr) { - zval_ptr_dtor(&retval_ptr); + if (zend_autoload_call(class_name_ptr, ZEND_AUTOLOAD_CLASS TSRMLS_CC) == SUCCESS && + zend_hash_quick_find(EG(class_table), lc_name, lc_length, hash, (void **) ce) == SUCCESS) { + retval = SUCCESS; } - if (retval == SUCCESS) { - retval = zend_hash_quick_find(EG(class_table), lc_name, lc_length, hash, (void **) ce); - } if (!key) { free_alloca(lc_free, use_heap); } diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index b9a5b39914a6c..ae2c719511982 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -131,9 +131,14 @@ struct _zend_compiler_globals { zval *current_namespace; HashTable *current_import; + HashTable *current_import_function; + HashTable *current_import_const; zend_bool in_namespace; zend_bool has_bracketed_namespaces; + HashTable function_filenames; + HashTable const_filenames; + zend_compiler_context context; zend_stack context_stack; @@ -201,8 +206,10 @@ struct _zend_executor_globals { int ticks_count; zend_bool in_execution; - HashTable *in_autoload; - zend_function *autoload_func; + HashTable *autoload_stack; + HashTable *autoload_funcs; + zend_function *autoload_legacy; + zend_bool full_tables_cleanup; /* for extended information support */ diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 6a9a24a87ea72..7cfab914debb4 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -240,6 +240,8 @@ top_statement: | T_NAMESPACE '{' { zend_do_begin_namespace(NULL, 1 TSRMLS_CC); } top_statement_list '}' { zend_do_end_namespace(TSRMLS_C); } | T_USE use_declarations ';' { zend_verify_namespace(TSRMLS_C); } + | T_USE T_FUNCTION use_function_declarations ';' { zend_verify_namespace(TSRMLS_C); } + | T_USE T_CONST use_const_declarations ';' { zend_verify_namespace(TSRMLS_C); } | constant_declaration ';' { zend_verify_namespace(TSRMLS_C); } ; @@ -255,6 +257,30 @@ use_declaration: | T_NS_SEPARATOR namespace_name T_AS T_STRING { zend_do_use(&$2, &$4, 1 TSRMLS_CC); } ; +use_function_declarations: + use_function_declarations ',' use_function_declaration + | use_function_declaration +; + +use_function_declaration: + namespace_name { zend_do_use_function(&$1, NULL, 0 TSRMLS_CC); } + | namespace_name T_AS T_STRING { zend_do_use_function(&$1, &$3, 0 TSRMLS_CC); } + | T_NS_SEPARATOR namespace_name { zend_do_use_function(&$2, NULL, 1 TSRMLS_CC); } + | T_NS_SEPARATOR namespace_name T_AS T_STRING { zend_do_use_function(&$2, &$4, 1 TSRMLS_CC); } +; + +use_const_declarations: + use_const_declarations ',' use_const_declaration + | use_const_declaration +; + +use_const_declaration: + namespace_name { zend_do_use_const(&$1, NULL, 0 TSRMLS_CC); } + | namespace_name T_AS T_STRING { zend_do_use_const(&$1, &$3, 0 TSRMLS_CC); } + | T_NS_SEPARATOR namespace_name { zend_do_use_const(&$2, NULL, 1 TSRMLS_CC); } + | T_NS_SEPARATOR namespace_name T_AS T_STRING { zend_do_use_const(&$2, &$4, 1 TSRMLS_CC); } +; + constant_declaration: constant_declaration ',' T_STRING '=' static_scalar { zend_do_declare_constant(&$3, &$5 TSRMLS_CC); } | T_CONST T_STRING '=' static_scalar { zend_do_declare_constant(&$2, &$4 TSRMLS_CC); } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 7e4f7a897d2db..52b1de40cd7e6 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2637,7 +2637,7 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST|TMP|VAR|CV) function_name = (zval*)(opline->op2.literal+1); if (CACHED_PTR(opline->op2.literal->cache_slot)) { call->fbc = CACHED_PTR(opline->op2.literal->cache_slot); - } else if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL_P(function_name), Z_STRLEN_P(function_name)+1, Z_HASH_P(function_name), (void **) &call->fbc) == FAILURE)) { + } else if (UNEXPECTED(zend_lookup_function_ex(Z_STRVAL_P(function_name), Z_STRLEN_P(function_name), (zend_literal*) function_name, 1, &call->fbc) == FAILURE)) { SAVE_OPLINE(); zend_error_noreturn(E_ERROR, "Call to undefined function %s()", Z_STRVAL_P(opline->op2.zv)); } else { @@ -2666,7 +2666,7 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST|TMP|VAR|CV) } else { lcname = zend_str_tolower_dup(function_name_strval, function_name_strlen); } - if (UNEXPECTED(zend_hash_find(EG(function_table), lcname, function_name_strlen+1, (void **) &call->fbc) == FAILURE)) { + if (UNEXPECTED(zend_lookup_function(lcname, function_name_strlen, &call->fbc) == FAILURE)) { zend_error_noreturn(E_ERROR, "Call to undefined function %s()", function_name_strval); } efree(lcname); @@ -2783,9 +2783,9 @@ ZEND_VM_HANDLER(69, ZEND_INIT_NS_FCALL_BY_NAME, ANY, CONST) func_name = opline->op2.literal + 1; if (CACHED_PTR(opline->op2.literal->cache_slot)) { call->fbc = CACHED_PTR(opline->op2.literal->cache_slot); - } else if (zend_hash_quick_find(EG(function_table), Z_STRVAL(func_name->constant), Z_STRLEN(func_name->constant)+1, func_name->hash_value, (void **) &call->fbc)==FAILURE) { + } else if (zend_lookup_function_ex(Z_STRVAL(func_name->constant), Z_STRLEN(func_name->constant), func_name->hash_value, 1, &call->fbc)==FAILURE) { func_name++; - if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL(func_name->constant), Z_STRLEN(func_name->constant)+1, func_name->hash_value, (void **) &call->fbc)==FAILURE)) { + if (UNEXPECTED(zend_lookup_function_ex(Z_STRVAL(func_name->constant), Z_STRLEN(func_name->constant), func_name->hash_value, 1, &call->fbc)==FAILURE)) { SAVE_OPLINE(); zend_error_noreturn(E_ERROR, "Call to undefined function %s()", Z_STRVAL_P(opline->op2.zv)); } else { @@ -2817,7 +2817,7 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, CONST, ANY) if (CACHED_PTR(opline->op1.literal->cache_slot)) { EX(function_state).function = CACHED_PTR(opline->op1.literal->cache_slot); - } else if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL_P(fname), Z_STRLEN_P(fname)+1, Z_HASH_P(fname), (void **) &EX(function_state).function)==FAILURE)) { + } else if (UNEXPECTED(zend_lookup_function_ex(Z_STRVAL_P(fname), Z_STRLEN_P(fname), (zend_literal*) fname, 1, &EX(function_state).function)==FAILURE)) { SAVE_OPLINE(); zend_error_noreturn(E_ERROR, "Call to undefined function %s()", fname->value.str.val); } else { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 9622fee5e4efa..4f13d7f1aa542 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1220,7 +1220,7 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPCODE function_name = (zval*)(opline->op2.literal+1); if (CACHED_PTR(opline->op2.literal->cache_slot)) { call->fbc = CACHED_PTR(opline->op2.literal->cache_slot); - } else if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL_P(function_name), Z_STRLEN_P(function_name)+1, Z_HASH_P(function_name), (void **) &call->fbc) == FAILURE)) { + } else if (UNEXPECTED(zend_lookup_function_ex(Z_STRVAL_P(function_name), Z_STRLEN_P(function_name), (zend_literal*) function_name, 1, &call->fbc) == FAILURE)) { SAVE_OPLINE(); zend_error_noreturn(E_ERROR, "Call to undefined function %s()", Z_STRVAL_P(opline->op2.zv)); } else { @@ -1249,7 +1249,7 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPCODE } else { lcname = zend_str_tolower_dup(function_name_strval, function_name_strlen); } - if (UNEXPECTED(zend_hash_find(EG(function_table), lcname, function_name_strlen+1, (void **) &call->fbc) == FAILURE)) { + if (UNEXPECTED(zend_lookup_function(lcname, function_name_strlen, &call->fbc) == FAILURE)) { zend_error_noreturn(E_ERROR, "Call to undefined function %s()", function_name_strval); } efree(lcname); @@ -1366,9 +1366,9 @@ static int ZEND_FASTCALL ZEND_INIT_NS_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPC func_name = opline->op2.literal + 1; if (CACHED_PTR(opline->op2.literal->cache_slot)) { call->fbc = CACHED_PTR(opline->op2.literal->cache_slot); - } else if (zend_hash_quick_find(EG(function_table), Z_STRVAL(func_name->constant), Z_STRLEN(func_name->constant)+1, func_name->hash_value, (void **) &call->fbc)==FAILURE) { + } else if (zend_lookup_function_ex(Z_STRVAL(func_name->constant), Z_STRLEN(func_name->constant), func_name->hash_value, 1, &call->fbc)==FAILURE) { func_name++; - if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL(func_name->constant), Z_STRLEN(func_name->constant)+1, func_name->hash_value, (void **) &call->fbc)==FAILURE)) { + if (UNEXPECTED(zend_lookup_function_ex(Z_STRVAL(func_name->constant), Z_STRLEN(func_name->constant), func_name->hash_value, 1, &call->fbc)==FAILURE)) { SAVE_OPLINE(); zend_error_noreturn(E_ERROR, "Call to undefined function %s()", Z_STRVAL_P(opline->op2.zv)); } else { @@ -1545,7 +1545,7 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_TMP_HANDLER(ZEND_OPCODE_H function_name = (zval*)(opline->op2.literal+1); if (CACHED_PTR(opline->op2.literal->cache_slot)) { call->fbc = CACHED_PTR(opline->op2.literal->cache_slot); - } else if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL_P(function_name), Z_STRLEN_P(function_name)+1, Z_HASH_P(function_name), (void **) &call->fbc) == FAILURE)) { + } else if (UNEXPECTED(zend_lookup_function_ex(Z_STRVAL_P(function_name), Z_STRLEN_P(function_name), (zend_literal*) function_name, 1, &call->fbc) == FAILURE)) { SAVE_OPLINE(); zend_error_noreturn(E_ERROR, "Call to undefined function %s()", Z_STRVAL_P(opline->op2.zv)); } else { @@ -1574,7 +1574,7 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_TMP_HANDLER(ZEND_OPCODE_H } else { lcname = zend_str_tolower_dup(function_name_strval, function_name_strlen); } - if (UNEXPECTED(zend_hash_find(EG(function_table), lcname, function_name_strlen+1, (void **) &call->fbc) == FAILURE)) { + if (UNEXPECTED(zend_lookup_function(lcname, function_name_strlen, &call->fbc) == FAILURE)) { zend_error_noreturn(E_ERROR, "Call to undefined function %s()", function_name_strval); } efree(lcname); @@ -1732,7 +1732,7 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_VAR_HANDLER(ZEND_OPCODE_H function_name = (zval*)(opline->op2.literal+1); if (CACHED_PTR(opline->op2.literal->cache_slot)) { call->fbc = CACHED_PTR(opline->op2.literal->cache_slot); - } else if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL_P(function_name), Z_STRLEN_P(function_name)+1, Z_HASH_P(function_name), (void **) &call->fbc) == FAILURE)) { + } else if (UNEXPECTED(zend_lookup_function_ex(Z_STRVAL_P(function_name), Z_STRLEN_P(function_name), (zend_literal*) function_name, 1, &call->fbc) == FAILURE)) { SAVE_OPLINE(); zend_error_noreturn(E_ERROR, "Call to undefined function %s()", Z_STRVAL_P(opline->op2.zv)); } else { @@ -1761,7 +1761,7 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_VAR_HANDLER(ZEND_OPCODE_H } else { lcname = zend_str_tolower_dup(function_name_strval, function_name_strlen); } - if (UNEXPECTED(zend_hash_find(EG(function_table), lcname, function_name_strlen+1, (void **) &call->fbc) == FAILURE)) { + if (UNEXPECTED(zend_lookup_function(lcname, function_name_strlen, &call->fbc) == FAILURE)) { zend_error_noreturn(E_ERROR, "Call to undefined function %s()", function_name_strval); } efree(lcname); @@ -1957,7 +1957,7 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CV_HANDLER(ZEND_OPCODE_HA function_name = (zval*)(opline->op2.literal+1); if (CACHED_PTR(opline->op2.literal->cache_slot)) { call->fbc = CACHED_PTR(opline->op2.literal->cache_slot); - } else if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL_P(function_name), Z_STRLEN_P(function_name)+1, Z_HASH_P(function_name), (void **) &call->fbc) == FAILURE)) { + } else if (UNEXPECTED(zend_lookup_function_ex(Z_STRVAL_P(function_name), Z_STRLEN_P(function_name), (zend_literal*) function_name, 1, &call->fbc) == FAILURE)) { SAVE_OPLINE(); zend_error_noreturn(E_ERROR, "Call to undefined function %s()", Z_STRVAL_P(opline->op2.zv)); } else { @@ -1986,7 +1986,7 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CV_HANDLER(ZEND_OPCODE_HA } else { lcname = zend_str_tolower_dup(function_name_strval, function_name_strlen); } - if (UNEXPECTED(zend_hash_find(EG(function_table), lcname, function_name_strlen+1, (void **) &call->fbc) == FAILURE)) { + if (UNEXPECTED(zend_lookup_function(lcname, function_name_strlen, &call->fbc) == FAILURE)) { zend_error_noreturn(E_ERROR, "Call to undefined function %s()", function_name_strval); } efree(lcname); @@ -2311,7 +2311,7 @@ static int ZEND_FASTCALL ZEND_DO_FCALL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_A if (CACHED_PTR(opline->op1.literal->cache_slot)) { EX(function_state).function = CACHED_PTR(opline->op1.literal->cache_slot); - } else if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL_P(fname), Z_STRLEN_P(fname)+1, Z_HASH_P(fname), (void **) &EX(function_state).function)==FAILURE)) { + } else if (UNEXPECTED(zend_lookup_function_ex(Z_STRVAL_P(fname), Z_STRLEN_P(fname), (zend_literal*) fname, 1, &EX(function_state).function)==FAILURE)) { SAVE_OPLINE(); zend_error_noreturn(E_ERROR, "Call to undefined function %s()", fname->value.str.val); } else { diff --git a/configure.in b/configure.in index 16738fe30ab67..f346bd111339d 100644 --- a/configure.in +++ b/configure.in @@ -1475,7 +1475,8 @@ PHP_ADD_SOURCES(Zend, \ zend_list.c zend_indent.c zend_builtin_functions.c zend_sprintf.c \ zend_ini.c zend_qsort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \ zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_gc.c \ - zend_closures.c zend_float.c zend_string.c zend_signal.c zend_generators.c) + zend_closures.c zend_float.c zend_string.c zend_signal.c zend_generators.c\ + zend_autoload.c) if test -r "$abs_srcdir/Zend/zend_objects.c"; then PHP_ADD_SOURCES(Zend, zend_objects.c zend_object_handlers.c zend_objects_API.c zend_default_classes.c) diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 716990d80f02a..8ce82168297d8 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -39,6 +39,7 @@ #include "spl_heap.h" #include "zend_exceptions.h" #include "zend_interfaces.h" +#include "zend_autoload.h" #include "ext/standard/php_rand.h" #include "ext/standard/php_lcg.h" #include "main/snprintf.h" @@ -391,65 +392,20 @@ PHP_FUNCTION(spl_autoload_extensions) } } /* }}} */ -typedef struct { - zend_function *func_ptr; - zval *obj; - zval *closure; - zend_class_entry *ce; -} autoload_func_info; - -static void autoload_func_info_dtor(autoload_func_info *alfi) -{ - if (alfi->obj) { - zval_ptr_dtor(&alfi->obj); - } - if (alfi->closure) { - zval_ptr_dtor(&alfi->closure); - } -} - /* {{{ proto void spl_autoload_call(string class_name) Try all registerd autoload function to load the requested class */ PHP_FUNCTION(spl_autoload_call) { - zval *class_name, *retval = NULL; - int class_name_len; - char *func_name, *lc_name; - uint func_name_len; - ulong dummy; - HashPosition function_pos; - autoload_func_info *alfi; + zval *class_name; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &class_name) == FAILURE || Z_TYPE_P(class_name) != IS_STRING) { return; } - if (SPL_G(autoload_functions)) { - int l_autoload_running = SPL_G(autoload_running); - SPL_G(autoload_running) = 1; - class_name_len = Z_STRLEN_P(class_name); - lc_name = zend_str_tolower_dup(Z_STRVAL_P(class_name), class_name_len); - zend_hash_internal_pointer_reset_ex(SPL_G(autoload_functions), &function_pos); - while(zend_hash_has_more_elements_ex(SPL_G(autoload_functions), &function_pos) == SUCCESS) { - zend_hash_get_current_key_ex(SPL_G(autoload_functions), &func_name, &func_name_len, &dummy, 0, &function_pos); - zend_hash_get_current_data_ex(SPL_G(autoload_functions), (void **) &alfi, &function_pos); - zend_call_method(alfi->obj ? &alfi->obj : NULL, alfi->ce, &alfi->func_ptr, func_name, func_name_len, &retval, 1, class_name, NULL TSRMLS_CC); - zend_exception_save(TSRMLS_C); - if (retval) { - zval_ptr_dtor(&retval); - retval = NULL; - } - if (zend_hash_exists(EG(class_table), lc_name, class_name_len + 1)) { - break; - } - zend_hash_move_forward_ex(SPL_G(autoload_functions), &function_pos); - } - zend_exception_restore(TSRMLS_C); - efree(lc_name); - SPL_G(autoload_running) = l_autoload_running; - } else { - /* do not use or overwrite &EG(autoload_func) here */ + if (EG(autoload_funcs) == NULL) { zend_call_method_with_1_params(NULL, NULL, NULL, "spl_autoload", NULL, class_name); + } else { + zend_autoload_call(class_name, ZEND_AUTOLOAD_CLASS); } } /* }}} */ @@ -465,159 +421,78 @@ PHP_FUNCTION(spl_autoload_call) Register given function as __autoload() implementation */ PHP_FUNCTION(spl_autoload_register) { - char *func_name, *error = NULL; - int func_name_len; - char *lc_name = NULL; + char *error = NULL; zval *zcallable = NULL; zend_bool do_throw = 1; zend_bool prepend = 0; - zend_function *spl_func_ptr; - autoload_func_info alfi; - zval *obj_ptr; - zend_fcall_info_cache fcc; + zend_autoload_func *func; if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "|zbb", &zcallable, &do_throw, &prepend) == FAILURE) { return; } - if (ZEND_NUM_ARGS()) { - if (Z_TYPE_P(zcallable) == IS_STRING) { - if (Z_STRLEN_P(zcallable) == sizeof("spl_autoload_call") - 1) { - if (!zend_binary_strcasecmp(Z_STRVAL_P(zcallable), sizeof("spl_autoload_call"), "spl_autoload_call", sizeof("spl_autoload_call"))) { - if (do_throw) { - zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Function spl_autoload_call() cannot be registered"); - } - RETURN_FALSE; - } - } - } - - if (!zend_is_callable_ex(zcallable, NULL, IS_CALLABLE_STRICT, &func_name, &func_name_len, &fcc, &error TSRMLS_CC)) { - alfi.ce = fcc.calling_scope; - alfi.func_ptr = fcc.function_handler; - obj_ptr = fcc.object_ptr; - 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 TSRMLS_CC, "Passed array specifies a non static method but no object (%s)", error); - } - if (error) { - efree(error); - } - efree(func_name); - RETURN_FALSE; - } - else if (do_throw) { - zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Passed array does not specify %s %smethod (%s)", alfi.func_ptr ? "a callable" : "an existing", !obj_ptr ? "static " : "", error); - } - if (error) { - efree(error); - } - efree(func_name); - RETURN_FALSE; - } else if (Z_TYPE_P(zcallable) == IS_STRING) { - if (do_throw) { - zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Function '%s' not %s (%s)", func_name, alfi.func_ptr ? "callable" : "found", error); - } - if (error) { - efree(error); - } - efree(func_name); - RETURN_FALSE; - } else { + func = emalloc(sizeof(zend_autoload_func)); + func->type = ZEND_AUTOLOAD_CLASS; + + if (!ZEND_NUM_ARGS()) { + MAKE_STD_ZVAL(zcallable); + ZVAL_STRING(zcallable, "spl_autoload", 1); + } + + if (zend_fcall_info_init(zcallable, IS_CALLABLE_STRICT, &func->fci, &func->fcc, NULL, &error) == FAILURE) { + if (Z_TYPE_P(zcallable) == IS_ARRAY) { + if (!func->fcc.object_ptr && func->fcc.function_handler && !(func->fcc.function_handler->common.fn_flags & ZEND_ACC_STATIC)) { if (do_throw) { - zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Illegal value passed (%s)", error); + zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Passed array specifies a non static method but no object (%s)", error); } + efree(func); if (error) { efree(error); } - efree(func_name); RETURN_FALSE; } - } - alfi.closure = NULL; - alfi.ce = fcc.calling_scope; - alfi.func_ptr = fcc.function_handler; - obj_ptr = fcc.object_ptr; - if (error) { - efree(error); - } - - lc_name = safe_emalloc(func_name_len, 1, sizeof(long) + 1); - zend_str_tolower_copy(lc_name, func_name, func_name_len); - efree(func_name); - - if (Z_TYPE_P(zcallable) == IS_OBJECT) { - alfi.closure = zcallable; - Z_ADDREF_P(zcallable); - - lc_name = erealloc(lc_name, func_name_len + 2 + sizeof(zend_object_handle)); - memcpy(lc_name + func_name_len, &Z_OBJ_HANDLE_P(zcallable), - sizeof(zend_object_handle)); - func_name_len += sizeof(zend_object_handle); - lc_name[func_name_len] = '\0'; - } - - if (SPL_G(autoload_functions) && zend_hash_exists(SPL_G(autoload_functions), (char*)lc_name, func_name_len+1)) { - if (alfi.closure) { - Z_DELREF_P(zcallable); + else if (do_throw) { + zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Passed array does not specify %s %smethod (%s)", func->fcc.function_handler ? "a callable" : "an existing", !func->fcc.object_ptr ? "static " : "", error); } - goto skip; - } - - 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 = erealloc(lc_name, func_name_len + 2 + sizeof(zend_object_handle)); - memcpy(lc_name + func_name_len, &Z_OBJ_HANDLE_P(obj_ptr), sizeof(zend_object_handle)); - func_name_len += sizeof(zend_object_handle); - lc_name[func_name_len] = '\0'; - alfi.obj = obj_ptr; - Z_ADDREF_P(alfi.obj); + efree(func); + if (error) { + efree(error); + } + RETURN_FALSE; + } else if (Z_TYPE_P(zcallable) == IS_STRING) { + if (do_throw) { + zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Function '%s' not %s (%s)", Z_STRVAL_P(zcallable), func->fcc.function_handler ? "callable" : "found", error); + } + efree(func); + if (error) { + efree(error); + } + RETURN_FALSE; } else { - alfi.obj = NULL; - } - - if (!SPL_G(autoload_functions)) { - ALLOC_HASHTABLE(SPL_G(autoload_functions)); - zend_hash_init(SPL_G(autoload_functions), 1, NULL, (dtor_func_t) autoload_func_info_dtor, 0); - } - - zend_hash_find(EG(function_table), "spl_autoload", sizeof("spl_autoload"), (void **) &spl_func_ptr); - - 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; - spl_alfi.obj = NULL; - spl_alfi.ce = NULL; - spl_alfi.closure = NULL; - zend_hash_add(SPL_G(autoload_functions), "spl_autoload", sizeof("spl_autoload"), &spl_alfi, sizeof(autoload_func_info), NULL); - 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 (do_throw) { + zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Illegal value passed (%s)", error); } - } - - if (zend_hash_add(SPL_G(autoload_functions), lc_name, func_name_len+1, &alfi.func_ptr, sizeof(autoload_func_info), NULL) == FAILURE) { - if (obj_ptr && !(alfi.func_ptr->common.fn_flags & ZEND_ACC_STATIC)) { - Z_DELREF_P(alfi.obj); - } - if (alfi.closure) { - Z_DELREF_P(alfi.closure); + efree(func); + if (error) { + efree(error); } + RETURN_FALSE; } - 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)); - } -skip: - efree(lc_name); + } + func->callable = zcallable; + if (ZEND_NUM_ARGS()) { + Z_ADDREF_P(func->callable); + } + if (error) { + efree(error); } - if (SPL_G(autoload_functions)) { - zend_hash_find(EG(function_table), "spl_autoload_call", sizeof("spl_autoload_call"), (void **) &EG(autoload_func)); - } else { - zend_hash_find(EG(function_table), "spl_autoload", sizeof("spl_autoload"), (void **) &EG(autoload_func)); + if (zend_autoload_register(func, prepend TSRMLS_CC) == FAILURE) { + if (ZEND_NUM_ARGS()) { + Z_DELREF_P(func->callable); + } + efree(func); + RETURN_FALSE; } RETURN_TRUE; } /* }}} */ @@ -628,11 +503,7 @@ PHP_FUNCTION(spl_autoload_unregister) { char *func_name, *error = NULL; int func_name_len; - char *lc_name = NULL; zval *zcallable; - int success = FAILURE; - zend_function *spl_func_ptr; - zval *obj_ptr; zend_fcall_info_cache fcc; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zcallable) == FAILURE) { @@ -649,54 +520,11 @@ PHP_FUNCTION(spl_autoload_unregister) } RETURN_FALSE; } - obj_ptr = fcc.object_ptr; if (error) { efree(error); } - lc_name = safe_emalloc(func_name_len, 1, sizeof(long) + 1); - zend_str_tolower_copy(lc_name, func_name, func_name_len); - efree(func_name); - - if (Z_TYPE_P(zcallable) == IS_OBJECT) { - lc_name = erealloc(lc_name, func_name_len + 2 + sizeof(zend_object_handle)); - memcpy(lc_name + func_name_len, &Z_OBJ_HANDLE_P(zcallable), - sizeof(zend_object_handle)); - func_name_len += sizeof(zend_object_handle); - lc_name[func_name_len] = '\0'; - } - - if (SPL_G(autoload_functions)) { - if (func_name_len == sizeof("spl_autoload_call")-1 && !strcmp(lc_name, "spl_autoload_call")) { - /* remove all */ - zend_hash_destroy(SPL_G(autoload_functions)); - FREE_HASHTABLE(SPL_G(autoload_functions)); - SPL_G(autoload_functions) = NULL; - EG(autoload_func) = NULL; - success = SUCCESS; - } else { - /* remove specific */ - success = zend_hash_del(SPL_G(autoload_functions), lc_name, func_name_len+1); - if (success != SUCCESS && obj_ptr) { - lc_name = erealloc(lc_name, func_name_len + 2 + sizeof(zend_object_handle)); - memcpy(lc_name + func_name_len, &Z_OBJ_HANDLE_P(obj_ptr), sizeof(zend_object_handle)); - func_name_len += sizeof(zend_object_handle); - lc_name[func_name_len] = '\0'; - success = zend_hash_del(SPL_G(autoload_functions), lc_name, func_name_len+1); - } - } - } else if (func_name_len == sizeof("spl_autoload")-1 && !strcmp(lc_name, "spl_autoload")) { - /* register single spl_autoload() */ - zend_hash_find(EG(function_table), "spl_autoload", sizeof("spl_autoload"), (void **) &spl_func_ptr); - - if (EG(autoload_func) == spl_func_ptr) { - success = SUCCESS; - EG(autoload_func) = NULL; - } - } - - efree(lc_name); - RETURN_BOOL(success == SUCCESS); + RETURN_BOOL(zend_autoload_unregister(&fcc TSRMLS_CC) == SUCCESS); } /* }}} */ /* {{{ proto false|array spl_autoload_functions() @@ -704,14 +532,12 @@ PHP_FUNCTION(spl_autoload_unregister) PHP_FUNCTION(spl_autoload_functions) { zend_function *fptr; - HashPosition function_pos; - autoload_func_info *alfi; if (zend_parse_parameters_none() == FAILURE) { return; } - if (!EG(autoload_func)) { + if (!EG(autoload_funcs)) { if (zend_hash_find(EG(function_table), ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME), (void **) &fptr) == SUCCESS) { array_init(return_value); add_next_index_stringl(return_value, ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME)-1, 1); @@ -720,48 +546,8 @@ PHP_FUNCTION(spl_autoload_functions) RETURN_FALSE; } - zend_hash_find(EG(function_table), "spl_autoload_call", sizeof("spl_autoload_call"), (void **) &fptr); - - if (EG(autoload_func) == fptr) { - array_init(return_value); - zend_hash_internal_pointer_reset_ex(SPL_G(autoload_functions), &function_pos); - while(zend_hash_has_more_elements_ex(SPL_G(autoload_functions), &function_pos) == SUCCESS) { - zend_hash_get_current_data_ex(SPL_G(autoload_functions), (void **) &alfi, &function_pos); - if (alfi->closure) { - Z_ADDREF_P(alfi->closure); - add_next_index_zval(return_value, alfi->closure); - } else if (alfi->func_ptr->common.scope) { - zval *tmp; - MAKE_STD_ZVAL(tmp); - array_init(tmp); - - if (alfi->obj) { - Z_ADDREF_P(alfi->obj); - add_next_index_zval(tmp, alfi->obj); - } else { - add_next_index_string(tmp, alfi->ce->name, 1); - } - add_next_index_string(tmp, alfi->func_ptr->common.function_name, 1); - add_next_index_zval(return_value, tmp); - } else { - if (strncmp(alfi->func_ptr->common.function_name, "__lambda_func", sizeof("__lambda_func") - 1)) { - add_next_index_string(return_value, alfi->func_ptr->common.function_name, 1); - } else { - char *key; - uint len; - long dummy; - zend_hash_get_current_key_ex(SPL_G(autoload_functions), &key, &len, &dummy, 0, &function_pos); - add_next_index_stringl(return_value, key, len - 1, 1); - } - } + /* TODO: Update SPL Functions */ - zend_hash_move_forward_ex(SPL_G(autoload_functions), &function_pos); - } - return; - } - - array_init(return_value); - add_next_index_string(return_value, EG(autoload_func)->common.function_name, 1); } /* }}} */ /* {{{ proto string spl_object_hash(object obj)