diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0dd2959..ea5e027 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: true matrix: - php: [ "8.0", "8.1", "8.2" ] + php: [ "8.0", "8.1", "8.2", "8.3", "8.4" ] runs-on: ubuntu-latest name: PHP@${{ matrix.php }} diff --git a/.gitignore b/.gitignore index cca6cbd..4732bab 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ vendor/ coverage/ composer.lock .phpunit.result.cache +.phpunit.cache coverage.xml junit.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 5787589..6e6df95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [5.0.1] - 2025-01-09 +### Added +- Support for PHP 8.3. and PHP 8.4. + +### Changed +- Code improvements to avoid type errors on runtime +- Implicitly nullable parameters to nullable (PHP 8.4 deprecation) + ## [5.0.0] - 2023-03-01 ### Added - Support for PHP 8.2. diff --git a/composer.json b/composer.json index 92824fb..0e1c976 100644 --- a/composer.json +++ b/composer.json @@ -21,13 +21,13 @@ } }, "require": { - "php": "8.0.* | 8.1.* | 8.2.*", + "php": "8.0.* | 8.1.* | 8.2.* | 8.3.* | 8.4.*", "php-di/phpdoc-reader": "^2.1" }, "require-dev": { "laravel/pint": "^1.5 | ^1.6", - "pestphp/pest": "^1.22", - "phpstan/phpstan": "^1.10" + "pestphp/pest": "^1.22 | ^2.0 | ^3.0", + "phpstan/phpstan": "^1.10 | ^2.0" }, "scripts": { "analyse": "phpstan analyse --memory-limit 512M", diff --git a/phpstan.neon b/phpstan.neon index c29b9d4..4dc5d4d 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,4 +2,6 @@ parameters: paths: - src level: max - checkMissingIterableValueType: false + checkExplicitMixed: false + ignoreErrors: + - identifier: missingType.iterableValue diff --git a/phpunit.xml b/phpunit.xml index 3bb4563..b9fc08d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,17 +1,13 @@ - - - - ./tests - - - - - ./src - - + + + + ./tests + + + + + ./src + + diff --git a/src/Bindings/DateTimeBinding.php b/src/Bindings/DateTimeBinding.php index f703ca0..c53e268 100644 --- a/src/Bindings/DateTimeBinding.php +++ b/src/Bindings/DateTimeBinding.php @@ -22,6 +22,9 @@ public function __construct( public function validate(array $jsonData): bool { if ($this->jsonField && array_key_exists($this->jsonField, $jsonData) && ! empty($jsonData[$this->jsonField])) { + if (! is_string($jsonData[$this->jsonField])) { + return false; + } return DateTime::createFromFormat($this->dateTimeFormat, $jsonData[$this->jsonField]) !== false; } @@ -31,10 +34,12 @@ public function validate(array $jsonData): bool public function bind(JsonDecoder $jsonDecoder, Property $property, array $jsonData = []): void { if ($this->jsonField && array_key_exists($this->jsonField, $jsonData)) { - $dateTimeObject = DateTime::createFromFormat($this->dateTimeFormat, $jsonData[$this->jsonField]); + if (is_string($jsonData[$this->jsonField])) { + $dateTimeObject = DateTime::createFromFormat($this->dateTimeFormat, $jsonData[$this->jsonField]); - if ($dateTimeObject !== false) { - $property->set($dateTimeObject); + if ($dateTimeObject !== false) { + $property->set($dateTimeObject); + } } } } diff --git a/src/Bindings/FieldBinding.php b/src/Bindings/FieldBinding.php index 88314e4..c9496ad 100644 --- a/src/Bindings/FieldBinding.php +++ b/src/Bindings/FieldBinding.php @@ -12,7 +12,9 @@ public function bind(JsonDecoder $jsonDecoder, Property $property, array $jsonDa { if ($this->jsonField && array_key_exists($this->jsonField, $jsonData) && $this->type) { $data = $jsonData[$this->jsonField]; - $property->set($jsonDecoder->decodeArray($data, $this->type)); + if (is_null($data) || is_array($data)) { + $property->set($jsonDecoder->decodeArray($data, $this->type)); + } } } } diff --git a/src/JsonDecoder.php b/src/JsonDecoder.php index 24207a6..2721a2f 100644 --- a/src/JsonDecoder.php +++ b/src/JsonDecoder.php @@ -60,7 +60,7 @@ public function scanAndRegister(string $class): void * @throws JsonValueException * @throws ReflectionException */ - public function decode(string $json, string $classType, string $root = null): mixed + public function decode(string $json, string $classType, ?string $root = null): mixed { return $this->decodeArray($this->parseJson($json, $root), $classType); } @@ -73,13 +73,13 @@ public function decode(string $json, string $classType, string $root = null): mi * @throws JsonValueException * @throws ReflectionException */ - public function decodeMultiple(string $json, string $classType, string $root = null): array + public function decodeMultiple(string $json, string $classType, ?string $root = null): array { $data = $this->parseJson($json, $root); return array_map( function ($element) use ($classType) { - return $this->decodeArray($element, $classType); + return is_array($element) ? $this->decodeArray($element, $classType) : $this->decodeArray(null, $classType); }, $data ); diff --git a/src/Property.php b/src/Property.php index 14e2978..8d0f845 100644 --- a/src/Property.php +++ b/src/Property.php @@ -108,8 +108,10 @@ private function typesMatch(?ReflectionType $reflectionType, mixed $value): bool if ($reflectionType instanceof ReflectionUnionType) { foreach ($reflectionType->getTypes() as $type) { - if ($type->getName() === $valueType) { - return true; + if ($type instanceof ReflectionNamedType) { + if ($type->getName() === $valueType) { + return true; + } } } } diff --git a/tests/PropertyTest.php b/tests/PropertyTest.php index 43a204e..d1af7f1 100644 --- a/tests/PropertyTest.php +++ b/tests/PropertyTest.php @@ -14,17 +14,11 @@ expect($sample->{$propertyName})->toEqual('value'); } })->with([ - 'public' => [ - 'name' => 'publicProperty', - ], - 'protected' => [ - 'name' => 'protectedProperty', - ], - 'private' => [ - 'name' => 'privateProperty', - ], + 'public' => ['publicProperty'], + 'protected' => ['protectedProperty'], + 'private' => ['privateProperty'], 'new' => [ - 'name' => 'newProperty', + 'newProperty', 'methodAccess' => false, ], ]);