Skip to content

Commit

Permalink
Implement delyed early binding for parentless classes
Browse files Browse the repository at this point in the history
  • Loading branch information
iluuu1994 committed May 10, 2023
1 parent 7304b56 commit a77457e
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 13 deletions.
2 changes: 1 addition & 1 deletion Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -8098,7 +8098,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
zend_add_literal_string(&key);

opline->opcode = ZEND_DECLARE_CLASS;
if (extends_ast && toplevel
if (toplevel
&& (CG(compiler_options) & ZEND_COMPILE_DELAYED_BINDING)
/* We currently don't early-bind classes that implement interfaces or use traits */
&& !ce->num_interfaces && !ce->num_traits
Expand Down
12 changes: 10 additions & 2 deletions Zend/zend_inheritance.c
Original file line number Diff line number Diff line change
Expand Up @@ -3184,6 +3184,10 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
/* Check whether early binding is prevented due to unresolved types in inheritance checks. */
static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */
{
if (parent_ce == NULL) {
return INHERITANCE_SUCCESS;
}

zend_string *key;
zend_function *parent_func;
zend_property_info *parent_info;
Expand Down Expand Up @@ -3278,7 +3282,9 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_
zend_class_entry *orig_linking_class;
uint32_t is_cacheable = ce->ce_flags & ZEND_ACC_IMMUTABLE;

UPDATE_IS_CACHEABLE(parent_ce);
if (parent_ce) {
UPDATE_IS_CACHEABLE(parent_ce);
}
if (is_cacheable) {
if (zend_inheritance_cache_get && zend_inheritance_cache_add) {
zend_class_entry *ret = zend_inheritance_cache_get(ce, parent_ce, NULL);
Expand Down Expand Up @@ -3321,7 +3327,9 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_
zend_begin_record_errors();
}

zend_do_inheritance_ex(ce, parent_ce, status == INHERITANCE_SUCCESS);
if (parent_ce) {
zend_do_inheritance_ex(ce, parent_ce, status == INHERITANCE_SUCCESS);
}
if (parent_ce && parent_ce->num_interfaces) {
zend_do_inherit_interfaces(ce, parent_ce);
}
Expand Down
59 changes: 59 additions & 0 deletions ext/opcache/tests/gh8846.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
--TEST--
Bug GH-8846: Always use delayed early binding over early binding in OPcache
--EXTENSIONS--
opcache
--CONFLICTS--
server
--INI--
opcache.validate_timestamps=1
opcache.revalidate_freq=0
--FILE--
<?php

file_put_contents(__DIR__ . '/gh8846-index.php', <<<'PHP'
<?php
if (!@$_GET['skip']) {
include __DIR__ . '/gh8846-1.php';
}
include __DIR__ . '/gh8846-2.php';
echo "Ok\n";
PHP);

file_put_contents(__DIR__ . '/gh8846-1.php', <<<'PHP'
<?php
class Foo {
const BAR = true;
}
PHP);

file_put_contents(__DIR__ . '/gh8846-2.php', <<<'PHP'
<?php
var_dump(Foo::BAR);
class Foo {
const BAR = true;
}
PHP);

// FIXME: Avoid this sleep
sleep(2);

include 'php_cli_server.inc';
php_cli_server_start('-d opcache.enable=1 -d opcache.enable_cli=1');

echo file_get_contents('http://' . PHP_CLI_SERVER_ADDRESS . '/gh8846-index.php');
echo "\n";
echo file_get_contents('http://' . PHP_CLI_SERVER_ADDRESS . '/gh8846-index.php?skip=1');
?>
--CLEAN--
<?php
@unlink(__DIR__ . '/gh8846-index.php');
@unlink(__DIR__ . '/gh8846-1.php');
@unlink(__DIR__ . '/gh8846-2.php');
?>
--EXPECTF--
bool(true)
<br />
<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 />

bool(true)
Ok
2 changes: 1 addition & 1 deletion ext/opcache/tests/php_cli_server.inc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function php_cli_server_start($ini = "") {
$ini_array = array_map(function($arg) {
return trim($arg, '\'"');
}, $ini_array);
$cmd = [$php_executable, '-t', $doc_root, '-n', ...$ini_array, '-S', PHP_CLI_SERVER_ADDRESS];
$cmd = [$php_executable, '-t', $doc_root, ...$ini_array, '-S', PHP_CLI_SERVER_ADDRESS];
$descriptorspec = array(
0 => STDIN,
1 => STDOUT,
Expand Down
20 changes: 13 additions & 7 deletions ext/opcache/zend_accelerator_util_funcs.c
Original file line number Diff line number Diff line change
Expand Up @@ -282,8 +282,9 @@ void zend_accel_build_delayed_early_binding_list(zend_persistent_script *persist
zval *lcname = RT_CONSTANT(opline, opline->op1);
early_binding->lcname = zend_string_copy(Z_STR_P(lcname));
early_binding->rtd_key = zend_string_copy(Z_STR_P(lcname + 1));
early_binding->lc_parent_name =
zend_string_copy(Z_STR_P(RT_CONSTANT(opline, opline->op2)));
early_binding->lc_parent_name = opline->op2_type != IS_UNUSED
? zend_string_copy(Z_STR_P(RT_CONSTANT(opline, opline->op2)))
: NULL;
early_binding->cache_slot = (uint32_t) -1;
early_binding++;
}
Expand Down Expand Up @@ -328,7 +329,9 @@ void zend_accel_free_delayed_early_binding_list(zend_persistent_script *persiste
zend_early_binding *early_binding = &persistent_script->early_bindings[i];
zend_string_release(early_binding->lcname);
zend_string_release(early_binding->rtd_key);
zend_string_release(early_binding->lc_parent_name);
if (early_binding->lc_parent_name) {
zend_string_release(early_binding->lc_parent_name);
}
}
efree(persistent_script->early_bindings);
persistent_script->early_bindings = NULL;
Expand Down Expand Up @@ -357,10 +360,13 @@ static void zend_accel_do_delayed_early_binding(
zval *zv = zend_hash_find_known_hash(EG(class_table), early_binding->rtd_key);
if (zv) {
zend_class_entry *orig_ce = Z_CE_P(zv);
zend_class_entry *parent_ce =
zend_hash_find_ex_ptr(EG(class_table), early_binding->lc_parent_name, 1);
if (parent_ce) {
ce = zend_try_early_bind(orig_ce, parent_ce, early_binding->lcname, zv);
if (early_binding->lc_parent_name) {
zend_class_entry *parent_ce = zend_hash_find_ex_ptr(EG(class_table), early_binding->lc_parent_name, 1);
if (parent_ce) {
ce = zend_try_early_bind(orig_ce, parent_ce, early_binding->lcname, zv);
}
} else {
ce = zend_try_early_bind(orig_ce, NULL, early_binding->lcname, zv);
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion ext/opcache/zend_persist.c
Original file line number Diff line number Diff line change
Expand Up @@ -1306,7 +1306,9 @@ static zend_early_binding *zend_persist_early_bindings(
for (uint32_t i = 0; i < num_early_bindings; i++) {
zend_accel_store_interned_string(early_bindings[i].lcname);
zend_accel_store_interned_string(early_bindings[i].rtd_key);
zend_accel_store_interned_string(early_bindings[i].lc_parent_name);
if (early_bindings[i].lc_parent_name) {
zend_accel_store_interned_string(early_bindings[i].lc_parent_name);
}
}
}
return early_bindings;
Expand Down
4 changes: 3 additions & 1 deletion ext/opcache/zend_persist_calc.c
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,9 @@ static void zend_persist_early_bindings_calc(
zend_early_binding *early_binding = &early_bindings[i];
ADD_INTERNED_STRING(early_binding->lcname);
ADD_INTERNED_STRING(early_binding->rtd_key);
ADD_INTERNED_STRING(early_binding->lc_parent_name);
if (early_binding->lc_parent_name) {
ADD_INTERNED_STRING(early_binding->lc_parent_name);
}
}
}

Expand Down

0 comments on commit a77457e

Please sign in to comment.