Skip to content
45 changes: 1 addition & 44 deletions Zend/Optimizer/zend_optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1526,52 +1526,9 @@ static bool needs_live_range(const zend_op_array *op_array, const zend_op *def_o
return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0;
}

static void zend_foreach_op_array_helper(
zend_op_array *op_array, zend_op_array_func_t func, void *context) {
func(op_array, context);
for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) {
zend_foreach_op_array_helper(op_array->dynamic_func_defs[i], func, context);
}
}

void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context)
{
zval *zv;
zend_op_array *op_array;

zend_foreach_op_array_helper(&script->main_op_array, func, context);

ZEND_HASH_MAP_FOREACH_PTR(&script->function_table, op_array) {
zend_foreach_op_array_helper(op_array, func, context);
} ZEND_HASH_FOREACH_END();

ZEND_HASH_MAP_FOREACH_VAL(&script->class_table, zv) {
if (Z_TYPE_P(zv) == IS_ALIAS_PTR) {
continue;
}
const zend_class_entry *ce = Z_CE_P(zv);
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
if (op_array->scope == ce
&& op_array->type == ZEND_USER_FUNCTION
&& !(op_array->fn_flags & ZEND_ACC_ABSTRACT)
&& !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
zend_foreach_op_array_helper(op_array, func, context);
}
} ZEND_HASH_FOREACH_END();

zend_property_info *property;
ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, property) {
zend_function **hooks = property->hooks;
if (property->ce == ce && property->hooks) {
for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
const zend_function *hook = hooks[i];
if (hook && hook->common.scope == ce && !(hooks[i]->op_array.fn_flags & ZEND_ACC_TRAIT_CLONE)) {
zend_foreach_op_array_helper(&hooks[i]->op_array, func, context);
}
}
}
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FOREACH_END();
zend_foreach_op_array_ex(&script->main_op_array, &script->function_table, &script->class_table, func, context);
}

static void step_optimize_op_array(zend_op_array *op_array, void *context) {
Expand Down
6 changes: 6 additions & 0 deletions Zend/tests/first_class_callable/constexpr/namespace_004.phpt
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
--TEST--
Allow defining FCC in const expressions in a namespace with function matching a global function later.
--SKIPIF--
<?php
if (function_exists('opcache_get_status')) {
die('skip not for opcache');
}
?>
--FILE--
<?php

Expand Down
17 changes: 17 additions & 0 deletions Zend/tests/ns_global_func_assumption_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
NS global function assumption: basic optimization
--FILE--
<?php

namespace Ns {
function test() {
// Unqualified strlen() should be compiled as a direct call to \strlen()
return strlen("hello");
}
}

namespace {
var_dump(Ns\test());
}
--EXPECT--
int(5)
8 changes: 8 additions & 0 deletions Zend/tests/ns_global_func_assumption_002.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Ns {
function test() {
// Unqualified strlen() should be compiled as \strlen() initially
return strlen("hello");
}
}
22 changes: 22 additions & 0 deletions Zend/tests/ns_global_func_assumption_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
NS global function assumption: deoptimization when shadow is declared at runtime
--FILE--
<?php

// Include a file with Ns\test() that calls strlen() unqualified.
// It should be compiled assuming strlen() resolves to \strlen().
include __DIR__ . '/ns_global_func_assumption_002.inc';

// First call: no shadow exists, uses global strlen() (constant-folded).
var_dump(Ns\test());

// Now declare Ns\strlen() which shadows the global strlen().
include __DIR__ . '/ns_global_func_assumption_002_shadow.inc';

// Second call: Ns\test() should be deoptimized and use Ns\strlen().
var_dump(Ns\test());

?>
--EXPECT--
int(5)
string(8) "shadow:5"
7 changes: 7 additions & 0 deletions Zend/tests/ns_global_func_assumption_002_shadow.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Ns {
function strlen(string $str): string {
return "shadow:" . \strlen($str);
}
}
1 change: 1 addition & 0 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -3042,6 +3042,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
internal_function->function_name = zend_string_init_interned(ptr->fname, fname_len, 1);
internal_function->scope = scope;
internal_function->prototype = NULL;
internal_function->fn_flags2 = 0;
internal_function->prop_info = NULL;
internal_function->attributes = NULL;
internal_function->frameless_function_infos = ptr->frameless_function_infos;
Expand Down
10 changes: 10 additions & 0 deletions Zend/zend_bitset.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,4 +296,14 @@ static inline int zend_bitset_pop_first(zend_bitset set, uint32_t len) {
return i;
}

static inline bool zend_bitset_has_intersection(zend_bitset set1, zend_bitset set2, uint32_t len)
{
for (uint32_t i = 0; i < len; i++) {
if (set1[i] & set2[i]) {
return 1;
}
}
return 0;
}

#endif /* _ZEND_BITSET_H_ */
Loading