From 8959fc30deceb5143996bb890c7974dfd64f3746 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Fri, 9 Feb 2024 11:17:26 +0300 Subject: [PATCH 01/12] Fix psalm --- .github/workflows/static.yml | 12 +++++++++++- composer.json | 2 +- psalm.xml | 1 + psalm80.xml | 25 +++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 psalm80.xml diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 96b2679..d2a03af 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -10,6 +10,8 @@ on: - 'phpunit.xml.dist' push: + branches: + - master paths-ignore: - 'docs/**' - 'README.md' @@ -28,4 +30,12 @@ jobs: os: >- ['ubuntu-latest'] php: >- - ['8.0', '8.1'] + ['8.1', '8.2', '8.3'] + psalm80: + uses: yiisoft/actions/.github/workflows/psalm.yml@master + with: + psalm-config: psalm80.xml + os: >- + ['ubuntu-latest'] + php: >- + ['8.0'] diff --git a/composer.json b/composer.json index c6c0ef3..7626b5e 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "rector/rector": "^1.0.0", "roave/infection-static-analysis-plugin": "^1.18", "spatie/phpunit-watcher": "^1.23", - "vimeo/psalm": "^4.30|^5.6", + "vimeo/psalm": "^4.30|^5.21", "yiisoft/test-support": "^1.4" }, "autoload": { diff --git a/psalm.xml b/psalm.xml index 796ae44..1e5dd14 100644 --- a/psalm.xml +++ b/psalm.xml @@ -15,6 +15,7 @@ + diff --git a/psalm80.xml b/psalm80.xml new file mode 100644 index 0000000..796ae44 --- /dev/null +++ b/psalm80.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + From be970a5e366c5bee644fb9e0c4eff1b3ee3556e8 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 21:15:12 +0300 Subject: [PATCH 02/12] fix test --- tests/Unit/Helpers/DefinitionResolverTest.php | 7 ++++--- tests/Unit/Helpers/DefinitionValidatorTest.php | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/Unit/Helpers/DefinitionResolverTest.php b/tests/Unit/Helpers/DefinitionResolverTest.php index 3b07274..8a535c8 100644 --- a/tests/Unit/Helpers/DefinitionResolverTest.php +++ b/tests/Unit/Helpers/DefinitionResolverTest.php @@ -43,9 +43,10 @@ public function testEnsureResolvableScalar(): void public function testEnsureResolvableDefinition(): void { $this->expectException(InvalidConfigException::class); - $this->expectExceptionMessage( - 'Only references are allowed in constructor arguments, a definition object was provided: ' . - ValueDefinition::class + $this->expectExceptionMessageMatches( + '/^Only references are allowed in constructor arguments, a definition object was provided: \\\\' . + preg_quote(ValueDefinition::class) . + '.*/' ); DefinitionResolver::ensureResolvable(new ValueDefinition(7)); } diff --git a/tests/Unit/Helpers/DefinitionValidatorTest.php b/tests/Unit/Helpers/DefinitionValidatorTest.php index 72c665f..d35b01f 100644 --- a/tests/Unit/Helpers/DefinitionValidatorTest.php +++ b/tests/Unit/Helpers/DefinitionValidatorTest.php @@ -309,9 +309,10 @@ public function testInteger(): void public function testDefinitionInArguments(): void { $this->expectException(InvalidConfigException::class); - $this->expectExceptionMessage( - 'Only references are allowed in constructor arguments, a definition object was provided: ' . - ValueDefinition::class + $this->expectExceptionMessageMatches( + '/^Only references are allowed in constructor arguments, a definition object was provided: \\\\' . + preg_quote(ValueDefinition::class) . + '.*/' ); DefinitionValidator::validate([ 'class' => GearBox::class, From 7fbcd872b29e6aa03788c59fea4b318778cd286f Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 21:16:50 +0300 Subject: [PATCH 03/12] fix --- tests/Unit/Helpers/DefinitionResolverTest.php | 2 +- tests/Unit/Helpers/DefinitionValidatorTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Unit/Helpers/DefinitionResolverTest.php b/tests/Unit/Helpers/DefinitionResolverTest.php index 8a535c8..c787543 100644 --- a/tests/Unit/Helpers/DefinitionResolverTest.php +++ b/tests/Unit/Helpers/DefinitionResolverTest.php @@ -44,7 +44,7 @@ public function testEnsureResolvableDefinition(): void { $this->expectException(InvalidConfigException::class); $this->expectExceptionMessageMatches( - '/^Only references are allowed in constructor arguments, a definition object was provided: \\\\' . + '/^Only references are allowed in constructor arguments, a definition object was provided: (\\\\|)' . preg_quote(ValueDefinition::class) . '.*/' ); diff --git a/tests/Unit/Helpers/DefinitionValidatorTest.php b/tests/Unit/Helpers/DefinitionValidatorTest.php index d35b01f..592f9f3 100644 --- a/tests/Unit/Helpers/DefinitionValidatorTest.php +++ b/tests/Unit/Helpers/DefinitionValidatorTest.php @@ -310,7 +310,7 @@ public function testDefinitionInArguments(): void { $this->expectException(InvalidConfigException::class); $this->expectExceptionMessageMatches( - '/^Only references are allowed in constructor arguments, a definition object was provided: \\\\' . + '/^Only references are allowed in constructor arguments, a definition object was provided: (\\\\|)' . preg_quote(ValueDefinition::class) . '.*/' ); From 861996a27c2d1545b6e80262b7c7c23e7774146a Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 22:05:09 +0300 Subject: [PATCH 04/12] Fix crush when intersection types used --- .github/workflows/build.yml | 4 +- .../workflows/composer-require-checker.yml | 4 +- .github/workflows/mutation.yml | 3 + .github/workflows/rector.yml | 4 +- CHANGELOG.md | 2 +- src/DefinitionStorage.php | 35 ++-- src/ParameterDefinition.php | 181 ++++++++---------- tests/Php8_1/DefinitionStorageTest.php | 21 ++ tests/Php8_1/ParameterDefinitionTest.php | 60 ++++++ tests/Support/MagicCall.php | 20 ++ ...nionTypeWithIntersectionTypeDependency.php | 13 ++ .../Unit/Helpers/DefinitionValidatorTest.php | 2 + 12 files changed, 230 insertions(+), 119 deletions(-) create mode 100644 tests/Php8_1/DefinitionStorageTest.php create mode 100644 tests/Php8_1/ParameterDefinitionTest.php create mode 100644 tests/Support/MagicCall.php create mode 100644 tests/Support/UnionTypeWithIntersectionTypeDependency.php diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8506ea1..0f73a90 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,6 +10,8 @@ on: - 'psalm.xml' push: + branches: + - master paths-ignore: - 'docs/**' - 'README.md' @@ -28,4 +30,4 @@ jobs: os: >- ['ubuntu-latest', 'windows-latest'] php: >- - ['8.0', '8.1'] + ['8.0', '8.1', '8.2', '8.3'] diff --git a/.github/workflows/composer-require-checker.yml b/.github/workflows/composer-require-checker.yml index 6cb4099..4336665 100644 --- a/.github/workflows/composer-require-checker.yml +++ b/.github/workflows/composer-require-checker.yml @@ -11,6 +11,8 @@ on: - 'psalm.xml' push: + branches: + - master paths-ignore: - 'docs/**' - 'README.md' @@ -30,4 +32,4 @@ jobs: os: >- ['ubuntu-latest'] php: >- - ['8.0'] + ['8.0', '8.1', '8.2', '8.3'] diff --git a/.github/workflows/mutation.yml b/.github/workflows/mutation.yml index c1aca98..43d6f50 100644 --- a/.github/workflows/mutation.yml +++ b/.github/workflows/mutation.yml @@ -9,6 +9,8 @@ on: - 'psalm.xml' push: + branches: + - master paths-ignore: - 'docs/**' - 'README.md' @@ -23,6 +25,7 @@ jobs: mutation: uses: yiisoft/actions/.github/workflows/roave-infection.yml@master with: + min-covered-msi: 100 os: >- ['ubuntu-latest'] php: >- diff --git a/.github/workflows/rector.yml b/.github/workflows/rector.yml index adacd73..35411d0 100644 --- a/.github/workflows/rector.yml +++ b/.github/workflows/rector.yml @@ -14,8 +14,10 @@ name: rector jobs: rector: uses: yiisoft/actions/.github/workflows/rector.yml@master + secrets: + token: ${{ secrets.YIISOFT_GITHUB_TOKEN }} with: os: >- ['ubuntu-latest'] php: >- - ['8.0'] + ['8.3'] diff --git a/CHANGELOG.md b/CHANGELOG.md index a550227..cdef2a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 3.2.1 under development -- no changes in this release. +- Bug #86: Fix crush when intersection types used (@vjik) ## 3.2.0 February 12, 2023 diff --git a/src/DefinitionStorage.php b/src/DefinitionStorage.php index 0cafb62..32b5424 100644 --- a/src/DefinitionStorage.php +++ b/src/DefinitionStorage.php @@ -5,6 +5,7 @@ namespace Yiisoft\Definitions; use Psr\Container\ContainerInterface; +use ReflectionIntersectionType; use ReflectionNamedType; use ReflectionUnionType; use RuntimeException; @@ -155,22 +156,24 @@ private function isResolvable(string $id, array $building): bool $isUnionTypeResolvable = false; $unionTypes = []; foreach ($type->getTypes() as $unionType) { - if (!$unionType->isBuiltin()) { - $typeName = $unionType->getName(); - /** - * @psalm-suppress TypeDoesNotContainType - * - * @link https://github.com/vimeo/psalm/issues/6756 - */ - if ($typeName === 'self') { - continue; - } - $unionTypes[] = $typeName; - if ($this->isResolvable($typeName, $building)) { - $isUnionTypeResolvable = true; - /** @infection-ignore-all Mutation don't change behaviour, but degrade performance. */ - break; - } + if ($unionType instanceof ReflectionIntersectionType || $unionType->isBuiltin()) { + continue; + } + + $typeName = $unionType->getName(); + /** + * @psalm-suppress TypeDoesNotContainType + * + * @link https://github.com/vimeo/psalm/issues/6756 + */ + if ($typeName === 'self') { + continue; + } + $unionTypes[] = $typeName; + if ($this->isResolvable($typeName, $building)) { + $isUnionTypeResolvable = true; + /** @infection-ignore-all Mutation don't change behaviour, but degrade performance. */ + break; } } diff --git a/src/ParameterDefinition.php b/src/ParameterDefinition.php index 2fe77e2..70e8bca 100644 --- a/src/ParameterDefinition.php +++ b/src/ParameterDefinition.php @@ -5,6 +5,7 @@ namespace Yiisoft\Definitions; use Psr\Container\ContainerInterface; +use ReflectionIntersectionType; use ReflectionNamedType; use ReflectionParameter; use ReflectionUnionType; @@ -46,61 +47,61 @@ public function hasValue(): bool public function resolve(ContainerInterface $container): mixed { + /** @var ReflectionNamedType|ReflectionUnionType|ReflectionIntersectionType|null $type */ $type = $this->parameter->getType(); - if ($type === null || $this->isVariadic()) { - return $this->resolveVariadicOrBuiltinOrNonTyped(); - } - - if ($this->isUnionType()) { + if ($type instanceof ReflectionUnionType) { return $this->resolveUnionType($container); } - /** @var ReflectionNamedType|null $type */ - $type = $this->parameter->getType(); - $isBuiltin = $type !== null && $type->isBuiltin(); - - if (!$isBuiltin) { - /** @var ReflectionNamedType $type */ - $typeName = $type->getName(); - if ($typeName === 'self') { - // If type name is "self", it means that called class and - // $parameter->getDeclaringClass() returned instance of `ReflectionClass`. - /** @psalm-suppress PossiblyNullReference */ - $typeName = $this->parameter->getDeclaringClass()->getName(); - } + if ($type === null + || $type instanceof ReflectionIntersectionType + || $this->isVariadic() + || $type->isBuiltin() + ) { + return $this->resolveVariadicOrBuiltinOrIntersectionOrNonTyped(); + } - try { - $result = $container->get($typeName); - } catch (Throwable $t) { - if ( - $this->parameter->isOptional() - && ( - $t instanceof CircularReferenceException - || !$container->has($typeName) - ) - ) { - return $this->parameter->getDefaultValue(); - } - throw $t; - } + $typeName = $type->getName(); + /** + * @psalm-suppress TypeDoesNotContainType + * @see https://github.com/vimeo/psalm/issues/6756 + */ + if ($typeName === 'self') { + // If type name is "self", it means that called class and + // $parameter->getDeclaringClass() returned instance of `ReflectionClass`. + /** @psalm-suppress PossiblyNullReference */ + $typeName = $this->parameter->getDeclaringClass()->getName(); + } - if (!$result instanceof $typeName) { - $actualType = get_debug_type($result); - throw new InvalidConfigException( - "Container returned incorrect type \"$actualType\" for service \"{$type->getName()}\"." - ); + try { + $result = $container->get($typeName); + } catch (Throwable $t) { + if ( + $this->parameter->isOptional() + && ( + $t instanceof CircularReferenceException + || !$container->has($typeName) + ) + ) { + return $this->parameter->getDefaultValue(); } - return $result; + throw $t; } - return $this->resolveVariadicOrBuiltinOrNonTyped(); + if (!$result instanceof $typeName) { + $actualType = get_debug_type($result); + throw new InvalidConfigException( + "Container returned incorrect type \"$actualType\" for service \"{$type->getName()}\"." + ); + } + return $result; } /** * @throws NotInstantiableException */ - private function resolveVariadicOrBuiltinOrNonTyped(): mixed + private function resolveVariadicOrBuiltinOrIntersectionOrNonTyped(): mixed { if ($this->parameter->isDefaultValueAvailable()) { return $this->parameter->getDefaultValue(); @@ -146,52 +147,54 @@ private function resolveUnionType(ContainerInterface $container): mixed $parameterType = $this->parameter->getType(); /** - * @var ReflectionNamedType[] $types + * @var ReflectionNamedType[]|ReflectionIntersectionType[] $types */ $types = $parameterType->getTypes(); $class = implode('|', $types); foreach ($types as $type) { - if (!$type->isBuiltin()) { - $typeName = $type->getName(); - /** - * @psalm-suppress TypeDoesNotContainType - * - * @link https://github.com/vimeo/psalm/issues/6756 - */ - if ($typeName === 'self') { - // If type name is "self", it means that called class and - // $parameter->getDeclaringClass() returned instance of `ReflectionClass`. - /** @psalm-suppress PossiblyNullReference */ - $typeName = $this->parameter->getDeclaringClass()->getName(); - } + if ($type instanceof ReflectionIntersectionType || $type->isBuiltin()) { + continue; + } - try { - $result = $container->get($typeName); - $resolved = true; - } catch (Throwable $t) { - $error = $t; - $resolved = false; - } + $typeName = $type->getName(); + /** + * @psalm-suppress TypeDoesNotContainType + * + * @link https://github.com/vimeo/psalm/issues/6756 + */ + if ($typeName === 'self') { + // If type name is "self", it means that called class and + // $parameter->getDeclaringClass() returned instance of `ReflectionClass`. + /** @psalm-suppress PossiblyNullReference */ + $typeName = $this->parameter->getDeclaringClass()->getName(); + } - if ($resolved) { - /** @var mixed $result Exist, because $resolved is true */ - if (!$result instanceof $typeName) { - $actualType = get_debug_type($result); - throw new InvalidConfigException( - "Container returned incorrect type \"$actualType\" for service \"$class\"." - ); - } - return $result; - } + try { + $result = $container->get($typeName); + $resolved = true; + } catch (Throwable $t) { + $error = $t; + $resolved = false; + } - /** @var Throwable $error Exist, because $resolved is false */ - if ( - !$error instanceof CircularReferenceException - && $container->has($typeName) - ) { - throw $error; + if ($resolved) { + /** @var mixed $result Exist, because $resolved is true */ + if (!$result instanceof $typeName) { + $actualType = get_debug_type($result); + throw new InvalidConfigException( + "Container returned incorrect type \"$actualType\" for service \"$class\"." + ); } + return $result; + } + + /** @var Throwable $error Exist, because $resolved is false */ + if ( + !$error instanceof CircularReferenceException + && $container->has($typeName) + ) { + throw $error; } } @@ -200,36 +203,16 @@ private function resolveUnionType(ContainerInterface $container): mixed } if (!isset($error)) { - return $this->resolveVariadicOrBuiltinOrNonTyped(); + return $this->resolveVariadicOrBuiltinOrIntersectionOrNonTyped(); } throw $error; } - private function isUnionType(): bool - { - return $this->parameter->getType() instanceof ReflectionUnionType; - } - private function getType(): ?string { $type = $this->parameter->getType(); - - if ($type instanceof ReflectionUnionType) { - $namedTypes = $type->getTypes(); - /** @infection-ignore-all Mutation don't change behaviour, but degrade performance. */ - $names = array_map( - static fn (ReflectionNamedType $t) => $t->getName(), - $namedTypes - ); - return implode('|', $names); - } - - if ($type instanceof ReflectionNamedType) { - return $type->getName(); - } - - return null; + return $type === null ? null : (string) $type; } private function getCallable(): string diff --git a/tests/Php8_1/DefinitionStorageTest.php b/tests/Php8_1/DefinitionStorageTest.php new file mode 100644 index 0000000..9104cef --- /dev/null +++ b/tests/Php8_1/DefinitionStorageTest.php @@ -0,0 +1,21 @@ +get(UnionTypeWithIntersectionTypeDependency::class); + + $this->assertSame(UnionTypeWithIntersectionTypeDependency::class, $definition); + } +} diff --git a/tests/Php8_1/ParameterDefinitionTest.php b/tests/Php8_1/ParameterDefinitionTest.php new file mode 100644 index 0000000..a3f641c --- /dev/null +++ b/tests/Php8_1/ParameterDefinitionTest.php @@ -0,0 +1,60 @@ +getFirstParameter(fn (GearBox&stdClass $class) => true) + ); + + $this->expectException(NotInstantiableException::class); + $this->expectExceptionMessage('Can not determine value of the "class" parameter of type '); + $definition->resolve($container); + } + + public function testResolveUnionTypeWithIntersectionType(): void + { + $container = new SimpleContainer([ + Chair::class => new Chair(), + ]); + + $definition = new ParameterDefinition( + $this->getFirstParameter(fn (Bike|(GearBox&stdClass)|Chair $class) => true) + ); + + $result = $definition->resolve($container); + + $this->assertInstanceOf(Chair::class, $result); + } + + /** + * @return ReflectionParameter[] + */ + private function getParameters(callable $callable): array + { + $closure = $callable instanceof Closure ? $callable : Closure::fromCallable($callable); + return (new ReflectionFunction($closure))->getParameters(); + } + + private function getFirstParameter(Closure $closure): ReflectionParameter + { + return $this->getParameters($closure)[0]; + } +} diff --git a/tests/Support/MagicCall.php b/tests/Support/MagicCall.php new file mode 100644 index 0000000..1dff635 --- /dev/null +++ b/tests/Support/MagicCall.php @@ -0,0 +1,20 @@ + get_debug_type($argument), + $arguments, + ); + + $this->record[] = 'Call ' . $name . '(' . implode(', ', $arguments) . ')'; + } +} diff --git a/tests/Support/UnionTypeWithIntersectionTypeDependency.php b/tests/Support/UnionTypeWithIntersectionTypeDependency.php new file mode 100644 index 0000000..46cb22e --- /dev/null +++ b/tests/Support/UnionTypeWithIntersectionTypeDependency.php @@ -0,0 +1,13 @@ + [[CarFactory::class, 'create']], 'array-definition' => [['class' => ColorPink::class]], 'magic method reference' => [['class' => Recorder::class, 'add()' => ['test magic method']]], + 'magic only call reference' => [['class' => MagicCall::class, 'add()' => ['test magic method']]], 'magic property reference' => [['class' => Recorder::class, '$add' => ['test magic property']]], ]; } From 62ae8d2865d48b254ff1ee039c629830311fa953 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Thu, 15 Feb 2024 19:05:31 +0000 Subject: [PATCH 05/12] Apply fixes from StyleCI --- src/ParameterDefinition.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ParameterDefinition.php b/src/ParameterDefinition.php index 70e8bca..d71c4e6 100644 --- a/src/ParameterDefinition.php +++ b/src/ParameterDefinition.php @@ -47,7 +47,7 @@ public function hasValue(): bool public function resolve(ContainerInterface $container): mixed { - /** @var ReflectionNamedType|ReflectionUnionType|ReflectionIntersectionType|null $type */ + /** @var ReflectionIntersectionType|ReflectionNamedType|ReflectionUnionType|null $type */ $type = $this->parameter->getType(); if ($type instanceof ReflectionUnionType) { @@ -147,7 +147,7 @@ private function resolveUnionType(ContainerInterface $container): mixed $parameterType = $this->parameter->getType(); /** - * @var ReflectionNamedType[]|ReflectionIntersectionType[] $types + * @var ReflectionIntersectionType[]|ReflectionNamedType[] $types */ $types = $parameterType->getTypes(); $class = implode('|', $types); From 21d7212346fbfcfdd1da15addffe678a01cb6dfb Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 22:08:19 +0300 Subject: [PATCH 06/12] fix --- composer-require-checker.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 composer-require-checker.json diff --git a/composer-require-checker.json b/composer-require-checker.json new file mode 100644 index 0000000..41f9903 --- /dev/null +++ b/composer-require-checker.json @@ -0,0 +1,5 @@ +{ + "symbol-whitelist" : [ + "ReflectionIntersectionType" + ] +} From 200c4d70b3624d8247be9de30bb7212e9b76bb89 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 22:09:32 +0300 Subject: [PATCH 07/12] fix --- .styleci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.styleci.yml b/.styleci.yml index 1ab379b..e121039 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -1,7 +1,7 @@ preset: psr12 risky: true -version: 8.1 +version: 8.2 finder: exclude: From 72bcb111f968d83f7ad48b2d4c271bcaae4f2d1d Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 22:11:57 +0300 Subject: [PATCH 08/12] fix tests --- phpunit.xml.dist | 1 + tests/Php8_1/ParameterDefinitionTest.php | 17 ------- .../DefinitionStorageTest.php | 2 +- tests/Php8_2/ParameterDefinitionTest.php | 46 +++++++++++++++++++ 4 files changed, 48 insertions(+), 18 deletions(-) rename tests/{Php8_1 => Php8_2}/DefinitionStorageTest.php (92%) create mode 100644 tests/Php8_2/ParameterDefinitionTest.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index dae0a14..351500b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -19,6 +19,7 @@ ./tests/Unit ./tests/Php8_1 + ./tests/Php8_2 diff --git a/tests/Php8_1/ParameterDefinitionTest.php b/tests/Php8_1/ParameterDefinitionTest.php index a3f641c..eb44b32 100644 --- a/tests/Php8_1/ParameterDefinitionTest.php +++ b/tests/Php8_1/ParameterDefinitionTest.php @@ -10,8 +10,6 @@ use ReflectionParameter; use Yiisoft\Definitions\Exception\NotInstantiableException; use Yiisoft\Definitions\ParameterDefinition; -use Yiisoft\Definitions\Tests\Support\Bike; -use Yiisoft\Definitions\Tests\Support\Chair; use Yiisoft\Test\Support\Container\SimpleContainer; final class ParameterDefinitionTest extends TestCase @@ -29,21 +27,6 @@ public function testNotResolveIntersectionType(): void $definition->resolve($container); } - public function testResolveUnionTypeWithIntersectionType(): void - { - $container = new SimpleContainer([ - Chair::class => new Chair(), - ]); - - $definition = new ParameterDefinition( - $this->getFirstParameter(fn (Bike|(GearBox&stdClass)|Chair $class) => true) - ); - - $result = $definition->resolve($container); - - $this->assertInstanceOf(Chair::class, $result); - } - /** * @return ReflectionParameter[] */ diff --git a/tests/Php8_1/DefinitionStorageTest.php b/tests/Php8_2/DefinitionStorageTest.php similarity index 92% rename from tests/Php8_1/DefinitionStorageTest.php rename to tests/Php8_2/DefinitionStorageTest.php index 9104cef..f2ffb58 100644 --- a/tests/Php8_1/DefinitionStorageTest.php +++ b/tests/Php8_2/DefinitionStorageTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Definitions\Tests\Php8_1; +namespace Yiisoft\Definitions\Tests\Php8_2; use PHPUnit\Framework\TestCase; use Yiisoft\Definitions\DefinitionStorage; diff --git a/tests/Php8_2/ParameterDefinitionTest.php b/tests/Php8_2/ParameterDefinitionTest.php new file mode 100644 index 0000000..75caf79 --- /dev/null +++ b/tests/Php8_2/ParameterDefinitionTest.php @@ -0,0 +1,46 @@ + new Chair(), + ]); + + $definition = new ParameterDefinition( + $this->getFirstParameter(fn (Bike|(GearBox&stdClass)|Chair $class) => true) + ); + + $result = $definition->resolve($container); + + $this->assertInstanceOf(Chair::class, $result); + } + + /** + * @return ReflectionParameter[] + */ + private function getParameters(callable $callable): array + { + $closure = $callable instanceof Closure ? $callable : Closure::fromCallable($callable); + return (new ReflectionFunction($closure))->getParameters(); + } + + private function getFirstParameter(Closure $closure): ReflectionParameter + { + return $this->getParameters($closure)[0]; + } +} From 320de24ba7388e90fb6a3769e61f244a66568feb Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 22:18:15 +0300 Subject: [PATCH 09/12] fix --- psalm80.xml | 5 +++++ src/ParameterDefinition.php | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/psalm80.xml b/psalm80.xml index 796ae44..e02c3f6 100644 --- a/psalm80.xml +++ b/psalm80.xml @@ -15,6 +15,11 @@ + + + + + diff --git a/src/ParameterDefinition.php b/src/ParameterDefinition.php index d71c4e6..7bd9774 100644 --- a/src/ParameterDefinition.php +++ b/src/ParameterDefinition.php @@ -47,7 +47,10 @@ public function hasValue(): bool public function resolve(ContainerInterface $container): mixed { - /** @var ReflectionIntersectionType|ReflectionNamedType|ReflectionUnionType|null $type */ + /** + * @psalm-suppress UndefinedDocblockClass Need for PHP 8.0 only + * @var ReflectionIntersectionType|ReflectionNamedType|ReflectionUnionType|null $type + */ $type = $this->parameter->getType(); if ($type instanceof ReflectionUnionType) { @@ -147,6 +150,7 @@ private function resolveUnionType(ContainerInterface $container): mixed $parameterType = $this->parameter->getType(); /** + * @psalm-suppress UndefinedDocblockClass Need for PHP 8.0 only * @var ReflectionIntersectionType[]|ReflectionNamedType[] $types */ $types = $parameterType->getTypes(); From 6368faeac19c7c82234b499722920cbcb2c3cfac Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 22:20:05 +0300 Subject: [PATCH 10/12] fix --- .github/workflows/mutation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mutation.yml b/.github/workflows/mutation.yml index 43d6f50..dc464eb 100644 --- a/.github/workflows/mutation.yml +++ b/.github/workflows/mutation.yml @@ -29,6 +29,6 @@ jobs: os: >- ['ubuntu-latest'] php: >- - ['8.1'] + ['8.3'] secrets: STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} From 4c187c25c97aa1e31168b98f1c6c53df9e41bff7 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Fri, 16 Feb 2024 09:13:09 +0300 Subject: [PATCH 11/12] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdef2a3..eee0a7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 3.2.1 under development -- Bug #86: Fix crush when intersection types used (@vjik) +- Bug #86: Fix crash when intersection types are used (@vjik) ## 3.2.0 February 12, 2023 From 6e83b95fbaf72f517aa99d858b2e71efcb105013 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Fri, 16 Feb 2024 09:36:31 +0300 Subject: [PATCH 12/12] improve --- composer-require-checker.json | 5 ----- psalm80.xml | 5 ----- src/DefinitionStorage.php | 6 ++++-- src/ParameterDefinition.php | 25 +++++++------------------ 4 files changed, 11 insertions(+), 30 deletions(-) delete mode 100644 composer-require-checker.json diff --git a/composer-require-checker.json b/composer-require-checker.json deleted file mode 100644 index 41f9903..0000000 --- a/composer-require-checker.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "symbol-whitelist" : [ - "ReflectionIntersectionType" - ] -} diff --git a/psalm80.xml b/psalm80.xml index e02c3f6..796ae44 100644 --- a/psalm80.xml +++ b/psalm80.xml @@ -15,11 +15,6 @@ - - - - - diff --git a/src/DefinitionStorage.php b/src/DefinitionStorage.php index 32b5424..c9e0459 100644 --- a/src/DefinitionStorage.php +++ b/src/DefinitionStorage.php @@ -5,7 +5,6 @@ namespace Yiisoft\Definitions; use Psr\Container\ContainerInterface; -use ReflectionIntersectionType; use ReflectionNamedType; use ReflectionUnionType; use RuntimeException; @@ -156,7 +155,10 @@ private function isResolvable(string $id, array $building): bool $isUnionTypeResolvable = false; $unionTypes = []; foreach ($type->getTypes() as $unionType) { - if ($unionType instanceof ReflectionIntersectionType || $unionType->isBuiltin()) { + /** + * @psalm-suppress DocblockTypeContradiction Need for PHP 8.0 and 8.1 only + */ + if (!$unionType instanceof ReflectionNamedType || $unionType->isBuiltin()) { continue; } diff --git a/src/ParameterDefinition.php b/src/ParameterDefinition.php index 7bd9774..5e74989 100644 --- a/src/ParameterDefinition.php +++ b/src/ParameterDefinition.php @@ -5,7 +5,6 @@ namespace Yiisoft\Definitions; use Psr\Container\ContainerInterface; -use ReflectionIntersectionType; use ReflectionNamedType; use ReflectionParameter; use ReflectionUnionType; @@ -47,18 +46,14 @@ public function hasValue(): bool public function resolve(ContainerInterface $container): mixed { - /** - * @psalm-suppress UndefinedDocblockClass Need for PHP 8.0 only - * @var ReflectionIntersectionType|ReflectionNamedType|ReflectionUnionType|null $type - */ $type = $this->parameter->getType(); if ($type instanceof ReflectionUnionType) { - return $this->resolveUnionType($container); + return $this->resolveUnionType($type, $container); } if ($type === null - || $type instanceof ReflectionIntersectionType + || !$type instanceof ReflectionNamedType || $this->isVariadic() || $type->isBuiltin() ) { @@ -142,22 +137,16 @@ private function resolveVariadicOrBuiltinOrIntersectionOrNonTyped(): mixed * * @return mixed Ready to use object or null if definition can not be resolved and is marked as optional. */ - private function resolveUnionType(ContainerInterface $container): mixed + private function resolveUnionType(ReflectionUnionType $parameterType, ContainerInterface $container): mixed { - /** - * @var ReflectionUnionType $parameterType - */ - $parameterType = $this->parameter->getType(); - - /** - * @psalm-suppress UndefinedDocblockClass Need for PHP 8.0 only - * @var ReflectionIntersectionType[]|ReflectionNamedType[] $types - */ $types = $parameterType->getTypes(); $class = implode('|', $types); foreach ($types as $type) { - if ($type instanceof ReflectionIntersectionType || $type->isBuiltin()) { + /** + * @psalm-suppress DocblockTypeContradiction Need for PHP 8.0 and 8.1 only + */ + if (!$type instanceof ReflectionNamedType || $type->isBuiltin()) { continue; }