diff --git a/composer.json b/composer.json index f59619a..3d6874e 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "yzen.dev/plain-to-class", - "version": "3.0.1", + "version": "3.0.2", "description": "Class-transformer to transform your dataset into a structured object", "minimum-stability": "dev", "prefer-stable": true, diff --git a/composer.lock b/composer.lock index 7155e47..43ff397 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e6392f91ae3712acc4a4fab8d25ef602", + "content-hash": "5a0b6ab9a620c712b6b5c2c8cc3bc364", "packages": [], "packages-dev": [ { @@ -1495,16 +1495,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.15.5", + "version": "v4.16.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" + "reference": "19526a33fb561ef417e822e85f08a00db4059c17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", - "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17", + "reference": "19526a33fb561ef417e822e85f08a00db4059c17", "shasum": "" }, "require": { @@ -1545,9 +1545,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0" }, - "time": "2023-05-19T20:20:00+00:00" + "time": "2023-06-25T14:52:30+00:00" }, { "name": "ocramius/package-versions", @@ -2162,16 +2162,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.22.0", + "version": "1.22.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "ec58baf7b3c7f1c81b3b00617c953249fb8cf30c" + "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/ec58baf7b3c7f1c81b3b00617c953249fb8cf30c", - "reference": "ec58baf7b3c7f1c81b3b00617c953249fb8cf30c", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/65c39594fbd8c67abfc68bb323f86447bab79cc0", + "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0", "shasum": "" }, "require": { @@ -2203,9 +2203,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.1" }, - "time": "2023-06-01T12:35:21+00:00" + "time": "2023-06-29T20:46:06+00:00" }, { "name": "phpunit/php-code-coverage", @@ -4206,16 +4206,16 @@ }, { "name": "symfony/filesystem", - "version": "v6.3.0", + "version": "v6.3.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "97b698e1d77d356304def77a8d0cd73090b359ea" + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/97b698e1d77d356304def77a8d0cd73090b359ea", - "reference": "97b698e1d77d356304def77a8d0cd73090b359ea", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", "shasum": "" }, "require": { @@ -4249,7 +4249,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.3.0" + "source": "https://github.com/symfony/filesystem/tree/v6.3.1" }, "funding": [ { @@ -4265,7 +4265,7 @@ "type": "tidelift" } ], - "time": "2023-05-30T17:12:32+00:00" + "time": "2023-06-01T08:30:39+00:00" }, { "name": "symfony/finder", diff --git a/src/Reflection/Types/PropertyType.php b/src/Reflection/Types/PropertyType.php index 41edc63..e608e0b 100644 --- a/src/Reflection/Types/PropertyType.php +++ b/src/Reflection/Types/PropertyType.php @@ -14,10 +14,12 @@ class PropertyType /** * @param string $name Name of type * @param bool $isScalar + * @param bool $isNullable */ public function __construct( public string $name, - public bool $isScalar + public bool $isScalar, + public bool $isNullable ) { } } diff --git a/src/Reflection/Types/PropertyTypeFactory.php b/src/Reflection/Types/PropertyTypeFactory.php index aec57c6..245a112 100644 --- a/src/Reflection/Types/PropertyTypeFactory.php +++ b/src/Reflection/Types/PropertyTypeFactory.php @@ -29,13 +29,16 @@ public static function create(RuntimeReflectionProperty $property) $type = TypeEnums::TYPE_MIXED; $isScalar = true; + $isNullable = true; if ($reflectionType instanceof ReflectionType) { $type = $reflectionType; + $isNullable = $reflectionType->allowsNull(); } if ($reflectionType instanceof ReflectionNamedType) { $type = $reflectionType->getName(); $isScalar = $reflectionType->isBuiltin(); + $isNullable = $reflectionType->allowsNull(); } if ($type === TypeEnums::TYPE_ARRAY) { @@ -49,7 +52,8 @@ public static function create(RuntimeReflectionProperty $property) $arrayType ??= TypeEnums::TYPE_MIXED; $type = new ArrayType( $type, - $isScalar + $isScalar, + $isNullable ); $type->itemsType = $arrayType ?? TypeEnums::TYPE_MIXED; $type->isScalarItems = in_array($arrayType, [TypeEnums::TYPE_INTEGER, TypeEnums::TYPE_FLOAT, TypeEnums::TYPE_STRING, TypeEnums::TYPE_BOOLEAN, TypeEnums::TYPE_MIXED]); @@ -60,20 +64,23 @@ public static function create(RuntimeReflectionProperty $property) if ($isScalar || $property->notTransform()) { return new ScalarType( $type, - $isScalar + $isScalar, + $isNullable ); } if (function_exists('enum_exists') && enum_exists($type)) { return new EnumType( $type, - $isScalar + $isScalar, + $isNullable ); } return new TransformableType( $type, - $isScalar + $isScalar, + $isNullable ); } } diff --git a/src/ValueCasting.php b/src/ValueCasting.php index 4b20ad8..add0973 100644 --- a/src/ValueCasting.php +++ b/src/ValueCasting.php @@ -47,6 +47,10 @@ public function __construct(ReflectionProperty $property, HydratorConfig $config */ public function castAttribute(mixed $value): mixed { + if ($this->property->type->isNullable && empty($value)) { + return null; + } + if (($this->property->type->isScalar && !$this->property->type instanceof ArrayType) || $this->property->notTransform()) { return $this->castScalar($this->property->type->name, $value); } diff --git a/tests/Units/DTO/TypesDto.php b/tests/Units/DTO/TypesDto.php new file mode 100644 index 0000000..4819397 --- /dev/null +++ b/tests/Units/DTO/TypesDto.php @@ -0,0 +1,17 @@ +castAttribute('1'); $this->assertIsInt($value); $this->assertEquals(1, $value); + + + $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'nullableInt'))); + $value = $caster->castAttribute(''); + $this->assertNull($value); + + $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'nullableString'))); + $value = $caster->castAttribute(''); + $this->assertNull($value); + + $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'nullableFloat'))); + $value = $caster->castAttribute(''); + $this->assertNull($value); + + $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'nullableBool'))); + $value = $caster->castAttribute(''); + $this->assertNull($value); } public function testCreateArrayProperty(): void