Skip to content

Commit 9118590

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

10 files changed

+454
-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: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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->prop2 = 'value2';
10+
$stdObj->{'special-name'} = 'special value';
11+
$stdObj->{'123numeric'} = 'numeric start';
12+
13+
function testDynamicProperty($obj, $property, $description) {
14+
try {
15+
$reflection = new ReflectionProperty($obj, $property);
16+
echo "$description:\n";
17+
echo " getName(): " . $reflection->getName() . "\n";
18+
echo " getMangledName(): " . $reflection->getMangledName() . "\n";
19+
20+
$array = (array) $obj;
21+
echo " Found in array cast: " . (array_key_exists($reflection->getMangledName(), $array) ? "yes" : "no") . "\n";
22+
echo "\n";
23+
} catch (ReflectionException $e) {
24+
echo "$description: EXCEPTION - " . $e->getMessage() . "\n\n";
25+
}
26+
}
27+
28+
testDynamicProperty($stdObj, 'prop1', 'stdClass property prop1');
29+
testDynamicProperty($stdObj, 'special-name', 'stdClass property with special name');
30+
testDynamicProperty($stdObj, '123numeric', 'stdClass property starting with number');
31+
32+
echo "=== Testing regular class with dynamic properties ===\n";
33+
#[AllowDynamicProperties]
34+
class TestClass {
35+
public $existing = 'existing';
36+
}
37+
38+
$obj = new TestClass();
39+
$obj->dynamic = 'dynamic value';
40+
$obj->anotherDynamic = 'another dynamic';
41+
42+
testDynamicProperty($obj, 'dynamic', 'Regular class dynamic property');
43+
testDynamicProperty($obj, 'anotherDynamic', 'Regular class another dynamic property');
44+
45+
$reflection = new ReflectionProperty($obj, 'existing');
46+
echo "Regular property:\n";
47+
echo " getName(): " . $reflection->getName() . "\n";
48+
echo " getMangledName(): " . $reflection->getMangledName() . "\n";
49+
50+
echo "\n=== Testing ReflectionProperty from class vs instance ===\n";
51+
try {
52+
$reflection = new ReflectionProperty('TestClass', 'dynamic');
53+
echo "This should not be reached\n";
54+
} catch (ReflectionException $e) {
55+
echo "Expected exception for class-based reflection: " . $e->getMessage() . "\n";
56+
}
57+
58+
try {
59+
$reflection = new ReflectionProperty($obj, 'dynamic');
60+
echo "Instance-based reflection works: " . $reflection->getMangledName() . "\n";
61+
} catch (ReflectionException $e) {
62+
echo "Unexpected exception: " . $e->getMessage() . "\n";
63+
}
64+
65+
?>
66+
--EXPECTF--
67+
=== Testing stdClass with dynamic properties ===
68+
stdClass property prop1:
69+
getName(): prop1
70+
getMangledName(): prop1
71+
Found in array cast: yes
72+
73+
stdClass property with special name:
74+
getName(): special-name
75+
getMangledName(): special-name
76+
Found in array cast: yes
77+
78+
stdClass property starting with number:
79+
getName(): 123numeric
80+
getMangledName(): 123numeric
81+
Found in array cast: yes
82+
83+
=== Testing regular class with dynamic properties ===
84+
Regular class dynamic property:
85+
getName(): dynamic
86+
getMangledName(): dynamic
87+
Found in array cast: yes
88+
89+
Regular class another dynamic property:
90+
getName(): anotherDynamic
91+
getMangledName(): anotherDynamic
92+
Found in array cast: yes
93+
94+
Regular property:
95+
getName(): existing
96+
getMangledName(): existing
97+
98+
=== Testing ReflectionProperty from class vs instance ===
99+
Expected exception for class-based reflection: Property TestClass::$dynamic does not exist
100+
Instance-based reflection works: dynamic
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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+
}
11+
12+
class ChildClass extends ParentClass {
13+
private $private = 'child_private';
14+
protected $childProp = 'child_protected';
15+
}
16+
17+
function testProperty($class, $property) {
18+
$reflection = new ReflectionProperty($class, $property);
19+
$obj = new $class();
20+
$array = (array) $obj;
21+
22+
echo "Class: $class, Property: $property\n";
23+
echo "Mangled name: '" . $reflection->getMangledName() . "'\n";
24+
echo "Key exists in array cast: " . (array_key_exists($reflection->getMangledName(), $array) ? "yes" : "no") . "\n";
25+
echo "\n";
26+
}
27+
28+
testProperty('ParentClass', 'public');
29+
testProperty('ParentClass', 'protected');
30+
testProperty('ParentClass', 'private');
31+
32+
testProperty('ChildClass', 'public');
33+
testProperty('ChildClass', 'protected');
34+
testProperty('ChildClass', 'childProp');
35+
testProperty('ChildClass', 'private');
36+
37+
?>
38+
--EXPECTF--
39+
Class: ParentClass, Property: public
40+
Mangled name: 'public'
41+
Key exists in array cast: yes
42+
43+
Class: ParentClass, Property: protected
44+
Mangled name: '%0*%0protected'
45+
Key exists in array cast: yes
46+
47+
Class: ParentClass, Property: private
48+
Mangled name: '%0ParentClass%0private'
49+
Key exists in array cast: yes
50+
51+
Class: ChildClass, Property: public
52+
Mangled name: 'public'
53+
Key exists in array cast: yes
54+
55+
Class: ChildClass, Property: protected
56+
Mangled name: '%0*%0protected'
57+
Key exists in array cast: yes
58+
59+
Class: ChildClass, Property: childProp
60+
Mangled name: '%0*%0childProp'
61+
Key exists in array cast: yes
62+
63+
Class: ChildClass, Property: private
64+
Mangled name: '%0ChildClass%0private'
65+
Key exists in array cast: yes
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
--TEST--
2+
Test ReflectionProperty::getMangledName() from instance vs class
3+
--FILE--
4+
<?php
5+
6+
#[AllowDynamicProperties]
7+
class TestClass {
8+
public $public = 'public';
9+
protected $protected = 'protected';
10+
private $private = 'private';
11+
}
12+
13+
$obj = new TestClass();
14+
$obj->dynamic = 'dynamic';
15+
16+
echo "=== Testing ReflectionProperty from CLASS ===\n";
17+
18+
function testFromClass($property) {
19+
try {
20+
$reflection = new ReflectionProperty('TestClass', $property);
21+
echo "Property $property from class:\n";
22+
echo " getName(): " . $reflection->getName() . "\n";
23+
echo " getMangledName(): " . $reflection->getMangledName() . "\n";
24+
echo "\n";
25+
} catch (ReflectionException $e) {
26+
echo "Property $property from class: EXCEPTION - " . $e->getMessage() . "\n\n";
27+
}
28+
}
29+
30+
testFromClass('public');
31+
testFromClass('protected');
32+
testFromClass('private');
33+
testFromClass('dynamic');
34+
35+
echo "=== Testing ReflectionProperty from INSTANCE ===\n";
36+
37+
function testFromInstance($obj, $property) {
38+
try {
39+
$reflection = new ReflectionProperty($obj, $property);
40+
echo "Property $property from instance:\n";
41+
echo " getName(): " . $reflection->getName() . "\n";
42+
echo " getMangledName(): " . $reflection->getMangledName() . "\n";
43+
44+
$array = (array) $obj;
45+
echo " Found in array cast: " . (array_key_exists($reflection->getMangledName(), $array) ? "yes" : "no") . "\n";
46+
echo "\n";
47+
} catch (ReflectionException $e) {
48+
echo "Property $property from instance: EXCEPTION - " . $e->getMessage() . "\n\n";
49+
}
50+
}
51+
52+
testFromInstance($obj, 'public');
53+
testFromInstance($obj, 'protected');
54+
testFromInstance($obj, 'private');
55+
56+
echo "=== Instance array keys ===\n";
57+
$array = (array) $obj;
58+
foreach (array_keys($array) as $key) {
59+
echo "Key: '$key'\n";
60+
}
61+
62+
?>
63+
--EXPECTF--
64+
=== Testing ReflectionProperty from CLASS ===
65+
Property public from class:
66+
getName(): public
67+
getMangledName(): public
68+
69+
Property protected from class:
70+
getName(): protected
71+
getMangledName(): %0*%0protected
72+
73+
Property private from class:
74+
getName(): private
75+
getMangledName(): %0TestClass%0private
76+
77+
Property dynamic from class: EXCEPTION - Property TestClass::$dynamic does not exist
78+
79+
=== Testing ReflectionProperty from INSTANCE ===
80+
Property public from instance:
81+
getName(): public
82+
getMangledName(): public
83+
Found in array cast: yes
84+
85+
Property protected from instance:
86+
getName(): protected
87+
getMangledName(): %0*%0protected
88+
Found in array cast: yes
89+
90+
Property private from instance:
91+
getName(): private
92+
getMangledName(): %0TestClass%0private
93+
Found in array cast: yes
94+
95+
=== Instance array keys ===
96+
Key: 'public'
97+
Key: '%0*%0protected'
98+
Key: '%0TestClass%0private'
99+
Key: 'dynamic'

0 commit comments

Comments
 (0)