Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion src/JsonMapper/Value/CustomTypeRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ final class CustomTypeRegistry
/**
* Registers the converter for the provided class name.
*
* @param callable(mixed):mixed|callable(mixed, MappingContext):mixed $converter
* @param string $className Fully-qualified class name handled by the converter.
* @param callable(mixed):mixed|callable(mixed, MappingContext):mixed $converter Callback responsible for creating the destination value.
*
* @return void
*/
public function register(string $className, callable $converter): void
{
Expand All @@ -39,6 +42,10 @@ public function register(string $className, callable $converter): void

/**
* Registers a custom type handler.
*
* @param TypeHandlerInterface $handler Handler performing support checks and conversion for a particular type.
*
* @return void
*/
public function registerHandler(TypeHandlerInterface $handler): void
{
Expand All @@ -47,6 +54,11 @@ public function registerHandler(TypeHandlerInterface $handler): void

/**
* Returns TRUE if a handler for the type exists.
*
* @param Type $type Type information describing the target property.
* @param mixed $value JSON value that should be converted.
*
* @return bool TRUE when at least one registered handler supports the value.
*/
public function supports(Type $type, mixed $value): bool
{
Expand All @@ -61,6 +73,12 @@ public function supports(Type $type, mixed $value): bool

/**
* Executes the converter for the class.
*
* @param Type $type Type information describing the target property.
* @param mixed $value JSON value that should be converted.
* @param MappingContext $context Mapping context providing runtime configuration and state.
*
* @return mixed Converted value returned by the first supporting handler.
*/
public function convert(Type $type, mixed $value, MappingContext $context): mixed
{
Expand Down
47 changes: 44 additions & 3 deletions src/JsonMapper/Value/Strategy/BuiltinValueConversionStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,29 @@
*/
final class BuiltinValueConversionStrategy implements ValueConversionStrategyInterface
{
/**
* Determines whether the provided type represents a builtin PHP value.
*
* @param mixed $value Raw value coming from the input payload.
* @param Type $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return bool TRUE when the target type is a builtin PHP type.
*/
public function supports(mixed $value, Type $type, MappingContext $context): bool
{
return $type instanceof BuiltinType;
}

/**
* Converts the provided value to the builtin type defined by the metadata.
*
* @param mixed $value Raw value coming from the input payload.
* @param Type $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return mixed Value cast to the requested builtin type when possible.
*/
public function convert(mixed $value, Type $type, MappingContext $context): mixed
{
assert($type instanceof BuiltinType);
Expand All @@ -64,7 +82,12 @@ public function convert(mixed $value, Type $type, MappingContext $context): mixe
}

/**
* @param BuiltinType<TypeIdentifier> $type
* Normalizes common scalar representations before the conversion happens.
*
* @param mixed $value Raw value coming from the input payload.
* @param BuiltinType<TypeIdentifier> $type Type metadata describing the target property.
*
* @return mixed Normalized value that is compatible with the builtin type conversion.
*/
private function normalizeValue(mixed $value, BuiltinType $type): mixed
{
Expand Down Expand Up @@ -126,7 +149,13 @@ private function normalizeValue(mixed $value, BuiltinType $type): mixed
}

/**
* @param BuiltinType<TypeIdentifier> $type
* Validates that the value matches the builtin type or records a mismatch.
*
* @param mixed $value Normalized value used during conversion.
* @param BuiltinType<TypeIdentifier> $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return void
*/
private function guardCompatibility(mixed $value, BuiltinType $type, MappingContext $context): void
{
Expand Down Expand Up @@ -160,13 +189,25 @@ private function guardCompatibility(mixed $value, BuiltinType $type, MappingCont
}

/**
* @param BuiltinType<TypeIdentifier> $type
* Determines whether the builtin type allows null values.
*
* @param BuiltinType<TypeIdentifier> $type Type metadata describing the target property.
*
* @return bool TRUE when the builtin type can be null.
*/
private function allowsNull(BuiltinType $type): bool
{
return $type->isNullable();
}

/**
* Checks whether the value matches the builtin type identifier.
*
* @param mixed $value Normalized value used during conversion.
* @param TypeIdentifier $identifier Identifier of the builtin type to check against.
*
* @return bool TRUE when the value matches the identifier requirements.
*/
private function isCompatibleValue(mixed $value, TypeIdentifier $identifier): bool
{
return match ($identifier->value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,38 @@
final readonly class CollectionValueConversionStrategy implements ValueConversionStrategyInterface
{
/**
* @param CollectionFactoryInterface<array-key, mixed> $collectionFactory
* Creates the strategy with the provided collection factory.
*
* @param CollectionFactoryInterface<array-key, mixed> $collectionFactory Factory responsible for instantiating collections during conversion.
*/
public function __construct(
private CollectionFactoryInterface $collectionFactory,
) {
}

/**
* Determines whether the supplied type represents a collection.
*
* @param mixed $value Raw value coming from the input payload.
* @param Type $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return bool TRUE when the target type is a collection type.
*/
public function supports(mixed $value, Type $type, MappingContext $context): bool
{
return $type instanceof CollectionType;
}

/**
* Converts the JSON value into a collection instance.
*
* @param mixed $value Raw value coming from the input payload.
* @param Type $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return mixed Collection created by the factory based on the type metadata.
*/
public function convert(mixed $value, Type $type, MappingContext $context): mixed
{
assert($type instanceof CollectionType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,39 @@
*/
final readonly class CustomTypeValueConversionStrategy implements ValueConversionStrategyInterface
{
/**
* Creates the strategy backed by the custom type registry.
*
* @param CustomTypeRegistry $registry Registry containing the custom handlers.
*/
public function __construct(
private CustomTypeRegistry $registry,
) {
}

/**
* Determines whether the registry can handle the provided type.
*
* @param mixed $value Raw value coming from the input payload.
* @param Type $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return bool TRUE when the registry has a matching custom handler.
*/
public function supports(mixed $value, Type $type, MappingContext $context): bool
{
return $this->registry->supports($type, $value);
}

/**
* Converts the value using the registered handler.
*
* @param mixed $value Raw value coming from the input payload.
* @param Type $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return mixed Value produced by the registered custom handler.
*/
public function convert(mixed $value, Type $type, MappingContext $context): mixed
{
return $this->registry->convert($type, $value, $context);
Expand Down
18 changes: 18 additions & 0 deletions src/JsonMapper/Value/Strategy/DateTimeValueConversionStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ final class DateTimeValueConversionStrategy implements ValueConversionStrategyIn
{
use ObjectTypeConversionGuardTrait;

/**
* Determines whether the requested type is a supported date or interval class.
*
* @param mixed $value Raw value coming from the input payload.
* @param Type $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return bool TRUE when the type represents a supported date/time object.
*/
public function supports(mixed $value, Type $type, MappingContext $context): bool
{
$objectType = $this->extractObjectType($type);
Expand All @@ -45,6 +54,15 @@ public function supports(mixed $value, Type $type, MappingContext $context): boo
return is_a($className, DateTimeImmutable::class, true) || is_a($className, DateInterval::class, true);
}

/**
* Converts ISO-8601 strings and timestamps into the desired date/time object.
*
* @param mixed $value Raw value coming from the input payload.
* @param Type $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return mixed Instance of the configured date/time class.
*/
public function convert(mixed $value, Type $type, MappingContext $context): mixed
{
return $this->convertObjectValue(
Expand Down
18 changes: 18 additions & 0 deletions src/JsonMapper/Value/Strategy/EnumValueConversionStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ final class EnumValueConversionStrategy implements ValueConversionStrategyInterf
{
use ObjectTypeConversionGuardTrait;

/**
* Determines whether the provided type is a backed enum.
*
* @param mixed $value Raw value coming from the input payload.
* @param Type $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return bool TRUE when the target type resolves to a backed enum.
*/
public function supports(mixed $value, Type $type, MappingContext $context): bool
{
$objectType = $this->extractObjectType($type);
Expand All @@ -48,6 +57,15 @@ public function supports(mixed $value, Type $type, MappingContext $context): boo
return is_a($className, BackedEnum::class, true);
}

/**
* Converts the JSON scalar into the matching enum case.
*
* @param mixed $value Raw value coming from the input payload.
* @param Type $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return mixed Backed enum instance returned by the case factory method.
*/
public function convert(mixed $value, Type $type, MappingContext $context): mixed
{
return $this->convertObjectValue(
Expand Down
18 changes: 18 additions & 0 deletions src/JsonMapper/Value/Strategy/NullValueConversionStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,29 @@
*/
final class NullValueConversionStrategy implements ValueConversionStrategyInterface
{
/**
* Determines whether the incoming value represents a null assignment.
*
* @param mixed $value Raw value coming from the input payload.
* @param Type $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return bool TRUE when the value is exactly null.
*/
public function supports(mixed $value, Type $type, MappingContext $context): bool
{
return $value === null;
}

/**
* Returns null to preserve the absence of a value.
*
* @param mixed $value Raw value coming from the input payload.
* @param Type $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return null Always returns null for supported values.
*/
public function convert(mixed $value, Type $type, MappingContext $context): null
{
return null;
Expand Down
17 changes: 14 additions & 3 deletions src/JsonMapper/Value/Strategy/ObjectTypeConversionGuardTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ trait ObjectTypeConversionGuardTrait
/**
* Returns the provided type when it represents an object with a class name.
*
* @return ObjectType<class-string>|null
* @param Type $type Type metadata describing the target property.
*
* @return ObjectType<class-string>|null Object type when the metadata targets a concrete class; otherwise null.
*/
private function extractObjectType(Type $type): ?ObjectType
{
Expand All @@ -42,7 +44,11 @@ private function extractObjectType(Type $type): ?ObjectType
/**
* Ensures null values comply with the target object's nullability.
*
* @param ObjectType<class-string> $type
* @param mixed $value Raw value coming from the input payload.
* @param ObjectType<class-string> $type Object type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return void
*/
private function guardNullableValue(mixed $value, ObjectType $type, MappingContext $context): void
{
Expand All @@ -60,7 +66,12 @@ private function guardNullableValue(mixed $value, ObjectType $type, MappingConte
/**
* Executes the provided converter when a valid object type is available.
*
* @param callable(string, mixed): mixed $converter
* @param Type $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
* @param mixed $value Raw value coming from the input payload.
* @param callable(string, mixed): mixed $converter Callback that performs the actual conversion when a class-string is available.
*
* @return mixed Result from the converter or the original value when no object type was detected.
*/
private function convertObjectValue(Type $type, MappingContext $context, mixed $value, callable $converter): mixed
{
Expand Down
27 changes: 24 additions & 3 deletions src/JsonMapper/Value/Strategy/ObjectValueConversionStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,40 @@
final readonly class ObjectValueConversionStrategy implements ValueConversionStrategyInterface
{
/**
* @param Closure(mixed, class-string, MappingContext):mixed $mapper
* Creates the strategy with the class resolver and mapper callback.
*
* @param ClassResolver $classResolver Resolver used to select the concrete class to instantiate.
* @param Closure(mixed, class-string, MappingContext):mixed $mapper Callback responsible for mapping values into objects.
*/
public function __construct(
private ClassResolver $classResolver,
private Closure $mapper,
) {
}

/**
* Determines whether the metadata describes an object type.
*
* @param mixed $value Raw value coming from the input payload.
* @param Type $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return bool TRUE when the target type represents an object.
*/
public function supports(mixed $value, Type $type, MappingContext $context): bool
{
return $type instanceof ObjectType;
}

/**
* Delegates conversion to the mapper for the resolved class.
*
* @param mixed $value Raw value coming from the input payload.
* @param Type $type Type metadata describing the target property.
* @param MappingContext $context Mapping context providing configuration such as strict mode.
*
* @return mixed Value returned by the mapper callback.
*/
public function convert(mixed $value, Type $type, MappingContext $context): mixed
{
if (!($type instanceof ObjectType)) {
Expand All @@ -68,9 +89,9 @@ public function convert(mixed $value, Type $type, MappingContext $context): mixe
/**
* Resolves the class name from the provided object type.
*
* @param ObjectType<class-string> $type
* @param ObjectType<class-string> $type Object type metadata describing the target property.
*
* @return class-string
* @return class-string Concrete class name extracted from the metadata.
*/
private function resolveClassName(ObjectType $type): string
{
Expand Down
Loading
Loading