Skip to content

Commit 54e83d2

Browse files
committed
Implement delyed early binding for classes without parents
Fixes phpGH-8846
1 parent 5820528 commit 54e83d2

7 files changed

+90
-13
lines changed

Zend/zend_compile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8098,7 +8098,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
80988098
zend_add_literal_string(&key);
80998099

81008100
opline->opcode = ZEND_DECLARE_CLASS;
8101-
if (extends_ast && toplevel
8101+
if (toplevel
81028102
&& (CG(compiler_options) & ZEND_COMPILE_DELAYED_BINDING)
81038103
/* We currently don't early-bind classes that implement interfaces or use traits */
81048104
&& !ce->num_interfaces && !ce->num_traits

Zend/zend_inheritance.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3184,6 +3184,10 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
31843184
/* Check whether early binding is prevented due to unresolved types in inheritance checks. */
31853185
static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */
31863186
{
3187+
if (parent_ce == NULL) {
3188+
return INHERITANCE_SUCCESS;
3189+
}
3190+
31873191
zend_string *key;
31883192
zend_function *parent_func;
31893193
zend_property_info *parent_info;
@@ -3278,7 +3282,9 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_
32783282
zend_class_entry *orig_linking_class;
32793283
uint32_t is_cacheable = ce->ce_flags & ZEND_ACC_IMMUTABLE;
32803284

3281-
UPDATE_IS_CACHEABLE(parent_ce);
3285+
if (parent_ce) {
3286+
UPDATE_IS_CACHEABLE(parent_ce);
3287+
}
32823288
if (is_cacheable) {
32833289
if (zend_inheritance_cache_get && zend_inheritance_cache_add) {
32843290
zend_class_entry *ret = zend_inheritance_cache_get(ce, parent_ce, NULL);
@@ -3321,7 +3327,9 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_
33213327
zend_begin_record_errors();
33223328
}
33233329

3324-
zend_do_inheritance_ex(ce, parent_ce, status == INHERITANCE_SUCCESS);
3330+
if (parent_ce) {
3331+
zend_do_inheritance_ex(ce, parent_ce, status == INHERITANCE_SUCCESS);
3332+
}
33253333
if (parent_ce && parent_ce->num_interfaces) {
33263334
zend_do_inherit_interfaces(ce, parent_ce);
33273335
}

ext/opcache/tests/gh8846.phpt

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
--TEST--
2+
Bug GH-8846: Delayed early binding can be used for classes without parents
3+
--EXTENSIONS--
4+
opcache
5+
--CONFLICTS--
6+
server
7+
--INI--
8+
opcache.validate_timestamps=1
9+
opcache.revalidate_freq=0
10+
--FILE--
11+
<?php
12+
13+
file_put_contents(__DIR__ . '/gh8846-index.php', <<<'PHP'
14+
<?php
15+
if (!@$_GET['skip']) {
16+
include __DIR__ . '/gh8846-1.php';
17+
}
18+
include __DIR__ . '/gh8846-2.php';
19+
echo "Ok\n";
20+
PHP);
21+
22+
file_put_contents(__DIR__ . '/gh8846-1.php', <<<'PHP'
23+
<?php
24+
class Foo {
25+
const BAR = true;
26+
}
27+
PHP);
28+
29+
file_put_contents(__DIR__ . '/gh8846-2.php', <<<'PHP'
30+
<?php
31+
var_dump(Foo::BAR);
32+
class Foo {
33+
const BAR = true;
34+
}
35+
PHP);
36+
37+
// FIXME: Avoid this sleep
38+
sleep(2);
39+
40+
include 'php_cli_server.inc';
41+
php_cli_server_start('-d opcache.enable=1 -d opcache.enable_cli=1');
42+
43+
echo file_get_contents('http://' . PHP_CLI_SERVER_ADDRESS . '/gh8846-index.php');
44+
echo "\n";
45+
echo file_get_contents('http://' . PHP_CLI_SERVER_ADDRESS . '/gh8846-index.php?skip=1');
46+
?>
47+
--CLEAN--
48+
<?php
49+
@unlink(__DIR__ . '/gh8846-index.php');
50+
@unlink(__DIR__ . '/gh8846-1.php');
51+
@unlink(__DIR__ . '/gh8846-2.php');
52+
?>
53+
--EXPECTF--
54+
bool(true)
55+
<br />
56+
<b>Fatal error</b>: Cannot declare class Foo, because the name is already in use in <b>%sgh8846-2.php</b> on line <b>%d</b><br />
57+
58+
bool(true)
59+
Ok

ext/opcache/tests/php_cli_server.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ function php_cli_server_start($ini = "") {
1111
$ini_array = array_map(function($arg) {
1212
return trim($arg, '\'"');
1313
}, $ini_array);
14-
$cmd = [$php_executable, '-t', $doc_root, '-n', ...$ini_array, '-S', PHP_CLI_SERVER_ADDRESS];
14+
$cmd = [$php_executable, '-t', $doc_root, ...$ini_array, '-S', PHP_CLI_SERVER_ADDRESS];
1515
$descriptorspec = array(
1616
0 => STDIN,
1717
1 => STDOUT,

ext/opcache/zend_accelerator_util_funcs.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,9 @@ void zend_accel_build_delayed_early_binding_list(zend_persistent_script *persist
282282
zval *lcname = RT_CONSTANT(opline, opline->op1);
283283
early_binding->lcname = zend_string_copy(Z_STR_P(lcname));
284284
early_binding->rtd_key = zend_string_copy(Z_STR_P(lcname + 1));
285-
early_binding->lc_parent_name =
286-
zend_string_copy(Z_STR_P(RT_CONSTANT(opline, opline->op2)));
285+
early_binding->lc_parent_name = opline->op2_type != IS_UNUSED
286+
? zend_string_copy(Z_STR_P(RT_CONSTANT(opline, opline->op2)))
287+
: NULL;
287288
early_binding->cache_slot = (uint32_t) -1;
288289
early_binding++;
289290
}
@@ -328,7 +329,9 @@ void zend_accel_free_delayed_early_binding_list(zend_persistent_script *persiste
328329
zend_early_binding *early_binding = &persistent_script->early_bindings[i];
329330
zend_string_release(early_binding->lcname);
330331
zend_string_release(early_binding->rtd_key);
331-
zend_string_release(early_binding->lc_parent_name);
332+
if (early_binding->lc_parent_name) {
333+
zend_string_release(early_binding->lc_parent_name);
334+
}
332335
}
333336
efree(persistent_script->early_bindings);
334337
persistent_script->early_bindings = NULL;
@@ -357,10 +360,13 @@ static void zend_accel_do_delayed_early_binding(
357360
zval *zv = zend_hash_find_known_hash(EG(class_table), early_binding->rtd_key);
358361
if (zv) {
359362
zend_class_entry *orig_ce = Z_CE_P(zv);
360-
zend_class_entry *parent_ce =
361-
zend_hash_find_ex_ptr(EG(class_table), early_binding->lc_parent_name, 1);
362-
if (parent_ce) {
363-
ce = zend_try_early_bind(orig_ce, parent_ce, early_binding->lcname, zv);
363+
if (early_binding->lc_parent_name) {
364+
zend_class_entry *parent_ce = zend_hash_find_ex_ptr(EG(class_table), early_binding->lc_parent_name, 1);
365+
if (parent_ce) {
366+
ce = zend_try_early_bind(orig_ce, parent_ce, early_binding->lcname, zv);
367+
}
368+
} else {
369+
ce = zend_try_early_bind(orig_ce, NULL, early_binding->lcname, zv);
364370
}
365371
}
366372
}

ext/opcache/zend_persist.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1306,7 +1306,9 @@ static zend_early_binding *zend_persist_early_bindings(
13061306
for (uint32_t i = 0; i < num_early_bindings; i++) {
13071307
zend_accel_store_interned_string(early_bindings[i].lcname);
13081308
zend_accel_store_interned_string(early_bindings[i].rtd_key);
1309-
zend_accel_store_interned_string(early_bindings[i].lc_parent_name);
1309+
if (early_bindings[i].lc_parent_name) {
1310+
zend_accel_store_interned_string(early_bindings[i].lc_parent_name);
1311+
}
13101312
}
13111313
}
13121314
return early_bindings;

ext/opcache/zend_persist_calc.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,9 @@ static void zend_persist_early_bindings_calc(
584584
zend_early_binding *early_binding = &early_bindings[i];
585585
ADD_INTERNED_STRING(early_binding->lcname);
586586
ADD_INTERNED_STRING(early_binding->rtd_key);
587-
ADD_INTERNED_STRING(early_binding->lc_parent_name);
587+
if (early_binding->lc_parent_name) {
588+
ADD_INTERNED_STRING(early_binding->lc_parent_name);
589+
}
588590
}
589591
}
590592

0 commit comments

Comments
 (0)