Hi, Please look into patch that implements __autoload() support for namespaces. The patch touches not only ZE but also SPL.
In the following code we cannot exactly know if "Exception" class is from current namespace or it is an internal PHP class. <?php namespace Foo; throw new Exception; In this case PHP first looks for Foo::Exception and only then for internal Exception, but the first lookup may call __autoload (or corresponding SPL functions) and it can emit error or throw exception. The patch provides an additional boolean argument to __autoload() that say if class is really required. In case if it false, user code shouldn't emit errors or throw exceptions. I am going to commit the patch on Wednesday. It is the last semantic patch for namespaces we have. After its commit we are going to look into syntax problems (namespace or package, brackets, import or using, ...). Thanks. Dmitry.
Index: Zend/zend_builtin_functions.c =================================================================== RCS file: /repository/ZendEngine2/zend_builtin_functions.c,v retrieving revision 1.349 diff -u -p -d -r1.349 zend_builtin_functions.c --- Zend/zend_builtin_functions.c 24 Aug 2007 13:50:52 -0000 1.349 +++ Zend/zend_builtin_functions.c 24 Aug 2007 14:33:21 -0000 @@ -689,7 +689,7 @@ static void is_a_impl(INTERNAL_FUNCTION_ convert_to_text_ex(class_name); - if (zend_u_lookup_class_ex(Z_TYPE_PP(class_name), Z_UNIVAL_PP(class_name), Z_UNILEN_PP(class_name), 0, 1, &ce TSRMLS_CC) == FAILURE) { + if (zend_u_lookup_class_ex(Z_TYPE_PP(class_name), Z_UNIVAL_PP(class_name), Z_UNILEN_PP(class_name), ZEND_AUTOLOAD_DISABLED, 1, &ce TSRMLS_CC) == FAILURE) { retval = 0; } else { if (only_subclass) { Index: Zend/zend_compile.c =================================================================== RCS file: /repository/ZendEngine2/zend_compile.c,v retrieving revision 1.765 diff -u -p -d -r1.765 zend_compile.c --- Zend/zend_compile.c 24 Aug 2007 13:50:52 -0000 1.765 +++ Zend/zend_compile.c 24 Aug 2007 14:33:22 -0000 @@ -1355,8 +1355,8 @@ void zend_do_end_function_declaration(zn } if (lcname_len == sizeof(ZEND_AUTOLOAD_FUNC_NAME) - 1 && ZEND_U_EQUAL(utype, lcname, lcname_len, ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME)-1) && - CG(active_op_array)->num_args != 1) { - zend_error(E_COMPILE_ERROR, "%s() must take exactly 1 argument", ZEND_AUTOLOAD_FUNC_NAME); + CG(active_op_array)->num_args != 1 && CG(active_op_array)->num_args != 2) { + zend_error(E_COMPILE_ERROR, "%s() must take exactly 1 or 2 arguments", ZEND_AUTOLOAD_FUNC_NAME); } if (lcname.s != lcname_buf) { efree(lcname.s); Index: Zend/zend_execute.h =================================================================== RCS file: /repository/ZendEngine2/zend_execute.h,v retrieving revision 1.108 diff -u -p -d -r1.108 zend_execute.h --- Zend/zend_execute.h 21 Jul 2007 00:34:41 -0000 1.108 +++ Zend/zend_execute.h 24 Aug 2007 14:33:22 -0000 @@ -75,6 +75,19 @@ static inline void safe_free_zval_ptr_re FREE_ZVAL_REL(p); } } + +/* The following constants should be used as "use_autoload" argument + for function zend_u_lookup_class_ex() and thay control the execution + of user function __autoload. + The function __autoload() is extended with additional boolean argument + that shows if class is stricly required or may be absent. + This argument is necessary to avoid error reporting in user code during + resolving of ambiguous class name in namespace. +*/ +#define ZEND_AUTOLOAD_DISABLED 0 /* doesn't call __autoload() */ +#define ZEND_AUTOLOAD_REQUIRED 1 /* calls __autoload($name, true); */ +#define ZEND_AUTOLOAD_ENABLED 2 /* calls __autoload($name, false); */ + ZEND_API int zend_lookup_class(char *name, int name_length, zend_class_entry ***ce TSRMLS_DC); ZEND_API int zend_lookup_class_ex(char *name, int name_length, int use_autoload, zend_class_entry ***ce TSRMLS_DC); ZEND_API int zend_u_lookup_class(zend_uchar type, zstr name, int name_length, zend_class_entry ***ce TSRMLS_DC); Index: Zend/zend_execute_API.c =================================================================== RCS file: /repository/ZendEngine2/zend_execute_API.c,v retrieving revision 1.413 diff -u -p -d -r1.413 zend_execute_API.c --- Zend/zend_execute_API.c 24 Aug 2007 13:50:52 -0000 1.413 +++ Zend/zend_execute_API.c 24 Aug 2007 14:33:22 -0000 @@ -853,7 +853,7 @@ int zend_call_function(zend_fcall_info * } else if (calling_scope && clen == sizeof("parent") - 1 && ZEND_U_EQUAL(Z_TYPE_P(fci->function_name), lcname, clen, "parent", sizeof("parent")-1)) { ce_child = EG(active_op_array) && EG(active_op_array)->scope ? EG(scope)->parent : NULL; - } else if (zend_u_lookup_class_ex(Z_TYPE_P(fci->function_name), lcname, clen, 1, 0, &pce TSRMLS_CC) == SUCCESS) { + } else if (zend_u_lookup_class_ex(Z_TYPE_P(fci->function_name), lcname, clen, ZEND_AUTOLOAD_REQUIRED, 0, &pce TSRMLS_CC) == SUCCESS) { ce_child = *pce; } efree(lcname.v); @@ -1128,9 +1128,9 @@ int zend_call_function(zend_fcall_info * ZEND_API int zend_u_lookup_class_ex(zend_uchar type, zstr name, int name_length, int use_autoload, int do_normalize, zend_class_entry ***ce TSRMLS_DC) /* {{{ */ { - zval **args[1]; + zval **args[2]; zval autoload_function; - zval *class_name_ptr; + zval *class_name_ptr, *required_ptr; zval *retval_ptr = NULL; int retval; unsigned int lc_name_len; @@ -1174,7 +1174,7 @@ ZEND_API int zend_u_lookup_class_ex(zend /* The compiler is not-reentrant. Make sure we __autoload() only during run-time * (doesn't impact fuctionality of __autoload() */ - if (!use_autoload || zend_is_compiling(TSRMLS_C)) { + if (use_autoload == ZEND_AUTOLOAD_DISABLED || zend_is_compiling(TSRMLS_C)) { if (do_normalize) { efree(lc_free.v); } @@ -1199,14 +1199,19 @@ ZEND_API int zend_u_lookup_class_ex(zend INIT_PZVAL(class_name_ptr); ZVAL_ZSTRL(class_name_ptr, type, name, name_length, 1); + ALLOC_ZVAL(required_ptr); + INIT_PZVAL(required_ptr); + ZVAL_BOOL(required_ptr, use_autoload == ZEND_AUTOLOAD_REQUIRED); + args[0] = &class_name_ptr; + args[1] = &required_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.param_count = 2; fcall_info.params = args; fcall_info.object_pp = NULL; fcall_info.no_separation = 1; @@ -1226,6 +1231,7 @@ ZEND_API int zend_u_lookup_class_ex(zend } zval_ptr_dtor(&class_name_ptr); + zval_ptr_dtor(&required_ptr); zend_u_hash_del(EG(in_autoload), type, lc_name, lc_name_len+1); @@ -1261,7 +1267,7 @@ ZEND_API int zend_u_lookup_class_ex(zend ZEND_API int zend_u_lookup_class(zend_uchar type, zstr name, int name_length, zend_class_entry ***ce TSRMLS_DC) /* {{{ */ { - return zend_u_lookup_class_ex(type, name, name_length, 1, 1, ce TSRMLS_CC); + return zend_u_lookup_class_ex(type, name, name_length, ZEND_AUTOLOAD_REQUIRED, 1, ce TSRMLS_CC); } /* }}} */ @@ -1658,9 +1664,12 @@ void zend_unset_timeout(TSRMLS_D) /* {{{ ZEND_API zend_class_entry *zend_u_fetch_class(zend_uchar type, zstr class_name, uint class_name_len, int fetch_type TSRMLS_DC) /* {{{ */ { zend_class_entry **pce; - int use_autoload = (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) ? 0 : 1; int do_normalize = (fetch_type & ZEND_FETCH_CLASS_NO_NORMALIZE) ? 0 : 1; int rt_ns_check = (fetch_type & ZEND_FETCH_CLASS_RT_NS_CHECK) ? 1 : 0; + int use_autoload = (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) ? + ZEND_AUTOLOAD_DISABLED : + (rt_ns_check ? ZEND_AUTOLOAD_ENABLED : + ZEND_AUTOLOAD_REQUIRED); zstr lcname = class_name; fetch_type = fetch_type & ~ZEND_FETCH_CLASS_FLAGS; @@ -1705,7 +1714,7 @@ check_fetch_type: php_name.u = u_memrchr(lcname.u, ':', class_name_len); if (php_name.u) { php_name.u++; - if (zend_u_lookup_class_ex(type, php_name, class_name_len-(php_name.u-lcname.u), 0, do_normalize, &pce TSRMLS_CC)==SUCCESS && + if (zend_u_lookup_class_ex(type, php_name, class_name_len-(php_name.u-lcname.u), ZEND_AUTOLOAD_DISABLED, do_normalize, &pce TSRMLS_CC)==SUCCESS && (*pce)->type == ZEND_INTERNAL_CLASS) { if (lcname.v != class_name.v) { efree(lcname.v); @@ -1717,7 +1726,7 @@ check_fetch_type: php_name.s = zend_memrchr(lcname.s, ':', class_name_len); if (php_name.s) { php_name.s++; - if (zend_u_lookup_class_ex(type, php_name, class_name_len-(php_name.s-lcname.s), 0, do_normalize, &pce TSRMLS_CC)==SUCCESS && + if (zend_u_lookup_class_ex(type, php_name, class_name_len-(php_name.s-lcname.s), ZEND_AUTOLOAD_DISABLED, do_normalize, &pce TSRMLS_CC)==SUCCESS && (*pce)->type == ZEND_INTERNAL_CLASS) { if (lcname.v != class_name.v) { efree(lcname.v); @@ -1727,7 +1736,7 @@ check_fetch_type: } } } - if (use_autoload) { + if (use_autoload != ZEND_AUTOLOAD_DISABLED) { if (fetch_type == ZEND_FETCH_CLASS_INTERFACE) { zend_error(E_ERROR, "Interface '%R' not found", type, class_name); } else { Index: Zend/tests/errmsg_012.phpt =================================================================== RCS file: /repository/ZendEngine2/tests/errmsg_012.phpt,v retrieving revision 1.1 diff -u -p -d -r1.1 errmsg_012.phpt --- Zend/tests/errmsg_012.phpt 2 Feb 2007 12:53:54 -0000 1.1 +++ Zend/tests/errmsg_012.phpt 24 Aug 2007 14:33:22 -0000 @@ -1,11 +1,11 @@ --TEST-- -errmsg: __autoload() must take exactly 1 argument +errmsg: __autoload() must take exactly 1 or 2 arguments --FILE-- <?php -function __autoload($a, $b) {} +function __autoload($a, $b, $c) {} echo "Done\n"; ?> --EXPECTF-- -Fatal error: __autoload() must take exactly 1 argument in %s on line %d +Fatal error: __autoload() must take exactly 1 or 2 arguments in %s on line %d Index: Zend/tests/ns_054.phpt =================================================================== RCS file: Zend/tests/ns_054.phpt diff -N Zend/tests/ns_054.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/ns_054.phpt 24 Aug 2007 14:33:22 -0000 @@ -0,0 +1,17 @@ +--TEST-- +054: namespaces and __autoload() +--FILE-- +<?php +namespace test::ns1; + +eval('function __autoload($x,$y) {echo "$x\n"; var_dump($y);}'); + +$x = new stdClass; +$x = new undefined; +--EXPECTF-- +test::ns1::stdClass +bool(false) +test::ns1::undefined +bool(true) + +Fatal error: Class 'test::ns1::undefined' not found in %sns_054.php on line 7 Index: Zend/tests/ns_055.phpt =================================================================== RCS file: Zend/tests/ns_055.phpt diff -N Zend/tests/ns_055.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/ns_055.phpt 24 Aug 2007 14:33:22 -0000 @@ -0,0 +1,24 @@ +--TEST-- +055: namespaces and apl_autoload() +--SKIPIF-- +<?php if (!extension_loaded("spl")) print "skip"; ?> +--FILE-- +<?php +namespace test::ns1; + +function autoload($x,$y) { + echo "$x\n"; + var_dump($y); +} + +spl_autoload_register(__NAMESPACE__ . "::autoload"); + +$x = new stdClass; +$x = new undefined; +--EXPECTF-- +test::ns1::stdClass +bool(false) +test::ns1::undefined +bool(true) + +Fatal error: Class 'test::ns1::undefined' not found in %sns_055.php on line 12 Index: ext/spl/php_spl.c =================================================================== RCS file: /repository/php-src/ext/spl/php_spl.c,v retrieving revision 1.114 diff -u -p -d -r1.114 php_spl.c --- ext/spl/php_spl.c 9 Apr 2007 15:33:59 -0000 1.114 +++ ext/spl/php_spl.c 24 Aug 2007 14:33:22 -0000 @@ -266,10 +266,28 @@ PHP_FUNCTION(spl_autoload) zend_op **original_opline_ptr = EG(opline_ptr); zend_op_array *original_active_op_array = EG(active_op_array); zend_function_state *original_function_state_ptr = EG(function_state_ptr); + zval *arg2 = NULL; + zend_bool required = 1; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "x|x", &class_name, &class_name_len, &file_exts, &file_exts_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "x|z", &class_name, &class_name_len, &arg2) == FAILURE) { RETURN_FALSE; } + if (arg2) { + if (Z_TYPE_P(arg2) == IS_BOOL) { + required = Z_LVAL_P(arg2); + } else { + convert_to_text(arg2); + if (Z_TYPE_P(arg2) == ZEND_STR_TYPE) { + file_exts = Z_UNIVAL_P(arg2); + file_exts_len = Z_UNILEN_P(arg2); + } else { + zend_error(E_WARNING, "spl_autoload() expects parameter 2 to be %s, %s given", + zend_get_type_by_const(ZEND_STR_TYPE), + zend_zval_type_name(arg2)); + RETURN_FALSE; + } + } + } copy = pos1 = ezstrndup(ZEND_STR_TYPE, file_exts, file_exts_len); lc_name = zend_u_str_tolower_dup(ZEND_STR_TYPE, class_name, class_name_len); @@ -308,7 +326,7 @@ PHP_FUNCTION(spl_autoload) EG(active_op_array) = original_active_op_array; EG(function_state_ptr) = original_function_state_ptr; - if (!found && !SPL_G(autoload_running)) { + if (!found && !SPL_G(autoload_running) && required) { zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Class %v could not be loaded", class_name); } } /* }}} */ @@ -349,20 +367,23 @@ static void autoload_func_info_dtor(auto Try all registerd autoload function to load the requested class */ PHP_FUNCTION(spl_autoload_call) { - zval *zclass_name, *retval = NULL; + zval *zclass_name, *zrequired, *retval = NULL; int class_name_len, func_name_type; zstr class_name, func_name, lc_name; uint func_name_len; ulong dummy; HashPosition function_pos; autoload_func_info *alfi; + zend_bool required = 1; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "x", &class_name, &class_name_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "x|b", &class_name, &class_name_len, &required) == FAILURE) { return; } MAKE_STD_ZVAL(zclass_name); ZVAL_ZSTRL(zclass_name, ZEND_STR_TYPE, class_name, class_name_len, 1); + MAKE_STD_ZVAL(zrequired); + ZVAL_BOOL(zrequired, required); if (SPL_G(autoload_functions)) { int l_autoload_running = SPL_G(autoload_running); SPL_G(autoload_running) = 1; @@ -371,7 +392,7 @@ PHP_FUNCTION(spl_autoload_call) while(zend_hash_has_more_elements_ex(SPL_G(autoload_functions), &function_pos) == SUCCESS && !EG(exception)) { func_name_type = 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_u_call_method(alfi->obj ? &alfi->obj : NULL, alfi->ce, &alfi->func_ptr, func_name_type, func_name, func_name_len, &retval, 1, zclass_name, NULL TSRMLS_CC); + zend_u_call_method(alfi->obj ? &alfi->obj : NULL, alfi->ce, &alfi->func_ptr, func_name_type, func_name, func_name_len, &retval, 2, zclass_name, zrequired TSRMLS_CC); if (retval) { zval_ptr_dtor(&retval); } @@ -387,6 +408,7 @@ PHP_FUNCTION(spl_autoload_call) zend_call_method_with_1_params(NULL, NULL, NULL, "spl_autoload", NULL, zclass_name); } zval_ptr_dtor(&zclass_name); + zval_ptr_dtor(&zrequired); } /* }}} */ /* {{{ proto bool spl_autoload_register([mixed autoload_function = "spl_autoload" [, throw = true]]) U
-- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php