diff --git a/Zend/tests/autoloading/function/exceptions_during_autoloading004.phpt b/Zend/tests/autoloading/function/exceptions_during_autoloading004.phpt index b3149ea08dc99..baa58dd83ab28 100644 --- a/Zend/tests/autoloading/function/exceptions_during_autoloading004.phpt +++ b/Zend/tests/autoloading/function/exceptions_during_autoloading004.phpt @@ -29,7 +29,6 @@ namespace bar { try { foo(); } catch (\Throwable $e) { - /* No autoloading for unqualified function names */ do { echo $e::class, ': ', $e->getMessage(), "\n"; } while ($e = $e->getPrevious()); @@ -52,7 +51,8 @@ Try-catch around function_exists() autoload_first first Try-catch around unqualified function call -Error: Call to undefined function bar\foo() +autoload_first +Exception: first Try-catch around qualified function call autoload_first Exception: first diff --git a/Zend/tests/autoloading/function/global_fallback002.phpt b/Zend/tests/autoloading/function/global_fallback002.phpt index f6a734afa9d03..df4d3d96a6aa4 100644 --- a/Zend/tests/autoloading/function/global_fallback002.phpt +++ b/Zend/tests/autoloading/function/global_fallback002.phpt @@ -1,5 +1,5 @@ --TEST-- -Fallback to global function should not trigger autoloading. +Fallback to global function triggers autoloading once. --FILE-- --EXPECT-- +function loader called with bar\foo I am foo in global namespace. diff --git a/Zend/tests/autoloading/function/global_fallback003.phpt b/Zend/tests/autoloading/function/global_fallback003.phpt new file mode 100644 index 0000000000000..1d93dd8499fec --- /dev/null +++ b/Zend/tests/autoloading/function/global_fallback003.phpt @@ -0,0 +1,27 @@ +--TEST-- +Fallback to non-existent function triggers autoloading once in namespace, once in global. +--FILE-- +getMessage() . "\n"; + } +} + +?> +--EXPECT-- +function loader called with bar\non_existent_function +function loader called with non_existent_function +Error correctly caught: Call to undefined function bar\non_existent_function() diff --git a/Zend/tests/autoloading/function/global_fallback_doesnt_repeat_autoloading.phpt b/Zend/tests/autoloading/function/global_fallback_doesnt_repeat_autoloading.phpt new file mode 100644 index 0000000000000..5d7dd91da002a --- /dev/null +++ b/Zend/tests/autoloading/function/global_fallback_doesnt_repeat_autoloading.phpt @@ -0,0 +1,42 @@ +--TEST-- +Fallback to global function should trigger autoloading only once per namespace. +--FILE-- + +--EXPECT-- +function loader called with bar\foo +I am foo in global namespace. +I am foo in global namespace. +I am foo in global namespace. +I am foo in global namespace. +I am foo in global namespace. +function loader called with Quux\foo +I am foo in global namespace. diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index a5c5e4458ccce..66b49323e8a0e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3859,19 +3859,27 @@ ZEND_VM_HOT_HANDLER(69, ZEND_INIT_NS_FCALL_BY_NAME, ANY, CONST, NUM|CACHE_SLOT) if (UNEXPECTED(fbc == NULL)) { zval *function_name = (zval *)RT_CONSTANT(opline, opline->op2); /* Fetch lowercase name stored in the next literal slot */ - fbc = zend_lookup_function_ex(Z_STR_P(function_name), Z_STR_P(function_name+1), /* use_autoload */ false); + fbc = zend_lookup_function_ex(Z_STR_P(function_name), Z_STR_P(function_name+1), /* use_autoload */ true); if (UNEXPECTED(fbc == NULL)) { if (UNEXPECTED(EG(exception))) { HANDLE_EXCEPTION(); } /* Fallback onto global namespace, by fetching the unqualified lowercase name stored in the second literal slot */ - fbc = zend_lookup_function_ex(Z_STR_P(function_name+2), Z_STR_P(function_name+2), /* use_autoload */ false); - if (fbc == NULL) { + fbc = zend_lookup_function_ex(Z_STR_P(function_name+2), Z_STR_P(function_name+2), /* use_autoload */ true); + if (UNEXPECTED(fbc == NULL)) { if (UNEXPECTED(EG(exception))) { HANDLE_EXCEPTION(); } ZEND_VM_DISPATCH_TO_HELPER(zend_undefined_function_helper); } + /* We bind the unqualified name to the global function + * Use the lowercase name of the function stored in the first cache slot as + * function names are case insensitive */ + else { + zval tmp; + ZVAL_STR(&tmp, Z_STR_P(function_name+1)); + do_bind_function(fbc, &tmp); + } } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 0a753f5ad3cd1..da921ba68d79d 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3764,19 +3764,27 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_NS_FCALL_BY_N if (UNEXPECTED(fbc == NULL)) { zval *function_name = (zval *)RT_CONSTANT(opline, opline->op2); /* Fetch lowercase name stored in the next literal slot */ - fbc = zend_lookup_function_ex(Z_STR_P(function_name), Z_STR_P(function_name+1), /* use_autoload */ false); + fbc = zend_lookup_function_ex(Z_STR_P(function_name), Z_STR_P(function_name+1), /* use_autoload */ true); if (UNEXPECTED(fbc == NULL)) { if (UNEXPECTED(EG(exception))) { HANDLE_EXCEPTION(); } /* Fallback onto global namespace, by fetching the unqualified lowercase name stored in the second literal slot */ - fbc = zend_lookup_function_ex(Z_STR_P(function_name+2), Z_STR_P(function_name+2), /* use_autoload */ false); - if (fbc == NULL) { + fbc = zend_lookup_function_ex(Z_STR_P(function_name+2), Z_STR_P(function_name+2), /* use_autoload */ true); + if (UNEXPECTED(fbc == NULL)) { if (UNEXPECTED(EG(exception))) { HANDLE_EXCEPTION(); } ZEND_VM_TAIL_CALL(zend_undefined_function_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + /* We bind the unqualified name to the global function + * Use the lowercase name of the function stored in the first cache slot as + * function names are case insensitive */ + else { + zval tmp; + ZVAL_STR(&tmp, Z_STR_P(function_name+1)); + do_bind_function(fbc, &tmp); + } } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array);