]> granicus.if.org Git - php/commitdiff
Cleanup SPL autoload implementation
authorNikita Popov <nikita.ppv@gmail.com>
Tue, 9 Jun 2020 15:36:36 +0000 (17:36 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Wed, 10 Jun 2020 07:38:47 +0000 (09:38 +0200)
Replace EG(autoload_func) with a C level zend_autoload hook.
This avoids having to do one indirection through PHP function
calls. The need for EG(autoload_func) was a leftover from the
__autoload() implementation.

Additionally, drop special-casing of spl_autoload(), and instead
register it just like any other autoloading function. This fixes
bug #71236 as a side-effect.

Finally, change spl_autoload_functions() to always return an array.
The distinction between false and an empty array no longer makes
sense here.

Closes GH-5696.

15 files changed:
NEWS
Zend/tests/bug49908.phpt
Zend/tests/generators/bug65161.phpt
Zend/tests/type_declarations/variance/loading_exception1.phpt
Zend/tests/type_declarations/variance/loading_exception2.phpt
Zend/tests/use_unlinked_class.phpt
Zend/zend_execute.h
Zend/zend_execute_API.c
Zend/zend_globals.h
ext/spl/php_spl.c
ext/spl/php_spl.stub.php
ext/spl/php_spl_arginfo.h
ext/spl/tests/bug71236.phpt [new file with mode: 0644]
ext/spl/tests/spl_autoload_002.phpt
ext/spl/tests/spl_autoload_012.phpt

diff --git a/NEWS b/NEWS
index 860bb9e879cf4ea01be35b4d2afb673c42dfe36b..970282d4d77769b9b1f5543a38696d11547e7875 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -160,6 +160,10 @@ PHP                                                                        NEWS
 - SQLite3:
   . Added SQLite3::setAuthorizer() and respective class constants. (bohwaz)
 
+- SPL:
+  . Fixed bug #71236 (Second call of spl_autoload_register() does nothing if it
+    has no arguments). (Nikita)
+
 - Standard:
   . Implemented FR #78638 (__PHP_Incomplete_Class should be final). (Laruence)
   . Fixed bug #77204 (getimagesize(): Read error! should mention file path).
index e108918b13ddb169be770df9fe8edb3b05b4e7c3..e545a0858cab6984bdcdd8757f029c64161c8091 100644 (file)
@@ -29,9 +29,7 @@ string(3) "Bar"
 
 Fatal error: Uncaught Exception: Bar in %s:%d
 Stack trace:
-#0 [internal function]: {closure}('Bar')
-#1 %s(%d): spl_autoload_call('Bar')
-#2 [internal function]: {closure}('Foo')
-#3 %s(%d): spl_autoload_call('Foo')
-#4 {main}
+#0 %s(%d): {closure}('Bar')
+#1 %s(%d): {closure}('Foo')
+#2 {main}
   thrown in %s on line %d
index ea0d65a8572477a8c4ef2240760d2c2e85c8786a..63c915b885b9c0fe44dc669b9c957be3c544e109 100644 (file)
@@ -19,8 +19,7 @@ foreach (testGenerator() as $i);
 --EXPECTF--
 Fatal error: Uncaught Error: Call to undefined function foo() in %s:%d
 Stack trace:
-#0 [internal function]: autoload('SyntaxError')
-#1 %s(%d): spl_autoload_call('SyntaxError')
-#2 %s(%d): testGenerator()
-#3 {main}
+#0 %s(%d): autoload('SyntaxError')
+#1 %s(%d): testGenerator()
+#2 {main}
   thrown in %s on line %d
index 89aeaf3c1222925caa4ffe1474f9a89bc7b59201..b535e4911254267e74db52904374caf7230caab4 100644 (file)
@@ -44,6 +44,5 @@ Class A does not exist
 
 Fatal error: During inheritance of B with variance dependencies: Uncaught Exception: Class A does not exist in %s:%d
 Stack trace:
-#0 [internal function]: {closure}('A')
-#1 %s(%d): spl_autoload_call('A')
-#2 {main} in %s on line %d
+#0 %s(%d): {closure}('A')
+#1 {main} in %s on line %d
index 2b46c183c24d0e21812efe03f446fdf60320fd31..f29264325855f646c1a8c6927b98272c1c7d3afb 100644 (file)
@@ -46,6 +46,5 @@ Class I does not exist
 
 Fatal error: During inheritance of B with variance dependencies: Uncaught Exception: Class I does not exist in %s:%d
 Stack trace:
-#0 [internal function]: {closure}('I')
-#1 %s(%d): spl_autoload_call('I')
-#2 {main} in %s on line %d
+#0 %s(%d): {closure}('I')
+#1 {main} in %s on line %d
index e85f97821fcde0773f248b90eb7262492ac3e73d..46ef2afd40d982de5f9e25229cd6c0df950922f0 100644 (file)
@@ -15,7 +15,6 @@ class A implements I {
 Fatal error: Uncaught ReflectionException: Class A does not exist in %s:%d
 Stack trace:
 #0 %s(%d): ReflectionClass->__construct('A')
-#1 [internal function]: {closure}('I')
-#2 %s(%d): spl_autoload_call('I')
-#3 {main}
+#1 %s(%d): {closure}('I')
+#2 {main}
   thrown in %s on line %d
index 58e1bbc2a9ef3d27b5e613569f013f399aabfdd0..0aa959d11f57b787a668668769b6952ee1306180 100644 (file)
@@ -31,6 +31,9 @@ struct _zend_fcall_info;
 ZEND_API extern void (*zend_execute_ex)(zend_execute_data *execute_data);
 ZEND_API extern void (*zend_execute_internal)(zend_execute_data *execute_data, zval *return_value);
 
+/* The lc_name may be stack allocated! */
+ZEND_API extern zend_class_entry *(*zend_autoload)(zend_string *name, zend_string *lc_name);
+
 void init_executor(void);
 void shutdown_executor(void);
 void shutdown_destructors(void);
index b650444d1c3943872bfeb16cb9c75c7ea25a593b..117eea7d6648fb44d210a652ae3f1ce9279c09d1 100644 (file)
@@ -44,6 +44,7 @@
 
 ZEND_API void (*zend_execute_ex)(zend_execute_data *execute_data);
 ZEND_API void (*zend_execute_internal)(zend_execute_data *execute_data, zval *return_value);
+ZEND_API zend_class_entry *(*zend_autoload)(zend_string *name, zend_string *lc_name);
 
 /* true globals */
 ZEND_API const zend_fcall_info empty_fcall_info = {0};
@@ -140,7 +141,6 @@ void init_executor(void) /* {{{ */
        EG(class_table) = CG(class_table);
 
        EG(in_autoload) = NULL;
-       EG(autoload_func) = NULL;
        EG(error_handling) = EH_NORMAL;
        EG(flags) = EG_FLAGS_INITIAL;
 
@@ -913,11 +913,9 @@ ZEND_API void zend_call_known_instance_method_with_2_params(
 ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *key, uint32_t flags) /* {{{ */
 {
        zend_class_entry *ce = NULL;
-       zval args[1], *zv;
-       zval local_retval;
+       zval *zv;
        zend_string *lc_name;
-       zend_fcall_info fcall_info;
-       zend_fcall_info_cache fcall_cache;
+       zend_string *autoload_name;
 
        if (key) {
                lc_name = key;
@@ -952,9 +950,7 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
                return ce;
        }
 
-       /* The compiler is not-reentrant. Make sure we __autoload() only during run-time
-        * (doesn't impact functionality of __autoload()
-       */
+       /* The compiler is not-reentrant. Make sure we autoload only during run-time. */
        if ((flags & ZEND_FETCH_CLASS_NO_AUTOLOAD) || zend_is_compiling()) {
                if (!key) {
                        zend_string_release_ex(lc_name, 0);
@@ -962,15 +958,14 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
                return NULL;
        }
 
-       if (!EG(autoload_func)) {
+       if (!zend_autoload) {
                if (!key) {
                        zend_string_release_ex(lc_name, 0);
                }
                return NULL;
-
        }
 
-       /* Verify class name before passing it to __autoload() */
+       /* Verify class name before passing it to the autoloader. */
        if (!key && strspn(ZSTR_VAL(name), "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\\") != ZSTR_LEN(name)) {
                zend_string_release_ex(lc_name, 0);
                return NULL;
@@ -988,39 +983,19 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
                return NULL;
        }
 
-       ZVAL_UNDEF(&local_retval);
-
        if (ZSTR_VAL(name)[0] == '\\') {
-               ZVAL_STRINGL(&args[0], ZSTR_VAL(name) + 1, ZSTR_LEN(name) - 1);
+               autoload_name = zend_string_init(ZSTR_VAL(name) + 1, ZSTR_LEN(name) - 1, 0);
        } else {
-               ZVAL_STR_COPY(&args[0], name);
+               autoload_name = zend_string_copy(name);
        }
 
-       fcall_info.size = sizeof(fcall_info);
-       ZVAL_STR_COPY(&fcall_info.function_name, EG(autoload_func)->common.function_name);
-       fcall_info.retval = &local_retval;
-       fcall_info.param_count = 1;
-       fcall_info.params = args;
-       fcall_info.object = NULL;
-       fcall_info.no_separation = 1;
-
-       fcall_cache.function_handler = EG(autoload_func);
-       fcall_cache.called_scope = NULL;
-       fcall_cache.object = NULL;
-
        zend_exception_save();
-       if ((zend_call_function(&fcall_info, &fcall_cache) == SUCCESS) && !EG(exception)) {
-               ce = zend_hash_find_ptr(EG(class_table), lc_name);
-       }
+       ce = zend_autoload(autoload_name, lc_name);
        zend_exception_restore();
 
-       zval_ptr_dtor(&args[0]);
-       zval_ptr_dtor_str(&fcall_info.function_name);
-
+       zend_string_release_ex(autoload_name, 0);
        zend_hash_del(EG(in_autoload), lc_name);
 
-       zval_ptr_dtor(&local_retval);
-
        if (!key) {
                zend_string_release_ex(lc_name, 0);
        }
index 049800a32d41488dea2443990cfe4315870e7ebb..a91833cd85010a4198b36e4101d1446e76e78339 100644 (file)
@@ -176,7 +176,6 @@ struct _zend_executor_globals {
        uint32_t persistent_classes_count;
 
        HashTable *in_autoload;
-       zend_function *autoload_func;
        zend_bool full_tables_cleanup;
 
        /* for extended information support */
index 324dbe4b7213e729bb915cb0c61f41aab93f56e6..e38a15582fbabd0d50fd43a2e060a7706ff79c8e 100644 (file)
@@ -47,16 +47,13 @@ ZEND_DECLARE_MODULE_GLOBALS(spl)
 
 #define SPL_DEFAULT_FILE_EXTENSIONS ".inc,.php"
 
-static zend_function *spl_autoload_fn = NULL;
-static zend_function *spl_autoload_call_fn = NULL;
-
 /* {{{ PHP_GINIT_FUNCTION
  */
 static PHP_GINIT_FUNCTION(spl)
 {
-       spl_globals->autoload_extensions     = NULL;
-       spl_globals->autoload_functions      = NULL;
-       spl_globals->autoload_running        = 0;
+       spl_globals->autoload_extensions = NULL;
+       spl_globals->autoload_functions = NULL;
+       spl_globals->autoload_running = 0;
 }
 /* }}} */
 
@@ -394,104 +391,93 @@ static void autoload_func_info_dtor(zval *element)
        efree(alfi);
 }
 
-/* {{{ proto void spl_autoload_call(string class_name)
- Try all registered autoload function to load the requested class */
-PHP_FUNCTION(spl_autoload_call)
-{
-       zend_string *class_name, *lc_name, *func_name;
-       autoload_func_info *alfi;
-       zval retval, params[1];
-
-       if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &class_name) == FAILURE) {
-               RETURN_THROWS();
+static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_string *lc_name) {
+       if (!SPL_G(autoload_functions)) {
+               return NULL;
        }
 
+       HashPosition pos;
+       zend_ulong num_idx;
+       zend_function *func;
+       zend_fcall_info fci;
+       zend_fcall_info_cache fcic;
+       zval params[1];
+       zval retval;
+       zend_string *func_name;
+       zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data));
+       int l_autoload_running = SPL_G(autoload_running);
+
+       SPL_G(autoload_running) = 1;
+
+       fci.size = sizeof(fci);
+       fci.retval = &retval;
+       fci.param_count = 1;
+       fci.params = params;
+       fci.no_separation = 1;
        ZVAL_STR(&params[0], class_name);
 
-       if (SPL_G(autoload_functions)) {
-               HashPosition pos;
-               zend_ulong num_idx;
-               zend_function *func;
-               zend_fcall_info fci;
-               zend_fcall_info_cache fcic;
-               zend_class_entry *called_scope = zend_get_called_scope(execute_data);
-               int l_autoload_running = SPL_G(autoload_running);
-
-               SPL_G(autoload_running) = 1;
-               lc_name = zend_string_tolower(class_name);
-
-               fci.size = sizeof(fci);
-               fci.retval = &retval;
-               fci.param_count = 1;
-               fci.params = params;
-               fci.no_separation = 1;
-
-               ZVAL_UNDEF(&fci.function_name); /* Unused */
-
-               zend_hash_internal_pointer_reset_ex(SPL_G(autoload_functions), &pos);
-               while (zend_hash_get_current_key_ex(SPL_G(autoload_functions), &func_name, &num_idx, &pos) == HASH_KEY_IS_STRING) {
-                       alfi = zend_hash_get_current_data_ptr_ex(SPL_G(autoload_functions), &pos);
-                       func = alfi->func_ptr;
-                       if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
-                               func = emalloc(sizeof(zend_op_array));
-                               memcpy(func, alfi->func_ptr, sizeof(zend_op_array));
-                               zend_string_addref(func->op_array.function_name);
-                       }
-                       ZVAL_UNDEF(&retval);
-                       fcic.function_handler = func;
-                       if (Z_ISUNDEF(alfi->obj)) {
-                               fci.object = NULL;
-                               fcic.object = NULL;
-                               if (alfi->ce &&
-                                   (!called_scope ||
-                                    !instanceof_function(called_scope, alfi->ce))) {
-                                       fcic.called_scope = alfi->ce;
-                               } else {
-                                       fcic.called_scope = called_scope;
-                               }
+       ZVAL_UNDEF(&fci.function_name); /* Unused */
+
+       zend_hash_internal_pointer_reset_ex(SPL_G(autoload_functions), &pos);
+       while (zend_hash_get_current_key_ex(SPL_G(autoload_functions), &func_name, &num_idx, &pos) == HASH_KEY_IS_STRING) {
+               autoload_func_info *alfi =
+                       zend_hash_get_current_data_ptr_ex(SPL_G(autoload_functions), &pos);
+               func = alfi->func_ptr;
+               if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
+                       func = emalloc(sizeof(zend_op_array));
+                       memcpy(func, alfi->func_ptr, sizeof(zend_op_array));
+                       zend_string_addref(func->op_array.function_name);
+               }
+               ZVAL_UNDEF(&retval);
+               fcic.function_handler = func;
+               if (Z_ISUNDEF(alfi->obj)) {
+                       fci.object = NULL;
+                       fcic.object = NULL;
+                       if (alfi->ce &&
+                               (!called_scope ||
+                                !instanceof_function(called_scope, alfi->ce))) {
+                               fcic.called_scope = alfi->ce;
                        } else {
-                               fci.object = Z_OBJ(alfi->obj);
-                               fcic.object = Z_OBJ(alfi->obj);
-                               fcic.called_scope = Z_OBJCE(alfi->obj);
+                               fcic.called_scope = called_scope;
                        }
+               } else {
+                       fci.object = Z_OBJ(alfi->obj);
+                       fcic.object = Z_OBJ(alfi->obj);
+                       fcic.called_scope = Z_OBJCE(alfi->obj);
+               }
 
-                       zend_call_function(&fci, &fcic);
-                       zval_ptr_dtor(&retval);
-
-                       if (EG(exception)) {
-                               break;
-                       }
+               zend_call_function(&fci, &fcic);
+               zval_ptr_dtor(&retval);
 
-                       if (pos + 1 == SPL_G(autoload_functions)->nNumUsed ||
-                           zend_hash_exists(EG(class_table), lc_name)) {
-                               break;
-                       }
-                       zend_hash_move_forward_ex(SPL_G(autoload_functions), &pos);
+               if (EG(exception)) {
+                       break;
                }
-               zend_string_release_ex(lc_name, 0);
-               SPL_G(autoload_running) = l_autoload_running;
-       } else {
-               /* do not use or overwrite &EG(autoload_func) here */
-               zend_fcall_info fcall_info;
-               zend_fcall_info_cache fcall_cache;
 
-               ZVAL_UNDEF(&retval);
+               zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name);
+               if (ce) {
+                       SPL_G(autoload_running) = l_autoload_running;
+                       return ce;
+               }
 
-               fcall_info.size = sizeof(fcall_info);
-               ZVAL_UNDEF(&fcall_info.function_name);
-               fcall_info.retval = &retval;
-               fcall_info.param_count = 1;
-               fcall_info.params = params;
-               fcall_info.object = NULL;
-               fcall_info.no_separation = 1;
+               zend_hash_move_forward_ex(SPL_G(autoload_functions), &pos);
+       }
+       SPL_G(autoload_running) = l_autoload_running;
+       return NULL;
+}
 
-               fcall_cache.function_handler = spl_autoload_fn;
-               fcall_cache.called_scope = NULL;
-               fcall_cache.object = NULL;
+/* {{{ proto void spl_autoload_call(string class_name)
+ Try all registered autoload function to load the requested class */
+PHP_FUNCTION(spl_autoload_call)
+{
+       zend_string *class_name;
 
-               zend_call_function(&fcall_info, &fcall_cache);
-               zval_ptr_dtor(&retval);
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &class_name) == FAILURE) {
+               RETURN_THROWS();
        }
+
+       zend_string *lc_name = zend_string_tolower(class_name);
+       spl_perform_autoload(class_name, lc_name);
+       zend_string_release(lc_name);
 } /* }}} */
 
 #define HT_MOVE_TAIL_TO_HEAD(ht)                                                       \
@@ -511,7 +497,6 @@ PHP_FUNCTION(spl_autoload_register)
        zend_string *lc_name;
        zend_bool do_throw = 1;
        zend_bool prepend  = 0;
-       zend_function *spl_func_ptr;
        autoload_func_info alfi;
        zend_object *obj_ptr;
        zend_fcall_info fci = {0};
@@ -529,6 +514,11 @@ PHP_FUNCTION(spl_autoload_register)
                        "spl_autoload_register() will always throw");
        }
 
+       if (!SPL_G(autoload_functions)) {
+               ALLOC_HASHTABLE(SPL_G(autoload_functions));
+               zend_hash_init(SPL_G(autoload_functions), 1, NULL, autoload_func_info_dtor, 0);
+       }
+
        /* If first arg is not null */
        if (ZEND_FCI_INITIALIZED(fci)) {
                alfi.ce = fcc.calling_scope;
@@ -561,7 +551,7 @@ PHP_FUNCTION(spl_autoload_register)
                }
                zend_string_release(func_name);
 
-               if (SPL_G(autoload_functions) && zend_hash_exists(SPL_G(autoload_functions), lc_name)) {
+               if (zend_hash_exists(SPL_G(autoload_functions), lc_name)) {
                        if (!Z_ISUNDEF(alfi.closure)) {
                                Z_DELREF_P(&alfi.closure);
                        }
@@ -579,28 +569,6 @@ PHP_FUNCTION(spl_autoload_register)
                        ZVAL_UNDEF(&alfi.obj);
                }
 
-               if (!SPL_G(autoload_functions)) {
-                       ALLOC_HASHTABLE(SPL_G(autoload_functions));
-                       zend_hash_init(SPL_G(autoload_functions), 1, NULL, autoload_func_info_dtor, 0);
-               }
-
-               spl_func_ptr = spl_autoload_fn;
-
-               if (EG(autoload_func) == spl_func_ptr) { /* registered already, so we insert that first */
-                       autoload_func_info spl_alfi;
-
-                       spl_alfi.func_ptr = spl_func_ptr;
-                       ZVAL_UNDEF(&spl_alfi.obj);
-                       ZVAL_UNDEF(&spl_alfi.closure);
-                       spl_alfi.ce = NULL;
-                       zend_hash_add_mem(SPL_G(autoload_functions), spl_autoload_fn->common.function_name,
-                                       &spl_alfi, sizeof(autoload_func_info));
-                       if (prepend && SPL_G(autoload_functions)->nNumOfElements > 1) {
-                               /* Move the newly created element to the head of the hashtable */
-                               HT_MOVE_TAIL_TO_HEAD(SPL_G(autoload_functions));
-                       }
-               }
-
                if (UNEXPECTED(alfi.func_ptr == &EG(trampoline))) {
                        zend_function *copy = emalloc(sizeof(zend_op_array));
 
@@ -626,12 +594,19 @@ PHP_FUNCTION(spl_autoload_register)
                }
 skip:
                zend_string_release_ex(lc_name, 0);
-       }
-
-       if (SPL_G(autoload_functions)) {
-               EG(autoload_func) = spl_autoload_call_fn;
        } else {
-               EG(autoload_func) = spl_autoload_fn;
+               autoload_func_info spl_alfi;
+               spl_alfi.func_ptr = zend_hash_str_find_ptr(
+                       CG(function_table), "spl_autoload", sizeof("spl_autoload") - 1);
+               ZVAL_UNDEF(&spl_alfi.obj);
+               ZVAL_UNDEF(&spl_alfi.closure);
+               spl_alfi.ce = NULL;
+               zend_hash_add_mem(SPL_G(autoload_functions), spl_alfi.func_ptr->common.function_name,
+                               &spl_alfi, sizeof(autoload_func_info));
+               if (prepend && SPL_G(autoload_functions)->nNumOfElements > 1) {
+                       /* Move the newly created element to the head of the hashtable */
+                       HT_MOVE_TAIL_TO_HEAD(SPL_G(autoload_functions));
+               }
        }
 
        RETURN_TRUE;
@@ -646,7 +621,6 @@ PHP_FUNCTION(spl_autoload_unregister)
        zend_string *lc_name;
        zval *zcallable;
        int success = FAILURE;
-       zend_function *spl_func_ptr;
        zend_object *obj_ptr;
        zend_fcall_info_cache fcc;
 
@@ -686,13 +660,12 @@ PHP_FUNCTION(spl_autoload_unregister)
        zend_string_release_ex(func_name, 0);
 
        if (SPL_G(autoload_functions)) {
-               if (zend_string_equals(lc_name, spl_autoload_call_fn->common.function_name)) {
+               if (zend_string_equals_literal(lc_name, "spl_autoload_call")) {
                        /* remove all */
                        if (!SPL_G(autoload_running)) {
                                zend_hash_destroy(SPL_G(autoload_functions));
                                FREE_HASHTABLE(SPL_G(autoload_functions));
                                SPL_G(autoload_functions) = NULL;
-                               EG(autoload_func) = NULL;
                        } else {
                                zend_hash_clean(SPL_G(autoload_functions));
                        }
@@ -707,14 +680,6 @@ PHP_FUNCTION(spl_autoload_unregister)
                                success = zend_hash_del(SPL_G(autoload_functions), lc_name);
                        }
                }
-       } else if (zend_string_equals(lc_name, spl_autoload_fn->common.function_name)) {
-               /* register single spl_autoload() */
-               spl_func_ptr = spl_autoload_fn;
-
-               if (EG(autoload_func) == spl_func_ptr) {
-                       success = SUCCESS;
-                       EG(autoload_func) = NULL;
-               }
        }
 
        zend_string_release_ex(lc_name, 0);
@@ -725,21 +690,14 @@ PHP_FUNCTION(spl_autoload_unregister)
  Return all registered autoloader functions */
 PHP_FUNCTION(spl_autoload_functions)
 {
-       zend_function *fptr;
        autoload_func_info *alfi;
 
        if (zend_parse_parameters_none() == FAILURE) {
                RETURN_THROWS();
        }
 
-       if (!EG(autoload_func)) {
-               RETURN_FALSE;
-       }
-
-       fptr = spl_autoload_call_fn;
-
-       if (EG(autoload_func) == fptr) {
-               array_init(return_value);
+       array_init(return_value);
+       if (SPL_G(autoload_functions)) {
                ZEND_HASH_FOREACH_PTR(SPL_G(autoload_functions), alfi) {
                        if (!Z_ISUNDEF(alfi->closure)) {
                                Z_ADDREF(alfi->closure);
@@ -760,11 +718,7 @@ PHP_FUNCTION(spl_autoload_functions)
                                add_next_index_str(return_value, zend_string_copy(alfi->func_ptr->common.function_name));
                        }
                } ZEND_HASH_FOREACH_END();
-               return;
        }
-
-       array_init(return_value);
-       add_next_index_str(return_value, zend_string_copy(EG(autoload_func)->common.function_name));
 } /* }}} */
 
 /* {{{ proto string spl_object_hash(object obj)
@@ -859,6 +813,8 @@ PHP_MINFO_FUNCTION(spl)
  */
 PHP_MINIT_FUNCTION(spl)
 {
+       zend_autoload = spl_perform_autoload;
+
        PHP_MINIT(spl_exceptions)(INIT_FUNC_ARGS_PASSTHRU);
        PHP_MINIT(spl_iterators)(INIT_FUNC_ARGS_PASSTHRU);
        PHP_MINIT(spl_array)(INIT_FUNC_ARGS_PASSTHRU);
@@ -868,10 +824,6 @@ PHP_MINIT_FUNCTION(spl)
        PHP_MINIT(spl_fixedarray)(INIT_FUNC_ARGS_PASSTHRU);
        PHP_MINIT(spl_observer)(INIT_FUNC_ARGS_PASSTHRU);
 
-       spl_autoload_fn = zend_hash_str_find_ptr(CG(function_table), "spl_autoload", sizeof("spl_autoload") - 1);
-       spl_autoload_call_fn = zend_hash_str_find_ptr(CG(function_table), "spl_autoload_call", sizeof("spl_autoload_call") - 1);
-       ZEND_ASSERT(spl_autoload_fn != NULL && spl_autoload_call_fn != NULL);
-
        return SUCCESS;
 }
 /* }}} */
index b2c570b5f135966549b06c0fff55cb18fdf28451..a4c6b2ab2290d7a1cf7c9f4374dd6e62dd85685a 100755 (executable)
@@ -10,12 +10,11 @@ function class_uses($what, bool $autoload = true): array|false {}
 
 function spl_autoload(string $class_name, ?string $file_extensions = null): void {}
 
-// This silently ignores non-string class names
-function spl_autoload_call($class_name): void {}
+function spl_autoload_call(string $class_name): void {}
 
 function spl_autoload_extensions(?string $file_extensions = null): string {}
 
-function spl_autoload_functions(): array|false {}
+function spl_autoload_functions(): array {}
 
 function spl_autoload_register(?callable $autoload_function = null, bool $throw = true, bool $prepend = false): bool {}
 
index 01d881d7407d01f64fcf94d14dbe575096736f2e..abe9392071faf770a58c11301ca4ed5ab6e1d0a9 100644 (file)
@@ -18,14 +18,14 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_autoload, 0, 1, IS_VOID, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_autoload_call, 0, 1, IS_VOID, 0)
-       ZEND_ARG_INFO(0, class_name)
+       ZEND_ARG_TYPE_INFO(0, class_name, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_autoload_extensions, 0, 0, IS_STRING, 0)
        ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, file_extensions, IS_STRING, 1, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_spl_autoload_functions, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_autoload_functions, 0, 0, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_autoload_register, 0, 0, _IS_BOOL, 0)
@@ -38,8 +38,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_autoload_unregister, 0, 1, _
        ZEND_ARG_INFO(0, autoload_function)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_classes, 0, 0, IS_ARRAY, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_spl_classes arginfo_spl_autoload_functions
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_object_hash, 0, 1, IS_STRING, 0)
        ZEND_ARG_TYPE_INFO(0, obj, IS_OBJECT, 0)
diff --git a/ext/spl/tests/bug71236.phpt b/ext/spl/tests/bug71236.phpt
new file mode 100644 (file)
index 0000000..55c7cb8
--- /dev/null
@@ -0,0 +1,23 @@
+--TEST--
+Bug #71236: Second call of spl_autoload_register() does nothing if it has no arguments
+--FILE--
+<?php
+
+spl_autoload_register(function ($class) {});
+spl_autoload_register();
+var_dump(spl_autoload_functions());
+
+?>
+--EXPECT--
+array(2) {
+  [0]=>
+  object(Closure)#1 (1) {
+    ["parameter"]=>
+    array(1) {
+      ["$class"]=>
+      string(10) "<required>"
+    }
+  }
+  [1]=>
+  string(12) "spl_autoload"
+}
index 98ba09dad870b6658b01b05427d49aead14fe86b..1a67baabd0b3c1807a8557d63117062330fd25b1 100644 (file)
@@ -1,9 +1,5 @@
 --TEST--
 SPL: spl_autoload_functions()
---SKIPIF--
-<?php
-if (spl_autoload_functions() !== false) die('skip __autoload() registered by php.ini');
-?>
 --FILE--
 <?php
 
@@ -40,7 +36,8 @@ var_dump(spl_autoload_functions());
 
 ?>
 --EXPECT--
-bool(false)
+array(0) {
+}
 array(1) {
   [0]=>
   string(12) "spl_autoload"
@@ -59,9 +56,11 @@ array(2) {
   [1]=>
   string(16) "SplAutoloadTest2"
 }
-bool(false)
+array(0) {
+}
 array(1) {
   [0]=>
   string(12) "spl_autoload"
 }
-bool(false)
+array(0) {
+}
index 7b13b7105a16bc476c1a806b6ac0df1cb77bec8e..218d3e800ff2304239276ae1073270c409140ef9 100644 (file)
@@ -47,7 +47,6 @@ autoload_first
 Fatal error: Uncaught Exception: first in %sspl_autoload_012.php:%d
 Stack trace:
 #0 [internal function]: autoload_first('ThisClassDoesNo...')
-#1 [internal function]: spl_autoload_call('ThisClassDoesNo...')
-#2 %sspl_autoload_012.php(%d): class_exists('ThisClassDoesNo...')
-#3 {main}
+#1 %sspl_autoload_012.php(%d): class_exists('ThisClassDoesNo...')
+#2 {main}
   thrown in %s on line %d