From 92ebcdef814120b14dc34f7c83b7fe5516416df5 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Sat, 12 Oct 2024 14:30:52 +0200 Subject: [PATCH] feat: add support for PHP 8.4 --- .github/workflows/tests.yml | 9 +++++++++ composer.json | 2 +- composer.lock | 12 ++++++------ .../ReflectionFunctionDefinitionRepository.php | 7 +++++-- .../ReflectionFunctionDefinitionRepositoryTest.php | 8 +++++++- .../TypeResolver/ReflectionTypeResolverTest.php | 8 ++++---- 6 files changed, 32 insertions(+), 14 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bda45f1b..500da7f1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,6 +15,7 @@ jobs: - "8.1" - "8.2" - "8.3" + - "8.4" env: php-extensions: ds,yaml @@ -33,6 +34,14 @@ jobs: - uses: "ramsey/composer-install@v2" with: dependency-versions: ${{ matrix.dependencies }} + composer-options: "--ignore-platform-reqs" # @todo remove when Psalm supports PHP 8.4 + + # @todo Removing Psalm until it is ready for PHP 8.4 + # @see https://github.com/vimeo/psalm/pull/10928 + - uses: php-actions/composer@v6 + with: + command: remove + args: --dev vimeo/psalm --no-interaction - name: Running unit tests run: php vendor/bin/phpunit --testsuite=unit diff --git a/composer.json b/composer.json index e9a9226b..3113cb2f 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", "composer-runtime-api": "^2.0", "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" }, diff --git a/composer.lock b/composer.lock index 5fee4489..474705b0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d8ad2085ad373dd8ded5f55b062aab34", + "content-hash": "410dad193a0cb42759cf51b835de7831", "packages": [ { "name": "psr/simple-cache", @@ -1185,12 +1185,12 @@ "version": "v5.2.13", "source": { "type": "git", - "url": "https://github.com/justinrainbow/json-schema.git", + "url": "https://github.com/jsonrainbow/json-schema.git", "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/fbbe7e5d79f618997bc3332a6f49246036c45793", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/fbbe7e5d79f618997bc3332a6f49246036c45793", "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793", "shasum": "" }, @@ -5484,13 +5484,13 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", "composer-runtime-api": "^2.0" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/src/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepository.php b/src/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepository.php index 59c61519..7daab277 100644 --- a/src/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepository.php +++ b/src/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepository.php @@ -19,6 +19,7 @@ use function array_map; use function str_ends_with; +use function str_starts_with; /** @internal */ final class ReflectionFunctionDefinitionRepository implements FunctionDefinitionRepository @@ -57,7 +58,8 @@ public function for(callable $function): FunctionDefinition $class = $reflection->getClosureScopeClass(); $returnType = $returnTypeResolver->resolveReturnTypeFor($reflection); $nativeReturnType = $returnTypeResolver->resolveNativeReturnTypeFor($reflection); - $isClosure = $name === '{closure}' || str_ends_with($name, '\\{closure}'); + // PHP8.2 use `ReflectionFunction::isAnonymous()` + $isClosure = $name === '{closure}' || str_ends_with($name, '\\{closure}') || str_starts_with($name, '{closure:'); if ($returnType instanceof UnresolvableType) { $returnType = $returnType->forFunctionReturnType($signature); @@ -83,7 +85,8 @@ public function for(callable $function): FunctionDefinition */ private function signature(ReflectionFunction $reflection): string { - if (str_contains($reflection->name, '{closure}')) { + // PHP8.2 use `ReflectionFunction::isAnonymous()` + if ($reflection->name === '{closure}' || str_ends_with($reflection->name, '\\{closure}') || str_starts_with($reflection->name, '{closure:')) { $startLine = $reflection->getStartLine(); $endLine = $reflection->getEndLine(); diff --git a/tests/Functional/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepositoryTest.php b/tests/Functional/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepositoryTest.php index 95b6a465..1248f93c 100644 --- a/tests/Functional/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepositoryTest.php +++ b/tests/Functional/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepositoryTest.php @@ -12,6 +12,8 @@ use CuyZ\Valinor\Type\Types\UnresolvableType; use PHPUnit\Framework\TestCase; +use function var_dump; + final class ReflectionFunctionDefinitionRepositoryTest extends TestCase { private ReflectionFunctionDefinitionRepository $repository; @@ -39,7 +41,11 @@ public function test_function_data_can_be_retrieved(): void $function = $this->repository->for($callback); $parameters = $function->parameters; - self::assertSame(__NAMESPACE__ . '\{closure}', $function->name); + if (PHP_VERSION_ID < 8_04_00) { + self::assertSame(__NAMESPACE__ . '\{closure}', $function->name); + } else { + self::assertSame('{closure:' . self::class . '::' . __FUNCTION__ . '():37}', $function->name); + } self::assertInstanceOf(NativeStringType::class, $function->returnType); self::assertTrue($parameters->has('foo')); diff --git a/tests/Functional/Definition/Repository/Reflection/TypeResolver/ReflectionTypeResolverTest.php b/tests/Functional/Definition/Repository/Reflection/TypeResolver/ReflectionTypeResolverTest.php index 76fad582..19687a64 100644 --- a/tests/Functional/Definition/Repository/Reflection/TypeResolver/ReflectionTypeResolverTest.php +++ b/tests/Functional/Definition/Repository/Reflection/TypeResolver/ReflectionTypeResolverTest.php @@ -98,7 +98,7 @@ public static function native_type_is_resolved_properly_data_provider(): iterabl } // PHP8.2 move to data provider - #[RequiresPhp('8.2')] + #[RequiresPhp('>=8.2')] public function test_disjunctive_normal_form_type_is_resolved_properly(): void { $reflectionType = (new ReflectionProperty(ObjectWithPropertyWithNativeDisjunctiveNormalFormType::class, 'someProperty'))->getType(); @@ -109,7 +109,7 @@ public function test_disjunctive_normal_form_type_is_resolved_properly(): void } // PHP8.2 move to data provider - #[RequiresPhp('8.2')] + #[RequiresPhp('>=8.2')] public function test_native_null_type_is_resolved_properly(): void { $reflectionType = (new ReflectionProperty(ObjectWithPropertyWithNativePhp82StandaloneTypes::class, 'nativeNull'))->getType(); @@ -120,7 +120,7 @@ public function test_native_null_type_is_resolved_properly(): void } // PHP8.2 move to data provider - #[RequiresPhp('8.2')] + #[RequiresPhp('>=8.2')] public function test_native_true_type_is_resolved_properly(): void { $reflectionType = (new ReflectionProperty(ObjectWithPropertyWithNativePhp82StandaloneTypes::class, 'nativeTrue'))->getType(); @@ -131,7 +131,7 @@ public function test_native_true_type_is_resolved_properly(): void } // PHP8.2 move to data provider - #[RequiresPhp('8.2')] + #[RequiresPhp('>=8.2')] public function test_native_false_type_is_resolved_properly(): void { $reflectionType = (new ReflectionProperty(ObjectWithPropertyWithNativePhp82StandaloneTypes::class, 'nativeFalse'))->getType();