diff --git a/system/Test/ReflectionHelper.php b/system/Test/ReflectionHelper.php index 47e85f4a8903..7b1584856c6b 100644 --- a/system/Test/ReflectionHelper.php +++ b/system/Test/ReflectionHelper.php @@ -55,7 +55,19 @@ public static function getPrivateMethodInvoker($obj, $method) */ private static function getAccessibleRefProperty($obj, $property) { - $refClass = is_object($obj) ? new ReflectionObject($obj) : new ReflectionClass($obj); + if (! is_object($obj)) { + return (new ReflectionClass($obj))->getProperty($property); + } + + $refClass = new ReflectionObject($obj); + + if (! $refClass->hasProperty($property) && str_contains($obj::class, '@anonymous')) { + $parentClass = $refClass->getParentClass(); + + if ($parentClass !== false) { + return $parentClass->getProperty($property); + } + } return $refClass->getProperty($property); } diff --git a/tests/system/Test/ReflectionHelperTest.php b/tests/system/Test/ReflectionHelperTest.php index e110a4820422..a024586ae167 100644 --- a/tests/system/Test/ReflectionHelperTest.php +++ b/tests/system/Test/ReflectionHelperTest.php @@ -93,4 +93,44 @@ public function testGetPrivateMethodInvokerWithStatic(): void $method('param1', 'param2'), ); } + + public function testGetPrivatePropertyWithAnonymousObject(): void + { + $anonClassObject = new class () extends TestForReflectionHelper { + private string $hideMe = 'active'; + + public function getHideMe(): string + { + return $this->hideMe; + } + }; + + $hideMe = $this->getPrivateProperty($anonClassObject, 'hideMe'); + $private = $this->getPrivateProperty($anonClassObject, 'private'); + + $this->assertSame('active', $hideMe); + $this->assertSame($anonClassObject->getPrivate(), $private); + } + + public function testSetPrivatePropertyWithAnonymousObject(): void + { + $anonClassObject = new class () extends TestForReflectionHelper { + private string $hideMe = 'active'; + + public function getHideMe(): string + { + return $this->hideMe; + } + }; + + $this->setPrivateProperty($anonClassObject, 'hideMe', 'inactive'); + $this->setPrivateProperty($anonClassObject, 'private', 'new secret'); + + $hideMe = $this->getPrivateProperty($anonClassObject, 'hideMe'); + $private = $this->getPrivateProperty($anonClassObject, 'private'); + + $this->assertSame('inactive', $hideMe); + $this->assertSame('new secret', $private); + $this->assertSame('new secret', $anonClassObject->getPrivate()); + } } diff --git a/user_guide_src/source/changelogs/v4.7.0.rst b/user_guide_src/source/changelogs/v4.7.0.rst index c4dcb7d7d40f..24cd86556eca 100644 --- a/user_guide_src/source/changelogs/v4.7.0.rst +++ b/user_guide_src/source/changelogs/v4.7.0.rst @@ -250,13 +250,14 @@ Libraries - **View:** Added the ability to override namespaced views (e.g., from modules/packages) by placing a matching file structure within the **app/Views/overrides** directory. See :ref:`Overriding Namespaced Views ` for details. - **Toolbar:** Fixed an issue where the Debug Toolbar was incorrectly injected into responses generated by third-party libraries (e.g., Dompdf) that use native PHP headers instead of the framework's Response object. - Commands ======== Testing ======= +-The ``CodeIgniter\Test\ReflectionHelper::getPrivateProperty`` and ``CodeIgniter\Test\ReflectionHelper::setPrivateProperty`` methods has added support for accessing the private properties of an anonymous class that extends the parent class. + Database ======== @@ -306,7 +307,6 @@ Changes - **Paths:** Added support for changing the location of the ``.env`` file via the ``Paths::$envDirectory`` property. - **Toolbar:** Added ``$disableOnHeaders`` property to **app/Config/Toolbar.php**. - ************ Deprecations ************ diff --git a/user_guide_src/source/testing/overview.rst b/user_guide_src/source/testing/overview.rst index 8a94d6bc382c..3149d9823fbc 100644 --- a/user_guide_src/source/testing/overview.rst +++ b/user_guide_src/source/testing/overview.rst @@ -224,6 +224,10 @@ instance of the class to test. The second parameter is the name of the property. .. literalinclude:: overview/014.php +.. versionadded:: 4.7.0 + +You can access private properties for anonymous classes that extend the parent class. + setPrivateProperty($instance, $property, $value) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -232,6 +236,10 @@ parameter is the name of the property to set the value of. The third parameter i .. literalinclude:: overview/015.php +.. versionadded:: 4.7.0 + +You can access private properties for anonymous classes that extend the parent class. + Mocking Services ================ diff --git a/user_guide_src/source/testing/overview/014.php b/user_guide_src/source/testing/overview/014.php index eb2509ac64b8..d9fe0a005337 100644 --- a/user_guide_src/source/testing/overview/014.php +++ b/user_guide_src/source/testing/overview/014.php @@ -5,5 +5,8 @@ // Create an instance of the class to test $obj = new Foo(); -// Test the value +// or anonymous class +// $obj = new class () extends Foo {}; + +// Test the value from Foo $this->assertEquals('bar', $this->getPrivateProperty($obj, 'baz')); diff --git a/user_guide_src/source/testing/overview/015.php b/user_guide_src/source/testing/overview/015.php index 059c55a12011..4a9dcbdba9a6 100644 --- a/user_guide_src/source/testing/overview/015.php +++ b/user_guide_src/source/testing/overview/015.php @@ -5,7 +5,10 @@ // Create an instance of the class to test $obj = new Foo(); -// Set the value +// or create anonymous class +// $obj = new class () extends Foo {}; + +// Set the value to Foo $this->setPrivateProperty($obj, 'baz', 'oops!'); // Do normal testing...