From fb539e2c1bf77e51dd5b7f5f900ad6a1e34fc744 Mon Sep 17 00:00:00 2001 From: Rudolph Gottesheim Date: Wed, 5 Mar 2025 23:34:21 +0100 Subject: [PATCH 1/8] Upgrade PHPStan to 2.x --- composer.json | 6 +++--- src/Json.php | 4 ++-- tests/unit/Fixtures/InvalidArrayConstructorParamTag.php | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 97ff953..09bfbf1 100644 --- a/composer.json +++ b/composer.json @@ -12,9 +12,9 @@ "infection/infection": "^0.26.20", "maglnet/composer-require-checker": "^4.6", "phpstan/extension-installer": "^1.3", - "phpstan/phpstan": "^1.10", - "phpstan/phpstan-phpunit": "^1.3", - "phpstan/phpstan-strict-rules": "^1.5", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^10.1", "psalm/plugin-phpunit": "^0.18.4", "vimeo/psalm": "^5.23" diff --git a/src/Json.php b/src/Json.php index 56e3be0..fe7406e 100644 --- a/src/Json.php +++ b/src/Json.php @@ -485,13 +485,13 @@ private static function parseUseStatements(string $file): array if ($result !== 1) { continue; } - $useStatements[$matches['alias'] ?? $matches['class']] = ($matches['ns'] ?? '') . $matches['class']; + $useStatements[$matches['alias'] ?? $matches['class']] = $matches['ns'] . $matches['class']; } return $useStatements; } /** - * @return list | array + * @return list | array */ private static function createConstructorArgumentForArrayType( ReflectionParameter $parameter, diff --git a/tests/unit/Fixtures/InvalidArrayConstructorParamTag.php b/tests/unit/Fixtures/InvalidArrayConstructorParamTag.php index f8c4ee9..32f35a0 100644 --- a/tests/unit/Fixtures/InvalidArrayConstructorParamTag.php +++ b/tests/unit/Fixtures/InvalidArrayConstructorParamTag.php @@ -9,10 +9,11 @@ final class InvalidArrayConstructorParamTag { /** + * @phpstan-ignore-next-line phpDoc.parseError * @param class-string items This is not a valid param tag * @param list $items */ - public function __construct(public readonly array $items) // @phpstan-ignore-line + public function __construct(public readonly array $items) { } } From 15d823d79e145226b30a3f065722de1fdee0d68e Mon Sep 17 00:00:00 2001 From: Rudolph Gottesheim Date: Wed, 5 Mar 2025 23:35:18 +0100 Subject: [PATCH 2/8] Upgrade Psalm --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 09bfbf1..d707cc2 100644 --- a/composer.json +++ b/composer.json @@ -16,8 +16,8 @@ "phpstan/phpstan-phpunit": "^2.0", "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^10.1", - "psalm/plugin-phpunit": "^0.18.4", - "vimeo/psalm": "^5.23" + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^5.26" }, "config": { "allow-plugins": { From 901d2e6393e544fdf793d9afe106665c33295f0b Mon Sep 17 00:00:00 2001 From: Rudolph Gottesheim Date: Wed, 5 Mar 2025 23:37:41 +0100 Subject: [PATCH 3/8] Upgrade dev tools --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index d707cc2..fa037b2 100644 --- a/composer.json +++ b/composer.json @@ -9,13 +9,13 @@ }, "require-dev": { "eventjet/coding-standard": "^3.15", - "infection/infection": "^0.26.20", - "maglnet/composer-require-checker": "^4.6", + "infection/infection": "^0.27.11", + "maglnet/composer-require-checker": "^4.15", "phpstan/extension-installer": "^1.3", "phpstan/phpstan": "^2.1", "phpstan/phpstan-phpunit": "^2.0", "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^10.1", + "phpunit/phpunit": "^10.5", "psalm/plugin-phpunit": "^0.19.0", "vimeo/psalm": "^5.26" }, From a5eb93184cad5da3eae233c9edf31d7a02b54af9 Mon Sep 17 00:00:00 2001 From: Thomas Rieschl Date: Fri, 7 Mar 2025 14:38:12 +0100 Subject: [PATCH 4/8] * require PHP 8.3 * upgrade Infection * upgrade PHPUnit * upgrade Psalm * upgrade GitHub workflow actions * normalize composer.json --- .github/workflows/checks.yml | 38 ++++++------- composer.json | 53 ++++++++++--------- phpstan.neon.dist | 1 + psalm.xml | 2 + src/Json.php | 13 +---- src/JsonError.php | 2 +- src/Type/Array_.php | 2 +- src/Type/Boolean.php | 2 +- src/Type/JsonType.php | 2 +- src/Type/Null_.php | 2 +- src/Type/Number.php | 2 +- src/Type/Object_.php | 4 +- src/Type/String_.php | 2 +- src/Type/Union.php | 2 +- .../unit/Fixtures/HasImportedListItemType.php | 2 +- tests/unit/JsonTest.php | 16 ++---- tests/unit/Type/ValidationIssueTest.php | 4 +- tests/unit/TypesTest.php | 10 ++-- 18 files changed, 70 insertions(+), 89 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 8459a8e..7251a4e 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -7,11 +7,11 @@ jobs: composer-require-checker: strategy: matrix: - php: [ "8.1", "8.2" ] + php: ["8.3", "8.4"] runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -23,7 +23,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}-composer-${{ hashFiles('**/composer.lock') }} @@ -38,12 +38,12 @@ jobs: phpcs: strategy: matrix: - php: [ "8.1", "8.2" ] + php: ["8.3", "8.4"] deps: [ "--prefer-lowest", "" ] runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -55,7 +55,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}-composer-${{ hashFiles('**/composer.lock') }} @@ -70,12 +70,12 @@ jobs: phpstan: strategy: matrix: - php: [ "8.1", "8.2" ] + php: ["8.3", "8.4"] deps: [ "--prefer-lowest", "" ] runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -87,7 +87,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}-composer-${{ hashFiles('**/composer.lock') }} @@ -102,12 +102,12 @@ jobs: psalm: strategy: matrix: - php: [ "8.1", "8.2" ] + php: ["8.3", "8.4"] deps: [ "--prefer-lowest", "" ] runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -119,7 +119,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}-composer-${{ hashFiles('**/composer.lock') }} @@ -134,12 +134,12 @@ jobs: infection: strategy: matrix: - php: [ "8.1", "8.2" ] + php: ["8.3", "8.4"] deps: [ "--prefer-lowest", "" ] runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -151,7 +151,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}-composer-${{ hashFiles('**/composer.lock') }} @@ -174,7 +174,7 @@ jobs: -jmax - name: Save Infection result - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: infection-log-${{ matrix.php }}-${{ matrix.deps }}.txt @@ -183,12 +183,12 @@ jobs: phpunit: strategy: matrix: - php: [ "8.1", "8.2" ] + php: ["8.3", "8.4"] deps: [ "--prefer-lowest", "" ] runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -200,7 +200,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}-composer-${{ hashFiles('**/composer.lock') }} diff --git a/composer.json b/composer.json index fa037b2..c39236d 100644 --- a/composer.json +++ b/composer.json @@ -1,32 +1,25 @@ { "name": "eventjet/json", - "type": "library", "description": "Type-safe JSON encoding and decoding", "license": "MIT", + "type": "library", "require": { - "php": ">=8.1", + "php": ">=8.3", "ext-json": "*" }, "require-dev": { - "eventjet/coding-standard": "^3.15", - "infection/infection": "^0.27.11", - "maglnet/composer-require-checker": "^4.15", - "phpstan/extension-installer": "^1.3", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-phpunit": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^10.5", - "psalm/plugin-phpunit": "^0.19.0", - "vimeo/psalm": "^5.26" - }, - "config": { - "allow-plugins": { - "dealerdirect/phpcodesniffer-composer-installer": true, - "infection/extension-installer": true, - "phpstan/extension-installer": true - }, - "sort-packages": true + "eventjet/coding-standard": "^3.18", + "infection/infection": "^0.29.14", + "maglnet/composer-require-checker": "^4.16.1", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.7", + "phpstan/phpstan-phpunit": "^2.0.4", + "phpstan/phpstan-strict-rules": "^2.0.3", + "phpunit/phpunit": "^12.0.7", + "psalm/plugin-phpunit": "^0.19.2", + "vimeo/psalm": "^6.8.8" }, + "minimum-stability": "stable", "autoload": { "psr-4": { "Eventjet\\Json\\": "src" @@ -37,7 +30,14 @@ "Eventjet\\Test\\Unit\\Json\\": "tests/unit" } }, - "minimum-stability": "stable", + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true, + "infection/extension-installer": true, + "phpstan/extension-installer": true + }, + "sort-packages": true + }, "scripts": { "check": [ "@check-deps", @@ -47,12 +47,13 @@ "@phpunit", "@infection" ], - "check-deps": "vendor/bin/composer-require-checker", + "check-deps": "composer-require-checker", "cs-check": "php-cs-fixer fix --dry-run", "cs-fix": "php-cs-fixer fix", - "infection": "vendor/bin/infection -jmax", - "phpstan": "vendor/bin/phpstan", - "phpunit": "vendor/bin/phpunit", - "psalm": "vendor/bin/psalm" + "infection": "infection -jmax", + "infection-diff": "infection -jmax --git-diff-lines --min-msi=100 --min-covered-msi=100 --ignore-msi-with-no-mutations", + "phpstan": "phpstan analyze", + "phpunit": "phpunit", + "psalm": "psalm" } } diff --git a/phpstan.neon.dist b/phpstan.neon.dist index e691e35..4548ef5 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,5 +1,6 @@ parameters: level: max + rememberPossiblyImpureFunctionValues: false paths: - src/ - tests/ diff --git a/psalm.xml b/psalm.xml index 2a2a87f..2cbaac8 100644 --- a/psalm.xml +++ b/psalm.xml @@ -5,8 +5,10 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" + ensureOverrideAttribute="false" findUnusedBaselineEntry="true" findUnusedCode="true" + findUnusedPsalmSuppress="true" > diff --git a/src/Json.php b/src/Json.php index fe7406e..f8f7a7d 100644 --- a/src/Json.php +++ b/src/Json.php @@ -62,12 +62,10 @@ public static function decode(string $json, object|string $value): object } /** - * @psalm-suppress MixedInferredReturnType * @return string|int|float|bool|array|null */ private static function preparePropertyValueForEncoding(mixed $value): string|int|float|bool|array|null { - /** @psalm-suppress MixedReturnStatement */ return match (true) { is_string($value), is_int($value), is_float($value), is_bool($value), $value === null => $value, is_array($value) => self::encodeArray($value), @@ -119,7 +117,6 @@ private static function getJsonKeyForProperty(ReflectionProperty $property): str * @template T of object * @param T | class-string $value * @return T - * @psalm-suppress InvalidReturnType */ private static function decodeClass(string $json, object|string $value): object { @@ -128,7 +125,7 @@ private static function decodeClass(string $json, object|string $value): object throw JsonError::decodeFailed(error_get_last()['message'] ?? null); } if (!is_array($data)) { - throw JsonError::decodeFailed(sprintf("Expected JSON object, got %s", gettype($data))); + throw JsonError::decodeFailed(sprintf('Expected JSON object, got %s', gettype($data))); } /** @psalm-suppress DocblockTypeContradiction */ if (!is_string($value)) { @@ -615,16 +612,8 @@ private static function createConstructorArgumentForEnumType( ), ); } - /** - * @psalm-suppress MixedAssignment - * @psalm-suppress MixedMethodCall - */ $case = $typeName::tryFrom($value); if ($case === null) { - /** - * @psalm-suppress MixedArgument - * @psalm-suppress MixedMethodCall - */ throw JsonError::decodeFailed( sprintf( '"%s" is not a valid value for enum %s. Valid values are: %s', diff --git a/src/JsonError.php b/src/JsonError.php index 83ddb3a..63ee9d4 100644 --- a/src/JsonError.php +++ b/src/JsonError.php @@ -9,7 +9,7 @@ final class JsonError extends RuntimeException { - private function __construct(string $message = "", int $code = 0, Throwable|null $previous = null) + private function __construct(string $message = '', int $code = 0, Throwable|null $previous = null) { parent::__construct($message, $code, $previous); } diff --git a/src/Type/Array_.php b/src/Type/Array_.php index 0c938d5..9709120 100644 --- a/src/Type/Array_.php +++ b/src/Type/Array_.php @@ -26,7 +26,7 @@ public function __toString(): string public function validateValue(mixed $value, string $path = ''): ValidationResult { if (!is_array($value)) { - return ValidationResult::error(sprintf('Expected array, got %s.', json_encode($value)), $path); + return ValidationResult::error(sprintf('Expected array, got %s.', (string)json_encode($value)), $path); } if (!array_is_list($value)) { return ValidationResult::error('Expected array, got object.', $path); diff --git a/src/Type/Boolean.php b/src/Type/Boolean.php index f1cd0bf..7021606 100644 --- a/src/Type/Boolean.php +++ b/src/Type/Boolean.php @@ -54,7 +54,7 @@ public function validateValue(mixed $value, string $path = ''): ValidationResult return ValidationResult::valid(); } return ValidationResult::error( - sprintf('Expected %s, got %s.', json_encode($this->value), json_encode($value)), + sprintf('Expected %s, got %s.', (string)json_encode($this->value), (string)json_encode($value)), $path, ); } diff --git a/src/Type/JsonType.php b/src/Type/JsonType.php index d55efde..12cf0fe 100644 --- a/src/Type/JsonType.php +++ b/src/Type/JsonType.php @@ -70,7 +70,7 @@ public static function union(self $first, self $second, self ...$other): Union protected static function joinPath(string $prefix, string|int $key): string { - return $prefix === '' ? (string)$key : sprintf("%s.%d", $prefix, $key); + return $prefix === '' ? (string)$key : sprintf('%s.%d', $prefix, $key); } /** diff --git a/src/Type/Null_.php b/src/Type/Null_.php index 03db7b7..0bc81b9 100644 --- a/src/Type/Null_.php +++ b/src/Type/Null_.php @@ -26,6 +26,6 @@ public function validateValue(mixed $value, string $path = ''): ValidationResult if ($value === null) { return ValidationResult::valid(); } - return ValidationResult::error(sprintf('Expected null, got %s.', json_encode($value)), $path); + return ValidationResult::error(sprintf('Expected null, got %s.', (string)json_encode($value)), $path); } } diff --git a/src/Type/Number.php b/src/Type/Number.php index 4065668..ab26783 100644 --- a/src/Type/Number.php +++ b/src/Type/Number.php @@ -28,6 +28,6 @@ public function validateValue(mixed $value, string $path = ''): ValidationResult if (is_int($value) || is_float($value)) { return ValidationResult::valid(); } - return ValidationResult::error(sprintf('Expected number, got %s.', json_encode($value)), $path); + return ValidationResult::error(sprintf('Expected number, got %s.', (string)json_encode($value)), $path); } } diff --git a/src/Type/Object_.php b/src/Type/Object_.php index 6ea44cb..0690bca 100644 --- a/src/Type/Object_.php +++ b/src/Type/Object_.php @@ -34,10 +34,10 @@ public function __toString(): string public function validateValue(mixed $value, string $path = ''): ValidationResult { if (!is_array($value)) { - return ValidationResult::error(sprintf('Expected object, got %s.', json_encode($value)), $path); + return ValidationResult::error(sprintf('Expected object, got %s.', (string)json_encode($value)), $path); } if ($value !== [] && array_is_list($value)) { - return ValidationResult::error(sprintf('Expected object, got %s.', json_encode($value)), $path); + return ValidationResult::error(sprintf('Expected object, got %s.', (string)json_encode($value)), $path); } $results = []; foreach ($this->members as $name => $member) { diff --git a/src/Type/String_.php b/src/Type/String_.php index a9bf930..33202b4 100644 --- a/src/Type/String_.php +++ b/src/Type/String_.php @@ -25,7 +25,7 @@ public function __toString(): string public function validateValue(mixed $value, string $path = ''): ValidationResult { if (!is_string($value)) { - return ValidationResult::error(sprintf('Expected string, got %s.', json_encode($value)), $path); + return ValidationResult::error(sprintf('Expected string, got %s.', (string)json_encode($value)), $path); } return ValidationResult::valid(); } diff --git a/src/Type/Union.php b/src/Type/Union.php index dabc61c..83fc1a4 100644 --- a/src/Type/Union.php +++ b/src/Type/Union.php @@ -56,7 +56,7 @@ public function validateValue(mixed $value, string $path = ''): ValidationResult } $expected = self::disjunction($this->canonicalize()->types); return ValidationResult::error( - sprintf('Expected %s, got %s.', $expected, json_encode($value)), + sprintf('Expected %s, got %s.', $expected, (string)json_encode($value)), $path, ); } diff --git a/tests/unit/Fixtures/HasImportedListItemType.php b/tests/unit/Fixtures/HasImportedListItemType.php index 6efbcd7..5aebb9f 100644 --- a/tests/unit/Fixtures/HasImportedListItemType.php +++ b/tests/unit/Fixtures/HasImportedListItemType.php @@ -11,7 +11,7 @@ final class HasImportedListItemType { /** * @param list $items1 - * @param list $items2 The second set of items + * @param list $items2 The second set of items * @param list $items3 The third set of items */ public function __construct( diff --git a/tests/unit/JsonTest.php b/tests/unit/JsonTest.php index ed7719e..93c0c6f 100644 --- a/tests/unit/JsonTest.php +++ b/tests/unit/JsonTest.php @@ -519,9 +519,7 @@ public function __construct(public DoesNotExist|null $nested = null) ]; } - /** - * @dataProvider encodeCases - */ + #[\PHPUnit\Framework\Attributes\DataProvider('encodeCases')] public function testEncode(mixed $value, string $expected): void { $encoded = Json::encode($value); @@ -532,8 +530,8 @@ public function testEncode(mixed $value, string $expected): void /** * @param object | class-string $object * @param callable(object): void $test - * @dataProvider decodeCases */ + #[\PHPUnit\Framework\Attributes\DataProvider('decodeCases')] public function testDecode(string $json, object|string $object, callable $test): void { $object = Json::decode($json, $object); @@ -541,9 +539,7 @@ public function testDecode(string $json, object|string $object, callable $test): $test($object); } - /** - * @dataProvider roundtripsCases - */ + #[\PHPUnit\Framework\Attributes\DataProvider('roundtripsCases')] public function testRoundtrips(object $value): void { $encoded1 = Json::encode($value); @@ -553,9 +549,7 @@ public function testRoundtrips(object $value): void self::assertJsonStringEqualsJsonString($encoded1, $encoded2); } - /** - * @dataProvider failingEncodeCases - */ + #[\PHPUnit\Framework\Attributes\DataProvider('failingEncodeCases')] public function testFailingEncode(mixed $value): void { $this->expectException(JsonError::class); @@ -566,8 +560,8 @@ public function testFailingEncode(mixed $value): void /** * @param object | class-string $object - * @dataProvider failingDecodeCases */ + #[\PHPUnit\Framework\Attributes\DataProvider('failingDecodeCases')] public function testFailingDecode(string $json, object|string $object, string|null $expectedMessage = null): void { $this->expectException(JsonError::class); diff --git a/tests/unit/Type/ValidationIssueTest.php b/tests/unit/Type/ValidationIssueTest.php index 31f5b2b..ca99106 100644 --- a/tests/unit/Type/ValidationIssueTest.php +++ b/tests/unit/Type/ValidationIssueTest.php @@ -31,9 +31,7 @@ public static function equalsCases(): iterable } } - /** - * @dataProvider equalsCases - */ + #[\PHPUnit\Framework\Attributes\DataProvider('equalsCases')] public function testEquals(ValidationIssue $a, ValidationIssue $b, bool $expected): void { if ($expected) { diff --git a/tests/unit/TypesTest.php b/tests/unit/TypesTest.php index 9ef414f..7f75b35 100644 --- a/tests/unit/TypesTest.php +++ b/tests/unit/TypesTest.php @@ -233,8 +233,8 @@ private static function expectIssues(ValidationResult $actual, array $expectedIs /** * @param JsonType | callable(): JsonType $type * @param list $expectedIssues - * @dataProvider validateCases */ + #[\PHPUnit\Framework\Attributes\DataProvider('validateCases')] public function testValidate(JsonType|callable $type, string $json, array $expectedIssues = []): void { if (!$type instanceof JsonType) { @@ -258,17 +258,13 @@ public function testValidate(JsonType|callable $type, string $json, array $expec } } - /** - * @dataProvider toStringCases - */ + #[\PHPUnit\Framework\Attributes\DataProvider('toStringCases')] public function testToString(JsonType $type, string $expected): void { self::assertSame($expected, (string)$type); } - /** - * @dataProvider equalsCases - */ + #[\PHPUnit\Framework\Attributes\DataProvider('equalsCases')] public function testEquals(JsonType $a, JsonType $b, bool $expected): void { $aString = (string)$a->canonicalize(); From 5d2ea0a724211fd19f3b303a3629a9fb4f493ec1 Mon Sep 17 00:00:00 2001 From: Thomas Rieschl Date: Fri, 7 Mar 2025 14:44:06 +0100 Subject: [PATCH 5/8] * remove running CI with prefer-lowest deps. It doesn't make sense in a package without dependencies --- .github/workflows/checks.yml | 43 ++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 7251a4e..b08a9f4 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -26,11 +26,11 @@ jobs: uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}- + key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-${{ matrix.php }}- - name: Install dependencies - run: composer update --no-interaction --no-progress --no-suggest ${{ matrix.deps }} + run: composer update --no-interaction --no-progress --no-suggest - name: Composer Require Checker run: composer check-deps @@ -39,7 +39,6 @@ jobs: strategy: matrix: php: ["8.3", "8.4"] - deps: [ "--prefer-lowest", "" ] runs-on: ubuntu-latest steps: - name: Checkout @@ -58,11 +57,11 @@ jobs: uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}- + key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-${{ matrix.php }}- - name: Install dependencies - run: composer update --no-interaction --no-progress --no-suggest ${{ matrix.deps }} + run: composer update --no-interaction --no-progress --no-suggest - name: PHPCS run: composer cs-check @@ -71,7 +70,6 @@ jobs: strategy: matrix: php: ["8.3", "8.4"] - deps: [ "--prefer-lowest", "" ] runs-on: ubuntu-latest steps: - name: Checkout @@ -90,11 +88,11 @@ jobs: uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}- + key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-${{ matrix.php }}- - name: Install dependencies - run: composer update --no-interaction --no-progress --no-suggest ${{ matrix.deps }} + run: composer update --no-interaction --no-progress --no-suggest - name: PHPStan run: composer phpstan @@ -103,7 +101,6 @@ jobs: strategy: matrix: php: ["8.3", "8.4"] - deps: [ "--prefer-lowest", "" ] runs-on: ubuntu-latest steps: - name: Checkout @@ -122,11 +119,11 @@ jobs: uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}- + key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-${{ matrix.php }}- - name: Install dependencies - run: composer update --no-interaction --no-progress --no-suggest ${{ matrix.deps }} + run: composer update --no-interaction --no-progress --no-suggest - name: Psalm run: composer psalm @@ -135,7 +132,6 @@ jobs: strategy: matrix: php: ["8.3", "8.4"] - deps: [ "--prefer-lowest", "" ] runs-on: ubuntu-latest steps: - name: Checkout @@ -154,11 +150,11 @@ jobs: uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}- + key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-${{ matrix.php }}- - name: Install dependencies - run: composer update --no-interaction --no-progress --no-suggest ${{ matrix.deps }} + run: composer update --no-interaction --no-progress --no-suggest - name: Infection coverage of changed lines if: "!contains(github.event.pull_request.labels.*.name, 'skip-infection')" @@ -177,14 +173,13 @@ jobs: uses: actions/upload-artifact@v4 if: always() with: - name: infection-log-${{ matrix.php }}-${{ matrix.deps }}.txt + name: infection-log-${{ matrix.php }}.txt path: infection-log.txt phpunit: strategy: matrix: php: ["8.3", "8.4"] - deps: [ "--prefer-lowest", "" ] runs-on: ubuntu-latest steps: - name: Checkout @@ -203,11 +198,11 @@ jobs: uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}- + key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-${{ matrix.php }}- - name: Install dependencies - run: composer update --no-interaction --no-progress --no-suggest ${{ matrix.deps }} + run: composer update --no-interaction --no-progress --no-suggest - name: Tests run: vendor/bin/phpunit --coverage-clover=coverage.xml --stop-on-failure From 03260e1af9e535c62c4ef6ecc6b84d267ecbd11f Mon Sep 17 00:00:00 2001 From: Thomas Rieschl Date: Fri, 7 Mar 2025 14:56:28 +0100 Subject: [PATCH 6/8] ignore unsupported PHP version in PHP-CS-Fixer --- .github/workflows/checks.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index b08a9f4..3d2a1bb 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -35,7 +35,7 @@ jobs: - name: Composer Require Checker run: composer check-deps - phpcs: + code-style: strategy: matrix: php: ["8.3", "8.4"] @@ -63,8 +63,8 @@ jobs: - name: Install dependencies run: composer update --no-interaction --no-progress --no-suggest - - name: PHPCS - run: composer cs-check + - name: PHP Code Style + run: PHP_CS_FIXER_IGNORE_ENV=1 composer cs-check phpstan: strategy: @@ -129,6 +129,7 @@ jobs: run: composer psalm infection: + strategy: matrix: php: ["8.3", "8.4"] From 07c5c1446e4c01a514e708998d60e1b31ce5b311 Mon Sep 17 00:00:00 2001 From: Thomas Rieschl Date: Fri, 7 Mar 2025 15:00:52 +0100 Subject: [PATCH 7/8] Psalm doesn't yet recognize dataProvider attributes --- tests/unit/JsonTest.php | 16 +++++++++++----- tests/unit/Type/ValidationIssueTest.php | 4 +++- tests/unit/TypesTest.php | 10 +++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/tests/unit/JsonTest.php b/tests/unit/JsonTest.php index 93c0c6f..13601af 100644 --- a/tests/unit/JsonTest.php +++ b/tests/unit/JsonTest.php @@ -39,6 +39,7 @@ use Eventjet\Test\Unit\Json\Fixtures\Worldline\AccountOnFileDisplayHints; use Eventjet\Test\Unit\Json\Fixtures\Worldline\LabelTemplateElement; use Eventjet\Test\Unit\Json\Fixtures\WrongArrayDocblockType; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use ThisClassDoesNotExist; @@ -51,6 +52,7 @@ final class JsonTest extends TestCase { /** * @return iterable + * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function encodeCases(): iterable { @@ -105,6 +107,7 @@ public function __construct(public array $foo = ['f1' => 'foo', 'f2' => 'bar']) /** * @return iterable + * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function decodeCases(): iterable { @@ -305,6 +308,7 @@ static function (object $object): void { /** * @return iterable + * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function roundtripsCases(): iterable { @@ -363,6 +367,7 @@ public static function roundtripsCases(): iterable /** * @return iterable + * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function failingEncodeCases(): iterable { @@ -372,6 +377,7 @@ public static function failingEncodeCases(): iterable /** * @return iterable + * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function failingDecodeCases(): iterable { @@ -519,7 +525,7 @@ public function __construct(public DoesNotExist|null $nested = null) ]; } - #[\PHPUnit\Framework\Attributes\DataProvider('encodeCases')] + #[DataProvider('encodeCases')] public function testEncode(mixed $value, string $expected): void { $encoded = Json::encode($value); @@ -531,7 +537,7 @@ public function testEncode(mixed $value, string $expected): void * @param object | class-string $object * @param callable(object): void $test */ - #[\PHPUnit\Framework\Attributes\DataProvider('decodeCases')] + #[DataProvider('decodeCases')] public function testDecode(string $json, object|string $object, callable $test): void { $object = Json::decode($json, $object); @@ -539,7 +545,7 @@ public function testDecode(string $json, object|string $object, callable $test): $test($object); } - #[\PHPUnit\Framework\Attributes\DataProvider('roundtripsCases')] + #[DataProvider('roundtripsCases')] public function testRoundtrips(object $value): void { $encoded1 = Json::encode($value); @@ -549,7 +555,7 @@ public function testRoundtrips(object $value): void self::assertJsonStringEqualsJsonString($encoded1, $encoded2); } - #[\PHPUnit\Framework\Attributes\DataProvider('failingEncodeCases')] + #[DataProvider('failingEncodeCases')] public function testFailingEncode(mixed $value): void { $this->expectException(JsonError::class); @@ -561,7 +567,7 @@ public function testFailingEncode(mixed $value): void /** * @param object | class-string $object */ - #[\PHPUnit\Framework\Attributes\DataProvider('failingDecodeCases')] + #[DataProvider('failingDecodeCases')] public function testFailingDecode(string $json, object|string $object, string|null $expectedMessage = null): void { $this->expectException(JsonError::class); diff --git a/tests/unit/Type/ValidationIssueTest.php b/tests/unit/Type/ValidationIssueTest.php index ca99106..61b36fe 100644 --- a/tests/unit/Type/ValidationIssueTest.php +++ b/tests/unit/Type/ValidationIssueTest.php @@ -5,6 +5,7 @@ namespace Eventjet\Test\Unit\Json\Type; use Eventjet\Json\Type\ValidationIssue; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use function sprintf; @@ -13,6 +14,7 @@ final class ValidationIssueTest extends TestCase { /** * @return iterable + * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function equalsCases(): iterable { @@ -31,7 +33,7 @@ public static function equalsCases(): iterable } } - #[\PHPUnit\Framework\Attributes\DataProvider('equalsCases')] + #[DataProvider('equalsCases')] public function testEquals(ValidationIssue $a, ValidationIssue $b, bool $expected): void { if ($expected) { diff --git a/tests/unit/TypesTest.php b/tests/unit/TypesTest.php index 7f75b35..fb52465 100644 --- a/tests/unit/TypesTest.php +++ b/tests/unit/TypesTest.php @@ -8,6 +8,7 @@ use Eventjet\Json\Type\Member; use Eventjet\Json\Type\ValidationIssue; use Eventjet\Json\Type\ValidationResult; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use function count; @@ -18,6 +19,7 @@ final class TypesTest extends TestCase { /** * @return iterable}> + * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function validateCases(): iterable { @@ -114,6 +116,7 @@ public static function validateCases(): iterable /** * @return iterable + * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function toStringCases(): iterable { @@ -148,6 +151,7 @@ public static function toStringCases(): iterable /** * @return iterable + * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function equalsCases(): iterable { @@ -234,7 +238,7 @@ private static function expectIssues(ValidationResult $actual, array $expectedIs * @param JsonType | callable(): JsonType $type * @param list $expectedIssues */ - #[\PHPUnit\Framework\Attributes\DataProvider('validateCases')] + #[DataProvider('validateCases')] public function testValidate(JsonType|callable $type, string $json, array $expectedIssues = []): void { if (!$type instanceof JsonType) { @@ -258,13 +262,13 @@ public function testValidate(JsonType|callable $type, string $json, array $expec } } - #[\PHPUnit\Framework\Attributes\DataProvider('toStringCases')] + #[DataProvider('toStringCases')] public function testToString(JsonType $type, string $expected): void { self::assertSame($expected, (string)$type); } - #[\PHPUnit\Framework\Attributes\DataProvider('equalsCases')] + #[DataProvider('equalsCases')] public function testEquals(JsonType $a, JsonType $b, bool $expected): void { $aString = (string)$a->canonicalize(); From 7a468d91e742104ec32b16a9ea3556429ef8b59b Mon Sep 17 00:00:00 2001 From: Rudolph Gottesheim Date: Mon, 25 Aug 2025 10:47:14 +0200 Subject: [PATCH 8/8] Refresh the PR --- composer.json | 2 +- infection.json | 1 + tests/unit/JsonTest.php | 5 ----- tests/unit/Type/ValidationIssueTest.php | 1 - tests/unit/TypesTest.php | 3 --- 5 files changed, 2 insertions(+), 10 deletions(-) diff --git a/composer.json b/composer.json index c39236d..48d11ab 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ }, "require-dev": { "eventjet/coding-standard": "^3.18", - "infection/infection": "^0.29.14", + "infection/infection": "^0.31.2", "maglnet/composer-require-checker": "^4.16.1", "phpstan/extension-installer": "^1.4.3", "phpstan/phpstan": "^2.1.7", diff --git a/infection.json b/infection.json index fbf03ce..7bc85ba 100644 --- a/infection.json +++ b/infection.json @@ -1,4 +1,5 @@ { + "staticAnalysisTool":"phpstan", "timeout": 10, "source": { "directories": [ diff --git a/tests/unit/JsonTest.php b/tests/unit/JsonTest.php index 13601af..f5bcb55 100644 --- a/tests/unit/JsonTest.php +++ b/tests/unit/JsonTest.php @@ -52,7 +52,6 @@ final class JsonTest extends TestCase { /** * @return iterable - * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function encodeCases(): iterable { @@ -107,7 +106,6 @@ public function __construct(public array $foo = ['f1' => 'foo', 'f2' => 'bar']) /** * @return iterable - * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function decodeCases(): iterable { @@ -308,7 +306,6 @@ static function (object $object): void { /** * @return iterable - * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function roundtripsCases(): iterable { @@ -367,7 +364,6 @@ public static function roundtripsCases(): iterable /** * @return iterable - * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function failingEncodeCases(): iterable { @@ -377,7 +373,6 @@ public static function failingEncodeCases(): iterable /** * @return iterable - * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function failingDecodeCases(): iterable { diff --git a/tests/unit/Type/ValidationIssueTest.php b/tests/unit/Type/ValidationIssueTest.php index 61b36fe..c2465da 100644 --- a/tests/unit/Type/ValidationIssueTest.php +++ b/tests/unit/Type/ValidationIssueTest.php @@ -14,7 +14,6 @@ final class ValidationIssueTest extends TestCase { /** * @return iterable - * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function equalsCases(): iterable { diff --git a/tests/unit/TypesTest.php b/tests/unit/TypesTest.php index fb52465..079467d 100644 --- a/tests/unit/TypesTest.php +++ b/tests/unit/TypesTest.php @@ -19,7 +19,6 @@ final class TypesTest extends TestCase { /** * @return iterable}> - * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function validateCases(): iterable { @@ -116,7 +115,6 @@ public static function validateCases(): iterable /** * @return iterable - * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function toStringCases(): iterable { @@ -151,7 +149,6 @@ public static function toStringCases(): iterable /** * @return iterable - * @psalm-suppress PossiblyUnusedMethod Psalm doesn't yet recognize dataProvider attributes */ public static function equalsCases(): iterable {