From c9621cff2c1f66cdc8eeb539e3baf10e15a51973 Mon Sep 17 00:00:00 2001 From: Asmir Mustafic Date: Tue, 12 Jun 2018 08:45:21 +0200 Subject: [PATCH 1/3] move the logic to determine the list of properties to get/set outside of a graph navigator --- src/Accessor/AccessorStrategyInterface.php | 24 +++-- src/Accessor/DefaultAccessorStrategy.php | 96 ++++++++++++++++--- .../DeserializationGraphNavigator.php | 19 ++-- .../SerializationGraphNavigator.php | 40 +------- 4 files changed, 114 insertions(+), 65 deletions(-) diff --git a/src/Accessor/AccessorStrategyInterface.php b/src/Accessor/AccessorStrategyInterface.php index 8f77af0f4..e922e8014 100644 --- a/src/Accessor/AccessorStrategyInterface.php +++ b/src/Accessor/AccessorStrategyInterface.php @@ -4,25 +4,37 @@ namespace JMS\Serializer\Accessor; +use JMS\Serializer\Context; +use JMS\Serializer\DeserializationContext; +use JMS\Serializer\Metadata\ClassMetadata; use JMS\Serializer\Metadata\PropertyMetadata; +use JMS\Serializer\SerializationContext; /** * @author Asmir Mustafic */ interface AccessorStrategyInterface { + /** + * @param ClassMetadata $metadata + * @param Context $context + * @return PropertyMetadata[] + */ + public function getProperties(ClassMetadata $metadata, Context $context): array; /** * @param object $object - * @param PropertyMetadata $metadata - * @return mixed + * @param PropertyMetadata[] $properties + * @param SerializationContext $context + * @return mixed[] */ - public function getValue(object $object, PropertyMetadata $metadata); + public function getValues(object $object, array $properties, SerializationContext $context):array; /** * @param object $object - * @param mixed $value - * @param PropertyMetadata $metadata + * @param mixed[] $values + * @param PropertyMetadata[] $properties + * @param DeserializationContext $context * @return void */ - public function setValue(object $object, $value, PropertyMetadata $metadata): void; + public function setValues(object $object, array $values, array $properties, DeserializationContext $context): void; } diff --git a/src/Accessor/DefaultAccessorStrategy.php b/src/Accessor/DefaultAccessorStrategy.php index 805cdba6d..04c2273b4 100644 --- a/src/Accessor/DefaultAccessorStrategy.php +++ b/src/Accessor/DefaultAccessorStrategy.php @@ -4,12 +4,17 @@ namespace JMS\Serializer\Accessor; -use JMS\Serializer\Exception\ExpressionLanguageRequiredException; +use JMS\Serializer\Context; +use JMS\Serializer\DeserializationContext; use JMS\Serializer\Exception\LogicException; +use JMS\Serializer\Exclusion\ExclusionStrategyInterface; +use JMS\Serializer\Exclusion\ExpressionLanguageExclusionStrategy; use JMS\Serializer\Expression\ExpressionEvaluatorInterface; +use JMS\Serializer\Metadata\ClassMetadata; use JMS\Serializer\Metadata\ExpressionPropertyMetadata; use JMS\Serializer\Metadata\PropertyMetadata; use JMS\Serializer\Metadata\StaticPropertyMetadata; +use JMS\Serializer\SerializationContext; /** * @author Asmir Mustafic @@ -19,28 +24,97 @@ final class DefaultAccessorStrategy implements AccessorStrategyInterface private $readAccessors = []; private $writeAccessors = []; private $propertyReflectionCache = []; - /** * @var ExpressionEvaluatorInterface */ private $evaluator; + /** + * @var ExclusionStrategyInterface + */ + private $exclusionStrategy; + /** + * @var ExpressionLanguageExclusionStrategy + */ + private $expressionExclusionStrategy; - public function __construct(ExpressionEvaluatorInterface $evaluator = null) - { + public function __construct( + ExclusionStrategyInterface $exclusionStrategy, + ExpressionEvaluatorInterface $evaluator + ) { + $this->exclusionStrategy = $exclusionStrategy; $this->evaluator = $evaluator; + $this->expressionExclusionStrategy = new ExpressionLanguageExclusionStrategy($evaluator); } - public function getValue(object $object, PropertyMetadata $metadata) + /** + * @param ClassMetadata $metadata + * @param Context $context + * @return PropertyMetadata[] + */ + public function getProperties(ClassMetadata $metadata, Context $context): array { - if ($metadata instanceof StaticPropertyMetadata) { - return $metadata->getValue(null); + $values = []; + foreach ($metadata->propertyMetadata as $propertyMetadata) { + if ($context instanceof DeserializationContext && $propertyMetadata->readOnly) { + continue; + } + + if ($this->exclusionStrategy->shouldSkipProperty($propertyMetadata, $context) || $this->expressionExclusionStrategy->shouldSkipProperty($propertyMetadata, $context)) { + continue; + } + + $values[] = $propertyMetadata; } - if ($metadata instanceof ExpressionPropertyMetadata) { - if ($this->evaluator === null) { - throw new ExpressionLanguageRequiredException(sprintf('The property %s on %s requires the expression accessor strategy to be enabled.', $metadata->name, $metadata->class)); + return $values; + } + + public function getValues(object $data, array $properties, SerializationContext $context): array + { + $shouldSerializeNull = $context->shouldSerializeNull(); + + $values = []; + foreach ($properties as $propertyMetadata) { + + $v = $this->getValue($data, $propertyMetadata, $context); + + if (null === $v && $shouldSerializeNull !== true) { + continue; } + $values[] = $v; + } + + return $values; + } + + /** + * @param object $object + * @param mixed[] $values + * @param PropertyMetadata[] $properties + * @param DeserializationContext $context + * @return void + */ + public function setValues(object $object, array $values, array $properties, DeserializationContext $context): void + { + $values = []; + foreach ($properties as $i => $propertyMetadata) { + + if (!array_key_exists($i, $values)) { + continue; + } + + $this->setValue($object, $values[$i], $propertyMetadata, $context); + } + } + + private function getValue(object $object, PropertyMetadata $metadata, SerializationContext $context) + { + if ($metadata instanceof StaticPropertyMetadata) { + return $metadata->getValue(); + } + + if ($metadata instanceof ExpressionPropertyMetadata) { return $this->evaluator->evaluate($metadata->expression, ['object' => $object]); } @@ -71,7 +145,7 @@ public function getValue(object $object, PropertyMetadata $metadata) return $object->{$metadata->getter}(); } - public function setValue(object $object, $value, PropertyMetadata $metadata): void + public function setValue(object $object, $value, PropertyMetadata $metadata, DeserializationContext $context): void { if ($metadata->readOnly) { throw new LogicException(sprintf('%s on %s is read only.', $metadata->name, $metadata->class)); diff --git a/src/GraphNavigator/DeserializationGraphNavigator.php b/src/GraphNavigator/DeserializationGraphNavigator.php index 39ee36bee..8ab178fe5 100644 --- a/src/GraphNavigator/DeserializationGraphNavigator.php +++ b/src/GraphNavigator/DeserializationGraphNavigator.php @@ -166,29 +166,22 @@ public function accept($data, array $type = null) $object = $this->objectConstructor->construct($this->visitor, $metadata, $data, $type, $this->context); $this->visitor->startVisitingObject($metadata, $object, $type); - foreach ($metadata->propertyMetadata as $propertyMetadata) { - if ($this->exclusionStrategy->shouldSkipProperty($propertyMetadata, $this->context)) { - continue; - } - if (null !== $this->expressionExclusionStrategy && $this->expressionExclusionStrategy->shouldSkipProperty($propertyMetadata, $this->context)) { - continue; - } - - if ($propertyMetadata->readOnly) { - continue; - } + $props = $this->accessor->getProperties($metadata, $this->context); + $values = []; + foreach ($props as $i => $propertyMetadata) { $this->context->pushPropertyMetadata($propertyMetadata); try { - $v = $this->visitor->visitProperty($propertyMetadata, $data); - $this->accessor->setValue($object, $v, $propertyMetadata); + $values[$i] = $this->visitor->visitProperty($propertyMetadata, $data); } catch (NotAcceptableException $e) { } $this->context->popPropertyMetadata(); } + $this->accessor->setValues($data, $props, $values, $this->context); + $rs = $this->visitor->endVisitingObject($metadata, $data, $type); $this->afterVisitingObject($metadata, $rs, $type); diff --git a/src/GraphNavigator/SerializationGraphNavigator.php b/src/GraphNavigator/SerializationGraphNavigator.php index bc7f22124..87f88ef17 100644 --- a/src/GraphNavigator/SerializationGraphNavigator.php +++ b/src/GraphNavigator/SerializationGraphNavigator.php @@ -12,11 +12,8 @@ use JMS\Serializer\EventDispatcher\PreSerializeEvent; use JMS\Serializer\Exception\CircularReferenceDetectedException; use JMS\Serializer\Exception\ExcludedClassException; -use JMS\Serializer\Exception\ExpressionLanguageRequiredException; use JMS\Serializer\Exception\NotAcceptableException; use JMS\Serializer\Exception\RuntimeException; -use JMS\Serializer\Exclusion\ExpressionLanguageExclusionStrategy; -use JMS\Serializer\Expression\ExpressionEvaluatorInterface; use JMS\Serializer\GraphNavigator; use JMS\Serializer\GraphNavigatorInterface; use JMS\Serializer\Handler\HandlerRegistryInterface; @@ -41,17 +38,10 @@ final class SerializationGraphNavigator extends GraphNavigator implements GraphN * @var SerializationVisitorInterface */ protected $visitor; - /** * @var SerializationContext */ protected $context; - - /** - * @var ExpressionLanguageExclusionStrategy - */ - private $expressionExclusionStrategy; - private $dispatcher; private $metadataFactory; private $handlerRegistry; @@ -59,7 +49,6 @@ final class SerializationGraphNavigator extends GraphNavigator implements GraphN * @var AccessorStrategyInterface */ private $accessor; - /** * @var bool */ @@ -69,17 +58,12 @@ public function __construct( MetadataFactoryInterface $metadataFactory, HandlerRegistryInterface $handlerRegistry, AccessorStrategyInterface $accessor, - EventDispatcherInterface $dispatcher = null, - ExpressionEvaluatorInterface $expressionEvaluator = null + EventDispatcherInterface $dispatcher = null ) { $this->dispatcher = $dispatcher ?: new EventDispatcher(); $this->metadataFactory = $metadataFactory; $this->handlerRegistry = $handlerRegistry; $this->accessor = $accessor; - - if ($expressionEvaluator) { - $this->expressionExclusionStrategy = new ExpressionLanguageExclusionStrategy($expressionEvaluator); - } } public function initialize(VisitorInterface $visitor, Context $context): void @@ -189,10 +173,6 @@ public function accept($data, array $type = null) /** @var $metadata ClassMetadata */ $metadata = $this->metadataFactory->getMetadataForClass($type['name']); - if ($metadata->usingExpression && $this->expressionExclusionStrategy === null) { - throw new ExpressionLanguageRequiredException("To use conditional exclude/expose in {$metadata->name} you must configure the expression language."); - } - if ($this->exclusionStrategy->shouldSkipClass($metadata, $this->context)) { $this->context->stopVisiting($data); @@ -206,23 +186,13 @@ public function accept($data, array $type = null) } $this->visitor->startVisitingObject($metadata, $data, $type); - foreach ($metadata->propertyMetadata as $propertyMetadata) { - if ($this->exclusionStrategy->shouldSkipProperty($propertyMetadata, $this->context)) { - continue; - } - if (null !== $this->expressionExclusionStrategy && $this->expressionExclusionStrategy->shouldSkipProperty($propertyMetadata, $this->context)) { - continue; - } - - $v = $this->accessor->getValue($data, $propertyMetadata); - - if (null === $v && $this->shouldSerializeNull !== true) { - continue; - } + $props = $this->accessor->getProperties($metadata, $this->context); + $values = $this->accessor->getValues($data, $props, $this->context); + foreach ($props as $i => $propertyMetadata) { $this->context->pushPropertyMetadata($propertyMetadata); - $this->visitor->visitProperty($propertyMetadata, $v); + $this->visitor->visitProperty($propertyMetadata, $values[$i]); $this->context->popPropertyMetadata(); } From 27542896ffb8205384e265aacf623af24e1f5f40 Mon Sep 17 00:00:00 2001 From: Asmir Mustafic Date: Tue, 12 Jun 2018 08:55:20 +0200 Subject: [PATCH 2/3] introduce property selector interface --- src/Accessor/AccessorStrategyInterface.php | 13 ++-- src/Accessor/DefaultAccessorStrategy.php | 45 ++---------- .../DeserializationGraphNavigator.php | 23 +++---- .../SerializationGraphNavigator.php | 14 +++- src/Selector/DefaultPropertySelector.php | 68 +++++++++++++++++++ src/Selector/PropertySelectorInterface.php | 24 +++++++ 6 files changed, 122 insertions(+), 65 deletions(-) create mode 100644 src/Selector/DefaultPropertySelector.php create mode 100644 src/Selector/PropertySelectorInterface.php diff --git a/src/Accessor/AccessorStrategyInterface.php b/src/Accessor/AccessorStrategyInterface.php index e922e8014..e765e8756 100644 --- a/src/Accessor/AccessorStrategyInterface.php +++ b/src/Accessor/AccessorStrategyInterface.php @@ -4,7 +4,6 @@ namespace JMS\Serializer\Accessor; -use JMS\Serializer\Context; use JMS\Serializer\DeserializationContext; use JMS\Serializer\Metadata\ClassMetadata; use JMS\Serializer\Metadata\PropertyMetadata; @@ -15,26 +14,22 @@ */ interface AccessorStrategyInterface { - /** - * @param ClassMetadata $metadata - * @param Context $context - * @return PropertyMetadata[] - */ - public function getProperties(ClassMetadata $metadata, Context $context): array; /** * @param object $object + * @param ClassMetadata $metadata * @param PropertyMetadata[] $properties * @param SerializationContext $context * @return mixed[] */ - public function getValues(object $object, array $properties, SerializationContext $context):array; + public function getValues(object $object, ClassMetadata $metadata, array $properties, SerializationContext $context):array; /** * @param object $object * @param mixed[] $values + * @param ClassMetadata $metadata * @param PropertyMetadata[] $properties * @param DeserializationContext $context * @return void */ - public function setValues(object $object, array $values, array $properties, DeserializationContext $context): void; + public function setValues(object $object, array $values, ClassMetadata $metadata, array $properties, DeserializationContext $context): void; } diff --git a/src/Accessor/DefaultAccessorStrategy.php b/src/Accessor/DefaultAccessorStrategy.php index 04c2273b4..f7bfb93ac 100644 --- a/src/Accessor/DefaultAccessorStrategy.php +++ b/src/Accessor/DefaultAccessorStrategy.php @@ -4,11 +4,8 @@ namespace JMS\Serializer\Accessor; -use JMS\Serializer\Context; use JMS\Serializer\DeserializationContext; use JMS\Serializer\Exception\LogicException; -use JMS\Serializer\Exclusion\ExclusionStrategyInterface; -use JMS\Serializer\Exclusion\ExpressionLanguageExclusionStrategy; use JMS\Serializer\Expression\ExpressionEvaluatorInterface; use JMS\Serializer\Metadata\ClassMetadata; use JMS\Serializer\Metadata\ExpressionPropertyMetadata; @@ -28,48 +25,14 @@ final class DefaultAccessorStrategy implements AccessorStrategyInterface * @var ExpressionEvaluatorInterface */ private $evaluator; - /** - * @var ExclusionStrategyInterface - */ - private $exclusionStrategy; - /** - * @var ExpressionLanguageExclusionStrategy - */ - private $expressionExclusionStrategy; public function __construct( - ExclusionStrategyInterface $exclusionStrategy, - ExpressionEvaluatorInterface $evaluator + ExpressionEvaluatorInterface $evaluator = null ) { - $this->exclusionStrategy = $exclusionStrategy; $this->evaluator = $evaluator; - $this->expressionExclusionStrategy = new ExpressionLanguageExclusionStrategy($evaluator); - } - - /** - * @param ClassMetadata $metadata - * @param Context $context - * @return PropertyMetadata[] - */ - public function getProperties(ClassMetadata $metadata, Context $context): array - { - $values = []; - foreach ($metadata->propertyMetadata as $propertyMetadata) { - if ($context instanceof DeserializationContext && $propertyMetadata->readOnly) { - continue; - } - - if ($this->exclusionStrategy->shouldSkipProperty($propertyMetadata, $context) || $this->expressionExclusionStrategy->shouldSkipProperty($propertyMetadata, $context)) { - continue; - } - - $values[] = $propertyMetadata; - } - - return $values; } - public function getValues(object $data, array $properties, SerializationContext $context): array + public function getValues(object $data, ClassMetadata $metadata, array $properties, SerializationContext $context): array { $shouldSerializeNull = $context->shouldSerializeNull(); @@ -95,7 +58,7 @@ public function getValues(object $data, array $properties, SerializationContext * @param DeserializationContext $context * @return void */ - public function setValues(object $object, array $values, array $properties, DeserializationContext $context): void + public function setValues(object $object, array $values, ClassMetadata $metadata, array $properties, DeserializationContext $context): void { $values = []; foreach ($properties as $i => $propertyMetadata) { @@ -145,7 +108,7 @@ private function getValue(object $object, PropertyMetadata $metadata, Serializat return $object->{$metadata->getter}(); } - public function setValue(object $object, $value, PropertyMetadata $metadata, DeserializationContext $context): void + private function setValue(object $object, $value, PropertyMetadata $metadata, DeserializationContext $context): void { if ($metadata->readOnly) { throw new LogicException(sprintf('%s on %s is read only.', $metadata->name, $metadata->class)); diff --git a/src/GraphNavigator/DeserializationGraphNavigator.php b/src/GraphNavigator/DeserializationGraphNavigator.php index 8ab178fe5..a7f7f6dff 100644 --- a/src/GraphNavigator/DeserializationGraphNavigator.php +++ b/src/GraphNavigator/DeserializationGraphNavigator.php @@ -22,6 +22,7 @@ use JMS\Serializer\Handler\HandlerRegistryInterface; use JMS\Serializer\Metadata\ClassMetadata; use JMS\Serializer\NullAwareVisitorInterface; +use JMS\Serializer\Selector\PropertySelectorInterface; use JMS\Serializer\Visitor\DeserializationVisitorInterface; use Metadata\MetadataFactoryInterface; @@ -58,23 +59,25 @@ final class DeserializationGraphNavigator extends GraphNavigator implements Grap * @var AccessorStrategyInterface */ private $accessor; + /** + * @var PropertySelectorInterface + */ + private $selector; public function __construct( MetadataFactoryInterface $metadataFactory, HandlerRegistryInterface $handlerRegistry, ObjectConstructorInterface $objectConstructor, AccessorStrategyInterface $accessor, - EventDispatcherInterface $dispatcher = null, - ExpressionEvaluatorInterface $expressionEvaluator = null + PropertySelectorInterface $selector, + EventDispatcherInterface $dispatcher = null ) { $this->dispatcher = $dispatcher ?: new EventDispatcher(); $this->metadataFactory = $metadataFactory; $this->handlerRegistry = $handlerRegistry; $this->objectConstructor = $objectConstructor; $this->accessor = $accessor; - if ($expressionEvaluator) { - $this->expressionExclusionStrategy = new ExpressionLanguageExclusionStrategy($expressionEvaluator); - } + $this->selector = $selector; } /** @@ -147,10 +150,6 @@ public function accept($data, array $type = null) /** @var $metadata ClassMetadata */ $metadata = $this->metadataFactory->getMetadataForClass($type['name']); - if ($metadata->usingExpression && !$this->expressionExclusionStrategy) { - throw new ExpressionLanguageRequiredException("To use conditional exclude/expose in {$metadata->name} you must configure the expression language."); - } - if (!empty($metadata->discriminatorMap) && $type['name'] === $metadata->discriminatorBaseClass) { $metadata = $this->resolveMetadata($data, $metadata); } @@ -167,10 +166,10 @@ public function accept($data, array $type = null) $this->visitor->startVisitingObject($metadata, $object, $type); - $props = $this->accessor->getProperties($metadata, $this->context); + $properties = $this->selector->select($metadata, $this->context); $values = []; - foreach ($props as $i => $propertyMetadata) { + foreach ($properties as $i => $propertyMetadata) { $this->context->pushPropertyMetadata($propertyMetadata); try { $values[$i] = $this->visitor->visitProperty($propertyMetadata, $data); @@ -180,7 +179,7 @@ public function accept($data, array $type = null) $this->context->popPropertyMetadata(); } - $this->accessor->setValues($data, $props, $values, $this->context); + $this->accessor->setValues($data, $values, $metadata, $properties, $this->context); $rs = $this->visitor->endVisitingObject($metadata, $data, $type); $this->afterVisitingObject($metadata, $rs, $type); diff --git a/src/GraphNavigator/SerializationGraphNavigator.php b/src/GraphNavigator/SerializationGraphNavigator.php index 87f88ef17..f02508fda 100644 --- a/src/GraphNavigator/SerializationGraphNavigator.php +++ b/src/GraphNavigator/SerializationGraphNavigator.php @@ -19,6 +19,8 @@ use JMS\Serializer\Handler\HandlerRegistryInterface; use JMS\Serializer\Metadata\ClassMetadata; use JMS\Serializer\NullAwareVisitorInterface; +use JMS\Serializer\Selector\DefaultPropertySelector; +use JMS\Serializer\Selector\PropertySelectorInterface; use JMS\Serializer\SerializationContext; use JMS\Serializer\Visitor\SerializationVisitorInterface; use JMS\Serializer\VisitorInterface; @@ -53,17 +55,23 @@ final class SerializationGraphNavigator extends GraphNavigator implements GraphN * @var bool */ private $shouldSerializeNull; + /** + * @var PropertySelectorInterface + */ + private $selector; public function __construct( MetadataFactoryInterface $metadataFactory, HandlerRegistryInterface $handlerRegistry, AccessorStrategyInterface $accessor, + PropertySelectorInterface $selector, EventDispatcherInterface $dispatcher = null ) { $this->dispatcher = $dispatcher ?: new EventDispatcher(); $this->metadataFactory = $metadataFactory; $this->handlerRegistry = $handlerRegistry; $this->accessor = $accessor; + $this->selector = $selector; } public function initialize(VisitorInterface $visitor, Context $context): void @@ -187,10 +195,10 @@ public function accept($data, array $type = null) $this->visitor->startVisitingObject($metadata, $data, $type); - $props = $this->accessor->getProperties($metadata, $this->context); - $values = $this->accessor->getValues($data, $props, $this->context); + $properties = $this->selector->select($metadata, $this->context); + $values = $this->accessor->getValues($data, $metadata, $properties, $this->context); - foreach ($props as $i => $propertyMetadata) { + foreach ($properties as $i => $propertyMetadata) { $this->context->pushPropertyMetadata($propertyMetadata); $this->visitor->visitProperty($propertyMetadata, $values[$i]); $this->context->popPropertyMetadata(); diff --git a/src/Selector/DefaultPropertySelector.php b/src/Selector/DefaultPropertySelector.php new file mode 100644 index 000000000..9bea9305c --- /dev/null +++ b/src/Selector/DefaultPropertySelector.php @@ -0,0 +1,68 @@ + + */ +final class DefaultPropertySelector implements PropertySelectorInterface +{ + /** + * @var ExpressionEvaluatorInterface + */ + private $evaluator; + /** + * @var ExclusionStrategyInterface + */ + private $exclusionStrategy; + /** + * @var ExpressionLanguageExclusionStrategy + */ + private $expressionExclusionStrategy; + + public function __construct( + ExclusionStrategyInterface $exclusionStrategy, + ExpressionEvaluatorInterface $evaluator + ) { + $this->exclusionStrategy = $exclusionStrategy; + $this->evaluator = $evaluator; + $this->expressionExclusionStrategy = new ExpressionLanguageExclusionStrategy($evaluator); + } + + /** + * @param ClassMetadata $metadata + * @param Context $context + * @return PropertyMetadata[] + */ + public function select(ClassMetadata $metadata, Context $context): array + { + $values = []; + foreach ($metadata->propertyMetadata as $propertyMetadata) { + if ($context instanceof DeserializationContext && $propertyMetadata->readOnly) { + continue; + } + + if ($this->exclusionStrategy->shouldSkipProperty($propertyMetadata, $context) || $this->expressionExclusionStrategy->shouldSkipProperty($propertyMetadata, $context)) { + continue; + } + + $values[] = $propertyMetadata; + } + + return $values; + } +} diff --git a/src/Selector/PropertySelectorInterface.php b/src/Selector/PropertySelectorInterface.php new file mode 100644 index 000000000..82e8e3c55 --- /dev/null +++ b/src/Selector/PropertySelectorInterface.php @@ -0,0 +1,24 @@ + + */ +interface PropertySelectorInterface +{ + /** + * @param ClassMetadata $metadata + * @param Context $context + * @return PropertyMetadata[] + */ + public function select(ClassMetadata $metadata, Context $context): array; +} From 19f5239ee1b7a97b65a6ad59e7fb0005b424e16f Mon Sep 17 00:00:00 2001 From: Asmir Mustafic Date: Tue, 12 Jun 2018 09:13:24 +0200 Subject: [PATCH 3/3] property selection caching --- .../DeserializationGraphNavigator.php | 2 +- .../SerializationGraphNavigator.php | 2 +- src/Selector/DefaultPropertySelector.php | 42 ++++++++++++------- src/Selector/PropertySelectorInterface.php | 3 +- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/GraphNavigator/DeserializationGraphNavigator.php b/src/GraphNavigator/DeserializationGraphNavigator.php index a7f7f6dff..dc17f3425 100644 --- a/src/GraphNavigator/DeserializationGraphNavigator.php +++ b/src/GraphNavigator/DeserializationGraphNavigator.php @@ -166,7 +166,7 @@ public function accept($data, array $type = null) $this->visitor->startVisitingObject($metadata, $object, $type); - $properties = $this->selector->select($metadata, $this->context); + $properties = $this->selector->select($metadata); $values = []; foreach ($properties as $i => $propertyMetadata) { diff --git a/src/GraphNavigator/SerializationGraphNavigator.php b/src/GraphNavigator/SerializationGraphNavigator.php index f02508fda..7891c6e54 100644 --- a/src/GraphNavigator/SerializationGraphNavigator.php +++ b/src/GraphNavigator/SerializationGraphNavigator.php @@ -195,7 +195,7 @@ public function accept($data, array $type = null) $this->visitor->startVisitingObject($metadata, $data, $type); - $properties = $this->selector->select($metadata, $this->context); + $properties = $this->selector->select($metadata); $values = $this->accessor->getValues($data, $metadata, $properties, $this->context); foreach ($properties as $i => $propertyMetadata) { diff --git a/src/Selector/DefaultPropertySelector.php b/src/Selector/DefaultPropertySelector.php index 9bea9305c..19cdb553a 100644 --- a/src/Selector/DefaultPropertySelector.php +++ b/src/Selector/DefaultPropertySelector.php @@ -34,35 +34,49 @@ final class DefaultPropertySelector implements PropertySelectorInterface */ private $expressionExclusionStrategy; + /** + * @var Context + */ + private $context; + + /** + * @var array + */ + private $cache = []; + public function __construct( - ExclusionStrategyInterface $exclusionStrategy, + Context $context, ExpressionEvaluatorInterface $evaluator ) { - $this->exclusionStrategy = $exclusionStrategy; + $this->exclusionStrategy = $context->getExclusionStrategy(); + $this->context = $context; $this->evaluator = $evaluator; $this->expressionExclusionStrategy = new ExpressionLanguageExclusionStrategy($evaluator); } /** * @param ClassMetadata $metadata - * @param Context $context * @return PropertyMetadata[] */ - public function select(ClassMetadata $metadata, Context $context): array + public function select(ClassMetadata $metadata): array { - $values = []; - foreach ($metadata->propertyMetadata as $propertyMetadata) { - if ($context instanceof DeserializationContext && $propertyMetadata->readOnly) { - continue; - } + if (!isset($this->cache[spl_object_hash($metadata)])) { - if ($this->exclusionStrategy->shouldSkipProperty($propertyMetadata, $context) || $this->expressionExclusionStrategy->shouldSkipProperty($propertyMetadata, $context)) { - continue; - } + $values = []; + foreach ($metadata->propertyMetadata as $propertyMetadata) { + if ($this->context instanceof DeserializationContext && $propertyMetadata->readOnly) { + continue; + } - $values[] = $propertyMetadata; + if ($this->exclusionStrategy->shouldSkipProperty($propertyMetadata, $this->context) || $this->expressionExclusionStrategy->shouldSkipProperty($propertyMetadata, $this->context)) { + continue; + } + + $values[] = $propertyMetadata; + } + $this->cache[spl_object_hash($metadata)] = $values; } - return $values; + return $this->cache[spl_object_hash($metadata)]; } } diff --git a/src/Selector/PropertySelectorInterface.php b/src/Selector/PropertySelectorInterface.php index 82e8e3c55..461821c01 100644 --- a/src/Selector/PropertySelectorInterface.php +++ b/src/Selector/PropertySelectorInterface.php @@ -17,8 +17,7 @@ interface PropertySelectorInterface { /** * @param ClassMetadata $metadata - * @param Context $context * @return PropertyMetadata[] */ - public function select(ClassMetadata $metadata, Context $context): array; + public function select(ClassMetadata $metadata): array; }