From b46a37a854ece4e975b40dbff0ec8c84f671133d Mon Sep 17 00:00:00 2001 From: Allon Moritz Date: Fri, 25 Apr 2025 14:11:50 +0200 Subject: [PATCH 1/2] Support proxies with new class parameter --- Tests/ContainerResourceTest.php | 64 +++++++++++++++++++++++++++++++++ src/Container.php | 24 +++++++------ src/ContainerResource.php | 6 +++- 3 files changed, 83 insertions(+), 11 deletions(-) diff --git a/Tests/ContainerResourceTest.php b/Tests/ContainerResourceTest.php index e35604b4..f387f4b7 100644 --- a/Tests/ContainerResourceTest.php +++ b/Tests/ContainerResourceTest.php @@ -233,4 +233,68 @@ public function testResetWithInstance() $this->assertNotSame($one, $two); } + + /** + * @testdox If the resource is created with a proxy class, the instance is created on first use + * + * @covers Joomla\DI\ContainerResource + * @uses Joomla\DI\Container + */ + public function testGetInstanceAsProxy() + { + if (PHP_VERSION_ID < 80400) { + $this->markTestSkipped('Lazy objects are only supported in PHP 8.4 or newer.'); + } + + $container = new Container(); + + $factoryCalled = false; + + $resource = new ContainerResource( + $container, + static function() use (&$factoryCalled) { + $factoryCalled = true; + return new Stub6('test'); + }, + 0, + Stub6::class + ); + + $stub = $resource->getInstance(); + + $this->assertTrue((new \ReflectionClass(Stub6::class))->isUninitializedLazyObject($stub) ); + $this->assertFalse($factoryCalled); + } + + /** + * @testdox If the resource is created with an invalid proxy class, the instance is created on first use + * + * @covers Joomla\DI\ContainerResource + * @uses Joomla\DI\Container + */ + public function testGetInstanceWithInvalidProxyClass() + { + if (PHP_VERSION_ID < 80400) { + $this->markTestSkipped('Lazy objects are only supported in PHP 8.4 or newer.'); + } + + $container = new Container(); + + $factoryCalled = false; + + $resource = new ContainerResource( + $container, + static function() use (&$factoryCalled) { + $factoryCalled = true; + return new Stub6('test'); + }, + 0, + 'invalid' + ); + + $stub = $resource->getInstance(); + + $this->assertTrue($stub instanceof Stub6); + $this->assertTrue($factoryCalled); + } } diff --git a/src/Container.php b/src/Container.php index 350a2e9d..753179c6 100644 --- a/src/Container.php +++ b/src/Container.php @@ -603,13 +603,14 @@ private function getMethodArgs(\ReflectionMethod $method): array * @param mixed $value Callable function to run or string to retrieve when requesting the specified $key. * @param boolean $shared True to create and store a shared instance. * @param boolean $protected True to protect this item from being overwritten. Useful for services. + * @param string $proxyClass The class to create the proxy for. * * @return $this * * @since 1.0 * @throws ProtectedKeyException Thrown if the provided key is already set and is protected. */ - public function set($key, $value, $shared = false, $protected = false) + public function set($key, $value, $shared = false, $protected = false, ?string $proxyClass = '') { $key = $this->resolveAlias($key); @@ -628,7 +629,7 @@ public function set($key, $value, $shared = false, $protected = false) $mode = $shared ? ContainerResource::SHARE : ContainerResource::NO_SHARE; $mode |= $protected ? ContainerResource::PROTECT : ContainerResource::NO_PROTECT; - $this->resources[$key] = new ContainerResource($this, $value, $mode); + $this->resources[$key] = new ContainerResource($this, $value, $mode, $proxyClass); return $this; } @@ -639,14 +640,15 @@ public function set($key, $value, $shared = false, $protected = false) * @param string $key Name of dataStore key to set. * @param mixed $value Callable function to run or string to retrieve when requesting the specified $key. * @param boolean $shared True to create and store a shared instance. + * @param string $proxyClass The class to create the proxy for. * * @return $this * * @since 1.0 */ - public function protect($key, $value, $shared = false) + public function protect($key, $value, $shared = false, ?string $proxyClass = '') { - return $this->set($key, $value, $shared, true); + return $this->set($key, $value, $shared, true, $proxyClass); } /** @@ -655,28 +657,30 @@ public function protect($key, $value, $shared = false) * @param string $key Name of dataStore key to set. * @param mixed $value Callable function to run or string to retrieve when requesting the specified $key. * @param boolean $protected True to protect this item from being overwritten. Useful for services. + * @param string $proxyClass The class to create the proxy for. * * @return $this * * @since 1.0 */ - public function share($key, $value, $protected = false) + public function share($key, $value, $protected = false, ?string $proxyClass = '') { - return $this->set($key, $value, true, $protected); + return $this->set($key, $value, true, $protected, $proxyClass); } /** * Get the raw data assigned to a key. * - * @param string $key The key for which to get the stored item. - * @param boolean $bail Throw an exception, if the key is not found + * @param string $key The key for which to get the stored item. + * @param boolean $bail Throw an exception, if the key is not found + * @param string $proxyClass The class to create the proxy for. * * @return ContainerResource|null The resource if present, or null if instructed to not bail * * @since 2.0.0 * @throws KeyNotFoundException */ - public function getResource(string $key, bool $bail = false): ?ContainerResource + public function getResource(string $key, bool $bail = false, ?string $proxyClass = ''): ?ContainerResource { if (isset($this->resources[$key])) { return $this->resources[$key]; @@ -687,7 +691,7 @@ public function getResource(string $key, bool $bail = false): ?ContainerResource } if ($this->parent instanceof ContainerInterface && $this->parent->has($key)) { - return new ContainerResource($this, $this->parent->get($key), ContainerResource::SHARE | ContainerResource::PROTECT); + return new ContainerResource($this, $this->parent->get($key), ContainerResource::SHARE | ContainerResource::PROTECT, $proxyClass); } if ($bail) { diff --git a/src/ContainerResource.php b/src/ContainerResource.php index 26e6e80f..03f74d25 100644 --- a/src/ContainerResource.php +++ b/src/ContainerResource.php @@ -95,10 +95,11 @@ final class ContainerResource * @param Container $container The container * @param mixed $value The resource or its factory closure * @param integer $mode Resource mode, defaults to Resource::NO_SHARE | Resource::NO_PROTECT + * @param string $proxyClass The class to create the proxy for, is only used when $value is a callable * * @since 2.0.0 */ - public function __construct(Container $container, $value, int $mode = 0) + public function __construct(Container $container, $value, int $mode = 0, ?string $proxyClass = '') { $this->container = $container; $this->shared = ($mode & self::SHARE) === self::SHARE; @@ -106,6 +107,9 @@ public function __construct(Container $container, $value, int $mode = 0) if (\is_callable($value)) { $this->factory = $value; + if ($proxyClass && class_exists($proxyClass, false) && PHP_VERSION_ID >= 80400) { + $this->factory = fn () => (new \ReflectionClass($proxyClass))->newLazyProxy(fn () => $value($this->container)); + } } else { if ($this->shared) { $this->instance = $value; From 7ea7bcd7360ff41cd461525c555909adcafb8fac Mon Sep 17 00:00:00 2001 From: Allon Moritz Date: Fri, 25 Apr 2025 14:28:19 +0200 Subject: [PATCH 2/2] cs --- src/ContainerResource.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ContainerResource.php b/src/ContainerResource.php index 03f74d25..3806f779 100644 --- a/src/ContainerResource.php +++ b/src/ContainerResource.php @@ -108,7 +108,7 @@ public function __construct(Container $container, $value, int $mode = 0, ?string if (\is_callable($value)) { $this->factory = $value; if ($proxyClass && class_exists($proxyClass, false) && PHP_VERSION_ID >= 80400) { - $this->factory = fn () => (new \ReflectionClass($proxyClass))->newLazyProxy(fn () => $value($this->container)); + $this->factory = fn () => (new \ReflectionClass($proxyClass))->newLazyProxy(fn () => $value($this->container)); } } else { if ($this->shared) {