From 064eee64b9d4212681f7b06dea309029295f0faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Barto=C5=A1?= Date: Sat, 18 Jan 2025 16:04:52 +0100 Subject: [PATCH] DefaultProcessor: creates MappedObjectType only when necessary --- CHANGELOG.md | 2 + src/Context/MappedObjectContext.php | 5 +++ src/Context/SkippedFieldsContext.php | 21 +++------- src/Context/TypeContext.php | 13 ++++++ src/Processing/DefaultProcessor.php | 40 +++++++++++-------- .../Unit/Context/MappedObjectContextTest.php | 4 ++ 6 files changed, 52 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f36a1f83..2281d6f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - initializes `Type` lazily (performance optimization) - `ArrayShapeType` - `getFields()` always returns the same instances +- `DefaultProcessor` + - creates `MappedObjectType` only when necessary (performance optimization) ### Removed diff --git a/src/Context/MappedObjectContext.php b/src/Context/MappedObjectContext.php index eb88e54e..1b2b9b8c 100644 --- a/src/Context/MappedObjectContext.php +++ b/src/Context/MappedObjectContext.php @@ -45,4 +45,9 @@ public function getType(): MappedObjectType return $this->type = $type; } + public function getTypeIfInitialized(): ?MappedObjectType + { + return $this->type; + } + } diff --git a/src/Context/SkippedFieldsContext.php b/src/Context/SkippedFieldsContext.php index 99997d32..92e85d7b 100644 --- a/src/Context/SkippedFieldsContext.php +++ b/src/Context/SkippedFieldsContext.php @@ -2,33 +2,22 @@ namespace Orisai\ObjectMapper\Context; -use Orisai\ObjectMapper\Processing\Options; -use Orisai\ObjectMapper\Types\MappedObjectType; - final class SkippedFieldsContext { - private MappedObjectType $type; - - private Options $options; + private MappedObjectContext $mappedObjectContext; /** @var array */ private array $skippedFields = []; - public function __construct(MappedObjectType $type, Options $options) - { - $this->type = $type; - $this->options = $options; - } - - public function getType(): MappedObjectType + public function __construct(MappedObjectContext $mappedObjectContext) { - return $this->type; + $this->mappedObjectContext = $mappedObjectContext; } - public function getOptions(): Options + public function getMappedObjectContext(): MappedObjectContext { - return $this->options; + return $this->mappedObjectContext; } /** diff --git a/src/Context/TypeContext.php b/src/Context/TypeContext.php index 3160adb2..f2dcf5da 100644 --- a/src/Context/TypeContext.php +++ b/src/Context/TypeContext.php @@ -83,4 +83,17 @@ public function createClone(): self return $clone; } + /** + * @return static + * + * @internal + */ + public function createCloneWithOptions(Options $options): self + { + $clone = clone $this; + $clone->options = $options; + + return $clone; + } + } diff --git a/src/Processing/DefaultProcessor.php b/src/Processing/DefaultProcessor.php index b00abb97..7270d636 100644 --- a/src/Processing/DefaultProcessor.php +++ b/src/Processing/DefaultProcessor.php @@ -252,9 +252,9 @@ private function handleFields( $data = $this->handleSentFields($data, $mappedObjectContext, $callContext); $data = $this->handleMissingFields($data, $mappedObjectContext, $callContext); - $type = $mappedObjectContext->getType(); + $type = $mappedObjectContext->getTypeIfInitialized(); - if ($type->hasInvalidFields()) { + if ($type !== null && $type->hasInvalidFields()) { throw InvalidData::create($type, Value::none()); } @@ -272,7 +272,7 @@ private function handleSentFields( ProcessorCallContext $callContext ): array { - $type = $mappedObjectContext->getType(); + $type = null; $options = $mappedObjectContext->getOptions(); $meta = $callContext->getMeta(); @@ -281,7 +281,7 @@ private function handleSentFields( foreach ($data as $fieldName => $value) { // Skip invalid field - if ($type->isFieldInvalid($fieldName)) { + if ($type !== null && $type->isFieldInvalid($fieldName)) { continue; } @@ -305,6 +305,7 @@ private function handleSentFields( : '.'; // Add error to type + $type ??= $mappedObjectContext->getType(); $type->overwriteInvalidField( $fieldName, ValueDoesNotMatch::create( @@ -346,6 +347,7 @@ private function handleSentFields( $fieldMeta, ); } catch (ValueDoesNotMatch | InvalidData $exception) { + $type ??= $mappedObjectContext->getType(); $type->overwriteInvalidField($fieldName, $exception); } } @@ -388,7 +390,7 @@ private function handleMissingFields( ProcessorCallContext $callContext ): array { - $type = $mappedObjectContext->getType(); + $type = null; $options = $mappedObjectContext->getOptions(); $initializeObjects = $mappedObjectContext->shouldInitializeObjects(); @@ -416,10 +418,14 @@ private function handleMissingFields( if ($fillDefaultValues) { $data[$missingField] = $defaultMeta->getValue(); } - } elseif ($requiredFields !== RequiredFields::none() && !$type->isFieldInvalid($missingField)) { + } elseif ( + $requiredFields !== RequiredFields::none() + && ($type === null || !$type->isFieldInvalid($missingField)) + ) { // Field is missing and have no default value, mark as invalid $fieldRuleMeta = $fieldMeta->getRule(); $fieldRule = $this->ruleManager->getRule($fieldRuleMeta->getType()); + $type ??= $mappedObjectContext->getType(); $type->overwriteInvalidField( $missingField, ValueDoesNotMatch::create( @@ -469,8 +475,7 @@ private function createFieldContext( ReflectionProperty $property ): FieldContext { - $parentType = $mappedObjectContext->getType(); - $typeCreator = static fn (): Type => $parentType->getField($fieldName); + $typeCreator = static fn (): Type => $mappedObjectContext->getType()->getField($fieldName); return new FieldContext( $this->metaLoader, @@ -543,11 +548,10 @@ private function handleClassCallbacks( string $callbackType ) { - $type = $mappedObjectContext->getType(); - try { $data = $this->applyCallbacks($data, $mappedObjectContext, $callContext, $meta, $callbackType); } catch (ValueDoesNotMatch | InvalidData $exception) { + $type = $mappedObjectContext->getType(); $caughtType = $exception->getType(); // User thrown type is not the actual type from MappedObjectContext @@ -616,7 +620,6 @@ private function fillObject( ProcessorCallContext $callContext ): void { - $type = $mappedObjectContext->getType(); $options = $mappedObjectContext->getOptions(); $meta = $callContext->getMeta(); @@ -639,7 +642,7 @@ private function fillObject( // Set skipped properties $skippedFields = $callContext->getSkippedFields(); if ($skippedFields !== []) { - $skippedContext = new SkippedFieldsContext($type, $options); + $skippedContext = new SkippedFieldsContext($mappedObjectContext); $this->skippedMap->setSkippedFieldsContext($object, $skippedContext); foreach ($skippedFields as $fieldName => $skippedFieldContext) { @@ -713,11 +716,13 @@ public function processSkippedFields( } $skippedFieldsContext = $this->skippedMap->getSkippedFieldsContext($object); + $mappedObjectContext = $skippedFieldsContext->getMappedObjectContext(); + + if ($options !== null) { + $mappedObjectContext = $mappedObjectContext->createCloneWithOptions($options); + } - $type = $skippedFieldsContext->getType(); - $typeCreator = static fn (): MappedObjectType => $type; - $options ??= $skippedFieldsContext->getOptions(); - $mappedObjectContext = $this->createMappedObjectContext($options, $typeCreator, true); + $type = null; $skippedFields = $skippedFieldsContext->getSkippedFields(); $meta = $this->metaLoader->load($class); @@ -757,6 +762,7 @@ public function processSkippedFields( $fieldMeta, ); } catch (ValueDoesNotMatch | InvalidData $exception) { + $type ??= $mappedObjectContext->getType(); $type->overwriteInvalidField($fieldName, $exception); continue; @@ -768,7 +774,7 @@ public function processSkippedFields( } // If any of fields is invalid, throw error - if ($type->hasInvalidFields()) { + if ($type !== null && $type->hasInvalidFields()) { throw InvalidData::create($type, Value::none()); } diff --git a/tests/Unit/Context/MappedObjectContextTest.php b/tests/Unit/Context/MappedObjectContextTest.php index fdd7d1ad..04bc0c74 100644 --- a/tests/Unit/Context/MappedObjectContextTest.php +++ b/tests/Unit/Context/MappedObjectContextTest.php @@ -31,9 +31,13 @@ public function test(): void self::assertSame($deps->processor, $context->getProcessor()); self::assertTrue($context->shouldInitializeObjects()); + self::assertNull($context->getTypeIfInitialized()); + self::assertEquals($typeCreator(), $context->getType()); self::assertNotSame($typeCreator(), $context->getType()); self::assertSame($context->getType(), $context->getType()); + + self::assertSame($context->getType(), $context->getTypeIfInitialized()); } }