From 613c04b4ecfef5a1eec4a25004486dec71c7738c Mon Sep 17 00:00:00 2001 From: Priyadi Iman Nurcahyo <1102197+priyadi@users.noreply.github.com> Date: Thu, 11 Jan 2024 14:04:49 +0700 Subject: [PATCH] Refactor MapperFactory to use property info caching --- CHANGELOG.md | 2 + composer.json | 1 + src/MapperFactory.php | 89 +++++++++++++++++++++++++++---------------- src/Util/TypeUtil.php | 1 - 4 files changed, 60 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 543dfc19..07b53b3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ * Use `MappingFactoryInterface` everywhere instead of `MappingFactory` * Move some `TypeUtil` methods to `TypeResolver` for optimization opportunities * use `ObjectCacheFactory` to generate `ObjectCache` instances +* Update `MapperFactory` to reflect framework usage +* Use property info caching in non-framework usage ## 0.5.2 diff --git a/composer.json b/composer.json index 30058a96..db6fa5fe 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,7 @@ ], "require": { "psr/container": "^2.0", + "symfony/cache": "^6.4 || ^7.0", "symfony/clock": "^6.4 || ^7.0", "symfony/config": "^6.4 || ^7.0", "symfony/dependency-injection": "^6.4 || ^7.0", diff --git a/src/MapperFactory.php b/src/MapperFactory.php index 94ed1fa0..a207a91c 100644 --- a/src/MapperFactory.php +++ b/src/MapperFactory.php @@ -13,6 +13,7 @@ namespace Rekalogika\Mapper; +use Psr\Cache\CacheItemPoolInterface; use Psr\Container\ContainerInterface; use Rekalogika\Mapper\Command\MappingCommand; use Rekalogika\Mapper\Command\TryCommand; @@ -34,6 +35,7 @@ use Rekalogika\Mapper\Transformer\TraversableToTraversableTransformer; use Rekalogika\Mapper\TypeResolver\TypeResolver; use Rekalogika\Mapper\TypeResolver\TypeResolverInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Console\Application; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessor; @@ -41,7 +43,9 @@ use Symfony\Component\PropertyInfo\Extractor\PhpStanExtractor; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyInfoCacheExtractor; use Symfony\Component\PropertyInfo\PropertyInfoExtractor; +use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface; use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface; use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; @@ -74,7 +78,8 @@ class MapperFactory private ?TraversableToArrayAccessTransformer $traversableToArrayAccessTransformer = null; private ?TraversableToTraversableTransformer $traversableToTraversableTransformer = null; - private ?PropertyTypeExtractorInterface $propertyTypeExtractor = null; + private CacheItemPoolInterface $propertyInfoExtractorCache; + private null|(PropertyInfoExtractorInterface&PropertyInitializableExtractorInterface) $propertyInfoExtractor = null; private ?TypeResolverInterface $typeResolver = null; private ?MainTransformer $mainTransformer = null; private ?MapperInterface $mapper = null; @@ -95,7 +100,9 @@ public function __construct( private ?PropertyAccessor $propertyAccessor = null, private ?NormalizerInterface $normalizer = null, private ?DenormalizerInterface $denormalizer = null, + ?CacheItemPoolInterface $propertyInfoExtractorCache = null, ) { + $this->propertyInfoExtractorCache = $propertyInfoExtractorCache ?? new ArrayAdapter(); } public function getMapper(): MapperInterface @@ -108,7 +115,7 @@ public function getMapper(): MapperInterface } // - // concrete services + // property info // private function getReflectionExtractor(): ReflectionExtractor @@ -129,6 +136,53 @@ private function getPhpStanExtractor(): PropertyTypeExtractorInterface return $this->phpStanExtractor; } + private function getPropertyInfoExtractor(): PropertyInfoExtractorInterface&PropertyInitializableExtractorInterface + { + if ($this->propertyInfoExtractor === null) { + $propertyInfoExtractor = new PropertyInfoExtractor( + listExtractors: [ + $this->getReflectionExtractor(), + ], + typeExtractors: [ + $this->getPhpStanExtractor(), + $this->getReflectionExtractor(), + ], + accessExtractors: [ + $this->getReflectionExtractor(), + ], + initializableExtractors: [ + $this->getReflectionExtractor(), + ], + ); + + $this->propertyInfoExtractor = new PropertyInfoCacheExtractor( + $propertyInfoExtractor, + $this->propertyInfoExtractorCache, + ); + } + + return $this->propertyInfoExtractor; + } + + private function getPropertyInitializableExtractor(): PropertyInitializableExtractorInterface + { + return $this->getPropertyInfoExtractor(); + } + + private function getPropertyAccessExtractor(): PropertyAccessExtractorInterface + { + return $this->getPropertyInfoExtractor(); + } + + private function getPropertyListExtractor(): PropertyListExtractorInterface + { + return $this->getPropertyInfoExtractor(); + } + + // + // concrete services + // + private function getConcretePropertyAccessor(): PropertyAccessorInterface { if (null === $this->propertyAccessor) { @@ -162,35 +216,6 @@ private function getSerializer(): Serializer // interfaces // - private function getPropertyListExtractor(): PropertyListExtractorInterface - { - return $this->getReflectionExtractor(); - } - - private function getPropertyTypeExtractor(): PropertyTypeExtractorInterface - { - if ($this->propertyTypeExtractor === null) { - $this->propertyTypeExtractor = new PropertyInfoExtractor( - typeExtractors: [ - $this->getPhpStanExtractor(), - $this->getReflectionExtractor(), - ], - ); - } - - return $this->propertyTypeExtractor; - } - - private function getPropertyInitializableExtractor(): PropertyInitializableExtractorInterface - { - return $this->getReflectionExtractor(); - } - - private function getPropertyAccessExtractor(): PropertyAccessExtractorInterface - { - return $this->getReflectionExtractor(); - } - private function getPropertyAccessor(): PropertyAccessorInterface { return $this->getConcretePropertyAccessor(); @@ -232,7 +257,7 @@ protected function getObjectToObjectTransformer(): TransformerInterface if (null === $this->objectToObjectTransformer) { $this->objectToObjectTransformer = new ObjectToObjectTransformer( $this->getPropertyListExtractor(), - $this->getPropertyTypeExtractor(), + $this->getPropertyInfoExtractor(), $this->getPropertyInitializableExtractor(), $this->getPropertyAccessExtractor(), $this->getPropertyAccessor(), diff --git a/src/Util/TypeUtil.php b/src/Util/TypeUtil.php index 66890d41..f9114b49 100644 --- a/src/Util/TypeUtil.php +++ b/src/Util/TypeUtil.php @@ -15,7 +15,6 @@ use DaveLiddament\PhpLanguageExtensions\Friend; use DaveLiddament\PhpLanguageExtensions\NamespaceVisibility; -use Rekalogika\Mapper\Contracts\TypeMapping; use Rekalogika\Mapper\Exception\InvalidArgumentException; use Rekalogika\Mapper\Exception\MapperReturnsUnexpectedValueException; use Rekalogika\Mapper\Model\MixedType;