diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 313c2cc..ef6c8d6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: [7.4, 8.0, 8.1] + php: [8.1, 8.2, 8.3, 8.4] steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/static-analysis.yaml b/.github/workflows/static-analysis.yaml index aa49540..3582d6c 100644 --- a/.github/workflows/static-analysis.yaml +++ b/.github/workflows/static-analysis.yaml @@ -17,10 +17,6 @@ jobs: script: make cs-check - description: PHPStan script: make phpstan - - description: PSalm - script: make psalm - - description: Type assertions - script: make type-assertions name: ${{ matrix.description }} runs-on: ubuntu-latest @@ -30,7 +26,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 7.4 + php-version: 8.1 - name: Install dependencies uses: ramsey/composer-install@v2 - run: ${{ matrix.script }} diff --git a/Makefile b/Makefile index 6476e3d..755a8ad 100644 --- a/Makefile +++ b/Makefile @@ -9,32 +9,22 @@ help: | xargs -I _ sh -c 'printf "%-40s " _; make _ -nB | (grep -i "^# Help:" || echo "") | tail -1 | sed "s/^# Help: //g"' -.PHONY: run run-php7.4 run-php8.0 run-php8.1 run-php8.2 -run-php7.4: - @# Help: It creates and runs a docker image with PHP 7.4 - docker-compose run --rm php74 bash -c "rm composer.lock || true; composer install --no-interaction; bash" -run-php8.0: - @# Help: It creates and runs a docker image with PHP 8.0 - docker-compose run --rm php80 bash -c "rm composer.lock || true; composer install --no-interaction; bash" +.PHONY: run run-php8.1 run-php8.2 run-php8.3 run-php8.4 run-php8.1: @# Help: It creates and runs a docker image with PHP 8.1 docker-compose run --rm php81 bash -c "rm composer.lock || true; composer install --no-interaction; bash" run-php8.2: @# Help: It creates and runs a docker image with PHP 8.2 docker-compose run --rm php82 bash -c "rm composer.lock || true; composer install --no-interaction; bash" -run: run-php7.4 +run-php8.3: + @# Help: It creates and runs a docker image with PHP 8.3 + docker-compose run --rm php83 bash -c "rm composer.lock || true; composer install --no-interaction; bash" +run-php8.4: + @# Help: It creates and runs a docker image with PHP 8.4 + docker-compose run --rm php84 bash -c "rm composer.lock || true; composer install --no-interaction; bash" +run: run-php8.1 @# Help: It creates and runs a docker image with the lowest supported PHP version -.PHONY: psalm psalm-update-baseline -psalm: - @# Help: It runs Psalm - ./vendor/bin/psalm --no-cache - -psalm-update-baseline: - @# Help: It updates the Psalm baseline - ./vendor/bin/psalm --update-baseline - - .PHONY: phpstan phpstan-update-baseline phpstan: @# Help: It runs PHPStan @@ -45,11 +35,7 @@ phpstan-update-baseline: ./vendor/bin/phpstan analyse src tests --generate-baseline -.PHONY: type-assertions test -type-assertions: - @# Help: It runs tests on Psalm types - ./vendor/bin/psalm tests/type-assertions --no-cache - +.PHONY: test test: @# Help: It runs PHPUnit tests XDEBUG_MODE=coverage ./vendor/bin/phpunit @@ -63,10 +49,10 @@ cs-check: @# Help: It runs the code style check ./vendor/bin/php-cs-fixer fix --ansi --verbose --dry-run -ci: test phpstan psalm type-assertions cs-fix +ci: test phpstan cs-fix @# Help: It runs all tests and code style fix -ci-check: test phpstan psalm type-assertions cs-check +ci-check: test phpstan cs-check @# Help: It runs all tests and code style check .PHONY: rector diff --git a/composer.json b/composer.json index 25ef517..60430d5 100644 --- a/composer.json +++ b/composer.json @@ -3,13 +3,12 @@ "description": "A partial porting of io-ts in PHP", "type": "library", "require-dev": { - "phpunit/phpunit": "^9", - "giorgiosironi/eris": "^0.14.0", - "phpat/phpat": "^0.10.18", - "facile-it/facile-coding-standard": "^1.2", - "vimeo/psalm": "4.30.0", - "phpstan/phpstan": "^1.8", - "rector/rector": "^1.2" + "phpunit/phpunit": "^10", + "giorgiosironi/eris": "^1", + "phpat/phpat": "^0.11", + "facile-it/facile-coding-standard": "^1.3", + "phpstan/phpstan": "^2", + "rector/rector": "^2" }, "license": "MIT", "authors": [ @@ -27,12 +26,11 @@ "psr-4": { "Tests\\Facile\\PhpCodec\\": "tests/unit/", "Examples\\Facile\\PhpCodec\\": "tests/examples/", - "TypeAssertions\\Facile\\PhpCodec\\": "tests/type-assertions/", "ArchitectureAssertions\\Facile\\PhpCodec\\": "tests/architecture/" } }, "require": { - "php": "^7.4 | ^8.0" + "php": "^8.1" }, "prefer-stable": true, "archive": { diff --git a/docker-compose.yml b/docker-compose.yml index 4482d1a..4e33d82 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,32 +1,32 @@ -version: "3.8" - services: - php74: &base + php81: &base build: context: docker/ args: - PHP_IMAGE: php:7.4 - XDEBUG: xdebug-3.1.5 + PHP_IMAGE: php:8.1 volumes: - .:/home/dev/lib tty: true user: dev working_dir: /home/dev/lib - php80: + + php82: <<: *base build: context: docker/ args: - PHP_IMAGE: php:8.0 - php81: + PHP_IMAGE: php:8.2 + + php83: <<: *base build: context: docker/ args: - PHP_IMAGE: php:8.1 - php82: + PHP_IMAGE: php:8.3 + + php84: <<: *base build: context: docker/ args: - PHP_IMAGE: php:8.2 + PHP_IMAGE: php:8.4 diff --git a/docker/Dockerfile b/docker/Dockerfile index 2233983..abd7bd6 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -ARG PHP_IMAGE +ARG PHP_IMAGE=php:8.1 FROM $PHP_IMAGE COPY --from=composer /usr/bin/composer /usr/bin/composer diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index d3515c0..a0bd2d5 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,31 +1,535 @@ parameters: ignoreErrors: - - message: "#^Template type ClassFactory of method Facile\\\\PhpCodec\\\\Decoders\\:\\:classFromArrayPropsDecoder\\(\\) is not referenced in a parameter\\.$#" + message: '#^Method Facile\\PhpCodec\\Decoders\:\:arrayProps\(\) should return Facile\\PhpCodec\\Decoder\\> but returns Facile\\PhpCodec\\Internal\\Combinators\\ArrayPropsDecoder\<\(int\|string\), mixed, mixed, MapOfDecoders of non\-empty\-array\\>\>\.$#' + identifier: return.type count: 1 path: src/Decoders.php - - message: "#^Template type T of method Facile\\\\PhpCodec\\\\Decoders\\:\\:classFromArrayPropsDecoder\\(\\) is not referenced in a parameter\\.$#" + message: '#^Method Facile\\PhpCodec\\Decoders\:\:pipe\(\) never returns Facile\\PhpCodec\\Decoder\ so it can be removed from the return type\.$#' + identifier: return.unusedType count: 1 path: src/Decoders.php - - message: "#^Template type VP of method Facile\\\\PhpCodec\\\\Validation\\\\ListOfValidation\\:\\:reduceToIndexedSuccessOrAllFailures\\(\\) is not referenced in a parameter\\.$#" + message: '#^Method Facile\\PhpCodec\\Decoders\:\:pipe\(\) never returns Facile\\PhpCodec\\Decoder\ so it can be removed from the return type\.$#' + identifier: return.unusedType + count: 1 + path: src/Decoders.php + + - + message: '#^Method Facile\\PhpCodec\\Decoders\:\:pipe\(\) never returns Facile\\PhpCodec\\Decoder\ so it can be removed from the return type\.$#' + identifier: return.unusedType + count: 1 + path: src/Decoders.php + + - + message: '#^Method Facile\\PhpCodec\\Decoders\:\:regex\(\) should return Facile\\PhpCodec\\Decoder\\> but returns Facile\\PhpCodec\\Internal\\Useful\\RegexDecoder\.$#' + identifier: return.type + count: 1 + path: src/Decoders.php + + - + message: '#^PHPDoc tag @param for parameter \$factory with type Facile\\PhpCodec\\ClassFactory is not subtype of native type callable\.$#' + identifier: parameter.phpDocType + count: 1 + path: src/Decoders.php + + - + message: '#^PHPDoc tag @template has invalid value \(ClassFactory of callable\(\.\.\.mixed\)\:T\)\: Unexpected token "\(", expected TOKEN_HORIZONTAL_WS at offset 139 on line 4$#' + identifier: phpDoc.parseError + count: 1 + path: src/Decoders.php + + - + message: '#^PHPDoc tag @var with type Facile\\PhpCodec\\Decoder\, T of object\> is not subtype of native type Facile\\PhpCodec\\Internal\\Combinators\\MapDecoder\\.$#' + identifier: varTag.nativeType + count: 1 + path: src/Decoders.php + + - + message: '#^Parameter \#1 \$db of class Facile\\PhpCodec\\Internal\\Combinators\\ComposeDecoder constructor expects Facile\\PhpCodec\\Decoder\<\(A of IB\)\|IB, B\>, Facile\\PhpCodec\\Decoder\ given\.$#' + identifier: argument.type + count: 1 + path: src/Decoders.php + + - + message: '#^Parameter \#1 \$db of static method Facile\\PhpCodec\\Decoders\:\:compose\(\) expects Facile\\PhpCodec\\Decoder\, Facile\\PhpCodec\\Decoder\\|Facile\\PhpCodec\\Decoder\\|Facile\\PhpCodec\\Decoder\ given\.$#' + identifier: argument.type + count: 1 + path: src/Decoders.php + + - + message: '#^Parameter \#2 \$da of class Facile\\PhpCodec\\Internal\\Combinators\\ComposeDecoder constructor expects Facile\\PhpCodec\\Decoder\, Facile\\PhpCodec\\Decoder\ given\.$#' + identifier: argument.type + count: 1 + path: src/Decoders.php + + - + message: '#^Parameter \$factory of method Facile\\PhpCodec\\Decoders\:\:classFromArrayPropsDecoder\(\) has invalid type Facile\\PhpCodec\\ClassFactory\.$#' + identifier: class.notFound + count: 1 + path: src/Decoders.php + + - + message: '#^Template type T of method Facile\\PhpCodec\\Decoders\:\:classFromArrayPropsDecoder\(\) is not referenced in a parameter\.$#' + identifier: method.templateTypeNotInParameter + count: 1 + path: src/Decoders.php + + - + message: '#^Unable to resolve the template type E in call to method static method Facile\\PhpCodec\\Decoders\:\:pipe\(\)$#' + identifier: argument.templateType + count: 1 + path: src/Decoders.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Arrays\\ListOfDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\\> but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\.$#' + identifier: return.type + count: 1 + path: src/Internal/Arrays/ListOfDecoder.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Combinators\\ArrayPropsDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\\> but returns Facile\\PhpCodec\\Validation\\Validation\\>\>\>\.$#' + identifier: return.type + count: 1 + path: src/Internal/Combinators/ArrayPropsDecoder.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Combinators\\ArrayPropsDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\\> but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\.$#' + identifier: return.type + count: 1 + path: src/Internal/Combinators/ArrayPropsDecoder.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Combinators\\IntersectionDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\ but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\.$#' + identifier: return.type + count: 1 + path: src/Internal/Combinators/IntersectionDecoder.php + + - + message: '#^PHPDoc tag @var with type Facile\\PhpCodec\\Decoder\ is not subtype of native type \$this\(Facile\\PhpCodec\\Internal\\Combinators\\IntersectionDecoder\\)\.$#' + identifier: varTag.nativeType + count: 1 + path: src/Internal/Combinators/IntersectionDecoder.php + + - + message: '#^PHPDoc tag @var with type Facile\\PhpCodec\\Validation\\Validation\ is not subtype of native type Facile\\PhpCodec\\Validation\\ValidationFailures\.$#' + identifier: varTag.nativeType + count: 2 + path: src/Internal/Combinators/IntersectionDecoder.php + + - + message: '#^PHPDoc tag @var with type T1&T2 is not subtype of native type array\.$#' + identifier: varTag.nativeType + count: 1 + path: src/Internal/Combinators/IntersectionDecoder.php + + - + message: '#^PHPDoc tag @var with type T1&T2 is not subtype of native type stdClass\.$#' + identifier: varTag.nativeType + count: 1 + path: src/Internal/Combinators/IntersectionDecoder.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Combinators\\LiteralDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\ but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\.$#' + identifier: return.type + count: 1 + path: src/Internal/Combinators/LiteralDecoder.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Combinators\\LiteralDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\ but returns Facile\\PhpCodec\\Validation\\ValidationSuccess\\.$#' + identifier: return.type + count: 1 + path: src/Internal/Combinators/LiteralDecoder.php + + - + message: '#^Cannot cast mixed to string\.$#' + identifier: cast.string + count: 1 + path: src/Internal/FunctionUtils.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\FunctionUtils\:\:nameFromProps\(\) has parameter \$props with generic interface Facile\\PhpCodec\\Decoder but does not specify its types\: I, A$#' + identifier: missingType.generics + count: 1 + path: src/Internal/FunctionUtils.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Primitives\\BoolDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\ but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\|Facile\\PhpCodec\\Validation\\ValidationSuccess\\.$#' + identifier: return.type + count: 1 + path: src/Internal/Primitives/BoolDecoder.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Primitives\\CallableDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\ but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\|Facile\\PhpCodec\\Validation\\ValidationSuccess\\.$#' + identifier: return.type + count: 1 + path: src/Internal/Primitives/CallableDecoder.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Primitives\\FloatDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\ but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\|Facile\\PhpCodec\\Validation\\ValidationSuccess\\.$#' + identifier: return.type + count: 1 + path: src/Internal/Primitives/FloatDecoder.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Primitives\\IntDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\ but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\|Facile\\PhpCodec\\Validation\\ValidationSuccess\\.$#' + identifier: return.type + count: 1 + path: src/Internal/Primitives/IntDecoder.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Primitives\\NullDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\ but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\|Facile\\PhpCodec\\Validation\\ValidationSuccess\\.$#' + identifier: return.type + count: 1 + path: src/Internal/Primitives/NullDecoder.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Primitives\\StringDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\ but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\|Facile\\PhpCodec\\Validation\\ValidationSuccess\\.$#' + identifier: return.type + count: 1 + path: src/Internal/Primitives/StringDecoder.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Primitives\\UndefinedDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\ but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\|Facile\\PhpCodec\\Validation\\ValidationSuccess\\.$#' + identifier: return.type + count: 1 + path: src/Internal/Primitives/UndefinedDecoder.php + + - + message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: src/Internal/Useful/DateTimeFromStringDecoder.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Useful\\DateTimeFromStringDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\ but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\.$#' + identifier: return.type + count: 2 + path: src/Internal/Useful/DateTimeFromStringDecoder.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Useful\\IntFromStringDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\ but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\|Facile\\PhpCodec\\Validation\\ValidationSuccess\\.$#' + identifier: return.type + count: 1 + path: src/Internal/Useful/IntFromStringDecoder.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Useful\\RegexDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\\> but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\.$#' + identifier: return.type + count: 1 + path: src/Internal/Useful/RegexDecoder.php + + - + message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: src/Internal/Useful/StringMatchingRegexDecoder.php + + - + message: '#^Method Facile\\PhpCodec\\Internal\\Useful\\StringMatchingRegexDecoder\:\:validate\(\) should return Facile\\PhpCodec\\Validation\\Validation\ but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\.$#' + identifier: return.type + count: 2 + path: src/Internal/Useful/StringMatchingRegexDecoder.php + + - + message: '#^Class Facile\\PhpCodec\\Validation\\Context implements generic interface Iterator but does not specify its types\: TKey, TValue$#' + identifier: missingType.generics + count: 1 + path: src/Validation/Context.php + + - + message: '#^Method Facile\\PhpCodec\\Validation\\Context\:\:__construct\(\) has parameter \$decoder with generic interface Facile\\PhpCodec\\Decoder but does not specify its types\: I, A$#' + identifier: missingType.generics + count: 1 + path: src/Validation/Context.php + + - + message: '#^Method Facile\\PhpCodec\\Validation\\ContextEntry\:\:getDecoder\(\) return type with generic interface Facile\\PhpCodec\\Decoder does not specify its types\: I, A$#' + identifier: missingType.generics + count: 1 + path: src/Validation/ContextEntry.php + + - + message: '#^Method Facile\\PhpCodec\\Validation\\ListOfValidation\:\:reduceToIndexedSuccessOrAllFailures\(\) should return VP of Facile\\PhpCodec\\Validation\\Validation\\> but returns Facile\\PhpCodec\\Validation\\ValidationSuccess\\>\.$#' + identifier: return.type + count: 1 + path: src/Validation/ListOfValidation.php + + - + message: '#^Method Facile\\PhpCodec\\Validation\\ListOfValidation\:\:reduceToSuccessOrAllFailures\(\) should return Facile\\PhpCodec\\Validation\\Validation\\> but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\.$#' + identifier: return.type count: 1 path: src/Validation/ListOfValidation.php - - message: "#^Template type T of method Facile\\\\PhpCodec\\\\Validation\\\\Validation\\:\\:failure\\(\\) is not referenced in a parameter\\.$#" + message: '#^Method Facile\\PhpCodec\\Validation\\ListOfValidation\:\:reduceToSuccessOrAllFailures\(\) should return Facile\\PhpCodec\\Validation\\Validation\\> but returns Facile\\PhpCodec\\Validation\\ValidationSuccess\\>\>\.$#' + identifier: return.type + count: 1 + path: src/Validation/ListOfValidation.php + + - + message: '#^Method Facile\\PhpCodec\\Validation\\ListOfValidation\:\:sequence\(\) should return Facile\\PhpCodec\\Validation\\Validation\\> but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\.$#' + identifier: return.type + count: 1 + path: src/Validation/ListOfValidation.php + + - + message: '#^Method Facile\\PhpCodec\\Validation\\ListOfValidation\:\:sequence\(\) should return Facile\\PhpCodec\\Validation\\Validation\\> but returns Facile\\PhpCodec\\Validation\\ValidationSuccess\\>\>\.$#' + identifier: return.type + count: 1 + path: src/Validation/ListOfValidation.php + + - + message: '#^Template type VP of method Facile\\PhpCodec\\Validation\\ListOfValidation\:\:reduceToIndexedSuccessOrAllFailures\(\) is not referenced in a parameter\.$#' + identifier: method.templateTypeNotInParameter + count: 1 + path: src/Validation/ListOfValidation.php + + - + message: '#^Method Facile\\PhpCodec\\Validation\\Validation\:\:failure\(\) should return Facile\\PhpCodec\\Validation\\ValidationFailures\ but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\.$#' + identifier: return.type + count: 1 + path: src/Validation/Validation.php + + - + message: '#^Method Facile\\PhpCodec\\Validation\\Validation\:\:failures\(\) should return Facile\\PhpCodec\\Validation\\ValidationFailures\ but returns Facile\\PhpCodec\\Validation\\ValidationFailures\\.$#' + identifier: return.type + count: 1 + path: src/Validation/Validation.php + + - + message: '#^Template type T of method Facile\\PhpCodec\\Validation\\Validation\:\:failure\(\) is not referenced in a parameter\.$#' + identifier: method.templateTypeNotInParameter count: 1 path: src/Validation/Validation.php - - message: "#^Template type T of method Facile\\\\PhpCodec\\\\Validation\\\\Validation\\:\\:failures\\(\\) is not referenced in a parameter\\.$#" + message: '#^Template type T of method Facile\\PhpCodec\\Validation\\Validation\:\:failures\(\) is not referenced in a parameter\.$#' + identifier: method.templateTypeNotInParameter count: 1 path: src/Validation/Validation.php - - message: "#^Template type R of method Tests\\\\Facile\\\\PhpCodec\\\\BaseTestCase\\:\\:asserSuccessSameTo\\(\\) is not referenced in a parameter\\.$#" + message: '#^Method Examples\\Facile\\PhpCodec\\DecodeApiResponse\\OpenWeatherResponse\:\:__construct\(\) has parameter \$weather with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/examples/DecodeApiResponse/OpenWeatherResponse.php + + - + message: '#^Method Examples\\Facile\\PhpCodec\\DecodeApiResponse\\OpenWeatherResponse\:\:getWeather\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/examples/DecodeApiResponse/OpenWeatherResponse.php + + - + message: '#^Call to an undefined method object\:\:getId\(\)\.$#' + identifier: method.notFound + count: 3 + path: tests/examples/ParseCsv/ParseCsvTest.php + + - + message: '#^Call to an undefined method object\:\:getItalianLandRegistryCode\(\)\.$#' + identifier: method.notFound + count: 3 + path: tests/examples/ParseCsv/ParseCsvTest.php + + - + message: '#^Call to an undefined method object\:\:getName\(\)\.$#' + identifier: method.notFound + count: 3 + path: tests/examples/ParseCsv/ParseCsvTest.php + + - + message: '#^Parameter \#2 \$b of static method Facile\\PhpCodec\\Decoders\:\:pipe\(\) expects Facile\\PhpCodec\\Decoder\, Facile\\PhpCodec\\Decoder\\> given\.$#' + identifier: argument.type + count: 1 + path: tests/examples/ParseCsv/ParseCsvTest.php + + - + message: '#^Template type R of method Tests\\Facile\\PhpCodec\\BaseTestCase\:\:asserSuccessSameTo\(\) is not referenced in a parameter\.$#' + identifier: method.templateTypeNotInParameter count: 1 path: tests/unit/BaseTestCase.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\GeneratorUtils\:\:scalar\(\) return type with generic interface Eris\\Generator does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: tests/unit/GeneratorUtils.php + + - + message: '#^Parameter \#1 \$f of static method Facile\\PhpCodec\\Internal\\FunctionUtils\:\:destructureIn\(\) expects callable\(mixed \.\.\.\)\: void, Closure\(int, string\)\: void given\.$#' + identifier: argument.type + count: 1 + path: tests/unit/Internal/Useful/StringMatchingRegexDecoderTest.php + + - + message: '#^Constructor of class Tests\\Facile\\PhpCodec\\Reporters\\Models\\A has an unused parameter \$a\.$#' + identifier: constructor.unusedParameter + count: 1 + path: tests/unit/Reporters/Models/A.php + + - + message: '#^Constructor of class Tests\\Facile\\PhpCodec\\Reporters\\Models\\A has an unused parameter \$b\.$#' + identifier: constructor.unusedParameter + count: 1 + path: tests/unit/Reporters/Models/A.php + + - + message: '#^Constructor of class Tests\\Facile\\PhpCodec\\Reporters\\Models\\A has an unused parameter \$c\.$#' + identifier: constructor.unusedParameter + count: 1 + path: tests/unit/Reporters/Models/A.php + + - + message: '#^Constructor of class Tests\\Facile\\PhpCodec\\Reporters\\Models\\SampleClass has an unused parameter \$amount\.$#' + identifier: constructor.unusedParameter + count: 1 + path: tests/unit/Reporters/Models/SampleClass.php + + - + message: '#^Constructor of class Tests\\Facile\\PhpCodec\\Reporters\\Models\\SampleClass has an unused parameter \$flag\.$#' + identifier: constructor.unusedParameter + count: 1 + path: tests/unit/Reporters/Models/SampleClass.php + + - + message: '#^Constructor of class Tests\\Facile\\PhpCodec\\Reporters\\Models\\SampleClass has an unused parameter \$name\.$#' + identifier: constructor.unusedParameter + count: 1 + path: tests/unit/Reporters/Models/SampleClass.php + + - + message: '#^Constructor of class Tests\\Facile\\PhpCodec\\Reporters\\Models\\SampleClass has an unused parameter \$number\.$#' + identifier: constructor.unusedParameter + count: 1 + path: tests/unit/Reporters/Models/SampleClass.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:assertReports\(\) has parameter \$expected with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:assertReports\(\) has parameter \$reporter with generic interface Facile\\PhpCodec\\Reporter but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:assertReports\(\) has parameter \$validation with generic class Facile\\PhpCodec\\Validation\\Validation but does not specify its types\: A$#' + identifier: missingType.generics + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:provideIntersectionReport\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:provideListOfClassReport\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:provideNestedArrayPropsReport\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:provideReportRootClassError\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:provideReportRootErrors\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:provideUnionReport\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:testIntersectionReport\(\) has parameter \$expected with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:testIntersectionReport\(\) has parameter \$reporter with generic interface Facile\\PhpCodec\\Reporter but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:testListOfClassReport\(\) has parameter \$expected with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:testListOfClassReport\(\) has parameter \$reporter with generic interface Facile\\PhpCodec\\Reporter but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:testNestedArrayPropsReport\(\) has parameter \$expected with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:testNestedArrayPropsReport\(\) has parameter \$reporter with generic interface Facile\\PhpCodec\\Reporter but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:testReportRootClassError\(\) has parameter \$expected with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:testReportRootClassError\(\) has parameter \$reporter with generic interface Facile\\PhpCodec\\Reporter but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:testReportRootError\(\) has parameter \$expected with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:testReportRootError\(\) has parameter \$reporter with generic interface Facile\\PhpCodec\\Reporter but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:testUnionReport\(\) has parameter \$expected with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/unit/Reporters/ReportersTest.php + + - + message: '#^Method Tests\\Facile\\PhpCodec\\Reporters\\ReportersTest\:\:testUnionReport\(\) has parameter \$reporter with generic interface Facile\\PhpCodec\\Reporter but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: tests/unit/Reporters/ReportersTest.php diff --git a/phpstan.neon b/phpstan.neon index edf254a..eedc7cf 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,7 +3,9 @@ includes: - phpat.neon - phpstan-baseline.neon parameters: - level: 0 + level: 10 paths: - src - - tests + - tests/examples + - tests/unit + reportUnmatchedIgnoredErrors: false \ No newline at end of file diff --git a/psalm-baseline.xml b/psalm-baseline.xml deleted file mode 100644 index 5605487..0000000 --- a/psalm-baseline.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - Generators::oneOf(Generators::int(), Generators::float(), Generators::bool()) - Generators::oneOf(Generators::string(), Generators::float(), Generators::bool()) - - - diff --git a/psalm.xml b/psalm.xml deleted file mode 100644 index 1fe9e97..0000000 --- a/psalm.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - diff --git a/rector.php b/rector.php index ec0d046..e4e11a3 100644 --- a/rector.php +++ b/rector.php @@ -15,6 +15,6 @@ // register a single rule $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class); $rectorConfig->sets([ - LevelSetList::UP_TO_PHP_74, + LevelSetList::UP_TO_PHP_81, ]); }; diff --git a/src/Decoder.php b/src/Decoder.php index 9b9dfa5..3513c0b 100644 --- a/src/Decoder.php +++ b/src/Decoder.php @@ -8,29 +8,24 @@ use Facile\PhpCodec\Validation\Validation; /** - * @psalm-template I - * @psalm-template A + * @template I + * @template A */ interface Decoder { /** - * @psalm-param I $i - * @psalm-param Context $context + * @param I $i * - * @psalm-return Validation - * - * @param mixed $i + * @return Validation */ - public function validate($i, Context $context): Validation; + public function validate(mixed $i, Context $context): Validation; /** - * @psalm-param I $i - * - * @psalm-return Validation + * @param I $i * - * @param mixed $i + * @return Validation */ - public function decode($i): Validation; + public function decode(mixed $i): Validation; public function getName(): string; } diff --git a/src/Decoders.php b/src/Decoders.php index d0d41ca..67916b3 100644 --- a/src/Decoders.php +++ b/src/Decoders.php @@ -26,7 +26,6 @@ use Facile\PhpCodec\Utils\ConcreteDecoder; use Facile\PhpCodec\Validation\Context; use Facile\PhpCodec\Validation\Validation; -use Psalm\Internal\Codebase\Properties; final class Decoders { @@ -36,10 +35,9 @@ private function __construct() {} * @template I * @template A * - * @psalm-param callable(I, Context):Validation $f - * @psalm-param string $name + * @param callable(I, Context):Validation $f * - * @psalm-return Decoder + * @return Decoder */ public static function make(callable $f, string $name = 'anon'): Decoder { @@ -47,49 +45,20 @@ public static function make(callable $f, string $name = 'anon'): Decoder } /** - * @template IA - * @template IB - * @template A of IB + * @template I + * @template A * @template B + * @template C + * @template D + * @template E * - * @psalm-param Decoder $db - * @psalm-param Decoder $da + * @param Decoder $a + * @param Decoder $b + * @param Decoder | null $c + * @param Decoder | null $d + * @param Decoder | null $e * - * @psalm-return Decoder - */ - public static function compose(Decoder $db, Decoder $da): Decoder - { - // TODO Fix this - /** @psalm-var Decoder */ - /** @psalm-suppress InvalidArgument */ - return new ComposeDecoder($db, $da); - } - - /** - * @psalm-template IA of mixed - * @psalm-template IB of mixed - * @psalm-template IC of mixed - * @psalm-template ID of mixed - * @psalm-template IE of mixed - * @psalm-template A - * @psalm-template B - * @psalm-template C - * @psalm-template D - * @psalm-template E - * - * // TODO provide better types for input - * - * @psalm-param Decoder $a - * @psalm-param Decoder $b - * @psalm-param Decoder | null $c - * @psalm-param Decoder | null $d - * @psalm-param Decoder | null $e - * - * @psalm-return (func_num_args() is 2 ? Decoder - * : (func_num_args() is 3 ? Decoder - * : (func_num_args() is 4 ? Decoder - * : (func_num_args() is 5 ? Decoder : Decoder) - * ))) + * @return ($c is null ? Decoder : ($d is null ? Decoder : ($e is null ? Decoder : Decoder))) */ public static function pipe( Decoder $a, @@ -99,36 +68,43 @@ public static function pipe( ?Decoder $e = null ): Decoder { // Order is important: composition is not commutative - // TODO fix this - /** @psalm-suppress InvalidArgument */ + /** @phpstan-ignore return.type */ return $c instanceof Decoder ? self::compose(self::pipe($b, $c, $d, $e), $a) : self::compose($b, $a); } /** - * @psalm-template IA - * @psalm-template A - * @psalm-template IB - * @psalm-template B - * @psalm-template IC - * @psalm-template C - * @psalm-template ID - * @psalm-template D - * @psalm-template IE - * @psalm-template E + * @template IA + * @template IB + * @template A of IB + * @template B * - * @psalm-param Decoder $a - * @psalm-param Decoder $b - * @psalm-param Decoder | null $c - * @psalm-param Decoder | null $d - * @psalm-param Decoder | null $e + * @psalm-param Decoder $db + * @psalm-param Decoder $da + * + * @return Decoder + */ + public static function compose(Decoder $db, Decoder $da): Decoder + { + return new ComposeDecoder($db, $da); + } + + /** + * @template I input type + * @template A + * @template B + * @template C + * @template D + * @template E + * + * @psalm-param Decoder $a + * @psalm-param Decoder $b + * @psalm-param Decoder | null $c + * @psalm-param Decoder | null $d + * @psalm-param Decoder | null $e * - * @psalm-return (func_num_args() is 2 ? Decoder - * : (func_num_args() is 3 ? Decoder - * : (func_num_args() is 4 ? Decoder - * : (func_num_args() is 5 ? Decoder : Decoder) - * ))) + * @return ($c is null ? Decoder : ($d is null ? Decoder : ($e is null ? Decoder : Decoder))) */ public static function union(Decoder $a, Decoder $b, ?Decoder $c = null, ?Decoder $d = null, ?Decoder $e = null): Decoder { @@ -152,24 +128,23 @@ public static function union(Decoder $a, Decoder $b, ?Decoder $c = null, ?Decode ); } + /** @phpstan-ignore return.type */ return $res; } /** - * @psalm-template IA - * @psalm-template IB - * @psalm-template A - * @psalm-template B + * @template IA + * @template IB + * @template A + * @template B * * @psalm-param Decoder $a * @psalm-param Decoder $b * - * @psalm-return Decoder + * @return Decoder */ public static function intersection(Decoder $a, Decoder $b): Decoder { - // Intersection seems to mess up implements annotation - /** @var Decoder */ return new IntersectionDecoder($a, $b); } @@ -187,7 +162,7 @@ public static function intersection(Decoder $a, Decoder $b): Decoder * @psalm-param callable(A):B $f * @psalm-param Decoder $da * - * @psalm-return Decoder + * @return Decoder */ public static function transformValidationSuccess(callable $f, Decoder $da): Decoder { @@ -198,22 +173,20 @@ public static function transformValidationSuccess(callable $f, Decoder $da): Dec } /** - * @psalm-template T of bool | string | int - * - * @psalm-param T $l + * @template T of bool | string | int * - * @psalm-return Decoder + * @param T $l * - * @param mixed $l + * @return Decoder */ - public static function literal($l): Decoder + public static function literal(bool|string|int $l): Decoder { return new LiteralDecoder($l); } /** - * @psalm-template I - * @psalm-template T + * @template I + * @template T * * @param Decoder $elementDecoder * @@ -225,19 +198,11 @@ public static function listOf(Decoder $elementDecoder): Decoder } /** - * @psalm-template MapOfDecoders of non-empty-array> + * @template MapOfDecoders of non-empty-array> * - * @psalm-param MapOfDecoders $props + * @param MapOfDecoders $props * - * @psalm-return Decoder> - * - * @param Decoder[] $props - * - * @return Decoder - * - * Waiting for this feature to provide a better typing. I need something like mapped types from Typescript. - * - * @see https://github.com/vimeo/psalm/issues/3589 + * @return Decoder> */ public static function arrayProps(array $props): Decoder { @@ -245,15 +210,14 @@ public static function arrayProps(array $props): Decoder } /** - * @psalm-template T of object - * @psalm-template Properties of non-empty-array - * @psalm-template ClassFactory of callable(...mixed):T + * @template T of object + * @template Properties of non-empty-array + * @template ClassFactory of callable(...mixed):T * - * @psalm-param Decoder $propsDecoder - * @psalm-param ClassFactory $factory - * @psalm-param string $decoderName + * @param Decoder $propsDecoder + * @param ClassFactory $factory * - * @psalm-return Decoder + * @return Decoder */ public static function classFromArrayPropsDecoder( Decoder $propsDecoder, @@ -279,13 +243,11 @@ public static function classFromArrayPropsDecoder( # ########################################################### /** - * @psalm-template U - * - * @psalm-param U $default + * @template U * - * @psalm-return Decoder + * @param U $default * - * @param null|mixed $default + * @return Decoder */ public static function undefined($default = null): Decoder { @@ -301,7 +263,7 @@ public static function null(): Decoder } /** - * @psalm-return Decoder + * @return Decoder */ public static function bool(): Decoder { @@ -309,7 +271,7 @@ public static function bool(): Decoder } /** - * @psalm-return Decoder + * @return Decoder */ public static function int(): Decoder { @@ -317,7 +279,7 @@ public static function int(): Decoder } /** - * @psalm-return Decoder + * @return Decoder */ public static function float(): Decoder { @@ -325,7 +287,7 @@ public static function float(): Decoder } /** - * @psalm-return Decoder + * @return Decoder */ public static function string(): Decoder { @@ -333,7 +295,7 @@ public static function string(): Decoder } /** - * @psalm-return Decoder + * @return Decoder */ public static function mixed(): Decoder { @@ -341,7 +303,7 @@ public static function mixed(): Decoder } /** - * @psalm-return Decoder + * @return Decoder */ public static function callable(): Decoder { @@ -355,7 +317,7 @@ public static function callable(): Decoder # ########################################################### /** - * @psalm-return Decoder + * @return Decoder */ public static function intFromString(): Decoder { @@ -363,7 +325,7 @@ public static function intFromString(): Decoder } /** - * @psalm-return Decoder + * @return Decoder */ public static function dateTimeFromString(string $format = \DATE_ATOM): Decoder { @@ -371,7 +333,7 @@ public static function dateTimeFromString(string $format = \DATE_ATOM): Decoder } /** - * @psalm-return Decoder> + * @return Decoder> */ public static function regex(string $regex): Decoder { @@ -379,9 +341,7 @@ public static function regex(string $regex): Decoder } /** - * @psalm-param string $regex - * - * @psalm-return Decoder + * @return Decoder */ public static function stringMatchingRegex(string $regex): Decoder { diff --git a/src/Internal/Arrays/ListOfDecoder.php b/src/Internal/Arrays/ListOfDecoder.php index 94e31b6..7900a34 100644 --- a/src/Internal/Arrays/ListOfDecoder.php +++ b/src/Internal/Arrays/ListOfDecoder.php @@ -22,16 +22,13 @@ */ final class ListOfDecoder implements Decoder { - /** @var Decoder */ - private \Facile\PhpCodec\Decoder $elementDecoder; - /** * @psalm-param Decoder $elementDecoder */ - public function __construct(Decoder $elementDecoder) - { - $this->elementDecoder = $elementDecoder; - } + public function __construct( + /** @var Decoder */ + private readonly \Facile\PhpCodec\Decoder $elementDecoder + ) {} public function validate($i, Context $context): Validation { diff --git a/src/Internal/Combinators/ArrayPropsDecoder.php b/src/Internal/Combinators/ArrayPropsDecoder.php index 015c776..d7cce51 100644 --- a/src/Internal/Combinators/ArrayPropsDecoder.php +++ b/src/Internal/Combinators/ArrayPropsDecoder.php @@ -24,16 +24,13 @@ */ final class ArrayPropsDecoder implements Decoder { - /** @var PD */ - private array $props; - /** * @psalm-param PD $props */ - public function __construct(array $props) - { - $this->props = $props; - } + public function __construct( + /** @var PD */ + private readonly array $props + ) {} public function validate($i, Context $context): Validation { diff --git a/src/Internal/Combinators/ComposeDecoder.php b/src/Internal/Combinators/ComposeDecoder.php index 06cfbde..34d8cd4 100644 --- a/src/Internal/Combinators/ComposeDecoder.php +++ b/src/Internal/Combinators/ComposeDecoder.php @@ -20,22 +20,16 @@ */ final class ComposeDecoder implements Decoder { - /** @var Decoder */ - private \Facile\PhpCodec\Decoder $db; - /** @var Decoder */ - private \Facile\PhpCodec\Decoder $da; - /** * @psalm-param Decoder $db * @psalm-param Decoder $da */ public function __construct( - Decoder $db, - Decoder $da - ) { - $this->db = $db; - $this->da = $da; - } + /** @var Decoder */ + private readonly \Facile\PhpCodec\Decoder $db, + /** @var Decoder */ + private readonly \Facile\PhpCodec\Decoder $da + ) {} /** * @psalm-param IA $i diff --git a/src/Internal/Combinators/IntersectionDecoder.php b/src/Internal/Combinators/IntersectionDecoder.php index 53cfe01..a189698 100644 --- a/src/Internal/Combinators/IntersectionDecoder.php +++ b/src/Internal/Combinators/IntersectionDecoder.php @@ -24,20 +24,16 @@ */ final class IntersectionDecoder implements Decoder { - /** @var Decoder */ - private \Facile\PhpCodec\Decoder $a; - /** @var Decoder */ - private \Facile\PhpCodec\Decoder $b; - /** * @psalm-param Decoder $a * @psalm-param Decoder $b */ - public function __construct(Decoder $a, Decoder $b) - { - $this->a = $a; - $this->b = $b; - } + public function __construct( + /** @var Decoder */ + private readonly \Facile\PhpCodec\Decoder $a, + /** @var Decoder */ + private readonly \Facile\PhpCodec\Decoder $b + ) {} public function validate($i, Context $context): Validation { diff --git a/src/Internal/Combinators/LiteralDecoder.php b/src/Internal/Combinators/LiteralDecoder.php index 0fe5d8b..f0e2996 100644 --- a/src/Internal/Combinators/LiteralDecoder.php +++ b/src/Internal/Combinators/LiteralDecoder.php @@ -10,10 +10,8 @@ use Facile\PhpCodec\Validation\Validation; /** - * @psalm-type literable = bool | string | int - * * @template I of mixed - * @template T of literable + * @template T of bool | string | int * * @template-implements Decoder * @@ -22,21 +20,9 @@ final class LiteralDecoder implements Decoder { /** - * @var T - * - * @readonly - */ - private $literal; - - /** - * @psalm-param T $literal - * - * @param mixed $literal + * @param T $literal */ - public function __construct($literal) - { - $this->literal = $literal; - } + public function __construct(private readonly string|bool|int $literal) {} public function validate($i, Context $context): Validation { @@ -57,12 +43,7 @@ public function getName(): string return self::literalName($this->literal); } - /** - * @psalm-param literable $x - * - * @param mixed $x - */ - private static function literalName($x): string + private static function literalName(string|bool|int $x): string { if (\is_string($x)) { return "'{$x}'"; diff --git a/src/Internal/Combinators/MapDecoder.php b/src/Internal/Combinators/MapDecoder.php index 33689c6..6bc7fe8 100644 --- a/src/Internal/Combinators/MapDecoder.php +++ b/src/Internal/Combinators/MapDecoder.php @@ -21,15 +21,13 @@ final class MapDecoder implements Decoder { /** @var callable(A):B */ private $f; - private string $name; /** * @psalm-param callable(A):B $f */ - public function __construct(callable $f, string $name = 'map') + public function __construct(callable $f, private readonly string $name = 'map') { $this->f = $f; - $this->name = $name; } public function validate($i, Context $context): Validation diff --git a/src/Internal/Combinators/UnionDecoder.php b/src/Internal/Combinators/UnionDecoder.php index 05d8f9a..f362432 100644 --- a/src/Internal/Combinators/UnionDecoder.php +++ b/src/Internal/Combinators/UnionDecoder.php @@ -12,36 +12,23 @@ use Facile\PhpCodec\Validation\ValidationFailures; /** - * @psalm-template IA - * @psalm-template IB - * @psalm-template A - * @psalm-template B + * @template I + * @template A + * @template B * - * @template-implements Decoder - * - * @psalm-internal Facile\PhpCodec + * @implements Decoder */ final class UnionDecoder implements Decoder { - /** @var Decoder */ - private \Facile\PhpCodec\Decoder $a; - /** @var Decoder */ - private \Facile\PhpCodec\Decoder $b; - private int $indexBegin; - /** - * @psalm-param Decoder $a - * @psalm-param Decoder $b + * @param Decoder $a + * @param Decoder $b */ public function __construct( - Decoder $a, - Decoder $b, - int $indexBegin = 0 - ) { - $this->a = $a; - $this->b = $b; - $this->indexBegin = $indexBegin; - } + private readonly \Facile\PhpCodec\Decoder $a, + private readonly \Facile\PhpCodec\Decoder $b, + private readonly int $indexBegin = 0 + ) {} public function validate($i, Context $context): Validation { @@ -71,24 +58,24 @@ public function validate($i, Context $context): Validation ); if ($vb instanceof ValidationFailures) { - return Validation::failures( + /** @var Validation $validationFailures */ + $validationFailures = Validation::failures( [...$va->getErrors(), ...$vb->getErrors()] ); + + return $validationFailures; } + /** @var Validation $vb */ return $vb; } + /** @var Validation $va */ return $va; } public function decode($i): Validation { - /** - * @psalm-var IA&IB $i - * @psalm-var Decoder $this - */ - return FunctionUtils::standardDecode($this, $i); } diff --git a/src/Internal/Primitives/UndefinedDecoder.php b/src/Internal/Primitives/UndefinedDecoder.php index 3c9dd6b..18fc787 100644 --- a/src/Internal/Primitives/UndefinedDecoder.php +++ b/src/Internal/Primitives/UndefinedDecoder.php @@ -19,18 +19,12 @@ */ final class UndefinedDecoder implements Decoder { - /** @var U */ - private $default; - /** * @psalm-param U $default * * @param mixed $default */ - public function __construct($default) - { - $this->default = $default; - } + public function __construct(private $default) {} public function validate($i, Context $context): Validation { diff --git a/src/Internal/Undefined.php b/src/Internal/Undefined.php index cf10c3e..4314a31 100644 --- a/src/Internal/Undefined.php +++ b/src/Internal/Undefined.php @@ -4,7 +4,7 @@ namespace Facile\PhpCodec\Internal; -final class Undefined +final class Undefined implements \Stringable { public function __toString(): string { diff --git a/src/Internal/Useful/DateTimeFromStringDecoder.php b/src/Internal/Useful/DateTimeFromStringDecoder.php index 525a108..5ac55aa 100644 --- a/src/Internal/Useful/DateTimeFromStringDecoder.php +++ b/src/Internal/Useful/DateTimeFromStringDecoder.php @@ -16,19 +16,15 @@ */ final class DateTimeFromStringDecoder implements Decoder { - /** - * @psalm-readonly - */ - private string $format; - - public function __construct(string $format = \DATE_ATOM) - { - $this->format = $format; - } + public function __construct( + /** + * @psalm-readonly + */ + private readonly string $format = \DATE_ATOM + ) {} public function validate($i, Context $context): Validation { - /** @psalm-suppress DocblockTypeContradiction */ if (! \is_string($i)) { return Validation::failure($i, $context); } diff --git a/src/Internal/Useful/RegexDecoder.php b/src/Internal/Useful/RegexDecoder.php index 1649f4c..e59c452 100644 --- a/src/Internal/Useful/RegexDecoder.php +++ b/src/Internal/Useful/RegexDecoder.php @@ -16,12 +16,7 @@ */ final class RegexDecoder implements Decoder { - private string $regex; - - public function __construct(string $regex) - { - $this->regex = $regex; - } + public function __construct(private readonly string $regex) {} public function validate($i, Context $context): Validation { diff --git a/src/Internal/Useful/StringMatchingRegexDecoder.php b/src/Internal/Useful/StringMatchingRegexDecoder.php index 733bc34..6d23b15 100644 --- a/src/Internal/Useful/StringMatchingRegexDecoder.php +++ b/src/Internal/Useful/StringMatchingRegexDecoder.php @@ -16,16 +16,10 @@ */ final class StringMatchingRegexDecoder implements Decoder { - private string $regex; - - public function __construct(string $regex) - { - $this->regex = $regex; - } + public function __construct(private readonly string $regex) {} public function validate($i, Context $context): Validation { - /** @psalm-suppress DocblockTypeContradiction */ if (! \is_string($i)) { return Validation::failure($i, $context); } diff --git a/src/Reporter.php b/src/Reporter.php index 2d02a7b..1e36f58 100644 --- a/src/Reporter.php +++ b/src/Reporter.php @@ -7,14 +7,14 @@ use Facile\PhpCodec\Validation\Validation; /** - * @psalm-template T + * @template T */ interface Reporter { /** - * @psalm-template A + * @template A * - * @psalm-param Validation $validation + * @param Validation $validation * * @return T */ diff --git a/src/Reporters/PathReporter.php b/src/Reporters/PathReporter.php index 77d2059..7d332b3 100644 --- a/src/Reporters/PathReporter.php +++ b/src/Reporters/PathReporter.php @@ -21,13 +21,13 @@ public static function create(): self } /** - * @psalm-return list + * @return list */ public function report(Validation $validation): array { return Validation::fold( fn(array $errors): array => \array_map( - [self::class, 'getMessage'], + self::getMessage(...), $errors ), fn(): array => ['No errors!'], diff --git a/src/Reporters/SimplePathReporter.php b/src/Reporters/SimplePathReporter.php index aa1dcf8..f4bd78a 100644 --- a/src/Reporters/SimplePathReporter.php +++ b/src/Reporters/SimplePathReporter.php @@ -23,7 +23,7 @@ public function report(Validation $validation): array { return Validation::fold( static fn(array $errors): array => array_map( - [self::class, 'getMessage'], + self::getMessage(...), $errors ), static fn(): array => ['No errors'], diff --git a/src/Utils/ConcreteDecoder.php b/src/Utils/ConcreteDecoder.php index d33a6e6..d218541 100644 --- a/src/Utils/ConcreteDecoder.php +++ b/src/Utils/ConcreteDecoder.php @@ -19,17 +19,15 @@ final class ConcreteDecoder implements Decoder { /** @var callable(I, Context):Validation */ private $validateFunc; - private string $name; /** * @psalm-param callable(I, Context):Validation $validate */ public function __construct( callable $validate, - string $name + private readonly string $name ) { $this->validateFunc = $validate; - $this->name = $name; } public function validate($i, Context $context): Validation diff --git a/src/Validation/Context.php b/src/Validation/Context.php index 6ccdb9b..caf071d 100644 --- a/src/Validation/Context.php +++ b/src/Validation/Context.php @@ -11,18 +11,16 @@ final class Context implements \Iterator /** @var ContextEntry[] */ private array $entries; private int $currentIndex = 0; - private \Facile\PhpCodec\Decoder $decoder; /** * @psalm-param Decoder $decoder * @psalm-param ContextEntry ...$entries */ public function __construct( - Decoder $decoder, + private readonly \Facile\PhpCodec\Decoder $decoder, ContextEntry ...$entries ) { $this->entries = $entries; - $this->decoder = $decoder; } public function appendEntries(ContextEntry ...$entries): self diff --git a/src/Validation/ContextEntry.php b/src/Validation/ContextEntry.php index de8296c..c8f5476 100644 --- a/src/Validation/ContextEntry.php +++ b/src/Validation/ContextEntry.php @@ -8,11 +8,6 @@ final class ContextEntry { - private string $key; - private \Facile\PhpCodec\Decoder $decoder; - /** @var mixed */ - private $actual; - /** * @psalm-param string $key * @psalm-param Decoder $decoder @@ -20,15 +15,7 @@ final class ContextEntry * * @param mixed $actual */ - public function __construct( - string $key, - Decoder $decoder, - $actual - ) { - $this->key = $key; - $this->decoder = $decoder; - $this->actual = $actual; - } + public function __construct(private readonly string $key, private readonly \Facile\PhpCodec\Decoder $decoder, private $actual) {} public function getKey(): string { diff --git a/src/Validation/VError.php b/src/Validation/VError.php index d2660cd..a0e864a 100644 --- a/src/Validation/VError.php +++ b/src/Validation/VError.php @@ -6,11 +6,6 @@ final class VError { - /** @var mixed */ - private $value; - private \Facile\PhpCodec\Validation\Context $context; - private ?string $message = null; - /** * @psalm-param mixed $value * @psalm-param Context $context @@ -18,15 +13,7 @@ final class VError * * @param mixed $value */ - public function __construct( - $value, - Context $context, - ?string $message = null - ) { - $this->value = $value; - $this->context = $context; - $this->message = $message; - } + public function __construct(private $value, private readonly \Facile\PhpCodec\Validation\Context $context, private readonly ?string $message = null) {} /** * @return mixed diff --git a/src/Validation/Validation.php b/src/Validation/Validation.php index 965cf44..e4a3cb2 100644 --- a/src/Validation/Validation.php +++ b/src/Validation/Validation.php @@ -10,13 +10,11 @@ abstract class Validation { /** - * @psalm-template T + * @template T * - * @psalm-param T $a + * @param T $a * - * @psalm-return ValidationSuccess - * - * @param mixed $a + * @return ValidationSuccess */ public static function success($a): self { @@ -24,13 +22,11 @@ public static function success($a): self } /** - * @psalm-template T - * - * @psalm-param list $errors + * @template T * - * @psalm-return ValidationFailures + * @param list $errors * - * @param VError[] $errors + * @return ValidationFailures */ public static function failures(array $errors): self { @@ -38,15 +34,11 @@ public static function failures(array $errors): self } /** - * @psalm-template T - * - * @psalm-param mixed $value - * @psalm-param Context $context - * @psalm-param string|null $message - * - * @psalm-return ValidationFailures + * @template T * * @param mixed $value + * + * @return ValidationFailures */ public static function failure($value, Context $context, ?string $message = null): self { @@ -56,14 +48,14 @@ public static function failure($value, Context $context, ?string $message = null } /** - * @psalm-template T - * @psalm-template R + * @template T + * @template R * - * @psalm-param callable(list):R $onFailures - * @psalm-param callable(T):R $onSuccess - * @psalm-param Validation $v + * @param callable(list):R $onFailures + * @param callable(T):R $onSuccess + * @param Validation $v * - * @psalm-return R + * @return R */ public static function fold(callable $onFailures, callable $onSuccess, self $v) { @@ -79,11 +71,11 @@ public static function fold(callable $onFailures, callable $onSuccess, self $v) } /** - * @psalm-template T + * @template T * - * @psalm-param Validation $v + * @param Validation $v * - * @psalm-assert-if-true ValidationSuccess $v + * @phpstan-assert-if-true ValidationSuccess $v */ private static function isSuccess(self $v): bool { @@ -91,11 +83,11 @@ private static function isSuccess(self $v): bool } /** - * @psalm-template T + * @template T * - * @psalm-param Validation $v + * @param Validation $v * - * @psalm-assert-if-true ValidationFailures $v + * @phpstan-assert-if-true ValidationFailures $v */ private static function isFailures(self $v): bool { @@ -103,11 +95,11 @@ private static function isFailures(self $v): bool } /** - * @psalm-template T + * @template T * - * @psalm-param list> $validations + * @param list> $validations * - * @psalm-return Validation> + * @return Validation> * * @deprecated use sequence in ListOfValidation * @see ListOfValidation::sequence() @@ -118,11 +110,11 @@ public static function sequence(array $validations): self } /** - * @psalm-template T + * @template T * - * @psalm-param list> $validations + * @param list> $validations * - * @psalm-return Validation> + * @return Validation> * * @deprecated use ListOfValidation instead * @see ListOfValidation::reduceToSuccessOrAllFailures() @@ -133,13 +125,13 @@ public static function reduceToSuccessOrAllFailures(array $validations): self } /** - * @psalm-template T1 - * @psalm-template T2 + * @template T1 + * @template T2 * - * @psalm-param callable(T1):T2 $f - * @psalm-param Validation $v + * @param callable(T1):T2 $f + * @param Validation $v * - * @psalm-return Validation + * @return Validation */ public static function map(callable $f, self $v): self { @@ -152,13 +144,13 @@ public static function map(callable $f, self $v): self } /** - * @psalm-template T1 - * @psalm-template T2 + * @template T1 + * @template T2 * - * @psalm-param callable(T1):Validation $f - * @psalm-param Validation $v + * @param callable(T1):Validation $f + * @param Validation $v * - * @psalm-return Validation + * @return Validation */ public static function bind(callable $f, self $v): self { diff --git a/src/Validation/ValidationFailures.php b/src/Validation/ValidationFailures.php index a28ae54..f910be7 100644 --- a/src/Validation/ValidationFailures.php +++ b/src/Validation/ValidationFailures.php @@ -5,24 +5,18 @@ namespace Facile\PhpCodec\Validation; /** - * @psalm-template A + * @template A * * @extends Validation */ final class ValidationFailures extends Validation { - /** @var list */ - private array $errors; - /** * @psalm-param list $errors * * @param VError[] $errors */ - public function __construct(array $errors) - { - $this->errors = $errors; - } + public function __construct(private readonly array $errors) {} /** * @psalm-return list diff --git a/src/Validation/ValidationSuccess.php b/src/Validation/ValidationSuccess.php index 8b2d1ac..0533ccd 100644 --- a/src/Validation/ValidationSuccess.php +++ b/src/Validation/ValidationSuccess.php @@ -5,24 +5,16 @@ namespace Facile\PhpCodec\Validation; /** - * @psalm-template A + * @template A * * @extends Validation */ final class ValidationSuccess extends Validation { - /** @var A */ - private $value; - /** - * @psalm-param A $a - * - * @param mixed $a + * @param A $value */ - public function __construct($a) - { - $this->value = $a; - } + public function __construct(private readonly mixed $value) {} /** * @psalm-return A diff --git a/tests/architecture/ArchitectureTest.php b/tests/architecture/ArchitectureTest.php index f26f40a..9daa307 100644 --- a/tests/architecture/ArchitectureTest.php +++ b/tests/architecture/ArchitectureTest.php @@ -16,12 +16,12 @@ class ArchitectureTest public function testAnyInternalClassShouldNotDependFromAnythingOutsideExceptDefinitionInterfaces(): Rule { return PHPat::rule() - ->classes(Selector::namespace('Facile\PhpCodec\Internal')) + ->classes(Selector::inNamespace('Facile\PhpCodec\Internal')) ->shouldNotDependOn() - ->classes(Selector::namespace('Facile\PhpCodec')) + ->classes(Selector::inNamespace('Facile\PhpCodec')) ->excluding( - Selector::namespace('Facile\PhpCodec\Internal'), - Selector::namespace('Facile\PhpCodec\Validation'), + Selector::inNamespace('Facile\PhpCodec\Internal'), + Selector::inNamespace('Facile\PhpCodec\Validation'), Selector::classname(Decoder::class), ); } @@ -29,7 +29,7 @@ public function testAnyInternalClassShouldNotDependFromAnythingOutsideExceptDefi public function testEndpointClasses(): Rule { return PHPat::rule() - ->classes(Selector::namespace('Facile\PhpCodec\*')) + ->classes(Selector::inNamespace('Facile\PhpCodec\*')) ->shouldNotDependOn() ->classes( Selector::classname(Decoders::class), diff --git a/tests/examples/DecodeApiResponse/Coordinates.php b/tests/examples/DecodeApiResponse/Coordinates.php index eff6c6e..6be6f91 100644 --- a/tests/examples/DecodeApiResponse/Coordinates.php +++ b/tests/examples/DecodeApiResponse/Coordinates.php @@ -9,14 +9,7 @@ */ class Coordinates { - private float $longitude; - private float $latitude; - - public function __construct(float $longitude, float $latitude) - { - $this->longitude = $longitude; - $this->latitude = $latitude; - } + public function __construct(private readonly float $longitude, private readonly float $latitude) {} public function getLatitude(): float { diff --git a/tests/examples/DecodeApiResponse/DecodeApiResponseTest.php b/tests/examples/DecodeApiResponse/DecodeApiResponseTest.php index 0f9ec5f..f2dc1ed 100644 --- a/tests/examples/DecodeApiResponse/DecodeApiResponseTest.php +++ b/tests/examples/DecodeApiResponse/DecodeApiResponseTest.php @@ -7,7 +7,6 @@ use Facile\PhpCodec\Decoders; use Tests\Facile\PhpCodec\BaseTestCase; -/** @psalm-suppress PropertyNotSetInConstructor */ class DecodeApiResponseTest extends BaseTestCase { public function testJsonDecoding(): void diff --git a/tests/examples/DecodeApiResponse/OpenWeatherResponse.php b/tests/examples/DecodeApiResponse/OpenWeatherResponse.php index ac48f6c..dcd7268 100644 --- a/tests/examples/DecodeApiResponse/OpenWeatherResponse.php +++ b/tests/examples/DecodeApiResponse/OpenWeatherResponse.php @@ -9,19 +9,7 @@ */ class OpenWeatherResponse { - private \Examples\Facile\PhpCodec\DecodeApiResponse\Coordinates $coordinates; - private array $weather; - private \Examples\Facile\PhpCodec\DecodeApiResponse\Sys $sys; - - public function __construct( - \Examples\Facile\PhpCodec\DecodeApiResponse\Coordinates $coordinates, - array $weathers, - Sys $sys - ) { - $this->coordinates = $coordinates; - $this->weather = $weathers; - $this->sys = $sys; - } + public function __construct(private readonly \Examples\Facile\PhpCodec\DecodeApiResponse\Coordinates $coordinates, private readonly array $weather, private readonly \Examples\Facile\PhpCodec\DecodeApiResponse\Sys $sys) {} public function getCoordinates(): \Examples\Facile\PhpCodec\DecodeApiResponse\Coordinates { diff --git a/tests/examples/DecodeApiResponse/Sys.php b/tests/examples/DecodeApiResponse/Sys.php index 4c83ba6..acff8a2 100644 --- a/tests/examples/DecodeApiResponse/Sys.php +++ b/tests/examples/DecodeApiResponse/Sys.php @@ -9,16 +9,7 @@ */ class Sys { - private string $country; - private \DateTimeInterface $sunrise; - private \DateTimeInterface $sunset; - - public function __construct(string $country, \DateTimeInterface $sunrise, \DateTimeInterface $sunset) - { - $this->country = $country; - $this->sunrise = $sunrise; - $this->sunset = $sunset; - } + public function __construct(private readonly string $country, private readonly \DateTimeInterface $sunrise, private readonly \DateTimeInterface $sunset) {} public function getCountry(): string { diff --git a/tests/examples/DecodeApiResponse/Weather.php b/tests/examples/DecodeApiResponse/Weather.php index 42b902e..fb950a5 100644 --- a/tests/examples/DecodeApiResponse/Weather.php +++ b/tests/examples/DecodeApiResponse/Weather.php @@ -9,16 +9,7 @@ */ class Weather { - private int $id; - private string $main; - private string $description; - - public function __construct(int $id, string $main, string $description) - { - $this->id = $id; - $this->main = $main; - $this->description = $description; - } + public function __construct(private readonly int $id, private readonly string $main, private readonly string $description) {} public function getId(): int { diff --git a/tests/examples/DecodePartialPropertiesTest.php b/tests/examples/DecodePartialPropertiesTest.php index d63886c..3dfbc59 100644 --- a/tests/examples/DecodePartialPropertiesTest.php +++ b/tests/examples/DecodePartialPropertiesTest.php @@ -7,7 +7,6 @@ use Facile\PhpCodec\Decoders; use Tests\Facile\PhpCodec\BaseTestCase; -/** @psalm-suppress PropertyNotSetInConstructor */ class DecodePartialPropertiesTest extends BaseTestCase { public function test(): void @@ -35,16 +34,7 @@ public function test(): void class A { - private string $foo; - private int $bar; - - public function __construct( - string $foo, - int $bar - ) { - $this->foo = $foo; - $this->bar = $bar; - } + public function __construct(private readonly string $foo, private readonly int $bar) {} public function getFoo(): string { diff --git a/tests/examples/DecoderForSumType/A.php b/tests/examples/DecoderForSumType/A.php index 36570fd..31f1717 100644 --- a/tests/examples/DecoderForSumType/A.php +++ b/tests/examples/DecoderForSumType/A.php @@ -12,16 +12,7 @@ final class A extends P public const SUB_foo = 'foo'; public const SUB_bar = 'bar'; - private string $subType; - private int $propertyA; - private string $propertyB; - - public function __construct(string $subType, int $propertyA, string $propertyB) - { - $this->subType = $subType; - $this->propertyA = $propertyA; - $this->propertyB = $propertyB; - } + public function __construct(private readonly string $subType, private readonly int $propertyA, private readonly string $propertyB) {} public function getType(): string { diff --git a/tests/examples/DecoderForSumType/B.php b/tests/examples/DecoderForSumType/B.php index 38c9025..6429015 100644 --- a/tests/examples/DecoderForSumType/B.php +++ b/tests/examples/DecoderForSumType/B.php @@ -13,16 +13,7 @@ final class B extends P public const CASE_B2 = 2; public const CASE_B3 = 3; - private int $case; - private float $amount; - private bool $flag; - - public function __construct(int $case, float $amount, bool $flag) - { - $this->case = $case; - $this->amount = $amount; - $this->flag = $flag; - } + public function __construct(private readonly int $case, private readonly float $amount, private readonly bool $flag) {} public function getType(): string { diff --git a/tests/examples/DecoderForSumType/DecoderForSumType.php b/tests/examples/DecoderForSumType/DecoderForSumType.php index e7b3c9c..14a944c 100644 --- a/tests/examples/DecoderForSumType/DecoderForSumType.php +++ b/tests/examples/DecoderForSumType/DecoderForSumType.php @@ -9,7 +9,6 @@ use Facile\PhpCodec\Decoders; use Tests\Facile\PhpCodec\BaseTestCase; -/** @psalm-suppress PropertyNotSetInConstructor */ class DecoderForSumType extends BaseTestCase { use TestTrait; @@ -46,7 +45,6 @@ public function testSumTypes(): void ) ); - /** @psalm-suppress UndefinedFunction */ $this ->forAll( Generators::associative([ @@ -66,7 +64,6 @@ public function testSumTypes(): void self::assertSame($i['propB'], $a->getPropertyB()); }); - /** @psalm-suppress UndefinedFunction */ $this ->forAll( Generators::associative([ diff --git a/tests/examples/ParseCsv/City.php b/tests/examples/ParseCsv/City.php index c5a90e9..0417b32 100644 --- a/tests/examples/ParseCsv/City.php +++ b/tests/examples/ParseCsv/City.php @@ -6,19 +6,7 @@ class City { - private int $id; - private string $name; - private string $italianLandRegistryCode; - - public function __construct( - int $id, - string $name, - string $italianLandRegistryCode - ) { - $this->id = $id; - $this->name = $name; - $this->italianLandRegistryCode = $italianLandRegistryCode; - } + public function __construct(private readonly int $id, private readonly string $name, private readonly string $italianLandRegistryCode) {} public function getId(): int { diff --git a/tests/examples/ParseCsv/ParseCsvTest.php b/tests/examples/ParseCsv/ParseCsvTest.php index 407b83b..8f2546b 100644 --- a/tests/examples/ParseCsv/ParseCsvTest.php +++ b/tests/examples/ParseCsv/ParseCsvTest.php @@ -7,7 +7,6 @@ use Facile\PhpCodec\Decoders; use Tests\Facile\PhpCodec\BaseTestCase; -/** @psalm-suppress PropertyNotSetInConstructor */ class ParseCsvTest extends BaseTestCase { public function test(): void diff --git a/tests/type-assertions/Internal/Combinators/ArrayPropsDecoderTest.php b/tests/type-assertions/Internal/Combinators/ArrayPropsDecoderTest.php deleted file mode 100644 index 90cd32e..0000000 --- a/tests/type-assertions/Internal/Combinators/ArrayPropsDecoderTest.php +++ /dev/null @@ -1,36 +0,0 @@ - Decoders::string(), - 'b' => Decoders::int(), - ]); - - /** @var mixed $i */ - $i = null; - - $v = $d->decode($i); - - /** - * @psalm-type K = 'a' | 'b' - * @psalm-type V = string | int - * - * @psalm-param Validation> $v - */ - $assert1 = function (Validation $v): void {}; - - $assert1($v); - } -} diff --git a/tests/type-assertions/TypeAssertion.php b/tests/type-assertions/TypeAssertion.php deleted file mode 100644 index ce17415..0000000 --- a/tests/type-assertions/TypeAssertion.php +++ /dev/null @@ -1,32 +0,0 @@ - new DecodersTest\A($v), - Decoders::int() - ); - - /** @psalm-suppress UndefinedFunction */ - $this - ->forAll( - Generators::int() - ) - ->then(function (int $i) use ($decoder): void { - $a = self::assertSuccessInstanceOf( - DecodersTest\A::class, - $decoder->decode($i) - ); - self::assertSame($i, $a->getValue()); - }); - } -} - -namespace Tests\Facile\PhpCodec\DecodersTest; - -class A -{ - private int $v; - - public function __construct(int $v) - { - $this->v = $v; - } - - public function getValue(): int - { - return $this->v; - } -} diff --git a/tests/unit/DecodersTest/A.php b/tests/unit/DecodersTest/A.php new file mode 100644 index 0000000..fce351b --- /dev/null +++ b/tests/unit/DecodersTest/A.php @@ -0,0 +1,15 @@ +v; + } +} diff --git a/tests/unit/DecodersTest/DecodersTest.php b/tests/unit/DecodersTest/DecodersTest.php new file mode 100644 index 0000000..7f12109 --- /dev/null +++ b/tests/unit/DecodersTest/DecodersTest.php @@ -0,0 +1,83 @@ + new A($v), + Decoders::int() + ); + + $this + ->forAll( + Generators::int() + ) + ->then(function (int $i) use ($decoder): void { + $a = self::assertSuccessInstanceOf( + A::class, + $decoder->decode($i) + ); + self::assertSame($i, $a->getValue()); + }); + } + + public function testPipe(): void + { + $d = Decoders::pipe( + Decoders::string(), + Decoders::stringMatchingRegex('/^\d+$/'), + ); + TypeAssertions::decoderMixedString($d); + $validation = $d->decode('3'); + self::assertInstanceOf(ValidationSuccess::class, $validation); + self::assertSame('3', $validation->getValue()); + + $d = Decoders::pipe( + Decoders::string(), + Decoders::stringMatchingRegex('/^\d+$/'), + Decoders::intFromString(), + ); + TypeAssertions::decoderMixedInt($d); + $validation = $d->decode('3'); + self::assertInstanceOf(ValidationSuccess::class, $validation); + self::assertSame(3, $validation->getValue()); + + $d = Decoders::pipe( + Decoders::string(), + Decoders::stringMatchingRegex('/^\d+$/'), + Decoders::intFromString(), + Decoders::make(fn(int $i) => new ValidationSuccess((float) $i)), + ); + TypeAssertions::decoderMixedFloat($d); + $validation = $d->decode('3'); + self::assertInstanceOf(ValidationSuccess::class, $validation); + self::assertSame(3.0, $validation->getValue()); + + $d = Decoders::pipe( + Decoders::string(), + Decoders::stringMatchingRegex('/^\d+$/'), + Decoders::intFromString(), + Decoders::make(fn(int $i) => new ValidationSuccess((float) $i)), + Decoders::make(fn(float $i) => Validation::success($i > 0)) + ); + TypeAssertions::decoderMixedBool($d); + $validation = $d->decode('3'); + self::assertInstanceOf(ValidationSuccess::class, $validation); + self::assertTrue($validation->getValue()); + } +} diff --git a/tests/unit/GeneratorUtils.php b/tests/unit/GeneratorUtils.php index 60b4910..d50dd74 100644 --- a/tests/unit/GeneratorUtils.php +++ b/tests/unit/GeneratorUtils.php @@ -9,15 +9,8 @@ final class GeneratorUtils { - /** - * @psalm-suppress MixedInferredReturnType - */ public static function scalar(): g { - /** - * @psalm-suppress UndefinedFunction - * @psalm-suppress MixedReturnStatement - */ return Generators::oneOf( Generators::int(), Generators::float(), diff --git a/tests/unit/Internal/Combinators/ArrayPropsDecoderTest.php b/tests/unit/Internal/Combinators/ArrayPropsDecoderTest.php index 3d11ecb..a3c8635 100644 --- a/tests/unit/Internal/Combinators/ArrayPropsDecoderTest.php +++ b/tests/unit/Internal/Combinators/ArrayPropsDecoderTest.php @@ -10,7 +10,6 @@ use Facile\PhpCodec\Reporters\PathReporter; use Tests\Facile\PhpCodec\BaseTestCase; -/** @psalm-suppress PropertyNotSetInConstructor */ class ArrayPropsDecoderTest extends BaseTestCase { use TestTrait; @@ -24,7 +23,6 @@ public function testProps(): void 'd' => Decoders::literal('hello'), ]); - /** @psalm-suppress UndefinedFunction */ $this ->forAll( Generators::associative([ diff --git a/tests/unit/Internal/Combinators/IntersectionDecoderTest.php b/tests/unit/Internal/Combinators/IntersectionDecoderTest.php index 3aaeb70..28ac11b 100644 --- a/tests/unit/Internal/Combinators/IntersectionDecoderTest.php +++ b/tests/unit/Internal/Combinators/IntersectionDecoderTest.php @@ -10,7 +10,6 @@ use Facile\PhpCodec\Validation\ValidationFailures; use Tests\Facile\PhpCodec\BaseTestCase; -/** @psalm-suppress PropertyNotSetInConstructor */ class IntersectionDecoderTest extends BaseTestCase { use TestTrait; @@ -48,7 +47,6 @@ public function testIntersectionOfProps(): void Decoders::arrayProps(['b' => Decoders::int()]) ); - /** @psalm-suppress UndefinedFunction */ $this ->forAll( Generators::associative([ @@ -64,7 +62,6 @@ public function testIntersectionOfProps(): void self::assertIsInt($a['b']); }); - /** @psalm-suppress UndefinedFunction */ $this ->forAll( Generators::oneOf( @@ -102,7 +99,6 @@ public function testIntersectionOfUnionOfProps(): void ) ); - /** @psalm-suppress UndefinedFunction */ $this ->forAll( Generators::oneOf( diff --git a/tests/unit/Internal/Combinators/UnionDecoderTest.php b/tests/unit/Internal/Combinators/UnionDecoderTest.php index fd2d029..5e53a0c 100644 --- a/tests/unit/Internal/Combinators/UnionDecoderTest.php +++ b/tests/unit/Internal/Combinators/UnionDecoderTest.php @@ -8,7 +8,6 @@ use Facile\PhpCodec\Reporters\PathReporter; use PHPUnit\Framework\TestCase; -/** @psalm-suppress PropertyNotSetInConstructor */ class UnionDecoderTest extends TestCase { public function testValidate(): void diff --git a/tests/unit/Internal/Primitives/UndefinedDecoderTest.php b/tests/unit/Internal/Primitives/UndefinedDecoderTest.php index e541180..84fe690 100644 --- a/tests/unit/Internal/Primitives/UndefinedDecoderTest.php +++ b/tests/unit/Internal/Primitives/UndefinedDecoderTest.php @@ -10,17 +10,12 @@ use Tests\Facile\PhpCodec\BaseTestCase; use Tests\Facile\PhpCodec\GeneratorUtils; -/** - * @psalm-suppress PropertyNotSetInConstructor - * @psalm-suppress UndefinedFunction - */ class UndefinedDecoderTest extends BaseTestCase { use TestTrait; public function testDefault(): void { - /** @psalm-suppress DeprecatedMethod */ $this ->forAll(GeneratorUtils::scalar()) ->then( diff --git a/tests/unit/Internal/Useful/DateTimeFromStringDecoderTest.php b/tests/unit/Internal/Useful/DateTimeFromStringDecoderTest.php index 42c3251..ea6350f 100644 --- a/tests/unit/Internal/Useful/DateTimeFromStringDecoderTest.php +++ b/tests/unit/Internal/Useful/DateTimeFromStringDecoderTest.php @@ -9,7 +9,6 @@ use Facile\PhpCodec\Decoders; use Tests\Facile\PhpCodec\BaseTestCase; -/** @psalm-suppress PropertyNotSetInConstructor */ class DateTimeFromStringDecoderTest extends BaseTestCase { use TestTrait; @@ -22,7 +21,6 @@ public function test(): void $decoder->decode('2021-03-12T06:22:48+01:00') ); - /** @psalm-suppress UndefinedFunction */ $this ->forAll( Generators::date() @@ -34,7 +32,6 @@ public function test(): void ); }); - /** @psalm-suppress UndefinedFunction */ $this ->limitTo(1_000) ->forAll( @@ -55,7 +52,6 @@ public function test(): void ]) ) ->then(function (\DateTimeInterface $date, string $format): void { - /** @psalm-suppress InternalClass */ $decoder = Decoders::dateTimeFromString($format); self::assertSuccessInstanceOf( diff --git a/tests/unit/Internal/Useful/StringMatchingRegexDecoderTest.php b/tests/unit/Internal/Useful/StringMatchingRegexDecoderTest.php index ef8e6b1..3cd5e2d 100644 --- a/tests/unit/Internal/Useful/StringMatchingRegexDecoderTest.php +++ b/tests/unit/Internal/Useful/StringMatchingRegexDecoderTest.php @@ -11,7 +11,6 @@ use Facile\PhpCodec\Validation\ValidationFailures; use Tests\Facile\PhpCodec\BaseTestCase; -/** @psalm-suppress PropertyNotSetInConstructor */ class StringMatchingRegexDecoderTest extends BaseTestCase { use TestTrait; diff --git a/tests/unit/Reporters/ReportersTest.php b/tests/unit/Reporters/ReportersTest.php index 286b955..826aac0 100644 --- a/tests/unit/Reporters/ReportersTest.php +++ b/tests/unit/Reporters/ReportersTest.php @@ -14,7 +14,6 @@ use Tests\Facile\PhpCodec\BaseTestCase; use Tests\Facile\PhpCodec\Reporters\Models\SampleClass; -/** @psalm-suppress PropertyNotSetInConstructor */ class ReportersTest extends BaseTestCase { use TestTrait; @@ -149,7 +148,6 @@ public function testReportClass(): void $pathReporter = Reporters::path(); $simplePathReporter = Reporters::simplePath(); - /** @psalm-suppress TooManyArguments */ $this ->forAll( Generators::associative([ diff --git a/tests/unit/TypeAssertions.php b/tests/unit/TypeAssertions.php new file mode 100644 index 0000000..71a008d --- /dev/null +++ b/tests/unit/TypeAssertions.php @@ -0,0 +1,30 @@ + $d + */ + public static function decoderMixedString(Decoder $d): void {} + + /** + * @param Decoder $d + */ + public static function decoderMixedInt(Decoder $d): void {} + + /** + * @param Decoder $d + */ + public static function decoderMixedFloat(Decoder $d): void {} + + /** + * @param Decoder $d + */ + public static function decoderMixedBool(Decoder $d): void {} +}