From a5d7bd3134035639106cb783681f04cc0f91294d Mon Sep 17 00:00:00 2001 From: Oliver Bartsch Date: Tue, 10 Sep 2024 18:15:31 +0200 Subject: [PATCH] [FEATURE] Respect ContainerInterface in StandardVariableProvider (#1002) With this change, it is possible to access properties of objects implementing `Psr\Container\ContainerInterface` from Fluid templates using the normal object accessor syntax. The interface requires two methods `has()` and `get()` which are used by Fluid to obtain the requested property from the object. --- composer.json | 3 +- .../Variables/StandardVariableProvider.php | 6 ++ ...andardVariableProviderContainerFixture.php | 30 +++++++++ .../StandardVariableProviderTest.php | 64 +++++++++++++++++++ 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 tests/Unit/Core/Variables/Fixtures/StandardVariableProviderContainerFixture.php diff --git a/composer.json b/composer.json index 722757a14..6e646ac22 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ "phpstan/phpstan": "^1.11.5", "phpstan/phpstan-phpunit": "^1.4.0", "phpunit/phpunit": "^11.2.5", - "t3docs/fluid-documentation-generator": "^4.3" + "t3docs/fluid-documentation-generator": "^4.3", + "psr/container": "^2.0" }, "suggest": { "ext-json": "PHP JSON is needed when using JSONVariableProvider: A relatively rare use case", diff --git a/src/Core/Variables/StandardVariableProvider.php b/src/Core/Variables/StandardVariableProvider.php index 872ba7e1f..5e071d4fc 100644 --- a/src/Core/Variables/StandardVariableProvider.php +++ b/src/Core/Variables/StandardVariableProvider.php @@ -9,6 +9,8 @@ namespace TYPO3Fluid\Fluid\Core\Variables; +use Psr\Container\ContainerInterface; + /** * Class StandardVariableProvider */ @@ -117,6 +119,10 @@ public function getByPath(string $path): mixed continue; } if (is_object($subject)) { + if ($subject instanceof ContainerInterface && $subject->has($pathSegment)) { + $subject = $subject->get($pathSegment); + continue; + } $upperCasePropertyName = ucfirst($pathSegment); $getMethod = 'get' . $upperCasePropertyName; if (method_exists($subject, $getMethod)) { diff --git a/tests/Unit/Core/Variables/Fixtures/StandardVariableProviderContainerFixture.php b/tests/Unit/Core/Variables/Fixtures/StandardVariableProviderContainerFixture.php new file mode 100644 index 000000000..2dd806c2d --- /dev/null +++ b/tests/Unit/Core/Variables/Fixtures/StandardVariableProviderContainerFixture.php @@ -0,0 +1,30 @@ +properties[$id] ?? null; + } + + public function has(string $id): bool + { + return array_key_exists($id, $this->properties); + } +} diff --git a/tests/Unit/Core/Variables/StandardVariableProviderTest.php b/tests/Unit/Core/Variables/StandardVariableProviderTest.php index 3ed261179..14ed95857 100644 --- a/tests/Unit/Core/Variables/StandardVariableProviderTest.php +++ b/tests/Unit/Core/Variables/StandardVariableProviderTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use TYPO3Fluid\Fluid\Core\Variables\InvalidVariableIdentifierException; use TYPO3Fluid\Fluid\Core\Variables\StandardVariableProvider; +use TYPO3Fluid\Fluid\Tests\Unit\Core\Variables\Fixtures\StandardVariableProviderContainerFixture; use TYPO3Fluid\Fluid\Tests\Unit\Core\Variables\Fixtures\StandardVariableProviderModelFixture; final class StandardVariableProviderTest extends TestCase @@ -245,6 +246,69 @@ public static function getPathTestValues(): array 'user.invalid', null, ], + 'access container' => [ + [ + 'user' => new StandardVariableProviderContainerFixture(['name' => 'Foobar Name']), + ], + 'user.name', + 'Foobar Name', + ], + 'access container getter that returns empty string' => [ + [ + 'user' => new StandardVariableProviderContainerFixture(['name' => '']), + ], + 'user.name', + '', + ], + 'access container getter that returns FALSE' => [ + [ + 'user' => new StandardVariableProviderContainerFixture(['name' => false]), + ], + 'user.name', + false, + ], + 'access container getter that returns object' => [ + [ + 'user' => new StandardVariableProviderContainerFixture(['object' => new \stdClass()]), + ], + 'user.object', + new \stdClass(), + ], + 'access container getter that returns object recursive' => [ + [ + 'user' => new StandardVariableProviderContainerFixture(['object' => new StandardVariableProviderModelFixture('Foobar Name')]), + ], + 'user.object.name', + 'Foobar Name', + ], + 'access container getter that returns container recursive' => [ + [ + 'user' => new StandardVariableProviderContainerFixture(['object' => new StandardVariableProviderContainerFixture(['name' => 'Foobar Name'])]), + ], + 'user.object.name', + 'Foobar Name', + ], + 'access container getter that returns array' => [ + [ + 'user' => new StandardVariableProviderContainerFixture(['array' => ['foo' => 'bar']]), + ], + 'user.array', + ['foo' => 'bar'], + ], + 'access container getter that returns value of array' => [ + [ + 'user' => new StandardVariableProviderContainerFixture(['array' => ['foo' => 'bar']]), + ], + 'user.array.foo', + 'bar', + ], + 'access container not existing returns null' => [ + [ + 'user' => new StandardVariableProviderContainerFixture(['name' => 'Foobar Name']), + ], + 'user.invalid', + null, + ], 'access dynamic variable using invalid variable reference' => [ [], '{invalid}',