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
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
8 changes: 4 additions & 4 deletions Zend/tests/type_declarations/typed_properties_065.phpt
Original file line number Diff line number Diff line change
@@ -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
@@ -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);
}

6 changes: 3 additions & 3 deletions ext/date/tests/bug65672.phpt
Original file line number Diff line number Diff line change
@@ -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"
4 changes: 2 additions & 2 deletions ext/opcache/tests/bug78937_1.phpt
Original file line number Diff line number Diff line change
@@ -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
@@ -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
@@ -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:
2 changes: 1 addition & 1 deletion ext/opcache/tests/bug78937_4.phpt
Original file line number Diff line number Diff line change
@@ -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
@@ -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
@@ -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:
24 changes: 21 additions & 3 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
@@ -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)
{
@@ -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));
}
/* }}} */
@@ -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));
}
@@ -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));
}
Original file line number Diff line number Diff line change
@@ -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
@@ -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
@@ -32,7 +32,7 @@ array(4) {
[12]=>
int(6)
["test"]=>
object(JsonSerializable@anonymous)#2 (0) {
object(class@anonymous)#2 (0) {
}
}
array(4) {
@@ -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
@@ -45,8 +45,8 @@ namespace {
ClassInGlobal
Demo\ClassInNamespace
class@anonymous
ToBeExtended@anonymous
ToBeImplemented@anonymous
class@anonymous
class@anonymous
string
bool
bool