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
21 changes: 8 additions & 13 deletions src/JsonMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ class JsonMapper

private CustomTypeRegistry $customTypeRegistry;

private JsonMapperConfig $config;

/**
* @param array<class-string, class-string|Closure(mixed):class-string|Closure(mixed, MappingContext):class-string> $classMap
* @param CacheItemPoolInterface|null $typeCache
Expand All @@ -119,9 +117,8 @@ public function __construct(
private readonly ?PropertyNameConverterInterface $nameConverter = null,
array $classMap = [],
?CacheItemPoolInterface $typeCache = null,
?JsonMapperConfig $config = null,
private JsonMapperConfig $config = new JsonMapperConfig(),
) {
$this->config = $config ?? new JsonMapperConfig();
$this->typeResolver = new TypeResolver($extractor, $typeCache);
$this->classResolver = new ClassResolver($classMap);
$this->customTypeRegistry = new CustomTypeRegistry();
Expand Down Expand Up @@ -212,15 +209,13 @@ public function map(
?MappingContext $context = null,
?MappingConfiguration $configuration = null,
): mixed {
if ($context === null) {
$configuration = $configuration ?? $this->createDefaultConfiguration();
$context = new MappingContext($json, $configuration->toOptions());
if (!$context instanceof MappingContext) {
$configuration ??= $this->createDefaultConfiguration();
$context = new MappingContext($json, $configuration->toOptions());
} elseif (!$configuration instanceof MappingConfiguration) {
$configuration = MappingConfiguration::fromContext($context);
} else {
if ($configuration === null) {
$configuration = MappingConfiguration::fromContext($context);
} else {
$context->replaceOptions($configuration->toOptions());
}
$context->replaceOptions($configuration->toOptions());
}

$resolvedClassName = $className === null
Expand Down Expand Up @@ -418,7 +413,7 @@ private function createDefaultConfiguration(): MappingConfiguration
$configuration = $configuration->withDefaultDateFormat($this->config->getDefaultDateFormat());

if ($this->config->shouldAllowScalarToObjectCasting()) {
$configuration = $configuration->withScalarToObjectCasting(true);
return $configuration->withScalarToObjectCasting(true);
}

return $configuration;
Expand Down
6 changes: 3 additions & 3 deletions src/JsonMapper/Attribute/ReplaceProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
* Attribute used to instruct the mapper to rename a JSON field.
*/
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
final class ReplaceProperty
final readonly class ReplaceProperty
{
public function __construct(
public readonly string $value,
public readonly string $replaces,
public string $value,
public string $replaces,
) {
}
}
12 changes: 3 additions & 9 deletions src/JsonMapper/Collection/CollectionDocBlockTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,10 @@ final class CollectionDocBlockTypeResolver
{
private DocBlockFactoryInterface $docBlockFactory;

private ContextFactory $contextFactory;

private PhpDocTypeHelper $phpDocTypeHelper;

public function __construct(
?DocBlockFactoryInterface $docBlockFactory = null,
?ContextFactory $contextFactory = null,
?PhpDocTypeHelper $phpDocTypeHelper = null,
private ContextFactory $contextFactory = new ContextFactory(),
private PhpDocTypeHelper $phpDocTypeHelper = new PhpDocTypeHelper(),
) {
if (!class_exists(DocBlockFactory::class)) {
throw new LogicException(
Expand All @@ -46,9 +42,7 @@ public function __construct(
);
}

$this->docBlockFactory = $docBlockFactory ?? DocBlockFactory::createInstance();
$this->contextFactory = $contextFactory ?? new ContextFactory();
$this->phpDocTypeHelper = $phpDocTypeHelper ?? new PhpDocTypeHelper();
$this->docBlockFactory = $docBlockFactory ?? DocBlockFactory::createInstance();
}

/**
Expand Down
20 changes: 13 additions & 7 deletions src/JsonMapper/Context/MappingContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,19 @@
*/
final class MappingContext
{
public const OPTION_STRICT_MODE = 'strict_mode';
public const OPTION_COLLECT_ERRORS = 'collect_errors';
public const OPTION_TREAT_EMPTY_STRING_AS_NULL = 'empty_string_is_null';
public const OPTION_IGNORE_UNKNOWN_PROPERTIES = 'ignore_unknown_properties';
public const OPTION_TREAT_NULL_AS_EMPTY_COLLECTION = 'treat_null_as_empty_collection';
public const OPTION_DEFAULT_DATE_FORMAT = 'default_date_format';
public const OPTION_ALLOW_SCALAR_TO_OBJECT_CASTING = 'allow_scalar_to_object_casting';
public const string OPTION_STRICT_MODE = 'strict_mode';

public const string OPTION_COLLECT_ERRORS = 'collect_errors';

public const string OPTION_TREAT_EMPTY_STRING_AS_NULL = 'empty_string_is_null';

public const string OPTION_IGNORE_UNKNOWN_PROPERTIES = 'ignore_unknown_properties';

public const string OPTION_TREAT_NULL_AS_EMPTY_COLLECTION = 'treat_null_as_empty_collection';

public const string OPTION_DEFAULT_DATE_FORMAT = 'default_date_format';

public const string OPTION_ALLOW_SCALAR_TO_OBJECT_CASTING = 'allow_scalar_to_object_casting';

/**
* @var list<string>
Expand Down
8 changes: 4 additions & 4 deletions src/JsonMapper/Context/MappingError.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
/**
* Represents a collected mapping error.
*/
final class MappingError
final readonly class MappingError
{
public function __construct(
private readonly string $path,
private readonly string $message,
private readonly ?MappingException $exception = null,
private string $path,
private string $message,
private ?MappingException $exception = null,
) {
}

Expand Down
4 changes: 2 additions & 2 deletions src/JsonMapper/Report/MappingReport.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
/**
* Represents the result of collecting mapping errors.
*/
final class MappingReport
final readonly class MappingReport
{
/**
* @param list<MappingError> $errors
*/
public function __construct(private readonly array $errors)
public function __construct(private array $errors)
{
}

Expand Down
6 changes: 3 additions & 3 deletions src/JsonMapper/Report/MappingResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
/**
* Represents the outcome of a mapping operation and its report.
*/
final class MappingResult
final readonly class MappingResult
{
public function __construct(
private readonly mixed $value,
private readonly MappingReport $report,
private mixed $value,
private MappingReport $report,
) {
}

Expand Down
12 changes: 4 additions & 8 deletions src/JsonMapper/Type/TypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
*/
final class TypeResolver
{
private const CACHE_KEY_PREFIX = 'jsonmapper.property_type.';
private const string CACHE_KEY_PREFIX = 'jsonmapper.property_type.';

private BuiltinType $defaultType;

Expand Down Expand Up @@ -61,11 +61,7 @@ public function resolve(string $className, string $propertyName): Type
$type = $this->resolveFromReflection($className, $propertyName);
}

if ($type instanceof Type) {
$resolved = $this->normalizeType($type);
} else {
$resolved = $this->defaultType;
}
$resolved = $type instanceof Type ? $this->normalizeType($type) : $this->defaultType;

$this->storeCachedType($className, $propertyName, $resolved);

Expand All @@ -91,7 +87,7 @@ private function normalizeType(Type $type): Type
*/
private function getCachedType(string $className, string $propertyName): ?Type
{
if ($this->cache === null) {
if (!$this->cache instanceof CacheItemPoolInterface) {
return null;
}

Expand Down Expand Up @@ -119,7 +115,7 @@ private function getCachedType(string $className, string $propertyName): ?Type
*/
private function storeCachedType(string $className, string $propertyName, Type $type): void
{
if ($this->cache === null) {
if (!$this->cache instanceof CacheItemPoolInterface) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
use Symfony\Component\TypeInfo\Type;
use Symfony\Component\TypeInfo\Type\BuiltinType;
use Symfony\Component\TypeInfo\TypeIdentifier;
use Traversable;

use function assert;
use function filter_var;
Expand Down Expand Up @@ -169,7 +168,7 @@ private function isCompatibleValue(mixed $value, TypeIdentifier $identifier): bo
'array' => is_array($value),
'object' => is_object($value),
'callable' => is_callable($value),
'iterable' => is_array($value) || $value instanceof Traversable,
'iterable' => is_iterable($value),
'null' => $value === null,
default => true,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use MagicSunday\JsonMapper\Context\MappingContext;
use MagicSunday\JsonMapper\Exception\TypeMismatchException;
use Symfony\Component\TypeInfo\Type;
use Symfony\Component\TypeInfo\Type\ObjectType;

use function get_debug_type;
use function is_a;
Expand All @@ -35,7 +36,7 @@ public function supports(mixed $value, Type $type, MappingContext $context): boo
{
$objectType = $this->extractObjectType($type);

if ($objectType === null) {
if (!$objectType instanceof ObjectType) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use MagicSunday\JsonMapper\Context\MappingContext;
use MagicSunday\JsonMapper\Exception\TypeMismatchException;
use Symfony\Component\TypeInfo\Type;
use Symfony\Component\TypeInfo\Type\ObjectType;
use ValueError;

use function enum_exists;
Expand All @@ -34,7 +35,7 @@ public function supports(mixed $value, Type $type, MappingContext $context): boo
{
$objectType = $this->extractObjectType($type);

if ($objectType === null) {
if (!$objectType instanceof ObjectType) {
return false;
}

Expand Down
14 changes: 6 additions & 8 deletions src/JsonMapper/Value/Strategy/ObjectValueConversionStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,12 @@ public function convert(mixed $value, Type $type, MappingContext $context): mixe
$className = $this->resolveClassName($type);
$resolvedClass = $this->classResolver->resolve($className, $value, $context);

if (($value !== null) && !is_array($value) && !is_object($value)) {
if (!$context->shouldAllowScalarToObjectCasting()) {
$exception = new TypeMismatchException($context->getPath(), $resolvedClass, get_debug_type($value));
$context->recordException($exception);

if ($context->isStrictMode()) {
throw $exception;
}
if ($value !== null && !is_array($value) && !is_object($value) && !$context->shouldAllowScalarToObjectCasting()) {
$exception = new TypeMismatchException($context->getPath(), $resolvedClass, get_debug_type($value));
$context->recordException($exception);

if ($context->isStrictMode()) {
throw $exception;
}
}

Expand Down
2 changes: 1 addition & 1 deletion tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ protected function getJsonMapper(array $classMap = [], ?JsonMapperConfig $config
new CamelCasePropertyNameConverter(),
$classMap,
null,
$config,
$config ?? new JsonMapperConfig(),
);
}

Expand Down