Created
June 24, 2023 18:31
-
-
Save adsr/fcd1790b59cc5d8ea66b0235c933649f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/Zend/tests/is_in_autoload.phpt b/Zend/tests/is_in_autoload.phpt | |
new file mode 100644 | |
index 0000000000..29d4ca7ccc | |
--- /dev/null | |
+++ b/Zend/tests/is_in_autoload.phpt | |
@@ -0,0 +1,49 @@ | |
+--TEST-- | |
+Test is_in_autoload() functionality | |
+--FILE-- | |
+<?php | |
+ | |
+$depth = 0; | |
+ | |
+function report($tag) { | |
+ global $depth; | |
+ printf( | |
+ "%-5s depth=%d is_in_autoload=%s A=%s \A=%s B=%s \B=%s C=%s \C=%s\n", | |
+ $tag, | |
+ $depth, | |
+ is_in_autoload() ? 'y' : 'n', | |
+ is_in_autoload('A') ? 'y' : 'n', | |
+ is_in_autoload('\A') ? 'y' : 'n', | |
+ is_in_autoload('B') ? 'y' : 'n', | |
+ is_in_autoload('\B') ? 'y' : 'n', | |
+ is_in_autoload('C') ? 'y' : 'n', | |
+ is_in_autoload('\C') ? 'y' : 'n', | |
+ ); | |
+} | |
+ | |
+spl_autoload_register(function ($name) use (&$depth) { | |
+ $depth += 1; | |
+ report("pre{$name}"); | |
+ if ($name === 'A') { | |
+ class_exists('B'); | |
+ } else if ($name === 'B') { | |
+ class_exists('C'); | |
+ } | |
+ report("post{$name}"); | |
+ $depth -= 1; | |
+}); | |
+ | |
+report('init'); | |
+class_exists('\A'); | |
+report('final'); | |
+ | |
+?> | |
+--EXPECT-- | |
+init depth=0 is_in_autoload=n A=n \A=n B=n \B=n C=n \C=n | |
+preA depth=1 is_in_autoload=y A=y \A=y B=n \B=n C=n \C=n | |
+preB depth=2 is_in_autoload=y A=y \A=y B=y \B=y C=n \C=n | |
+preC depth=3 is_in_autoload=y A=y \A=y B=y \B=y C=y \C=y | |
+postC depth=3 is_in_autoload=y A=y \A=y B=y \B=y C=y \C=y | |
+postB depth=2 is_in_autoload=y A=y \A=y B=y \B=y C=n \C=n | |
+postA depth=1 is_in_autoload=y A=y \A=y B=n \B=n C=n \C=n | |
+final depth=0 is_in_autoload=n A=n \A=n B=n \B=n C=n \C=n | |
diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c | |
index acf69536d4..2dd3d7ca5a 100644 | |
--- a/Zend/zend_builtin_functions.c | |
+++ b/Zend/zend_builtin_functions.c | |
@@ -659,20 +659,51 @@ ZEND_FUNCTION(is_subclass_of) | |
} | |
/* }}} */ | |
/* {{{ Returns true if the first argument is an object and is this class or has this class as one of its parents, */ | |
ZEND_FUNCTION(is_a) | |
{ | |
is_a_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); | |
} | |
/* }}} */ | |
+/* {{{ Returns true if in autoload, optionally for a specific class */ | |
+ZEND_FUNCTION(is_in_autoload) | |
+{ | |
+ zend_string *class_name = NULL; | |
+ zend_string *lc_name; | |
+ bool is_in_autoload; | |
+ | |
+ ZEND_PARSE_PARAMETERS_START(0, 1) | |
+ Z_PARAM_OPTIONAL | |
+ Z_PARAM_STR_OR_NULL(class_name) | |
+ ZEND_PARSE_PARAMETERS_END(); | |
+ | |
+ if (class_name) { | |
+ if (ZSTR_VAL(class_name)[0] == '\\') { | |
+ lc_name = zend_string_alloc(ZSTR_LEN(class_name) - 1, 0); | |
+ zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(class_name) + 1, ZSTR_LEN(class_name) - 1); | |
+ } else { | |
+ lc_name = zend_string_tolower(class_name); | |
+ } | |
+ is_in_autoload = zend_is_in_autoload(lc_name); | |
+ zend_string_release_ex(lc_name, 0); | |
+ if (is_in_autoload) { | |
+ RETURN_TRUE; | |
+ } | |
+ } else if (zend_in_autoload_count() > 0) { | |
+ RETURN_TRUE; | |
+ } | |
+ RETURN_FALSE; | |
+} | |
+/* }}} */ | |
+ | |
/* {{{ add_class_vars */ | |
static void add_class_vars(zend_class_entry *scope, zend_class_entry *ce, bool statics, zval *return_value) | |
{ | |
zend_property_info *prop_info; | |
zval *prop, prop_copy; | |
zend_string *key; | |
zval *default_properties_table = CE_DEFAULT_PROPERTIES_TABLE(ce); | |
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->properties_info, key, prop_info) { | |
if (((prop_info->flags & ZEND_ACC_PROTECTED) && | |
diff --git a/Zend/zend_builtin_functions.stub.php b/Zend/zend_builtin_functions.stub.php | |
index 94d356e790..c38ce5e32f 100644 | |
--- a/Zend/zend_builtin_functions.stub.php | |
+++ b/Zend/zend_builtin_functions.stub.php | |
@@ -42,20 +42,22 @@ function get_class(object $object = UNKNOWN): string {} | |
function get_called_class(): string {} | |
function get_parent_class(object|string $object_or_class = UNKNOWN): string|false {} | |
/** @param object|string $object_or_class */ | |
function is_subclass_of(mixed $object_or_class, string $class, bool $allow_string = true): bool {} | |
/** @param object|string $object_or_class */ | |
function is_a(mixed $object_or_class, string $class, bool $allow_string = false): bool {} | |
+function is_in_autoload(string $class = null): bool {} | |
+ | |
/** | |
* @return array<string, mixed|ref> | |
* @refcount 1 | |
*/ | |
function get_class_vars(string $class): array {} | |
function get_object_vars(object $object): array {} | |
function get_mangled_object_vars(object $object): array {} | |
diff --git a/Zend/zend_builtin_functions_arginfo.h b/Zend/zend_builtin_functions_arginfo.h | |
index c39f847871..1096a205c6 100644 | |
--- a/Zend/zend_builtin_functions_arginfo.h | |
+++ b/Zend/zend_builtin_functions_arginfo.h | |
@@ -1,12 +1,12 @@ | |
/* This is a generated file, edit the .stub.php file instead. | |
- * Stub hash: 73e9ef76bde5ab44254185175d4d8dae2e797d12 */ | |
+ * Stub hash: e55c3b5b817c50603b5d5262f5d7be19ae303a2f */ | |
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_version, 0, 0, IS_STRING, 0) | |
ZEND_END_ARG_INFO() | |
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_func_num_args, 0, 0, IS_LONG, 0) | |
ZEND_END_ARG_INFO() | |
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_func_get_arg, 0, 1, IS_MIXED, 0) | |
ZEND_ARG_TYPE_INFO(0, position, IS_LONG, 0) | |
ZEND_END_ARG_INFO() | |
@@ -62,20 +62,24 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_is_subclass_of, 0, 2, _IS_BOOL, | |
ZEND_ARG_TYPE_INFO(0, class, IS_STRING, 0) | |
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, allow_string, _IS_BOOL, 0, "true") | |
ZEND_END_ARG_INFO() | |
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_is_a, 0, 2, _IS_BOOL, 0) | |
ZEND_ARG_TYPE_INFO(0, object_or_class, IS_MIXED, 0) | |
ZEND_ARG_TYPE_INFO(0, class, IS_STRING, 0) | |
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, allow_string, _IS_BOOL, 0, "false") | |
ZEND_END_ARG_INFO() | |
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_is_in_autoload, 0, 0, _IS_BOOL, 0) | |
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, class, IS_STRING, 0, "null") | |
+ZEND_END_ARG_INFO() | |
+ | |
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_get_class_vars, 0, 1, IS_ARRAY, 0) | |
ZEND_ARG_TYPE_INFO(0, class, IS_STRING, 0) | |
ZEND_END_ARG_INFO() | |
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_get_object_vars, 0, 1, IS_ARRAY, 0) | |
ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) | |
ZEND_END_ARG_INFO() | |
#define arginfo_get_mangled_object_vars arginfo_get_object_vars | |
@@ -228,20 +232,21 @@ ZEND_FUNCTION(strncmp); | |
ZEND_FUNCTION(strcasecmp); | |
ZEND_FUNCTION(strncasecmp); | |
ZEND_FUNCTION(error_reporting); | |
ZEND_FUNCTION(define); | |
ZEND_FUNCTION(defined); | |
ZEND_FUNCTION(get_class); | |
ZEND_FUNCTION(get_called_class); | |
ZEND_FUNCTION(get_parent_class); | |
ZEND_FUNCTION(is_subclass_of); | |
ZEND_FUNCTION(is_a); | |
+ZEND_FUNCTION(is_in_autoload); | |
ZEND_FUNCTION(get_class_vars); | |
ZEND_FUNCTION(get_object_vars); | |
ZEND_FUNCTION(get_mangled_object_vars); | |
ZEND_FUNCTION(get_class_methods); | |
ZEND_FUNCTION(method_exists); | |
ZEND_FUNCTION(property_exists); | |
ZEND_FUNCTION(class_exists); | |
ZEND_FUNCTION(interface_exists); | |
ZEND_FUNCTION(trait_exists); | |
ZEND_FUNCTION(enum_exists); | |
@@ -289,20 +294,21 @@ static const zend_function_entry ext_functions[] = { | |
ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(strcasecmp, arginfo_strcasecmp) | |
ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(strncasecmp, arginfo_strncasecmp) | |
ZEND_FE(error_reporting, arginfo_error_reporting) | |
ZEND_FE(define, arginfo_define) | |
ZEND_FE(defined, arginfo_defined) | |
ZEND_FE(get_class, arginfo_get_class) | |
ZEND_FE(get_called_class, arginfo_get_called_class) | |
ZEND_FE(get_parent_class, arginfo_get_parent_class) | |
ZEND_FE(is_subclass_of, arginfo_is_subclass_of) | |
ZEND_FE(is_a, arginfo_is_a) | |
+ ZEND_FE(is_in_autoload, arginfo_is_in_autoload) | |
ZEND_FE(get_class_vars, arginfo_get_class_vars) | |
ZEND_FE(get_object_vars, arginfo_get_object_vars) | |
ZEND_FE(get_mangled_object_vars, arginfo_get_mangled_object_vars) | |
ZEND_FE(get_class_methods, arginfo_get_class_methods) | |
ZEND_FE(method_exists, arginfo_method_exists) | |
ZEND_FE(property_exists, arginfo_property_exists) | |
ZEND_FE(class_exists, arginfo_class_exists) | |
ZEND_FE(interface_exists, arginfo_interface_exists) | |
ZEND_FE(trait_exists, arginfo_trait_exists) | |
ZEND_FE(enum_exists, arginfo_enum_exists) | |
diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h | |
index f2cb676478..7cbb4e8ece 100644 | |
--- a/Zend/zend_execute.h | |
+++ b/Zend/zend_execute.h | |
@@ -29,20 +29,23 @@ | |
#include <stdint.h> | |
BEGIN_EXTERN_C() | |
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); | |
+ZEND_API uint32_t zend_in_autoload_count(void); | |
+ZEND_API bool zend_is_in_autoload(zend_string *lc_name); | |
+ | |
void init_executor(void); | |
void shutdown_executor(void); | |
void shutdown_destructors(void); | |
ZEND_API void zend_shutdown_executor_values(bool fast_shutdown); | |
ZEND_API void zend_init_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value); | |
ZEND_API void zend_init_func_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value); | |
ZEND_API void zend_init_code_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value); | |
ZEND_API void zend_execute(zend_op_array *op_array, zval *return_value); | |
ZEND_API void execute_ex(zend_execute_data *execute_data); | |
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c | |
index dfb13005f6..e55b9e2b53 100644 | |
--- a/Zend/zend_execute_API.c | |
+++ b/Zend/zend_execute_API.c | |
@@ -260,20 +260,48 @@ void shutdown_destructors(void) /* {{{ */ | |
zend_hash_reverse_apply(&EG(symbol_table), (apply_func_t) zval_call_destructor); | |
} while (symbols != zend_hash_num_elements(&EG(symbol_table))); | |
zend_objects_store_call_destructors(&EG(objects_store)); | |
} zend_catch { | |
/* if we couldn't destruct cleanly, mark all objects as destructed anyway */ | |
zend_objects_store_mark_destructed(&EG(objects_store)); | |
} zend_end_try(); | |
} | |
/* }}} */ | |
+static zend_result zend_clear_in_autoload(zend_string *lc_name) /* {{{ */ | |
+{ | |
+ if (!EG(in_autoload)) { | |
+ return FAILURE; | |
+ } | |
+ return zend_hash_del(EG(in_autoload), lc_name); | |
+} | |
+/* }}} */ | |
+ | |
+static zend_result zend_set_in_autoload(zend_string *lc_name) /* {{{ */ | |
+{ | |
+ if (!EG(in_autoload)) { | |
+ ALLOC_HASHTABLE(EG(in_autoload)); | |
+ zend_hash_init(EG(in_autoload), 8, NULL, NULL, 0); | |
+ } | |
+ return zend_hash_add_empty_element(EG(in_autoload), lc_name) ? SUCCESS : FAILURE; | |
+} | |
+/* }}} */ | |
+ | |
+static void zend_in_autoload_destroy(void) /* {{{ */ | |
+{ | |
+ if (EG(in_autoload)) { | |
+ zend_hash_destroy(EG(in_autoload)); | |
+ FREE_HASHTABLE(EG(in_autoload)); | |
+ } | |
+} | |
+/* }}} */ | |
+ | |
/* Free values held by the executor. */ | |
ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) | |
{ | |
zend_string *key; | |
zval *zv; | |
EG(flags) |= EG_FLAGS_IN_RESOURCE_SHUTDOWN; | |
zend_try { | |
zend_close_rsrc_list(&EG(regular_list)); | |
} zend_end_try(); | |
@@ -459,24 +487,21 @@ void shutdown_executor(void) /* {{{ */ | |
zend_hash_destroy(*EG(symtable_cache_ptr)); | |
FREE_HASHTABLE(*EG(symtable_cache_ptr)); | |
} | |
zend_hash_destroy(&EG(included_files)); | |
zend_stack_destroy(&EG(user_error_handlers_error_reporting)); | |
zend_stack_destroy(&EG(user_error_handlers)); | |
zend_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)); | |
- } | |
+ zend_in_autoload_destroy(); | |
if (EG(ht_iterators) != EG(ht_iterators_slots)) { | |
efree(EG(ht_iterators)); | |
} | |
} | |
#if ZEND_DEBUG | |
if (EG(ht_iterators_used) && !CG(unclean_shutdown)) { | |
zend_error(E_WARNING, "Leaked %" PRIu32 " hashtable iterators", EG(ht_iterators_used)); | |
} | |
@@ -1187,64 +1212,78 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * | |
} | |
return NULL; | |
} | |
/* Verify class name before passing it to the autoloader. */ | |
if (!key && !ZSTR_HAS_CE_CACHE(name) && !zend_is_valid_class_name(name)) { | |
zend_string_release_ex(lc_name, 0); | |
return NULL; | |
} | |
- if (EG(in_autoload) == NULL) { | |
- ALLOC_HASHTABLE(EG(in_autoload)); | |
- zend_hash_init(EG(in_autoload), 8, NULL, NULL, 0); | |
- } | |
- | |
- if (zend_hash_add_empty_element(EG(in_autoload), lc_name) == NULL) { | |
+ if (zend_set_in_autoload(lc_name) != SUCCESS) { | |
+ /* Already autoloading class; bail to avoid recursing forever */ | |
if (!key) { | |
zend_string_release_ex(lc_name, 0); | |
} | |
return NULL; | |
} | |
if (ZSTR_VAL(name)[0] == '\\') { | |
autoload_name = zend_string_init(ZSTR_VAL(name) + 1, ZSTR_LEN(name) - 1, 0); | |
} else { | |
autoload_name = zend_string_copy(name); | |
} | |
zend_exception_save(); | |
ce = zend_autoload(autoload_name, lc_name); | |
zend_exception_restore(); | |
zend_string_release_ex(autoload_name, 0); | |
- zend_hash_del(EG(in_autoload), lc_name); | |
+ zend_clear_in_autoload(lc_name); | |
if (!key) { | |
zend_string_release_ex(lc_name, 0); | |
} | |
if (ce) { | |
ZEND_ASSERT(!CG(in_compilation)); | |
if (ce_cache) { | |
SET_CE_CACHE(ce_cache, ce); | |
} | |
} | |
return ce; | |
} | |
/* }}} */ | |
ZEND_API zend_class_entry *zend_lookup_class(zend_string *name) /* {{{ */ | |
{ | |
return zend_lookup_class_ex(name, NULL, 0); | |
} | |
/* }}} */ | |
+ZEND_API uint32_t zend_in_autoload_count(void) /* {{{ */ | |
+{ | |
+ if (!EG(in_autoload)) { | |
+ return 0; | |
+ } | |
+ return zend_hash_num_elements(EG(in_autoload)); | |
+} | |
+/* }}} */ | |
+ | |
+ZEND_API bool zend_is_in_autoload(zend_string *lc_name) /* {{{ */ | |
+{ | |
+ if (!EG(in_autoload)) { | |
+ return false; | |
+ } | |
+ return zend_hash_exists(EG(in_autoload), lc_name); | |
+} | |
+/* }}} */ | |
+ | |
ZEND_API zend_class_entry *zend_get_called_scope(zend_execute_data *ex) /* {{{ */ | |
{ | |
while (ex) { | |
if (Z_TYPE(ex->This) == IS_OBJECT) { | |
return Z_OBJCE(ex->This); | |
} else if (Z_CE(ex->This)) { | |
return Z_CE(ex->This); | |
} else if (ex->func) { | |
if (ex->func->type != ZEND_INTERNAL_FUNCTION || ex->func->common.scope) { | |
return NULL; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment