summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2019-05-23 10:41:27 +0200
committerNikita Popov <nikita.ppv@gmail.com>2019-05-23 10:41:27 +0200
commitc3ee12e7868dbc08da36e780eee80a9d3c2e30d1 (patch)
tree5b3b13a66aa54acab89246a8ae3908c021eb0cf7
parentb63cb6742123e9fe125cd4bc6c7ac0bdddf0f3b2 (diff)
parent64918c770282c0f60b407e8de3201a6b68c88e78 (diff)
downloadphp-git-c3ee12e7868dbc08da36e780eee80a9d3c2e30d1.tar.gz
Merge branch 'PHP-7.4'
-rw-r--r--Zend/tests/bug30922.phpt2
-rw-r--r--Zend/tests/use_unlinked_class.phpt20
-rw-r--r--Zend/zend_compile.h3
-rw-r--r--Zend/zend_execute_API.c6
-rw-r--r--Zend/zend_inheritance.c4
-rw-r--r--Zend/zend_interfaces.c4
-rw-r--r--Zend/zend_opcode.c4
7 files changed, 36 insertions, 7 deletions
diff --git a/Zend/tests/bug30922.phpt b/Zend/tests/bug30922.phpt
index 8da0f2192d..0d5d8ae838 100644
--- a/Zend/tests/bug30922.phpt
+++ b/Zend/tests/bug30922.phpt
@@ -10,4 +10,4 @@ var_dump($a instanceOf A);
echo "ok\n";
?>
--EXPECTF--
-Fatal error: Interface RecurisiveFooFar cannot implement itself in %sbug30922.php on line %d
+Fatal error: Interface 'RecurisiveFooFar' not found in %sbug30922.php on line %d
diff --git a/Zend/tests/use_unlinked_class.phpt b/Zend/tests/use_unlinked_class.phpt
new file mode 100644
index 0000000000..ed874ff101
--- /dev/null
+++ b/Zend/tests/use_unlinked_class.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Classes can only be used once they are fully linked
+--FILE--
+<?php
+
+spl_autoload_register(function($class) {
+ echo new ReflectionClass(A::class), "\n";
+});
+
+class A implements I {
+}
+
+?>
+--EXPECTF--
+Fatal error: During class fetch: Uncaught ReflectionException: Class A does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionClass->__construct('A')
+#1 [internal function]: {closure}('I')
+#2 %s(%d): spl_autoload_call('I')
+#3 {main} in %s on line %d
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 74a8b26368..8cded9ed22 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -269,6 +269,9 @@ typedef struct _zend_oparray_context {
/* Children must reuse parent get_iterator() | | | */
#define ZEND_ACC_REUSE_GET_ITERATOR (1 << 18) /* X | | | */
/* | | | */
+/* Class is being linked. Don't free strings. | | | */
+#define ZEND_ACC_LINKING_IN_PROGRESS (1 << 19) /* X | | | */
+/* | | | */
/* Function Flags (unused: 28...30) | | | */
/* ============== | | | */
/* | | | */
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index 5e8e2f467c..eea297fb56 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -861,7 +861,11 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
if (!key) {
zend_string_release_ex(lc_name, 0);
}
- return (zend_class_entry*)Z_PTR_P(zv);
+ ce = (zend_class_entry*)Z_PTR_P(zv);
+ if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED))) {
+ return NULL;
+ }
+ return ce;
}
/* The compiler is not-reentrant. Make sure we __autoload() only during run-time
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 38e5c205fb..e4292b3484 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -1961,7 +1961,7 @@ void zend_verify_abstract_class(zend_class_entry *ce) /* {{{ */
ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_class_entry *parent) /* {{{ */
{
- ce->ce_flags |= ZEND_ACC_LINKED;
+ ce->ce_flags |= ZEND_ACC_LINKING_IN_PROGRESS;
if (parent) {
zend_do_inheritance(ce, parent);
}
@@ -1976,5 +1976,7 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_class_entry *parent)
}
zend_build_properties_info_table(ce);
+ ce->ce_flags &= ~ZEND_ACC_LINKING_IN_PROGRESS;
+ ce->ce_flags |= ZEND_ACC_LINKED;
}
/* }}} */
diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c
index 4c9bb28310..90cceec38e 100644
--- a/Zend/zend_interfaces.c
+++ b/Zend/zend_interfaces.c
@@ -291,7 +291,7 @@ static int zend_implement_traversable(zend_class_entry *interface, zend_class_en
return SUCCESS;
}
if (class_type->num_interfaces) {
- ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_LINKED);
+ ZEND_ASSERT(class_type->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS));
for (i = 0; i < class_type->num_interfaces; i++) {
if (class_type->interfaces[i] == zend_ce_aggregate || class_type->interfaces[i] == zend_ce_iterator) {
return SUCCESS;
@@ -321,7 +321,7 @@ static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entr
} else if (class_type->get_iterator != zend_user_it_get_new_iterator) {
/* c-level get_iterator cannot be changed (exception being only Traversable is implemented) */
if (class_type->num_interfaces) {
- ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_LINKED);
+ ZEND_ASSERT(class_type->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS));
for (i = 0; i < class_type->num_interfaces; i++) {
if (class_type->interfaces[i] == zend_ce_iterator) {
zend_error_noreturn(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c
index 7d70d297d4..5c954b6c30 100644
--- a/Zend/zend_opcode.c
+++ b/Zend/zend_opcode.c
@@ -238,7 +238,7 @@ ZEND_API void destroy_zend_class(zval *zv)
}
switch (ce->type) {
case ZEND_USER_CLASS:
- if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) {
+ if (ce->parent_name && !(ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS))) {
zend_string_release_ex(ce->parent_name, 0);
}
if (ce->default_properties_table) {
@@ -298,7 +298,7 @@ ZEND_API void destroy_zend_class(zval *zv)
}
zend_hash_destroy(&ce->constants_table);
if (ce->num_interfaces > 0) {
- if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
+ if (!(ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS))) {
uint32_t i;
for (i = 0; i < ce->num_interfaces; i++) {