Commit da58c655198 for php.net
commit da58c655198f787b1e7f100cc537561fab64d98a
Author: Arnaud Le Blanc <arnaud.lb@gmail.com>
Date: Tue Apr 21 10:43:10 2026 +0200
gen_stub: Support fn_flags2 flags
Change zend_function_entry.flags to a uint64_t to that both ZEND_ACC_ and
ZEND_ACC2_ flags can be represented.
Introduce ZEND_FENTRY_FLAGS(flags, flags2) to pass ZEND_ACC2_ flags to
ZEND_RAW_FENTRY(), ZEND_FENTRY().
Source-level backwards compatibility is maintained, as passing raw ZEND_ACC_
flags to ZEND_RAW_FENTRY(), ZEND_FENTRY() still works.
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index 2541486c492..c97d9308e20 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -3045,7 +3045,6 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
internal_function->prop_info = NULL;
internal_function->attributes = NULL;
internal_function->frameless_function_infos = ptr->frameless_function_infos;
- internal_function->fn_flags2 = 0;
if (EG(active)) { // at run-time: this ought to only happen if registered with dl() or somehow temporarily at runtime
ZEND_MAP_PTR_INIT(internal_function->run_time_cache, zend_arena_calloc(&CG(arena), 1, zend_internal_run_time_cache_reserved_size()));
} else {
@@ -3055,7 +3054,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
ZEND_MAP_PTR_INIT(internal_function->run_time_cache, NULL);
#endif
}
- if (ptr->flags) {
+ if (ptr->flags & UINT32_MAX) {
if (!(ptr->flags & ZEND_ACC_PPP_MASK)) {
if (ptr->flags != ZEND_ACC_DEPRECATED && scope) {
zend_error(error_type, "Invalid access level for %s::%s() - access must be exactly one of public, protected or private", ZSTR_VAL(scope->name), ptr->fname);
@@ -3067,6 +3066,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
} else {
internal_function->fn_flags = ZEND_ACC_PUBLIC;
}
+ internal_function->fn_flags2 = ptr->flags >> 32;
if (ptr->arg_info) {
zend_internal_function_info *info = (zend_internal_function_info*)ptr->arg_info;
diff --git a/Zend/zend_API.h b/Zend/zend_API.h
index 17f7ce3263f..aff6a5bba22 100644
--- a/Zend/zend_API.h
+++ b/Zend/zend_API.h
@@ -36,7 +36,7 @@ typedef struct _zend_function_entry {
zif_handler handler;
const struct _zend_internal_arg_info *arg_info;
uint32_t num_args;
- uint32_t flags;
+ uint64_t flags;
const zend_frameless_function_info *frameless_function_infos;
const char *doc_comment;
} zend_function_entry;
@@ -74,6 +74,8 @@ typedef struct _zend_fcall_info_cache {
#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(zif_##name)
#define ZEND_METHOD(classname, name) ZEND_NAMED_FUNCTION(zim_##classname##_##name)
+#define ZEND_FENTRY_FLAGS(flags, flags2) (((uint64_t)flags) | ((uint64_t)flags2 << 32))
+
#define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags, NULL, NULL },
#define ZEND_RAW_FENTRY(zend_name, name, arg_info, flags, frameless_function_infos, doc_comment) { zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags, frameless_function_infos, doc_comment },
diff --git a/build/gen_stub.php b/build/gen_stub.php
index db7f4d400ed..857c6cb7087 100755
--- a/build/gen_stub.php
+++ b/build/gen_stub.php
@@ -1184,6 +1184,32 @@ public function isEmpty(): bool {
);
}
+ /**
+ * If we have ZEND_ACC2_ flags, represent as 'ZEND_FENTRY_FLAGS(flags1, flags2)'.
+ * Otherwise, represent as just 'flags1' (backwards compatible).
+ */
+ private function formatFlags(array $flags): string {
+ $flags1 = [];
+ $flags2 = [];
+ foreach ($flags as $flag) {
+ if (str_starts_with($flag, 'ZEND_ACC2_')) {
+ $flags2[] = $flag;
+ } else {
+ $flags1[] = $flag;
+ }
+ }
+
+ if ($flags2 !== []) {
+ return sprintf(
+ 'ZEND_FENTRY_FLAGS(%s, %s)',
+ $flags1 === [] ? 0 : implode("|", $flags1),
+ implode("|", $flags2),
+ );
+ }
+
+ return implode("|", $flags1);
+ }
+
public function generateVersionDependentFlagCode(
string $codeTemplate,
?int $phpVersionIdMinimumCompatibility,
@@ -1199,7 +1225,7 @@ public function generateVersionDependentFlagCode(
if (empty($flagsByPhpVersions[$currentPhpVersion])) {
return '';
}
- return sprintf($codeTemplate, implode("|", $flagsByPhpVersions[$currentPhpVersion]));
+ return sprintf($codeTemplate, $this->formatFlags($flagsByPhpVersions[$currentPhpVersion]));
}
ksort($flagsByPhpVersions);
@@ -1240,7 +1266,7 @@ public function generateVersionDependentFlagCode(
reset($flagsByPhpVersions);
$firstVersion = key($flagsByPhpVersions);
if ($firstVersion === $phpVersionIdMinimumCompatibility) {
- return sprintf($codeTemplate, implode("|", reset($flagsByPhpVersions)));
+ return sprintf($codeTemplate, $this->formatFlags(reset($flagsByPhpVersions)));
}
}
@@ -1253,7 +1279,7 @@ public function generateVersionDependentFlagCode(
$code .= "$if (PHP_VERSION_ID >= $version)\n";
- $code .= sprintf($codeTemplate, implode("|", $versionFlags));
+ $code .= sprintf($codeTemplate, $this->formatFlags($versionFlags));
$code .= $endif;
$i++;
diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php
index c437f0d7f6c..0f04cc036d4 100644
--- a/ext/standard/basic_functions.stub.php
+++ b/ext/standard/basic_functions.stub.php
@@ -1641,7 +1641,10 @@ function in_array(mixed $needle, array $haystack, bool $strict = false): bool {}
*/
function array_search(mixed $needle, array $haystack, bool $strict = false): int|string|false {}
-/** @prefer-ref $array */
+/**
+ * @prefer-ref $array
+ * @forbid-dynamic-calls
+ */
function extract(array &$array, int $flags = EXTR_OVERWRITE, string $prefix = ""): int {}
/**
diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h
index 1ba20c6b26c..7ad59cfc689 100644
Binary files a/ext/standard/basic_functions_arginfo.h and b/ext/standard/basic_functions_arginfo.h differ
diff --git a/ext/standard/basic_functions_decl.h b/ext/standard/basic_functions_decl.h
index ab27bb64f0c..0dda2f894f1 100644
Binary files a/ext/standard/basic_functions_decl.h and b/ext/standard/basic_functions_decl.h differ