Skip to content

Commit

Permalink
DefaultProcessor: creates MappedObjectType only when necessary
Browse files Browse the repository at this point in the history
  • Loading branch information
mabar committed Jan 18, 2025
1 parent c69b42d commit 064eee6
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 33 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 5 additions & 0 deletions src/Context/MappedObjectContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,9 @@ public function getType(): MappedObjectType
return $this->type = $type;
}

public function getTypeIfInitialized(): ?MappedObjectType
{
return $this->type;
}

}
21 changes: 5 additions & 16 deletions src/Context/SkippedFieldsContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<int|string, SkippedFieldContext> */
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;
}

/**
Expand Down
13 changes: 13 additions & 0 deletions src/Context/TypeContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

}
40 changes: 23 additions & 17 deletions src/Processing/DefaultProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand All @@ -272,7 +272,7 @@ private function handleSentFields(
ProcessorCallContext $callContext
): array
{
$type = $mappedObjectContext->getType();
$type = null;
$options = $mappedObjectContext->getOptions();

$meta = $callContext->getMeta();
Expand All @@ -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;
}

Expand All @@ -305,6 +305,7 @@ private function handleSentFields(
: '.';

// Add error to type
$type ??= $mappedObjectContext->getType();
$type->overwriteInvalidField(
$fieldName,
ValueDoesNotMatch::create(
Expand Down Expand Up @@ -346,6 +347,7 @@ private function handleSentFields(
$fieldMeta,
);
} catch (ValueDoesNotMatch | InvalidData $exception) {
$type ??= $mappedObjectContext->getType();
$type->overwriteInvalidField($fieldName, $exception);
}
}
Expand Down Expand Up @@ -388,7 +390,7 @@ private function handleMissingFields(
ProcessorCallContext $callContext
): array
{
$type = $mappedObjectContext->getType();
$type = null;
$options = $mappedObjectContext->getOptions();
$initializeObjects = $mappedObjectContext->shouldInitializeObjects();

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -616,7 +620,6 @@ private function fillObject(
ProcessorCallContext $callContext
): void
{
$type = $mappedObjectContext->getType();
$options = $mappedObjectContext->getOptions();
$meta = $callContext->getMeta();

Expand All @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -757,6 +762,7 @@ public function processSkippedFields(
$fieldMeta,
);
} catch (ValueDoesNotMatch | InvalidData $exception) {
$type ??= $mappedObjectContext->getType();
$type->overwriteInvalidField($fieldName, $exception);

continue;
Expand All @@ -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());
}

Expand Down
4 changes: 4 additions & 0 deletions tests/Unit/Context/MappedObjectContextTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

}

0 comments on commit 064eee6

Please sign in to comment.