From 2924e874f0871d8e289fb5e6d14b76fa5906cc05 Mon Sep 17 00:00:00 2001 From: Priyadi Iman Nurcahyo <1102197+priyadi@users.noreply.github.com> Date: Fri, 11 Oct 2024 23:42:54 +0700 Subject: [PATCH] feat: process proxies of all classes in the inheritance chain (#222) --- CHANGELOG.md | 1 + Makefile | 6 +++- src/Proxy/Implementation/ProxyFactory.php | 5 +++ .../CachingTargetClassResolver.php | 24 +++++++++++--- .../TargetClassResolver.php | 29 +++++++++++++++++ .../TargetClassResolverInterface.php | 10 ++++++ .../CachingObjectToObjectMetadataFactory.php | 32 ++++++++++++++++--- 7 files changed, 97 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69811b1c..db60d8cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * refactor: spin off `resolveTargetClass()` to separate class * perf: proxy warming +* feat: process proxies of all classes in the inheritance chain ## 1.10.0 diff --git a/Makefile b/Makefile index 45d1dc4c..74823385 100644 --- a/Makefile +++ b/Makefile @@ -25,8 +25,12 @@ psalm: clean: rm -rf tests/var +.PHONY: warmup +warmup: + $(PHP) tests/bin/console cache:warmup --env=test + .PHONY: phpunit -phpunit: clean +phpunit: clean warmup $(eval c ?=) $(PHP) vendor/bin/phpunit $(c) diff --git a/src/Proxy/Implementation/ProxyFactory.php b/src/Proxy/Implementation/ProxyFactory.php index bf92a13f..5ad466f2 100644 --- a/src/Proxy/Implementation/ProxyFactory.php +++ b/src/Proxy/Implementation/ProxyFactory.php @@ -44,6 +44,11 @@ public function warmingCreateProxy(string $class): void $sourceCode = $this->proxyGenerator ->generateProxyCode($class, $targetProxyClass); + if (!class_exists($targetProxyClass, false)) { + // @phpstan-ignore ekinoBannedCode.expression + eval($sourceCode); + } + $proxyRegistry = $this->proxyRegistry; if (!$proxyRegistry instanceof WarmableProxyRegistryInterface) { diff --git a/src/Transformer/MetadataUtil/TargetClassResolver/CachingTargetClassResolver.php b/src/Transformer/MetadataUtil/TargetClassResolver/CachingTargetClassResolver.php index 30198510..af736cd8 100644 --- a/src/Transformer/MetadataUtil/TargetClassResolver/CachingTargetClassResolver.php +++ b/src/Transformer/MetadataUtil/TargetClassResolver/CachingTargetClassResolver.php @@ -23,16 +23,32 @@ final class CachingTargetClassResolver implements TargetClassResolverInterface /** * @var array> */ - private array $cache = []; + private array $resolveTargetClassCache = []; + + /** + * @var array>> + */ + private array $getAllConcreteTargetClassesCache = []; public function __construct( private readonly TargetClassResolverInterface $decorated, ) {} #[\Override] - public function resolveTargetClass(string $sourceClass, string $targetClass): string - { - return $this->cache[$sourceClass][$targetClass] + public function resolveTargetClass( + string $sourceClass, + string $targetClass, + ): string { + return $this->resolveTargetClassCache[$sourceClass][$targetClass] ??= $this->decorated->resolveTargetClass($sourceClass, $targetClass); } + + #[\Override] + public function getAllConcreteTargetClasses( + string $sourceClass, + string $targetClass, + ): array { + return $this->getAllConcreteTargetClassesCache[$sourceClass][$targetClass] + ??= $this->decorated->getAllConcreteTargetClasses($sourceClass, $targetClass); + } } diff --git a/src/Transformer/MetadataUtil/TargetClassResolver/TargetClassResolver.php b/src/Transformer/MetadataUtil/TargetClassResolver/TargetClassResolver.php index b263733a..1ef0ba88 100644 --- a/src/Transformer/MetadataUtil/TargetClassResolver/TargetClassResolver.php +++ b/src/Transformer/MetadataUtil/TargetClassResolver/TargetClassResolver.php @@ -23,6 +23,7 @@ */ final readonly class TargetClassResolver implements TargetClassResolverInterface { + #[\Override] public function resolveTargetClass( string $sourceClass, string $targetClass, @@ -72,4 +73,32 @@ public function resolveTargetClass( return $targetClass; } + + #[\Override] + public function getAllConcreteTargetClasses( + string $sourceClass, + string $targetClass, + ): array { + $sourceReflection = new \ReflectionClass($sourceClass); + $targetReflection = new \ReflectionClass($targetClass); + + $targetAttributes = $targetReflection->getAttributes(InheritanceMap::class); + + if ($targetAttributes !== []) { + return array_values(array_unique($targetAttributes[0]->newInstance()->getMap())); + } elseif ($targetReflection->isAbstract() || $targetReflection->isInterface()) { + $sourceClasses = ClassUtil::getAllClassesFromObject($sourceClass); + + foreach ($sourceClasses as $currentSourceClass) { + $sourceReflection = new \ReflectionClass($currentSourceClass); + $sourceAttributes = $sourceReflection->getAttributes(InheritanceMap::class); + + if ($sourceAttributes !== []) { + return array_keys($sourceAttributes[0]->newInstance()->getMap()); + } + } + } + + return [$targetClass]; + } } diff --git a/src/Transformer/MetadataUtil/TargetClassResolverInterface.php b/src/Transformer/MetadataUtil/TargetClassResolverInterface.php index ad6b8570..297f640e 100644 --- a/src/Transformer/MetadataUtil/TargetClassResolverInterface.php +++ b/src/Transformer/MetadataUtil/TargetClassResolverInterface.php @@ -33,4 +33,14 @@ public function resolveTargetClass( string $sourceClass, string $targetClass, ): string; + + /** + * @param class-string $sourceClass + * @param class-string $targetClass + * @return list + */ + public function getAllConcreteTargetClasses( + string $sourceClass, + string $targetClass, + ): array; } diff --git a/src/Transformer/ObjectToObjectMetadata/Implementation/CachingObjectToObjectMetadataFactory.php b/src/Transformer/ObjectToObjectMetadata/Implementation/CachingObjectToObjectMetadataFactory.php index 60eb0b5b..e94dd1d6 100644 --- a/src/Transformer/ObjectToObjectMetadata/Implementation/CachingObjectToObjectMetadataFactory.php +++ b/src/Transformer/ObjectToObjectMetadata/Implementation/CachingObjectToObjectMetadataFactory.php @@ -18,6 +18,7 @@ use Rekalogika\Mapper\CacheWarmer\WarmableObjectToObjectMetadataFactoryInterface; use Rekalogika\Mapper\CacheWarmer\WarmableProxyFactoryInterface; use Rekalogika\Mapper\Exception\RuntimeException; +use Rekalogika\Mapper\Transformer\MetadataUtil\TargetClassResolver\TargetClassResolver; use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\ObjectToObjectMetadata; use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\ObjectToObjectMetadataFactoryInterface; use Rekalogika\Mapper\Util\ClassUtil; @@ -139,14 +140,15 @@ public function warmingCreateObjectToObjectMetadata( string $sourceClass, string $targetClass, ): ObjectToObjectMetadata { - $result = $this->decorated->createObjectToObjectMetadata($sourceClass, $targetClass); - if (!$this->cacheItemPool instanceof WarmableCacheInterface) { - return $result; + return $this->decorated + ->createObjectToObjectMetadata($sourceClass, $targetClass); } - // warm proxy - $this->proxyFactory->warmingCreateProxy($result->getTargetClass()); + $this->warmProxy($sourceClass, $targetClass); + + $result = $this->decorated + ->createObjectToObjectMetadata($sourceClass, $targetClass); // warm cache $cacheKey = hash('xxh128', $sourceClass . $targetClass); @@ -157,4 +159,24 @@ public function warmingCreateObjectToObjectMetadata( return $result; } + + /** + * @param class-string $sourceClass + * @param class-string $targetClass + */ + private function warmProxy(string $sourceClass, string $targetClass): void + { + $targetClassResolver = new TargetClassResolver(); + + $allConcreteTargetClasses = $targetClassResolver + ->getAllConcreteTargetClasses($sourceClass, $targetClass); + + foreach ($allConcreteTargetClasses as $concreteTargetClass) { + $this->proxyFactory->warmingCreateProxy($concreteTargetClass); + } + + $this->proxyFactory->warmingCreateProxy($targetClass); + + clearstatcache(); + } }