Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix GH-12895: Incorrect ReflectionClass::getNamespaceName for anonymous class #12907

Open
wants to merge 1 commit into
base: PHP-8.2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Zend/tests/anon/006.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ namespace {
var_dump ($hello);
}
?>
--EXPECTF--
object(class@%s)#1 (0) {
--EXPECT--
object(lone\@anonymous)#1 (0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be happy if the "1st parent non-anonymous class" can be kept dumped, as from anonymous class it is often not easy to tell quickly what the anonymous class is about and the "1st parent non-anonymous class" was helpful, compare:

DateTime@anonymous vs. class@anonymous (or Ns\@anonymous) - https://3v4l.org/Bj2qg

}
4 changes: 2 additions & 2 deletions Zend/tests/anon/007.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ namespace lone {
new Outer();
}
?>
--EXPECTF--
object(class@%s)#2 (0) {
--EXPECT--
object(lone\@anonymous)#2 (0) {
}
10 changes: 5 additions & 5 deletions Zend/tests/anon_class_name.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ namespace UsingNS {

?>
--EXPECT--
class@anonymous
DeclaringNS\Test1@anonymous
DeclaringNS\Test1@anonymous
DeclaringNS\Test2@anonymous
DeclaringNS\Test2@anonymous
UsingNS\@anonymous
UsingNS\@anonymous
UsingNS\@anonymous
UsingNS\@anonymous
UsingNS\@anonymous
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ $class = new class extends DateTime {

?>
--EXPECTF--
Fatal error: During inheritance of DateTime: Uncaught Exception: Return type of DateTime@anonymous::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in %s:%d
Fatal error: During inheritance of DateTime: Uncaught Exception: Return type of class@anonymous::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in %s:%d
Stack trace:
#0 %s(%d): {closure}(8192, 'Return type of ...', '%s', 8)
#1 {main} in %s on line %d
2 changes: 1 addition & 1 deletion Zend/tests/generators/gh8289.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ foreach (yieldFromIteratorGeneratorThrows() as $k => $v) {
int(1)
Exception in %s:%d
Stack trace:
#0 %s(%d): IteratorIterator@anonymous->key()
#0 %s(%d): class@anonymous->key()
#1 %s(%d): yieldFromIteratorGeneratorThrows()
#2 {main}
int(2)
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 @@ -19,8 +19,8 @@ $three = new class extends Two {
$three->a();
?>
--EXPECTF--
Fatal error: Uncaught TypeError: Two@anonymous::a(): Return value must be of type object, int returned in %s:%d
Fatal error: Uncaught TypeError: class@anonymous::a(): Return value must be of type object, int returned in %s:%d
Stack trace:
#0 %s(%d): Two@anonymous->a()
#0 %s(%d): class@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 @@ -19,8 +19,8 @@ $three = new class extends Two {
$three->a();
?>
--EXPECTF--
Fatal error: Uncaught TypeError: Two@anonymous::a(): Return value must be of type object, int returned in %s:%d
Fatal error: Uncaught TypeError: class@anonymous::a(): Return value must be of type object, int returned in %s:%d
Stack trace:
#0 %s(%d): Two@anonymous->a()
#0 %s(%d): class@anonymous->a()
#1 {main}
thrown in %s on line 13
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ $three = new class implements Two {
$three->a();
?>
--EXPECTF--
Fatal error: Uncaught TypeError: Two@anonymous::a(): Return value must be of type object, int returned in %s:%d
Fatal error: Uncaught TypeError: class@anonymous::a(): Return value must be of type object, int returned in %s:%d
Stack trace:
#0 %s(%d): Two@anonymous->a()
#0 %s(%d): class@anonymous->a()
#1 {main}
thrown in %s on line 13
2 changes: 1 addition & 1 deletion Zend/tests/record_errors_001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ new class extends DateTime {
};
?>
--EXPECT--
Error: Return type of DateTime@anonymous::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
Error: Return type of class@anonymous::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
4 changes: 2 additions & 2 deletions Zend/tests/temporary_cleaning_013.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -301,10 +301,10 @@ caught Exception 13
Deprecated: Creation of dynamic property class@anonymous::$foo is deprecated in %s on line %d
caught Exception 14

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

Notice: Indirect modification of overloaded element of ArrayAccess@anonymous has no effect in %s on line %d
Notice: Indirect modification of overloaded element of class@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 ArrayAccess@anonymous::$foo of type int past its minimal value
Cannot decrement a reference held by property class@anonymous::$foo of type int past its minimal value
integer
Cannot decrement a reference held by property ArrayAccess@anonymous::$foo of type int past its minimal value
Cannot decrement a reference held by property class@anonymous::$foo of type int past its minimal value
integer
Cannot increment a reference held by property ArrayAccess@anonymous::$foo of type int past its maximal value
Cannot increment a reference held by property class@anonymous::$foo of type int past its maximal value
integer
Cannot increment a reference held by property ArrayAccess@anonymous::$foo of type int past its maximal value
Cannot increment a reference held by property class@anonymous::$foo of type int past its maximal value
integer
17 changes: 8 additions & 9 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -7833,18 +7833,17 @@ static zend_string *zend_generate_anon_class_name(zend_ast_decl *decl)
zend_string *filename = CG(active_op_array)->filename;
uint32_t start_lineno = decl->start_lineno;

/* Use parent or first interface as prefix. */
/* Use current_namespace\\ or "class" 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");
const char *prefix_suffix = "";
if (FC(current_namespace)) {
prefix = FC(current_namespace);
/* This is necessary to get the right short name of anonymous classes. */
prefix_suffix = "\\";
}

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);
zend_string *result = zend_strpprintf(0, "%s%s@anonymous%c%s:%" PRIu32 "$%" PRIx32,
ZSTR_VAL(prefix), prefix_suffix, '\0', ZSTR_VAL(filename), start_lineno, CG(rtd_key_counter)++);
return zend_new_interned_string(result);
}

Expand Down
6 changes: 3 additions & 3 deletions ext/date/tests/bug65672.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ array(1) {
}
bool(false)

Deprecated: Creation of dynamic property DatePeriod@anonymous::$dynamic1 is deprecated in %s on line %d
Deprecated: Creation of dynamic property class@anonymous::$dynamic1 is deprecated in %s on line %d
string(7) "dynamic"

Deprecated: Creation of dynamic property DatePeriod@anonymous::$dynamic2 is deprecated in %s on line %d
Deprecated: Creation of dynamic property class@anonymous::$dynamic2 is deprecated in %s on line %d
array(1) {
[0]=>
string(5) "array"
}

Deprecated: Creation of dynamic property DatePeriod@anonymous::$dynamic3 is deprecated in %s on line %d
Deprecated: Creation of dynamic property class@anonymous::$dynamic3 is deprecated in %s on line %d
array(1) {
[0]=>
string(5) "array"
Expand Down
4 changes: 2 additions & 2 deletions ext/opcache/tests/bug78937_1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ class Bar {
var_dump(foo());
?>
--EXPECTF--
Warning: Can't preload unlinked class Bar@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
object(Bar@anonymous)#%d (0) {
Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
object(class@anonymous)#1 (0) {
}
4 changes: 2 additions & 2 deletions ext/opcache/tests/bug78937_2.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ class Bar {
var_dump(foo());
?>
--EXPECTF--
Warning: Can't preload unlinked class Bar@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
object(Bar@anonymous)#%d (0) {
Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
object(class@anonymous)#1 (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 @@ -17,7 +17,7 @@ include(__DIR__ . "/preload_bug78937.inc");
var_dump(foo());
?>
--EXPECTF--
Warning: Can't preload unlinked class Bar@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
Warning: Can't preload unlinked class class@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 @@ -19,6 +19,6 @@ bar();
var_dump(new Foo);
?>
--EXPECTF--
Warning: Can't preload unlinked class Bar@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
Warning: Can't preload unlinked class class@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_5.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ bar();
var_dump(new Foo);
?>
--EXPECTF--
Warning: Can't preload unlinked class Bar@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
Warning: Can't preload unlinked class class@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 @@ -18,7 +18,7 @@ bar();
var_dump(new Foo);
?>
--EXPECTF--
Warning: Can't preload unlinked class Bar@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
Warning: Can't preload unlinked class class@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
24 changes: 21 additions & 3 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -5397,6 +5397,24 @@ ZEND_METHOD(ReflectionClass, getExtensionName)
}
/* }}} */

static const char *_class_get_namespace_backslash(const zend_class_entry *ce)
{
const zend_string *name = ce->name;
size_t name_length;

if (ce->ce_flags & ZEND_ACC_ANON_CLASS) {
/* Paths may contain backslashes. This is true on Linux and especially on Windows.
* Search first for '@' and then look for the first '\' before that. */
const char *at = zend_memrchr(ZSTR_VAL(name), '@', ZSTR_LEN(name));
ZEND_ASSERT(at != NULL);
name_length = at - ZSTR_VAL(name);
} else {
name_length = ZSTR_LEN(name);
}

return zend_memrchr(ZSTR_VAL(name), '\\', name_length);
}

/* {{{ Returns whether this class is defined in namespace */
ZEND_METHOD(ReflectionClass, inNamespace)
{
Expand All @@ -5410,7 +5428,7 @@ ZEND_METHOD(ReflectionClass, inNamespace)
GET_REFLECTION_OBJECT_PTR(ce);

zend_string *name = ce->name;
const char *backslash = zend_memrchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name));
const char *backslash = _class_get_namespace_backslash(ce);
RETURN_BOOL(backslash && backslash > ZSTR_VAL(name));
}
/* }}} */
Expand All @@ -5428,7 +5446,7 @@ ZEND_METHOD(ReflectionClass, getNamespaceName)
GET_REFLECTION_OBJECT_PTR(ce);

zend_string *name = ce->name;
const char *backslash = zend_memrchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name));
const char *backslash = _class_get_namespace_backslash(ce);
if (backslash && backslash > ZSTR_VAL(name)) {
RETURN_STRINGL(ZSTR_VAL(name), backslash - ZSTR_VAL(name));
}
Expand All @@ -5449,7 +5467,7 @@ ZEND_METHOD(ReflectionClass, getShortName)
GET_REFLECTION_OBJECT_PTR(ce);

zend_string *name = ce->name;
const char *backslash = zend_memrchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name));
const char *backslash = _class_get_namespace_backslash(ce);
if (backslash && backslash > ZSTR_VAL(name)) {
RETURN_STRINGL(backslash + 1, ZSTR_LEN(name) - (backslash - ZSTR_VAL(name) + 1));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ echo "Done\n";
?>
--EXPECTF--
After first check
Checking for Base@%s
Checking for class@%s
true
Done
50 changes: 50 additions & 0 deletions ext/reflection/tests/gh12895.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
--TEST--
GH-12895 (Incorrect ReflectionClass::getNamespaceName for anonymous class)
--FILE--
<?php

namespace Foo {
interface InterfaceFoo {}
}

namespace Bar {
new class {};
}

namespace Bar\Baz {
new class implements \Foo\InterfaceFoo {};
}

namespace {
new class implements \Foo\InterfaceFoo {};

foreach (get_declared_classes() as $class) {
if (!str_contains($class, '@anonymous')) {
continue;
}
$rc = new ReflectionClass($class);
echo 'Class: ', $class, PHP_EOL;
echo 'getNamespaceName: ', $rc->getNamespaceName(), PHP_EOL;
echo 'getShortName: ', $rc->getShortName(), PHP_EOL;
echo 'inNamespace: ', $rc->inNamespace() ? "true" : "false", PHP_EOL;
echo "---\n";
}
}

?>
--EXPECTF--
Class: Bar\@anonymous%s
getNamespaceName: Bar
getShortName: @anonymous%s
inNamespace: true
---
Class: Bar\Baz\@anonymous%s
getNamespaceName: Bar\Baz
getShortName: @anonymous%s
inNamespace: true
---
Class: class@anonymous%s
getNamespaceName:
getShortName: class@anonymous%s
inNamespace: false
---
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 __PHP_Incomplete_Class@anonymous cannot extend final class __PHP_Incomplete_Class in %s on line %d
Fatal error: Class class@anonymous cannot extend final class __PHP_Incomplete_Class in %s on line %d
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ array(4) {
[12]=>
int(6)
["test"]=>
object(JsonSerializable@anonymous)#2 (0) {
object(class@anonymous)#2 (0) {
}
}
array(4) {
Expand All @@ -43,6 +43,6 @@ array(4) {
[12]=>
int(6)
["test"]=>
object(JsonSerializable@anonymous)#2 (0) {
object(class@anonymous)#2 (0) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ namespace {
ClassInGlobal
Demo\ClassInNamespace
class@anonymous
ToBeExtended@anonymous
ToBeImplemented@anonymous
class@anonymous
class@anonymous
string
bool
bool
Expand Down