summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2019-09-11 15:31:04 +0200
committerNikita Popov <nikita.ppv@gmail.com>2019-09-12 16:41:18 +0200
commit4b9ebd837b6fc20bd907ae858375737e91365ede (patch)
treed3384a015615af07d26e296fca42802b8a02b750
parent679cbee870691936f7f426c0cb7ecaa70018b563 (diff)
downloadphp-git-4b9ebd837b6fc20bd907ae858375737e91365ede.tar.gz
Allow throwing exception while loading parent class
This is a fix for symfony/symfony#32995. The behavior is: * Throwing exception when loading parent/interface is allowed (and we will also throw one if the class is simply not found). * If this happens, the bucket key for the class is reset, so it's possibly to try registering the same class again. * However, if the class has already been used due to a variance obligation, the exception is upgraded to a fatal error, as we cannot safely unregister the class stub anymore.
-rw-r--r--Zend/tests/bug30519.phpt5
-rw-r--r--Zend/tests/bug30922.phpt5
-rw-r--r--Zend/tests/bug49908.phpt7
-rw-r--r--Zend/tests/type_declarations/variance/loading_exception1.phpt49
-rw-r--r--Zend/tests/type_declarations/variance/loading_exception2.phpt51
-rw-r--r--Zend/tests/type_declarations/variance/unlinked_parent_1.phpt10
-rw-r--r--Zend/tests/type_declarations/variance/unlinked_parent_2.phpt10
-rw-r--r--Zend/tests/use_unlinked_class.phpt5
-rw-r--r--Zend/zend_compile.c6
-rw-r--r--Zend/zend_compile.h5
-rw-r--r--Zend/zend_execute_API.c1
-rw-r--r--Zend/zend_inheritance.c91
-rw-r--r--Zend/zend_inheritance.h2
-rw-r--r--Zend/zend_vm_def.h11
-rw-r--r--Zend/zend_vm_execute.h11
-rw-r--r--ext/opcache/ZendAccelerator.c4
-rw-r--r--ext/spl/tests/bug73423.phpt13
-rw-r--r--tests/classes/autoload_010.phpt5
-rw-r--r--tests/classes/autoload_011.phpt5
-rw-r--r--tests/classes/bug75765.phpt8
20 files changed, 253 insertions, 51 deletions
diff --git a/Zend/tests/bug30519.phpt b/Zend/tests/bug30519.phpt
index f633d11445..f6c7d15bc8 100644
--- a/Zend/tests/bug30519.phpt
+++ b/Zend/tests/bug30519.phpt
@@ -6,4 +6,7 @@ class test implements a {
}
?>
--EXPECTF--
-Fatal error: Interface 'a' not found in %sbug30519.php on line 2
+Fatal error: Uncaught Error: Interface 'a' not found in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/bug30922.phpt b/Zend/tests/bug30922.phpt
index 444758c903..7a0316bee8 100644
--- a/Zend/tests/bug30922.phpt
+++ b/Zend/tests/bug30922.phpt
@@ -10,4 +10,7 @@ var_dump($a instanceOf A);
echo "ok\n";
?>
--EXPECTF--
-Fatal error: Interface 'RecurisiveFooFar' not found in %s on line %d
+Fatal error: Uncaught Error: Interface 'RecurisiveFooFar' not found in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/bug49908.phpt b/Zend/tests/bug49908.phpt
index af8ab45934..e3e91d3452 100644
--- a/Zend/tests/bug49908.phpt
+++ b/Zend/tests/bug49908.phpt
@@ -24,11 +24,14 @@ var_dump(new Foo());
--EXPECTF--
string(3) "Foo"
string(3) "Bar"
+string(3) "Foo"
+string(3) "Bar"
-Fatal error: During class fetch: Uncaught Exception: Bar in %s:%d
+Fatal error: Uncaught Exception: Bar in %s:%d
Stack trace:
#0 [internal function]: {closure}('Bar')
#1 %s(%d): spl_autoload_call('Bar')
#2 [internal function]: {closure}('Foo')
#3 %s(%d): spl_autoload_call('Foo')
-#4 {main} in %s on line %d
+#4 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/loading_exception1.phpt b/Zend/tests/type_declarations/variance/loading_exception1.phpt
new file mode 100644
index 0000000000..89aeaf3c12
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/loading_exception1.phpt
@@ -0,0 +1,49 @@
+--TEST--
+Exception while loading class -- parent case
+--FILE--
+<?php
+
+spl_autoload_register(function($class) {
+ throw new Exception("Class $class does not exist");
+});
+
+// Graceful failure allowed
+for ($i = 0; $i < 2; $i++) {
+ try {
+ class B extends A {
+ }
+ } catch (Exception $e) {
+ echo $e->getMessage(), "\n";
+ }
+}
+
+interface I {}
+
+spl_autoload_register(function($class) {
+ // Tie up B in a variance obligation.
+ class X {
+ public function test(): I {}
+ }
+ class Y extends X {
+ public function test(): B {}
+ }
+}, true, true);
+
+// Fallback to fatal error, as we can't unlink class B anymore.
+try {
+ class B extends A implements I {
+ }
+} catch (Exception $e) {
+ echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECTF--
+Class A does not exist
+Class A does not exist
+
+Fatal error: During inheritance of B with variance dependencies: Uncaught Exception: Class A does not exist in %s:%d
+Stack trace:
+#0 [internal function]: {closure}('A')
+#1 %s(%d): spl_autoload_call('A')
+#2 {main} in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/loading_exception2.phpt b/Zend/tests/type_declarations/variance/loading_exception2.phpt
new file mode 100644
index 0000000000..2b46c183c2
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/loading_exception2.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Exception while loading class -- interface case
+--FILE--
+<?php
+
+spl_autoload_register(function($class) {
+ throw new Exception("Class $class does not exist");
+});
+
+class A {}
+
+// Graceful failure allowed
+for ($i = 0; $i < 2; $i++) {
+ try {
+ class B extends A implements I {
+ }
+ } catch (Exception $e) {
+ echo $e->getMessage(), "\n";
+ }
+}
+
+interface J {}
+
+spl_autoload_register(function($class) {
+ // Tie up B in a variance obligation.
+ class X {
+ public function test(): J {}
+ }
+ class Y extends X {
+ public function test(): B {}
+ }
+}, true, true);
+
+// Fallback to fatal error, as we can't unlink class B anymore.
+try {
+ class B extends A implements I, J {
+ }
+} catch (Exception $e) {
+ echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECTF--
+Class I does not exist
+Class I does not exist
+
+Fatal error: During inheritance of B with variance dependencies: Uncaught Exception: Class I does not exist in %s:%d
+Stack trace:
+#0 [internal function]: {closure}('I')
+#1 %s(%d): spl_autoload_call('I')
+#2 {main} in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/unlinked_parent_1.phpt b/Zend/tests/type_declarations/variance/unlinked_parent_1.phpt
index 2030f3bd8b..fcce42424d 100644
--- a/Zend/tests/type_declarations/variance/unlinked_parent_1.phpt
+++ b/Zend/tests/type_declarations/variance/unlinked_parent_1.phpt
@@ -7,9 +7,13 @@ spl_autoload_register(function($class) {
class X extends B {}
});
-class B extends A {
+try {
+ class B extends A {
+ }
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
}
?>
---EXPECTF--
-Fatal error: Class 'B' not found in %s on line %d
+--EXPECT--
+Class 'B' not found
diff --git a/Zend/tests/type_declarations/variance/unlinked_parent_2.phpt b/Zend/tests/type_declarations/variance/unlinked_parent_2.phpt
index 9e21f85770..b947c6f4b6 100644
--- a/Zend/tests/type_declarations/variance/unlinked_parent_2.phpt
+++ b/Zend/tests/type_declarations/variance/unlinked_parent_2.phpt
@@ -7,9 +7,13 @@ spl_autoload_register(function($class) {
class X implements B {}
});
-interface B extends A {
+try {
+ interface B extends A {
+ }
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
}
?>
---EXPECTF--
-Fatal error: Interface 'B' not found in %s on line %d
+--EXPECT--
+Interface 'B' not found
diff --git a/Zend/tests/use_unlinked_class.phpt b/Zend/tests/use_unlinked_class.phpt
index ed874ff101..e85f97821f 100644
--- a/Zend/tests/use_unlinked_class.phpt
+++ b/Zend/tests/use_unlinked_class.phpt
@@ -12,9 +12,10 @@ class A implements I {
?>
--EXPECTF--
-Fatal error: During class fetch: Uncaught ReflectionException: Class A does not exist in %s:%d
+Fatal error: 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
+#3 {main}
+ thrown in %s on line %d
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index fc2efd2e6c..e46c5b0d10 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -1081,7 +1081,11 @@ ZEND_API int do_bind_class(zval *lcname, zend_string *lc_parent_name) /* {{{ */
return FAILURE;
}
- zend_do_link_class(ce, lc_parent_name);
+ if (zend_do_link_class(ce, lc_parent_name) == FAILURE) {
+ zend_hash_set_bucket_key(EG(class_table), (Bucket *) zv, Z_STR_P(rtd_key));
+ return FAILURE;
+ }
+
return SUCCESS;
}
/* }}} */
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 2a8ac77145..f82507c88c 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -229,7 +229,7 @@ typedef struct _zend_oparray_context {
/* op_array or class is preloaded | | | */
#define ZEND_ACC_PRELOADED (1 << 10) /* X | X | | */
/* | | | */
-/* Class Flags (unused: 23...) | | | */
+/* Class Flags (unused: 24...) | | | */
/* =========== | | | */
/* | | | */
/* Special class types | | | */
@@ -281,6 +281,9 @@ typedef struct _zend_oparray_context {
/* Class is linked apart from variance obligations. | | | */
#define ZEND_ACC_NEARLY_LINKED (1 << 22) /* X | | | */
/* | | | */
+/* Whether this class was used in its unlinked state. | | | */
+#define ZEND_ACC_HAS_UNLINKED_USES (1 << 23) /* X | | | */
+/* | | | */
/* Function Flags (unused: 23, 26) | | | */
/* ============== | | | */
/* | | | */
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index ddef72ebd3..3c78d2524d 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -921,6 +921,7 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
if ((flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED) ||
((flags & ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED) &&
(ce->ce_flags & ZEND_ACC_NEARLY_LINKED))) {
+ ce->ce_flags |= ZEND_ACC_HAS_UNLINKED_USES;
return ce;
}
return NULL;
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 1ece946080..30899982c8 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -25,6 +25,7 @@
#include "zend_interfaces.h"
#include "zend_smart_str.h"
#include "zend_operators.h"
+#include "zend_exceptions.h"
static void add_dependency_obligation(zend_class_entry *ce, zend_class_entry *dependency_ce);
static void add_compatibility_obligation(
@@ -1437,26 +1438,17 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry
}
/* }}} */
-static void zend_do_implement_interfaces(zend_class_entry *ce) /* {{{ */
+static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry **interfaces) /* {{{ */
{
- zend_class_entry **interfaces, *iface;
- uint32_t num_interfaces = 0;
+ zend_class_entry *iface;
+ uint32_t num_parent_interfaces = ce->parent ? ce->parent->num_interfaces : 0;
+ uint32_t num_interfaces = num_parent_interfaces;
zend_string *key;
zend_class_constant *c;
uint32_t i, j;
- if (ce->parent && ce->parent->num_interfaces) {
- interfaces = emalloc(sizeof(zend_class_entry*) * (ce->parent->num_interfaces + ce->num_interfaces));
- memcpy(interfaces, ce->parent->interfaces, sizeof(zend_class_entry*) * ce->parent->num_interfaces);
- num_interfaces = ce->parent->num_interfaces;
- } else {
- interfaces = emalloc(sizeof(zend_class_entry*) * ce->num_interfaces);
- }
-
for (i = 0; i < ce->num_interfaces; i++) {
- iface = zend_fetch_class_by_name(
- ce->interface_names[i].name, ce->interface_names[i].lc_name,
- ZEND_FETCH_CLASS_INTERFACE|ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED);
+ iface = interfaces[num_parent_interfaces + i];
if (!(iface->ce_flags & ZEND_ACC_LINKED)) {
add_dependency_obligation(ce, iface);
}
@@ -1467,7 +1459,7 @@ static void zend_do_implement_interfaces(zend_class_entry *ce) /* {{{ */
}
for (j = 0; j < num_interfaces; j++) {
if (interfaces[j] == iface) {
- if (!ce->parent || j >= ce->parent->num_interfaces) {
+ if (j >= num_parent_interfaces) {
efree(interfaces);
zend_error_noreturn(E_COMPILE_ERROR, "Class %s cannot implement previously implemented interface %s", ZSTR_VAL(ce->name), ZSTR_VAL(iface->name));
return;
@@ -1497,7 +1489,7 @@ static void zend_do_implement_interfaces(zend_class_entry *ce) /* {{{ */
ce->interfaces = interfaces;
ce->ce_flags |= ZEND_ACC_RESOLVED_INTERFACES;
- i = ce->parent ? ce->parent->num_interfaces : 0;
+ i = num_parent_interfaces;
for (; i < ce->num_interfaces; i++) {
do_interface_implementation(ce, ce->interfaces[i]);
}
@@ -2399,11 +2391,66 @@ static void report_variance_errors(zend_class_entry *ce) {
zend_hash_index_del(all_obligations, num_key);
}
-ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name) /* {{{ */
+static void check_unrecoverable_load_failure(zend_class_entry *ce) {
+ /* If this class has been used while unlinked through a variance obligation, it is not legal
+ * to remove the class from the class table and throw an exception, because there is already
+ * a dependence on the inheritance hierarchy of this specific class. Instead we fall back to
+ * a fatal error, as would happen if we did not allow exceptions in the first place. */
+ if (ce->ce_flags & ZEND_ACC_HAS_UNLINKED_USES) {
+ zend_string *exception_str;
+ zval exception_zv;
+ ZEND_ASSERT(EG(exception) && "Exception must have been thrown");
+ ZVAL_OBJ(&exception_zv, EG(exception));
+ Z_ADDREF(exception_zv);
+ zend_clear_exception();
+ exception_str = zval_get_string(&exception_zv);
+ zend_error_noreturn(E_ERROR,
+ "During inheritance of %s with variance dependencies: Uncaught %s", ZSTR_VAL(ce->name), ZSTR_VAL(exception_str));
+ }
+}
+
+ZEND_API int zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name) /* {{{ */
{
+ /* Load parent/interface dependencies first, so we can still gracefully abort linking
+ * with an exception and remove the class from the class table. This is only possible
+ * if no variance obligations on the current class have been added during autoloading. */
+ zend_class_entry *parent = NULL;
+ zend_class_entry **interfaces = NULL;
+
if (ce->parent_name) {
- zend_class_entry *parent = zend_fetch_class_by_name(
- ce->parent_name, lc_parent_name, ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED);
+ parent = zend_fetch_class_by_name(
+ ce->parent_name, lc_parent_name,
+ ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION);
+ if (!parent) {
+ check_unrecoverable_load_failure(ce);
+ return FAILURE;
+ }
+ }
+
+ if (ce->num_interfaces) {
+ /* Also copy the parent interfaces here, so we don't need to reallocate later. */
+ uint32_t i, num_parent_interfaces = parent ? parent->num_interfaces : 0;
+ interfaces = emalloc(
+ sizeof(zend_class_entry *) * (ce->num_interfaces + num_parent_interfaces));
+ if (num_parent_interfaces) {
+ memcpy(interfaces, parent->interfaces,
+ sizeof(zend_class_entry *) * num_parent_interfaces);
+ }
+ for (i = 0; i < ce->num_interfaces; i++) {
+ zend_class_entry *iface = zend_fetch_class_by_name(
+ ce->interface_names[i].name, ce->interface_names[i].lc_name,
+ ZEND_FETCH_CLASS_INTERFACE |
+ ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION);
+ if (!iface) {
+ check_unrecoverable_load_failure(ce);
+ efree(interfaces);
+ return FAILURE;
+ }
+ interfaces[num_parent_interfaces + i] = iface;
+ }
+ }
+
+ if (parent) {
if (!(parent->ce_flags & ZEND_ACC_LINKED)) {
add_dependency_obligation(ce, parent);
}
@@ -2413,7 +2460,7 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_na
zend_do_bind_traits(ce);
}
if (ce->ce_flags & ZEND_ACC_IMPLEMENT_INTERFACES) {
- zend_do_implement_interfaces(ce);
+ zend_do_implement_interfaces(ce, interfaces);
}
if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
zend_verify_abstract_class(ce);
@@ -2423,7 +2470,7 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_na
if (!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE)) {
ce->ce_flags |= ZEND_ACC_LINKED;
- return;
+ return SUCCESS;
}
ce->ce_flags |= ZEND_ACC_NEARLY_LINKED;
@@ -2434,6 +2481,8 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_na
report_variance_errors(ce);
}
}
+
+ return SUCCESS;
}
/* }}} */
diff --git a/Zend/zend_inheritance.h b/Zend/zend_inheritance.h
index 120a56a1ef..c4e9e10675 100644
--- a/Zend/zend_inheritance.h
+++ b/Zend/zend_inheritance.h
@@ -30,7 +30,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
#define zend_do_inheritance(ce, parent_ce) \
zend_do_inheritance_ex(ce, parent_ce, 0)
-ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name);
+ZEND_API int zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name);
void zend_verify_abstract_class(zend_class_entry *ce);
void zend_check_deprecated_constructor(const zend_class_entry *ce);
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index f8e7cad985..8bcfe5f660 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -7273,8 +7273,8 @@ ZEND_VM_HANDLER(145, ZEND_DECLARE_CLASS_DELAYED, CONST, CONST)
if (UNEXPECTED(!zv)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
} else {
- zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2)));
- if (UNEXPECTED(EG(exception))) {
+ if (zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2))) == FAILURE) {
+ zend_hash_set_bucket_key(EG(class_table), (Bucket *) zv, Z_STR_P(lcname + 1));
HANDLE_EXCEPTION();
}
}
@@ -7292,13 +7292,14 @@ ZEND_VM_HANDLER(146, ZEND_DECLARE_ANON_CLASS, ANY, ANY, CACHE_SLOT)
ce = CACHED_PTR(opline->extended_value);
if (UNEXPECTED(ce == NULL)) {
- zv = zend_hash_find_ex(EG(class_table), Z_STR_P(RT_CONSTANT(opline, opline->op1)), 1);
+ zend_string *rtd_key = Z_STR_P(RT_CONSTANT(opline, opline->op1));
+ zv = zend_hash_find_ex(EG(class_table), rtd_key, 1);
ZEND_ASSERT(zv != NULL);
ce = Z_CE_P(zv);
if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
SAVE_OPLINE();
- zend_do_link_class(ce, (OP2_TYPE == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL);
- if (UNEXPECTED(EG(exception))) {
+ if (zend_do_link_class(ce, (OP2_TYPE == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL) == FAILURE) {
+ zend_hash_set_bucket_key(EG(class_table), (Bucket *) zv, rtd_key);
HANDLE_EXCEPTION();
}
}
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 8882f18d14..7a5121aad1 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -2444,13 +2444,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_CLASS_SPEC_HANDLE
ce = CACHED_PTR(opline->extended_value);
if (UNEXPECTED(ce == NULL)) {
- zv = zend_hash_find_ex(EG(class_table), Z_STR_P(RT_CONSTANT(opline, opline->op1)), 1);
+ zend_string *rtd_key = Z_STR_P(RT_CONSTANT(opline, opline->op1));
+ zv = zend_hash_find_ex(EG(class_table), rtd_key, 1);
ZEND_ASSERT(zv != NULL);
ce = Z_CE_P(zv);
if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
SAVE_OPLINE();
- zend_do_link_class(ce, (opline->op2_type == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL);
- if (UNEXPECTED(EG(exception))) {
+ if (zend_do_link_class(ce, (opline->op2_type == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL) == FAILURE) {
+ zend_hash_set_bucket_key(EG(class_table), (Bucket *) zv, rtd_key);
HANDLE_EXCEPTION();
}
}
@@ -6382,8 +6383,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_CLASS_DELAYED_SPEC_CON
if (UNEXPECTED(!zv)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
} else {
- zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2)));
- if (UNEXPECTED(EG(exception))) {
+ if (zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2))) == FAILURE) {
+ zend_hash_set_bucket_key(EG(class_table), (Bucket *) zv, Z_STR_P(lcname + 1));
HANDLE_EXCEPTION();
}
}
diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index c9a467bd2d..056c7739a0 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -3731,7 +3731,9 @@ static void preload_link(void)
} else {
CG(zend_lineno) = ce->info.user.line_start;
}
- zend_do_link_class(ce, NULL);
+ if (zend_do_link_class(ce, NULL) == FAILURE) {
+ ZEND_ASSERT(0 && "Class linking failed?");
+ }
CG(in_compilation) = 0;
CG(compiled_filename) = NULL;
diff --git a/ext/spl/tests/bug73423.phpt b/ext/spl/tests/bug73423.phpt
index 177da624be..549ceabb8a 100644
--- a/ext/spl/tests/bug73423.phpt
+++ b/ext/spl/tests/bug73423.phpt
@@ -68,4 +68,15 @@ foreach (new \RecursiveIteratorIterator (new fooIterator ($foo)) as $bar) ;
?>
--EXPECTF--
-Fatal error: Class 'NotExists' not found in %s(%d) : eval()'d code on line 1
+Fatal error: Uncaught Error: Class 'NotExists' not found in %s:%d
+Stack trace:
+#0 %s(%d): eval()
+#1 %s(%d): fooIterator->__destruct()
+#2 {main}
+
+Next Error: Class 'NotExists' not found in %s:%d
+Stack trace:
+#0 %s(%d): eval()
+#1 %s(%d): fooIterator->__destruct()
+#2 {main}
+ thrown in %s on line %d
diff --git a/tests/classes/autoload_010.phpt b/tests/classes/autoload_010.phpt
index e65c2565a2..b8aa660730 100644
--- a/tests/classes/autoload_010.phpt
+++ b/tests/classes/autoload_010.phpt
@@ -14,4 +14,7 @@ class C implements UndefI
--EXPECTF--
In autoload: string(6) "UndefI"
-Fatal error: Interface 'UndefI' not found in %s on line %d
+Fatal error: Uncaught Error: Interface 'UndefI' not found in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/tests/classes/autoload_011.phpt b/tests/classes/autoload_011.phpt
index aaea38c6f0..d255a0d23b 100644
--- a/tests/classes/autoload_011.phpt
+++ b/tests/classes/autoload_011.phpt
@@ -14,4 +14,7 @@ class C extends UndefBase
--EXPECTF--
In autoload: string(9) "UndefBase"
-Fatal error: Class 'UndefBase' not found in %s on line %d
+Fatal error: Uncaught Error: Class 'UndefBase' not found in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/tests/classes/bug75765.phpt b/tests/classes/bug75765.phpt
index 213d514dea..650ca65e96 100644
--- a/tests/classes/bug75765.phpt
+++ b/tests/classes/bug75765.phpt
@@ -13,4 +13,10 @@ try {
?>
--EXPECTF--
-Fatal error: Class 'B' not found in %s on line %d
+bool(false)
+bool(false)
+
+Fatal error: Uncaught Error: Class 'B' not found in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d