Skip to content

Commit

Permalink
Improve generated names for anonymous classes
Browse files Browse the repository at this point in the history
In order of preference, the generated name will be:

    new class extends ParentClass {};
    // -> ParentClass@anonymous
    new class implements FirstInterface, SecondInterface {};
    // -> FirstInterface@anonymous
    new class {};
    // -> class@anonymous

This is intended to display a more useful class name in error messages
and stack traces, and thus make debugging easier.

Closes GH-5153.
  • Loading branch information
nikic committed Feb 17, 2020
1 parent 4344385 commit 72bd559
Show file tree
Hide file tree
Showing 17 changed files with 80 additions and 28 deletions.
13 changes: 12 additions & 1 deletion UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,18 @@ PHP 8.0 UPGRADE NOTES
exception.
. Some "Only variables should be passed by reference" notices have been converted
to "Cannot pass parameter by reference" exception.
. The generated name for anonymous classes has changed. It will now include
the name of the first parent or interface:

new class extends ParentClass {};
// -> ParentClass@anonymous
new class implements FirstInterface, SecondInterface {};
// -> FirstInterface@anonymous
new class {};
// -> class@anonymous

The name shown above is still followed by a null byte and and a unique
suffix.

- COM:
. Removed the ability to import case-insensitive constants from type
Expand Down Expand Up @@ -386,7 +398,6 @@ PHP 8.0 UPGRADE NOTES
writing `Foo::BAR::$baz` is now allowed.
RFC: https://wiki.php.net/rfc/variable_syntax_tweaks


- Date:
. Added DateTime::createFromInterface() and
DateTimeImmutable::createFromInterface().
Expand Down
30 changes: 30 additions & 0 deletions Zend/tests/anon_class_name.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
--TEST--
Generated names for anonymous classes
--FILE--
<?php

namespace DeclaringNS {
class Test1 {}
interface Test2 {}
interface Test3 {}
}

namespace UsingNS {
function print_name(object $obj) {
echo strstr(get_class($obj), "\0", true), "\n";
}

print_name(new class {});
print_name(new class extends \DeclaringNS\Test1 {});
print_name(new class extends \DeclaringNS\Test1 implements \DeclaringNS\Test2 {});
print_name(new class implements \DeclaringNS\Test2 {});
print_name(new class implements \DeclaringNS\Test2, \DeclaringNS\Test3 {});
}

?>
--EXPECT--
class@anonymous
DeclaringNS\Test1@anonymous
DeclaringNS\Test1@anonymous
DeclaringNS\Test2@anonymous
DeclaringNS\Test2@anonymous
4 changes: 2 additions & 2 deletions Zend/tests/object_types/return_type_in_class.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ $three = new class extends Two {
};
$three->a();
--EXPECTF--
Fatal error: Uncaught TypeError: Return value of class@anonymous::a() must be an object, int returned in %s:13
Fatal error: Uncaught TypeError: Return value of Two@anonymous::a() must be an object, int returned in %s:%d
Stack trace:
#0 %s(16): class@anonymous->a()
#0 %s(%d): Two@anonymous->a()
#1 {main}
thrown in %s on line 13
4 changes: 2 additions & 2 deletions Zend/tests/object_types/return_type_inheritance_in_class.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ $three = new class extends Two {
};
$three->a();
--EXPECTF--
Fatal error: Uncaught TypeError: Return value of class@anonymous::a() must be an object, int returned in %s:13
Fatal error: Uncaught TypeError: Return value of Two@anonymous::a() must be an object, int returned in %s:%d
Stack trace:
#0 %s(16): class@anonymous->a()
#0 %s(%d): Two@anonymous->a()
#1 {main}
thrown in %s on line 13
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ $three = new class implements Two {
};
$three->a();
--EXPECTF--
Fatal error: Uncaught TypeError: Return value of class@anonymous::a() must be an object, int returned in %s:13
Fatal error: Uncaught TypeError: Return value of Two@anonymous::a() must be an object, int returned in %s:%d
Stack trace:
#0 %s(16): class@anonymous->a()
#0 %s(%d): Two@anonymous->a()
#1 {main}
thrown in %s on line 13
4 changes: 2 additions & 2 deletions Zend/tests/temporary_cleaning_013.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -288,10 +288,10 @@ caught Exception 12
caught Exception 13
caught Exception 14

Notice: Indirect modification of overloaded element of class@anonymous has no effect in %s on line %d
Notice: Indirect modification of overloaded element of ArrayAccess@anonymous has no effect in %s on line %d
caught Exception 15

Notice: Indirect modification of overloaded element of class@anonymous has no effect in %s on line %d
Notice: Indirect modification of overloaded element of ArrayAccess@anonymous has no effect in %s on line %d
caught Exception 16
caught Exception 17
caught Exception 18
Expand Down
8 changes: 4 additions & 4 deletions Zend/tests/type_declarations/typed_properties_065.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ offsetSet(1e50)
int(1)
int(0)
int(-1)
Cannot decrement a reference held by property class@anonymous::$foo of type int past its minimal value
Cannot decrement a reference held by property ArrayAccess@anonymous::$foo of type int past its minimal value
integer
Cannot decrement a reference held by property class@anonymous::$foo of type int past its minimal value
Cannot decrement a reference held by property ArrayAccess@anonymous::$foo of type int past its minimal value
integer
Cannot increment a reference held by property class@anonymous::$foo of type int past its maximal value
Cannot increment a reference held by property ArrayAccess@anonymous::$foo of type int past its maximal value
integer
Cannot increment a reference held by property class@anonymous::$foo of type int past its maximal value
Cannot increment a reference held by property ArrayAccess@anonymous::$foo of type int past its maximal value
integer
21 changes: 16 additions & 5 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -6569,14 +6569,25 @@ void zend_compile_implements(zend_ast *ast) /* {{{ */
}
/* }}} */

static zend_string *zend_generate_anon_class_name(uint32_t start_lineno) /* {{{ */
static zend_string *zend_generate_anon_class_name(zend_ast_decl *decl)
{
zend_string *filename = CG(active_op_array)->filename;
zend_string *result = zend_strpprintf(0, "class@anonymous%c%s:%" PRIu32 "$%" PRIx32,
'\0', ZSTR_VAL(filename), start_lineno, CG(rtd_key_counter)++);
uint32_t start_lineno = decl->start_lineno;

/* Use parent or first interface as prefix. */
zend_string *prefix = ZSTR_KNOWN(ZEND_STR_CLASS);
if (decl->child[0]) {
prefix = zend_resolve_const_class_name_reference(decl->child[0], "class name");
} else if (decl->child[1]) {
zend_ast_list *list = zend_ast_get_list(decl->child[1]);
prefix = zend_resolve_const_class_name_reference(list->child[0], "interface name");
}

zend_string *result = zend_strpprintf(0, "%s@anonymous%c%s:%" PRIu32 "$%" PRIx32,
ZSTR_VAL(prefix), '\0', ZSTR_VAL(filename), start_lineno, CG(rtd_key_counter)++);
zend_string_release(prefix);
return zend_new_interned_string(result);
}
/* }}} */

zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
{
Expand Down Expand Up @@ -6613,7 +6624,7 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */

zend_register_seen_symbol(lcname, ZEND_SYMBOL_CLASS);
} else {
name = zend_generate_anon_class_name(decl->start_lineno);
name = zend_generate_anon_class_name(decl);
lcname = zend_string_tolower(name);
}
lcname = zend_new_interned_string(lcname);
Expand Down
2 changes: 1 addition & 1 deletion ext/opcache/tests/bug78937_1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var_dump(foo());
--EXPECTF--
Warning: Can't preload unlinked class Foo: Unknown parent Bar in %spreload_bug78937.inc on line 6

Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
Warning: Can't preload unlinked class Bar@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3

Fatal error: Anonymous class wasn't preloaded in %spreload_bug78937.inc on line 3

4 changes: 2 additions & 2 deletions ext/opcache/tests/bug78937_2.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ var_dump(foo());
--EXPECTF--
Warning: Can't preload unlinked class Foo: Unknown parent Bar in %spreload_bug78937.inc on line 6

Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
object(class@anonymous)#%d (0) {
Warning: Can't preload unlinked class Bar@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
object(Bar@anonymous)#%d (0) {
}
2 changes: 1 addition & 1 deletion ext/opcache/tests/bug78937_3.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var_dump(foo());
--EXPECTF--
Warning: Can't preload unlinked class Foo: Unknown parent Bar in %spreload_bug78937.inc on line 6

Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
Warning: Can't preload unlinked class Bar@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3

Fatal error: Uncaught Error: Class 'Bar' not found in %spreload_bug78937.inc:3
Stack trace:
Expand Down
2 changes: 1 addition & 1 deletion ext/opcache/tests/bug78937_4.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ var_dump(new Foo);
--EXPECTF--
Warning: Can't preload unlinked class Foo: Unknown parent Bar in %spreload_bug78937.inc on line 6

Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
Warning: Can't preload unlinked class Bar@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3

Fatal error: Class foo wasn't preloaded in %spreload_bug78937.inc on line 6
2 changes: 1 addition & 1 deletion ext/opcache/tests/bug78937_5.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ var_dump(new Foo);
--EXPECTF--
Warning: Can't preload unlinked class Foo: Unknown parent Bar in %spreload_bug78937.inc on line 6

Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
Warning: Can't preload unlinked class Bar@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
object(Foo)#%d (0) {
}
2 changes: 1 addition & 1 deletion ext/opcache/tests/bug78937_6.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var_dump(new Foo);
--EXPECTF--
Warning: Can't preload unlinked class Foo: Unknown parent Bar in %spreload_bug78937.inc on line 6

Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
Warning: Can't preload unlinked class Bar@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3

Fatal error: Uncaught Error: Class 'Bar' not found in %spreload_bug78937.inc:6
Stack trace:
Expand Down
4 changes: 2 additions & 2 deletions ext/reflection/tests/ReflectionClass_isSubclassOf_error2.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Base {}
$check = function () {
$base = Base::class;
foreach (get_declared_classes() as $class) {
if (strpos($class, 'class@anonymous') === false) {
if (strpos($class, '@anonymous') === false) {
continue;
}
echo "Checking for $class\n";
Expand All @@ -30,6 +30,6 @@ echo "Done\n";
?>
--EXPECTF--
After first check
Checking for class@%s
Checking for Base@%s
true
Done
2 changes: 1 addition & 1 deletion ext/standard/tests/class_object/bug78638.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ $c = new class('bar') extends __PHP_Incomplete_Class {
};
?>
--EXPECTF--
Fatal error: Class class@anonymous may not inherit from final class (__PHP_Incomplete_Class) in %sbug78638.php on line %d
Fatal error: Class __PHP_Incomplete_Class@anonymous may not inherit from final class (__PHP_Incomplete_Class) in %s on line %d
Binary file modified ext/standard/tests/class_object/get_object_vars_variation_004.phpt
Binary file not shown.

0 comments on commit 72bd559

Please sign in to comment.