Skip to content

Commit af055f1

Browse files
Add ReflectionProperty::getMangledName()
1 parent c9249e2 commit af055f1

10 files changed

+475
-1
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ PHP NEWS
187187
zval for uninitialized typed properties). (nielsdos)
188188
. Fixed bug GH-15766 (ReflectionClass::toString() should have better output
189189
for enums). (DanielEScherzer)
190+
. Added ReflectionProperty::getMangledName() method. (alexandre-daubois)
190191

191192
- Session:
192193
. session_start() throws a ValueError on option argument if not a hashmap

UPGRADING

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ PHP 8.5 UPGRADE NOTES
420420
ReflectionConstant::getExtensionName() were introduced.
421421
. ReflectionConstant::getAttributes() was introduced.
422422
RFC: https://wiki.php.net/rfc/attributes-on-constants
423+
. ReflectionProperty::getMangledName() was introduced.
423424

424425
- Sqlite:
425426
. Sqlite3Stmt::busy to check if a statement had been fetched

ext/reflection/php_reflection.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5753,6 +5753,21 @@ ZEND_METHOD(ReflectionProperty, getName)
57535753
}
57545754
/* }}} */
57555755

5756+
ZEND_METHOD(ReflectionProperty, getMangledName)
5757+
{
5758+
reflection_object *intern;
5759+
property_reference *ref;
5760+
5761+
ZEND_PARSE_PARAMETERS_NONE();
5762+
5763+
GET_REFLECTION_OBJECT_PTR(ref);
5764+
if (ref->prop == NULL) {
5765+
RETURN_STR_COPY(ref->unmangled_name);
5766+
}
5767+
5768+
RETURN_STR_COPY(ref->prop->name);
5769+
}
5770+
57565771
static void _property_check_flag(INTERNAL_FUNCTION_PARAMETERS, int mask) /* {{{ */
57575772
{
57585773
reflection_object *intern;

ext/reflection/php_reflection.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,8 @@ public function __toString(): string {}
482482
/** @tentative-return-type */
483483
public function getName(): string {}
484484

485+
public function getMangledName(): string {}
486+
485487
/** @tentative-return-type */
486488
public function getValue(?object $object = null): mixed {}
487489

ext/reflection/php_reflection_arginfo.h

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
--TEST--
2+
Test ReflectionProperty::getMangledName() method
3+
--FILE--
4+
<?php
5+
6+
class TestClass {
7+
public $publicProp = 'public';
8+
protected $protectedProp = 'protected';
9+
private $privateProp = 'private';
10+
}
11+
12+
function testMangledName($class, $property) {
13+
$reflection = new ReflectionProperty($class, $property);
14+
echo "Property: $property\n";
15+
echo "getName(): " . $reflection->getName() . "\n";
16+
echo "getMangledName(): " . $reflection->getMangledName() . "\n";
17+
18+
$obj = new $class();
19+
$array = (array) $obj;
20+
echo "In array cast: " . (array_key_exists($reflection->getMangledName(), $array) ? "found" : "not found") . "\n";
21+
echo "\n";
22+
}
23+
24+
testMangledName('TestClass', 'publicProp');
25+
testMangledName('TestClass', 'protectedProp');
26+
testMangledName('TestClass', 'privateProp');
27+
28+
?>
29+
--EXPECTF--
30+
Property: publicProp
31+
getName(): publicProp
32+
getMangledName(): publicProp
33+
In array cast: found
34+
35+
Property: protectedProp
36+
getName(): protectedProp
37+
getMangledName(): %0*%0protectedProp
38+
In array cast: found
39+
40+
Property: privateProp
41+
getName(): privateProp
42+
getMangledName(): %0TestClass%0privateProp
43+
In array cast: found
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
--TEST--
2+
Test ReflectionProperty::getMangledName() with dynamic properties
3+
--FILE--
4+
<?php
5+
6+
echo "=== Testing stdClass with dynamic properties ===\n";
7+
$stdObj = new stdClass();
8+
$stdObj->prop1 = 'value1';
9+
$stdObj->{'special-name'} = 'special value';
10+
$stdObj->{'123numeric'} = 'numeric start';
11+
12+
function testDynamicProperty($obj, $property, $description) {
13+
try {
14+
$reflection = new ReflectionProperty($obj, $property);
15+
echo "$description:\n";
16+
echo " getName(): " . $reflection->getName() . "\n";
17+
echo " getMangledName(): " . $reflection->getMangledName() . "\n";
18+
19+
$array = (array) $obj;
20+
echo " Found in array cast: " . (array_key_exists($reflection->getMangledName(), $array) ? "yes" : "no") . "\n";
21+
echo "\n";
22+
} catch (ReflectionException $e) {
23+
echo "$description: EXCEPTION - " . $e->getMessage() . "\n\n";
24+
}
25+
}
26+
27+
testDynamicProperty($stdObj, 'prop1', 'stdClass property prop1');
28+
testDynamicProperty($stdObj, 'special-name', 'stdClass property with special name');
29+
testDynamicProperty($stdObj, '123numeric', 'stdClass property starting with number');
30+
31+
echo "=== Testing regular class with dynamic properties ===\n";
32+
#[AllowDynamicProperties]
33+
class TestClass {
34+
public $existing = 'existing';
35+
}
36+
37+
$obj = new TestClass();
38+
$obj->dynamic = 'dynamic value';
39+
$obj->anotherDynamic = 'another dynamic';
40+
41+
testDynamicProperty($obj, 'dynamic', 'Regular class dynamic property');
42+
testDynamicProperty($obj, 'anotherDynamic', 'Regular class another dynamic property');
43+
44+
$reflection = new ReflectionProperty($obj, 'existing');
45+
echo "Regular property:\n";
46+
echo " getName(): " . $reflection->getName() . "\n";
47+
echo " getMangledName(): " . $reflection->getMangledName() . "\n";
48+
49+
echo "\n=== Testing ReflectionProperty from class vs instance ===\n";
50+
try {
51+
$reflection = new ReflectionProperty('TestClass', 'dynamic');
52+
echo "This should not be reached\n";
53+
} catch (ReflectionException $e) {
54+
echo "Expected exception for class-based reflection: " . $e->getMessage() . "\n";
55+
}
56+
57+
try {
58+
$reflection = new ReflectionProperty($obj, 'dynamic');
59+
echo "Instance-based reflection works: " . $reflection->getMangledName() . "\n";
60+
} catch (ReflectionException $e) {
61+
echo "Unexpected exception: " . $e->getMessage() . "\n";
62+
}
63+
64+
?>
65+
--EXPECT--
66+
=== Testing stdClass with dynamic properties ===
67+
stdClass property prop1:
68+
getName(): prop1
69+
getMangledName(): prop1
70+
Found in array cast: yes
71+
72+
stdClass property with special name:
73+
getName(): special-name
74+
getMangledName(): special-name
75+
Found in array cast: yes
76+
77+
stdClass property starting with number:
78+
getName(): 123numeric
79+
getMangledName(): 123numeric
80+
Found in array cast: yes
81+
82+
=== Testing regular class with dynamic properties ===
83+
Regular class dynamic property:
84+
getName(): dynamic
85+
getMangledName(): dynamic
86+
Found in array cast: yes
87+
88+
Regular class another dynamic property:
89+
getName(): anotherDynamic
90+
getMangledName(): anotherDynamic
91+
Found in array cast: yes
92+
93+
Regular property:
94+
getName(): existing
95+
getMangledName(): existing
96+
97+
=== Testing ReflectionProperty from class vs instance ===
98+
Expected exception for class-based reflection: Property TestClass::$dynamic does not exist
99+
Instance-based reflection works: dynamic
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
--TEST--
2+
Test ReflectionProperty::getMangledName() with inheritance
3+
--FILE--
4+
<?php
5+
6+
class ParentClass {
7+
public $public = 'parent_public';
8+
protected $protected = 'parent_protected';
9+
private $private = 'parent_private';
10+
private $parentOnly = 'parent_only_private';
11+
}
12+
13+
class ChildClass extends ParentClass {
14+
private $private = 'child_private';
15+
protected $childProp = 'child_protected';
16+
}
17+
18+
function testProperty($class, $property) {
19+
$reflection = new ReflectionProperty($class, $property);
20+
$obj = new $class();
21+
$array = (array) $obj;
22+
23+
echo "Class: $class, Property: \$$property\n";
24+
echo "Mangled name: '" . $reflection->getMangledName() . "'\n";
25+
echo "Key exists in array cast: " . (array_key_exists($reflection->getMangledName(), $array) ? "yes" : "no") . "\n";
26+
echo "\n";
27+
}
28+
29+
testProperty('ParentClass', 'public');
30+
testProperty('ParentClass', 'protected');
31+
testProperty('ParentClass', 'private');
32+
33+
testProperty('ChildClass', 'public');
34+
testProperty('ChildClass', 'protected');
35+
testProperty('ChildClass', 'childProp');
36+
testProperty('ChildClass', 'private');
37+
38+
echo "Access to parent's private property not in child:\n";
39+
40+
try {
41+
$reflection = new ReflectionProperty('ChildClass', 'parentOnly');
42+
echo "ERROR: Should have failed\n";
43+
} catch (ReflectionException $e) {
44+
echo "Instance-based creation failed as expected: " . $e->getMessage() . "\n";
45+
}
46+
47+
try {
48+
$obj = new ChildClass();
49+
$reflection = new ReflectionProperty($obj, 'parentOnly');
50+
echo "ERROR: Should have failed\n";
51+
} catch (ReflectionException $e) {
52+
echo "Object-based creation failed as expected: " . $e->getMessage() . "\n";
53+
}
54+
55+
?>
56+
--EXPECTF--
57+
Class: ParentClass, Property: $public
58+
Mangled name: 'public'
59+
Key exists in array cast: yes
60+
61+
Class: ParentClass, Property: $protected
62+
Mangled name: '%0*%0protected'
63+
Key exists in array cast: yes
64+
65+
Class: ParentClass, Property: $private
66+
Mangled name: '%0ParentClass%0private'
67+
Key exists in array cast: yes
68+
69+
Class: ChildClass, Property: $public
70+
Mangled name: 'public'
71+
Key exists in array cast: yes
72+
73+
Class: ChildClass, Property: $protected
74+
Mangled name: '%0*%0protected'
75+
Key exists in array cast: yes
76+
77+
Class: ChildClass, Property: $childProp
78+
Mangled name: '%0*%0childProp'
79+
Key exists in array cast: yes
80+
81+
Class: ChildClass, Property: $private
82+
Mangled name: '%0ChildClass%0private'
83+
Key exists in array cast: yes
84+
85+
Access to parent's private property not in child:
86+
Instance-based creation failed as expected: Property ChildClass::$parentOnly does not exist
87+
Object-based creation failed as expected: Property ChildClass::$parentOnly does not exist

0 commit comments

Comments
 (0)