diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 4d84a5b3db9..78ddabb1e76 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -54,6 +54,18 @@ parameters: - tests/Fixtures/TestBundle/Document/ - tests/Fixtures/TestBundle/Entity/ - src/OpenApi/Factory/OpenApiFactory.php + - + message: '#is never assigned .* so it can be removed from the property type.#' + paths: + - src/Doctrine/Common/Tests/Fixtures/ + - src/Doctrine/Orm/Tests/Fixtures/ + - src/Doctrine/Odm/Tests/Fixtures/ + - src/Elasticsearch/Tests/Fixtures/ + - src/GraphQl/Tests/Fixtures/ + - src/JsonSchema/Tests/Fixtures/ + - src/Metadata/Tests/Fixtures/ + - src/Serializer/Tests/Fixtures/ + - tests/Fixtures/ - message: '#is never written, only read.#' paths: @@ -97,18 +109,6 @@ parameters: message: '#^Service "[^"]+" is private.$#' path: src + # Allow extra assertions in tests: https://github.com/phpstan/phpstan-strict-rules/issues/130 - '#^Call to (static )?method PHPUnit\\Framework\\Assert::.* will always evaluate to true\.$#' - - # TODO For PHPStan 2.0 - - - path: tests/ - identifier: function.alreadyNarrowedType - - - identifier: property.unusedType - - - identifier: varTag.nativeType - - - identifier: trait.unused - - - identifier: catch.neverThrown diff --git a/src/Doctrine/Odm/PropertyHelperTrait.php b/src/Doctrine/Odm/PropertyHelperTrait.php index 6e73db7893e..168e0cec6bc 100644 --- a/src/Doctrine/Odm/PropertyHelperTrait.php +++ b/src/Doctrine/Odm/PropertyHelperTrait.php @@ -39,7 +39,11 @@ abstract protected function splitPropertyParts(string $property, string $resourc */ protected function getClassMetadata(string $resourceClass): ClassMetadata { - /** @var ?ManagerRegistry $managerRegistry */ + /** + * @var ?ManagerRegistry $managerRegistry + * + * @phpstan-ignore varTag.nativeType (https://github.com/phpstan/phpstan/issues/9515) + */ $managerRegistry = $this->getManagerRegistry(); $manager = $managerRegistry?->getManagerForClass($resourceClass); diff --git a/src/Doctrine/Orm/Util/QueryBuilderHelper.php b/src/Doctrine/Orm/Util/QueryBuilderHelper.php index 54956fcc595..52bccafce9e 100644 --- a/src/Doctrine/Orm/Util/QueryBuilderHelper.php +++ b/src/Doctrine/Orm/Util/QueryBuilderHelper.php @@ -186,13 +186,7 @@ public static function getExistingJoin(QueryBuilder $queryBuilder, string $alias */ private static function mapRootAliases(array $rootAliases, array $rootEntities): array { - /** @var false|array $aliasMap */ - $aliasMap = array_combine($rootAliases, $rootEntities); - if (false === $aliasMap) { - throw new \LogicException('Number of root aliases and root entities do not match.'); - } - - return $aliasMap; + return array_combine($rootAliases, $rootEntities); } /** diff --git a/src/GraphQl/Subscription/SubscriptionManager.php b/src/GraphQl/Subscription/SubscriptionManager.php index 8e2532aa33e..afebe45bfb1 100644 --- a/src/GraphQl/Subscription/SubscriptionManager.php +++ b/src/GraphQl/Subscription/SubscriptionManager.php @@ -82,7 +82,6 @@ public function getPushPayloads(object $object): array $payloads = []; foreach ($subscriptions as [$subscriptionId, $subscriptionFields, $subscriptionResult]) { $resolverContext = ['fields' => $subscriptionFields, 'is_collection' => false, 'is_mutation' => false, 'is_subscription' => true]; - /** @var Operation */ $operation = (new Subscription())->withName('update_subscription')->withShortName($shortName); $data = $this->normalizeProcessor->process($object, $operation, [], $resolverContext); diff --git a/src/GraphQl/Tests/Type/FieldsBuilderTest.php b/src/GraphQl/Tests/Type/FieldsBuilderTest.php index eeefb7ca35d..9f7053b0688 100644 --- a/src/GraphQl/Tests/Type/FieldsBuilderTest.php +++ b/src/GraphQl/Tests/Type/FieldsBuilderTest.php @@ -855,7 +855,6 @@ public function testResolveResourceArgs(array $args, array $expectedResolvedArgs $this->typeConverterProphecy->resolveType(Argument::type('string'))->willReturn(GraphQLType::string()); - /** @var Operation $operation */ $operation = (new Query())->withName('operation')->withShortName('shortName'); $args = $this->fieldsBuilder->resolveResourceArgs($args, $operation); diff --git a/src/GraphQl/Tests/Type/TypeBuilderTest.php b/src/GraphQl/Tests/Type/TypeBuilderTest.php index 3bd8ddf31ca..2bfc54760c5 100644 --- a/src/GraphQl/Tests/Type/TypeBuilderTest.php +++ b/src/GraphQl/Tests/Type/TypeBuilderTest.php @@ -86,7 +86,6 @@ public function testGetResourceObjectType(): void $this->typesContainerProphecy->has('Node')->shouldBeCalled()->willReturn(false); $this->typesContainerProphecy->set('Node', Argument::type(InterfaceType::class))->shouldBeCalled(); - /** @var Operation $operation */ $operation = (new Query())->withShortName('shortName')->withDescription('description')->withClass('resourceClass'); /** @var ObjectType $resourceObjectType */ $resourceObjectType = $this->typeBuilder->getResourceObjectType($resourceMetadataCollection, $operation, null, ['input' => false]); @@ -112,7 +111,6 @@ public function testGetResourceObjectTypeOutputClass(): void $this->typesContainerProphecy->has('Node')->shouldBeCalled()->willReturn(false); $this->typesContainerProphecy->set('Node', Argument::type(InterfaceType::class))->shouldBeCalled(); - /** @var Operation $operation */ $operation = (new Query())->withShortName('shortName')->withDescription('description')->withOutput(['class' => 'outputClass']); /** @var ObjectType $resourceObjectType */ $resourceObjectType = $this->typeBuilder->getResourceObjectType($resourceMetadataCollection, $operation, null, ['input' => false]); @@ -177,7 +175,6 @@ public function testGetResourceObjectTypeInput(): void $this->typesContainerProphecy->has('Node')->shouldBeCalled()->willReturn(false); $this->typesContainerProphecy->set('Node', Argument::type(InterfaceType::class))->shouldBeCalled(); - /** @var Operation $operation */ $operation = (new Mutation())->withName('custom')->withShortName('shortName')->withDescription('description')->withClass('resourceClass'); /** @var NonNull $resourceObjectType */ $resourceObjectType = $this->typeBuilder->getResourceObjectType($resourceMetadata, $operation, null, ['input' => true]); @@ -202,7 +199,6 @@ public function testGetResourceObjectTypeNestedInput(): void $this->typesContainerProphecy->has('Node')->shouldBeCalled()->willReturn(false); $this->typesContainerProphecy->set('Node', Argument::type(InterfaceType::class))->shouldBeCalled(); - /** @var Operation $operation */ $operation = (new Mutation())->withName('custom')->withShortName('shortName')->withDescription('description')->withClass('resourceClass'); /** @var NonNull $resourceObjectType */ $resourceObjectType = $this->typeBuilder->getResourceObjectType($resourceMetadata, $operation, null, ['input' => true, 'wrapped' => false, 'depth' => 1]); @@ -227,9 +223,7 @@ public function testGetResourceObjectTypeNestedInputNullable(): void $this->typesContainerProphecy->has('Node')->shouldBeCalled()->willReturn(false); $this->typesContainerProphecy->set('Node', Argument::type(InterfaceType::class))->shouldBeCalled(); - /** @var Operation $operation */ $operation = (new Mutation())->withName('custom')->withShortName('shortNameNullable')->withDescription('description nullable')->withClass('resourceClass'); - /** @var ApiProperty $propertyMetadata */ $propertyMetadata = (new ApiProperty())->withRequired(false); /** @var InputObjectType $resourceObjectType */ $resourceObjectType = $this->typeBuilder->getResourceObjectType($resourceMetadata, $operation, $propertyMetadata, [ @@ -257,7 +251,6 @@ public function testGetResourceObjectTypeCustomMutationInputArgs(): void $this->typesContainerProphecy->has('Node')->shouldBeCalled()->willReturn(false); $this->typesContainerProphecy->set('Node', Argument::type(InterfaceType::class))->shouldBeCalled(); - /** @var Operation $operation */ $operation = (new Mutation())->withArgs([])->withName('custom')->withShortName('shortName')->withDescription('description')->withClass('resourceClass'); /** @var NonNull $resourceObjectType */ $resourceObjectType = $this->typeBuilder->getResourceObjectType($resourceMetadata, $operation, null, ['input' => true]); @@ -288,7 +281,6 @@ public function testGetResourceObjectTypeMutation(): void $this->typesContainerProphecy->has('Node')->shouldBeCalled()->willReturn(false); $this->typesContainerProphecy->set('Node', Argument::type(InterfaceType::class))->shouldBeCalled(); - /** @var Operation $operation */ $operation = (new Mutation())->withName('create')->withShortName('shortName')->withDescription('description')->withClass('resourceClass'); /** @var ObjectType $resourceObjectType */ $resourceObjectType = $this->typeBuilder->getResourceObjectType($resourceMetadata, $operation, null, ['input' => false]); @@ -320,7 +312,6 @@ public function testGetResourceObjectTypeMutationWrappedType(): void $this->typesContainerProphecy->has('Node')->shouldBeCalled()->willReturn(false); $this->typesContainerProphecy->set('Node', Argument::type(InterfaceType::class))->shouldBeCalled(); - /** @var Operation $operation */ $operation = (new Mutation())->withName('create')->withShortName('shortName')->withDescription('description')->withNormalizationContext(['groups' => ['create']])->withClass('resourceClass'); /** @var ObjectType $resourceObjectType */ $resourceObjectType = $this->typeBuilder->getResourceObjectType($resourceMetadata, $operation, null, ['input' => false]); @@ -362,7 +353,6 @@ public function testGetResourceObjectTypeMutationNested(): void $this->typesContainerProphecy->has('Node')->shouldBeCalled()->willReturn(false); $this->typesContainerProphecy->set('Node', Argument::type(InterfaceType::class))->shouldBeCalled(); - /** @var Operation $operation */ $operation = (new Mutation())->withName('create')->withShortName('shortName')->withDescription('description')->withClass('resourceClass'); /** @var ObjectType $resourceObjectType */ $resourceObjectType = $this->typeBuilder->getResourceObjectType($resourceMetadata, $operation, null, ['input' => false, 'wrapped' => false, 'depth' => 1]); @@ -390,7 +380,6 @@ public function testGetResourceObjectTypeSubscription(): void $this->typesContainerProphecy->has('Node')->shouldBeCalled()->willReturn(false); $this->typesContainerProphecy->set('Node', Argument::type(InterfaceType::class))->shouldBeCalled(); - /** @var Operation $operation */ $operation = (new Subscription())->withName('update')->withShortName('shortName')->withDescription('description')->withMercure(true)->withClass('resourceClass'); /** @var ObjectType $resourceObjectType */ $resourceObjectType = $this->typeBuilder->getResourceObjectType($resourceMetadata, $operation, null, ['input' => false]); @@ -424,7 +413,6 @@ public function testGetResourceObjectTypeSubscriptionWrappedType(): void $this->typesContainerProphecy->has('Node')->shouldBeCalled()->willReturn(false); $this->typesContainerProphecy->set('Node', Argument::type(InterfaceType::class))->shouldBeCalled(); - /** @var Operation $operation */ $operation = (new Subscription())->withName('update')->withShortName('shortName')->withDescription('description')->withNormalizationContext(['groups' => ['update']])->withClass('resourceClass'); /** @var ObjectType $resourceObjectType */ $resourceObjectType = $this->typeBuilder->getResourceObjectType($resourceMetadata, $operation, null, ['input' => false]); @@ -467,7 +455,6 @@ public function testGetResourceObjectTypeSubscriptionNested(): void $this->typesContainerProphecy->has('Node')->shouldBeCalled()->willReturn(false); $this->typesContainerProphecy->set('Node', Argument::type(InterfaceType::class))->shouldBeCalled(); - /** @var Operation $operation */ $operation = (new Subscription())->withName('update')->withShortName('shortName')->withDescription('description')->withMercure(true)->withClass('resourceClass'); /** @var ObjectType $resourceObjectType */ $resourceObjectType = $this->typeBuilder->getResourceObjectType($resourceMetadata, $operation, null, ['input' => false, 'wrapped' => false, 'depth' => 1]); @@ -496,18 +483,17 @@ public function testGetNodeInterface(): void $this->assertNull($nodeInterface->resolveType([], [], $this->prophesize(ResolveInfo::class)->reveal())); $this->typesContainerProphecy->has('Dummy')->shouldBeCalled()->willReturn(false); - $this->assertNull($nodeInterface->resolveType([ItemNormalizer::ITEM_RESOURCE_CLASS_KEY => Dummy::class], [], $this->prophesize(ResolveInfo::class)->reveal())); + $resolvedType = $nodeInterface->resolveType([ItemNormalizer::ITEM_RESOURCE_CLASS_KEY => Dummy::class], [], $this->prophesize(ResolveInfo::class)->reveal()); + $this->assertNull($resolvedType); $this->typesContainerProphecy->has('Dummy')->shouldBeCalled()->willReturn(true); $this->typesContainerProphecy->get('Dummy')->shouldBeCalled()->willReturn(GraphQLType::string()); - /** @var GraphQLType $resolvedType */ $resolvedType = $nodeInterface->resolveType([ItemNormalizer::ITEM_RESOURCE_CLASS_KEY => Dummy::class], [], $this->prophesize(ResolveInfo::class)->reveal()); $this->assertSame(GraphQLType::string(), $resolvedType); } public function testCursorBasedGetPaginatedCollectionType(): void { - /** @var Operation $operation */ $operation = (new Query())->withPaginationType('cursor'); $this->typesContainerProphecy->has('StringCursorConnection')->shouldBeCalled()->willReturn(false); $this->typesContainerProphecy->set('StringCursorConnection', Argument::type(ObjectType::class))->shouldBeCalled(); @@ -563,7 +549,6 @@ public function testCursorBasedGetPaginatedCollectionType(): void public function testPageBasedGetPaginatedCollectionType(): void { - /** @var Operation $operation */ $operation = (new Query())->withPaginationType('page'); $this->typesContainerProphecy->has('StringPageConnection')->shouldBeCalled()->willReturn(false); $this->typesContainerProphecy->set('StringPageConnection', Argument::type(ObjectType::class))->shouldBeCalled(); @@ -601,7 +586,6 @@ public function testGetEnumType(): void $enumClass = GamePlayMode::class; $enumName = 'GamePlayMode'; $enumDescription = 'GamePlayMode description'; - /** @var Operation $operation */ $operation = (new Operation()) ->withClass($enumClass) ->withShortName($enumName) diff --git a/src/GraphQl/Tests/Type/TypeConverterTest.php b/src/GraphQl/Tests/Type/TypeConverterTest.php index 8c4eb980391..fe6e4c96e76 100644 --- a/src/GraphQl/Tests/Type/TypeConverterTest.php +++ b/src/GraphQl/Tests/Type/TypeConverterTest.php @@ -74,7 +74,6 @@ public function testConvertTypeLegacy(LegacyType $type, bool $input, int $depth, $this->resourceMetadataCollectionFactoryProphecy->create(Argument::type('string'))->willReturn(new ResourceMetadataCollection('resourceClass')); $this->typeBuilderProphecy->getEnumType(Argument::type(Operation::class))->willReturn($expectedGraphqlType); - /** @var Operation $operation */ $operation = (new Query())->withName('test'); $graphqlType = $this->typeConverter->convertType($type, $input, $operation, 'resourceClass', 'rootClass', null, $depth); $this->assertSame($expectedGraphqlType, $graphqlType); @@ -104,7 +103,6 @@ public function testConvertType(Type $type, bool $input, int $depth, GraphQLType $this->resourceMetadataCollectionFactoryProphecy->create(Argument::type('string'))->willReturn(new ResourceMetadataCollection('resourceClass')); $this->typeBuilderProphecy->getEnumType(Argument::type(Operation::class))->willReturn($expectedGraphqlType); - /** @var Operation $operation */ $operation = (new Query())->withName('test'); $graphqlType = $this->typeConverter->convertPhpType($type, $input, $operation, 'resourceClass', 'rootClass', null, $depth); $this->assertSame($expectedGraphqlType, $graphqlType); @@ -136,7 +134,6 @@ public function testConvertTypeNoGraphQlResourceMetadataLegacy(): void $this->typeBuilderProphecy->isCollection($type)->shouldBeCalled()->willReturn(false); $this->resourceMetadataCollectionFactoryProphecy->create('dummy')->shouldBeCalled()->willReturn(new ResourceMetadataCollection('dummy', [new ApiResource()])); - /** @var Operation $operation */ $operation = (new Query())->withName('test'); $graphqlType = $this->typeConverter->convertType($type, false, $operation, 'resourceClass', 'rootClass', null, 0); $this->assertNull($graphqlType); @@ -148,7 +145,6 @@ public function testConvertTypeNoGraphQlResourceMetadata(): void $this->resourceMetadataCollectionFactoryProphecy->create('dummy')->shouldBeCalled()->willReturn(new ResourceMetadataCollection('dummy', [new ApiResource()])); - /** @var Operation $operation */ $operation = (new Query())->withName('test'); $graphqlType = $this->typeConverter->convertPhpType($type, false, $operation, 'resourceClass', 'rootClass', null, 0); $this->assertNull($graphqlType); @@ -165,7 +161,6 @@ public function testConvertTypeNodeResourceLegacy(): void $this->expectException(\UnexpectedValueException::class); $this->expectExceptionMessage('A "Node" resource cannot be used with GraphQL because the type is already used by the Relay specification.'); - /** @var Operation $operation */ $operation = (new Query())->withName('test'); $this->typeConverter->convertType($type, false, $operation, 'resourceClass', 'rootClass', null, 0); } @@ -179,7 +174,6 @@ public function testConvertTypeNodeResource(): void $this->expectException(\UnexpectedValueException::class); $this->expectExceptionMessage('A "Node" resource cannot be used with GraphQL because the type is already used by the Relay specification.'); - /** @var Operation $operation */ $operation = (new Query())->withName('test'); $this->typeConverter->convertPhpType($type, false, $operation, 'resourceClass', 'rootClass', null, 0); } @@ -192,7 +186,6 @@ public function testConvertTypeResourceClassNotFoundLegacy(): void $this->typeBuilderProphecy->isCollection($type)->shouldBeCalled()->willReturn(false); $this->resourceMetadataCollectionFactoryProphecy->create('dummy')->shouldBeCalled()->willThrow(new ResourceClassNotFoundException()); - /** @var Operation $operation */ $operation = (new Query())->withName('test'); $graphqlType = $this->typeConverter->convertType($type, false, $operation, 'resourceClass', 'rootClass', null, 0); $this->assertNull($graphqlType); @@ -204,7 +197,6 @@ public function testConvertTypeResourceClassNotFound(): void $this->resourceMetadataCollectionFactoryProphecy->create('dummy')->shouldBeCalled()->willThrow(new ResourceClassNotFoundException()); - /** @var Operation $operation */ $operation = (new Query())->withName('test'); $graphqlType = $this->typeConverter->convertPhpType($type, false, $operation, 'resourceClass', 'rootClass', null, 0); $this->assertNull($graphqlType); @@ -220,7 +212,6 @@ public function testConvertTypeResourceIriLegacy(): void $this->typeBuilderProphecy->isCollection($type)->willReturn(false); $this->propertyMetadataFactoryProphecy->create('rootClass', 'dummyProperty', Argument::type('array'))->shouldBeCalled()->willReturn((new ApiProperty())->withWritableLink(false)); - /** @var Operation $operation */ $operation = (new Query())->withName('test'); $graphqlType = $this->typeConverter->convertType($type, true, $operation, 'dummy', 'rootClass', 'dummyProperty', 1); $this->assertSame(GraphQLType::string(), $graphqlType); @@ -234,7 +225,6 @@ public function testConvertTypeResourceIri(): void $this->resourceMetadataCollectionFactoryProphecy->create('dummy')->willReturn($graphqlResourceMetadata); $this->propertyMetadataFactoryProphecy->create('rootClass', 'dummyProperty', Argument::type('array'))->shouldBeCalled()->willReturn((new ApiProperty())->withWritableLink(false)); - /** @var Operation $operation */ $operation = (new Query())->withName('test'); $graphqlType = $this->typeConverter->convertPhpType($type, true, $operation, 'dummy', 'rootClass', 'dummyProperty', 1); $this->assertSame(GraphQLType::string(), $graphqlType); @@ -244,9 +234,7 @@ public function testConvertTypeResourceIri(): void public function testConvertTypeInputResourceLegacy(): void { $type = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, 'dummy'); - /** @var Operation $operation */ $operation = new Query(); - /** @var ApiProperty $propertyMetadata */ $propertyMetadata = (new ApiProperty())->withWritableLink(true); $graphqlResourceMetadata = new ResourceMetadataCollection('dummy', [(new ApiResource())->withGraphQlOperations(['item_query' => $operation])]); $expectedGraphqlType = new ObjectType(['name' => 'resourceObjectType', 'fields' => []]); @@ -263,9 +251,7 @@ public function testConvertTypeInputResourceLegacy(): void public function testConvertTypeInputResource(): void { $type = Type::object('dummy'); - /** @var Operation $operation */ $operation = new Query(); - /** @var ApiProperty $propertyMetadata */ $propertyMetadata = (new ApiProperty())->withWritableLink(true); $graphqlResourceMetadata = new ResourceMetadataCollection('dummy', [(new ApiResource())->withGraphQlOperations(['item_query' => $operation])]); $expectedGraphqlType = new ObjectType(['name' => 'resourceObjectType', 'fields' => []]); @@ -295,7 +281,6 @@ public function testConvertTypeCollectionResourceLegacy(LegacyType $type, Object 'depth' => 0, ])->shouldBeCalled()->willReturn($expectedGraphqlType); - /** @var Operation $rootOperation */ $rootOperation = (new Query())->withName('test'); $graphqlType = $this->typeConverter->convertType($type, false, $rootOperation, 'resourceClass', 'rootClass', null, 0); $this->assertSame($expectedGraphqlType, $graphqlType); @@ -324,7 +309,6 @@ public function testConvertTypeCollectionResource(Type $type, ObjectType $expect 'depth' => 0, ])->shouldBeCalled()->willReturn($expectedGraphqlType); - /** @var Operation $rootOperation */ $rootOperation = (new Query())->withName('test'); $graphqlType = $this->typeConverter->convertPhpType($type, false, $rootOperation, 'resourceClass', 'rootClass', null, 0); $this->assertSame($expectedGraphqlType, $graphqlType); @@ -347,7 +331,6 @@ public function testConvertTypeCollectionEnumLegacy(): void $this->resourceMetadataCollectionFactoryProphecy->create(GenderTypeEnum::class)->shouldBeCalled()->willReturn(new ResourceMetadataCollection(GenderTypeEnum::class, [])); $this->typeBuilderProphecy->getEnumType(Argument::type(Operation::class))->willReturn($expectedGraphqlType); - /** @var Operation $rootOperation */ $rootOperation = (new Query())->withName('test'); $graphqlType = $this->typeConverter->convertType($type, false, $rootOperation, 'resourceClass', 'rootClass', null, 0); $this->assertSame($expectedGraphqlType, $graphqlType); @@ -360,7 +343,6 @@ public function testConvertTypeCollectionEnum(): void $this->resourceMetadataCollectionFactoryProphecy->create(GenderTypeEnum::class)->shouldBeCalled()->willReturn(new ResourceMetadataCollection(GenderTypeEnum::class, [])); $this->typeBuilderProphecy->getEnumType(Argument::type(Operation::class))->willReturn($expectedGraphqlType); - /** @var Operation $rootOperation */ $rootOperation = (new Query())->withName('test'); $graphqlType = $this->typeConverter->convertPhpType($type, false, $rootOperation, 'resourceClass', 'rootClass', null, 0); $this->assertSame($expectedGraphqlType, $graphqlType); diff --git a/src/GraphQl/Type/TypeConverter.php b/src/GraphQl/Type/TypeConverter.php index 83b82990825..037a32f1209 100644 --- a/src/GraphQl/Type/TypeConverter.php +++ b/src/GraphQl/Type/TypeConverter.php @@ -246,7 +246,7 @@ private function getResourceType(Type|LegacyType $type, bool $input, Operation $ private function resolveAstTypeNode(TypeNode $astTypeNode, string $fromType): ?GraphQLType { if ($astTypeNode instanceof NonNullTypeNode) { - /** @var NullableType|null $nullableAstTypeNode */ + /** @var (GraphQLType&NullableType)|null $nullableAstTypeNode */ $nullableAstTypeNode = $this->resolveNullableAstTypeNode($astTypeNode->type, $fromType); return $nullableAstTypeNode ? GraphQLType::nonNull($nullableAstTypeNode) : null; diff --git a/src/HttpCache/Tests/SouinPurgerTest.php b/src/HttpCache/Tests/SouinPurgerTest.php index 1a07617d9da..7bba11d8272 100644 --- a/src/HttpCache/Tests/SouinPurgerTest.php +++ b/src/HttpCache/Tests/SouinPurgerTest.php @@ -14,15 +14,13 @@ namespace ApiPlatform\HttpCache\Tests; use ApiPlatform\HttpCache\SouinPurger; -use GuzzleHttp\ClientInterface; -use GuzzleHttp\Promise\PromiseInterface; -use GuzzleHttp\Psr7\Response; use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; -use Psr\Http\Message\RequestInterface; -use Psr\Http\Message\ResponseInterface; +use Symfony\Component\HttpClient\Response\MockResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; +use Symfony\Contracts\HttpClient\ResponseStreamInterface; /** * @author Sylvain Combraque @@ -59,35 +57,24 @@ private function generateXResourcesTags(int $number, int $minimum = 0): array public function testMultiChunkedTags(): void { - /** @var HttpClientInterface $client */ - $client = new class implements ClientInterface { + $client = new class implements HttpClientInterface { public array $sentRegexes = []; - public function send(RequestInterface $request, array $options = []): ResponseInterface - { - throw new \LogicException('Not implemented'); - } - - public function sendAsync(RequestInterface $request, array $options = []): PromiseInterface - { - throw new \LogicException('Not implemented'); - } - - public function request($method, $uri, array $options = []): ResponseInterface + public function request(string $method, string $url, array $options = []): ResponseInterface { $this->sentRegexes[] = $options['headers']['Surrogate-Key']; - return new Response(); + return new MockResponse(); } - public function requestAsync($method, $uri, array $options = []): PromiseInterface + public function stream(ResponseInterface|iterable $responses, ?float $timeout = null): ResponseStreamInterface { throw new \LogicException('Not implemented'); } - public function getConfig($option = null): void + public function withOptions(array $options): static { - throw new \LogicException('Not implemented'); + return $this; } }; $purger = new SouinPurger([$client]); @@ -96,71 +83,49 @@ public function getConfig($option = null): void self::assertSame([ implode(', ', $this->generateXResourcesTags(146)), implode(', ', $this->generateXResourcesTags(200, 146)), - ], $client->sentRegexes); // @phpstan-ignore-line + ], $client->sentRegexes); } public function testPurgeWithMultipleClients(): void { - /** @var HttpClientInterface $client1 */ - $client1 = new class implements ClientInterface { - public $requests = []; + $client1 = new class implements HttpClientInterface { + public array $requests = []; - public function send(RequestInterface $request, array $options = []): ResponseInterface - { - throw new \LogicException('Not implemented'); - } - - public function sendAsync(RequestInterface $request, array $options = []): PromiseInterface - { - throw new \LogicException('Not implemented'); - } - - public function request($method, $uri, array $options = []): ResponseInterface + public function request(string $method, string $url, array $options = []): ResponseInterface { $this->requests[] = [$method, 'http://dummy_host/dummy_api_path/souin_api', $options]; - return new Response(); + return new MockResponse(); } - public function requestAsync($method, $uri, array $options = []): PromiseInterface + public function stream(ResponseInterface|iterable $responses, ?float $timeout = null): ResponseStreamInterface { throw new \LogicException('Not implemented'); } - public function getConfig($option = null): void + public function withOptions(array $options): static { - throw new \LogicException('Not implemented'); + return $this; } }; - /** @var HttpClientInterface $client2 */ - $client2 = new class implements ClientInterface { - public $requests = []; + $client2 = new class implements HttpClientInterface { + public array $requests = []; - public function send(RequestInterface $request, array $options = []): ResponseInterface - { - throw new \LogicException('Not implemented'); - } - - public function sendAsync(RequestInterface $request, array $options = []): PromiseInterface - { - throw new \LogicException('Not implemented'); - } - - public function request($method, $uri, array $options = []): ResponseInterface + public function request(string $method, string $url, array $options = []): ResponseInterface { $this->requests[] = [$method, 'http://dummy_host/dummy_api_path/souin_api', $options]; - return new Response(); + return new MockResponse(); } - public function requestAsync($method, $uri, array $options = []): PromiseInterface + public function stream(ResponseInterface|iterable $responses, ?float $timeout = null): ResponseStreamInterface { throw new \LogicException('Not implemented'); } - public function getConfig($option = null): void + public function withOptions(array $options): static { - throw new \LogicException('Not implemented'); + return $this; } }; @@ -170,12 +135,12 @@ public function getConfig($option = null): void Request::METHOD_PURGE, 'http://dummy_host/dummy_api_path/souin_api', ['headers' => ['Surrogate-Key' => '/foo']], - ], $client1->requests[0]); // @phpstan-ignore-line + ], $client1->requests[0]); self::assertSame([ Request::METHOD_PURGE, 'http://dummy_host/dummy_api_path/souin_api', ['headers' => ['Surrogate-Key' => '/foo']], - ], $client2->requests[0]); // @phpstan-ignore-line + ], $client2->requests[0]); } public function testGetResponseHeaders(): void diff --git a/src/HttpCache/Tests/VarnishPurgerTest.php b/src/HttpCache/Tests/VarnishPurgerTest.php index 0cfb5883458..9577d81c8d5 100644 --- a/src/HttpCache/Tests/VarnishPurgerTest.php +++ b/src/HttpCache/Tests/VarnishPurgerTest.php @@ -14,15 +14,13 @@ namespace ApiPlatform\HttpCache\Tests; use ApiPlatform\HttpCache\VarnishPurger; -use GuzzleHttp\ClientInterface; -use GuzzleHttp\Promise\PromiseInterface; -use GuzzleHttp\Psr7\Response; use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; -use Psr\Http\Message\RequestInterface; -use Psr\Http\Message\ResponseInterface; use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; +use Symfony\Component\HttpClient\Response\MockResponse; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; +use Symfony\Contracts\HttpClient\ResponseStreamInterface; /** * @author Kévin Dunglas @@ -59,10 +57,9 @@ public function testPurge(): void public function testEmptyTags(): void { - $clientProphecy1 = $this->prophesize(ClientInterface::class); + $clientProphecy1 = $this->prophesize(HttpClientInterface::class); $clientProphecy1->request()->shouldNotBeCalled(); - /** @var HttpClientInterface $client */ $client = $clientProphecy1->reveal(); $purger = new VarnishPurger([$client]); $purger->purge([]); @@ -71,42 +68,31 @@ public function testEmptyTags(): void #[\PHPUnit\Framework\Attributes\DataProvider('provideChunkHeaderCases')] public function testItChunksHeaderToAvoidHittingVarnishLimit(int $maxHeaderLength, array $iris, array $regexesToSend): void { - /** @var HttpClientInterface $client */ - $client = new class implements ClientInterface { + $client = new class implements HttpClientInterface { public array $sentRegexes = []; - public function send(RequestInterface $request, array $options = []): ResponseInterface - { - throw new \LogicException('Not implemented'); - } - - public function sendAsync(RequestInterface $request, array $options = []): PromiseInterface - { - throw new \LogicException('Not implemented'); - } - - public function request($method, $uri, array $options = []): ResponseInterface + public function request(string $method, string $url, array $options = []): ResponseInterface { $this->sentRegexes[] = $options['headers']['ApiPlatform-Ban-Regex']; - return new Response(); + return new MockResponse(); } - public function requestAsync($method, $uri, array $options = []): PromiseInterface + public function stream(ResponseInterface|iterable $responses, ?float $timeout = null): ResponseStreamInterface { throw new \LogicException('Not implemented'); } - public function getConfig($option = null): void + public function withOptions(array $options): static { - throw new \LogicException('Not implemented'); + return $this; } }; $purger = new VarnishPurger([$client], $maxHeaderLength); $purger->purge($iris); - self::assertSame($regexesToSend, $client->sentRegexes); // @phpstan-ignore-line + self::assertSame($regexesToSend, $client->sentRegexes); } public static function provideChunkHeaderCases(): \Generator diff --git a/src/HttpCache/Tests/VarnishXKeyPurgerTest.php b/src/HttpCache/Tests/VarnishXKeyPurgerTest.php index b73dc0e7458..bef8ad2b2bf 100644 --- a/src/HttpCache/Tests/VarnishXKeyPurgerTest.php +++ b/src/HttpCache/Tests/VarnishXKeyPurgerTest.php @@ -15,14 +15,14 @@ use ApiPlatform\HttpCache\VarnishXKeyPurger; use GuzzleHttp\ClientInterface; -use GuzzleHttp\Promise\PromiseInterface; use GuzzleHttp\Psr7\Response; use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; -use Psr\Http\Message\RequestInterface; -use Psr\Http\Message\ResponseInterface; use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; +use Symfony\Component\HttpClient\Response\MockResponse; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; +use Symfony\Contracts\HttpClient\ResponseStreamInterface; /** * @author Kévin Dunglas @@ -101,42 +101,31 @@ public function testCustomGlue(): void #[\PHPUnit\Framework\Attributes\DataProvider('provideChunkHeaderCases')] public function testItChunksHeaderToAvoidHittingVarnishLimit(int $maxHeaderLength, array $iris, array $keysToSend): void { - /** @var HttpClientInterface $client */ - $client = new class implements ClientInterface { + $client = new class implements HttpClientInterface { public array $sentKeys = []; - public function send(RequestInterface $request, array $options = []): ResponseInterface - { - throw new \LogicException('Not implemented'); - } - - public function sendAsync(RequestInterface $request, array $options = []): PromiseInterface - { - throw new \LogicException('Not implemented'); - } - - public function request($method, $uri, array $options = []): ResponseInterface + public function request(string $method, string $url, array $options = []): ResponseInterface { $this->sentKeys[] = $options['headers']['xkey']; - return new Response(); + return new MockResponse(); } - public function requestAsync($method, $uri, array $options = []): PromiseInterface + public function stream(ResponseInterface|iterable $responses, ?float $timeout = null): ResponseStreamInterface { throw new \LogicException('Not implemented'); } - public function getConfig($option = null): void + public function withOptions(array $options): static { - throw new \LogicException('Not implemented'); + return $this; } }; $purger = new VarnishXKeyPurger([$client], $maxHeaderLength); $purger->purge($iris); - self::assertSame($keysToSend, $client->sentKeys); // @phpstan-ignore-line + self::assertSame($keysToSend, $client->sentKeys); } public static function provideChunkHeaderCases(): \Generator diff --git a/src/Laravel/Eloquent/Paginator.php b/src/Laravel/Eloquent/Paginator.php index 93ee8379aec..4085cb18255 100644 --- a/src/Laravel/Eloquent/Paginator.php +++ b/src/Laravel/Eloquent/Paginator.php @@ -16,10 +16,9 @@ use ApiPlatform\State\Pagination\HasNextPagePaginatorInterface; use ApiPlatform\State\Pagination\PaginatorInterface; use Illuminate\Pagination\LengthAwarePaginator; -use IteratorAggregate; /** - * @implements IteratorAggregate + * @implements \IteratorAggregate * @implements PaginatorInterface */ final class Paginator implements PaginatorInterface, HasNextPagePaginatorInterface, \IteratorAggregate diff --git a/src/Laravel/Eloquent/PartialPaginator.php b/src/Laravel/Eloquent/PartialPaginator.php index 9560b2d859d..004cb228ab1 100644 --- a/src/Laravel/Eloquent/PartialPaginator.php +++ b/src/Laravel/Eloquent/PartialPaginator.php @@ -15,10 +15,9 @@ use ApiPlatform\State\Pagination\PartialPaginatorInterface; use Illuminate\Pagination\AbstractPaginator; -use IteratorAggregate; /** - * @implements IteratorAggregate + * @implements \IteratorAggregate * @implements PartialPaginatorInterface */ final class PartialPaginator implements PartialPaginatorInterface, \IteratorAggregate diff --git a/src/Metadata/ApiProperty.php b/src/Metadata/ApiProperty.php index 632e5c077fe..4feca9647ba 100644 --- a/src/Metadata/ApiProperty.php +++ b/src/Metadata/ApiProperty.php @@ -230,7 +230,7 @@ public function __construct( private array $extraProperties = [], ) { $this->types = \is_string($types) ? (array) $types : $types; - $this->serialize = \is_array($serialize) ? $serialize : [$serialize]; + $this->serialize = (null === $serialize || \is_array($serialize)) ? $serialize : [$serialize]; $this->nativeType = $nativeType; if ($this->builtinTypes) { diff --git a/src/Metadata/IdentifiersExtractorInterface.php b/src/Metadata/IdentifiersExtractorInterface.php index a8623a06119..6579ba41f82 100644 --- a/src/Metadata/IdentifiersExtractorInterface.php +++ b/src/Metadata/IdentifiersExtractorInterface.php @@ -13,6 +13,7 @@ namespace ApiPlatform\Metadata; +use ApiPlatform\Metadata\Exception\InvalidArgumentException; use ApiPlatform\Metadata\Exception\RuntimeException; /** @@ -28,6 +29,7 @@ interface IdentifiersExtractorInterface * @param array $context * * @throws RuntimeException + * @throws InvalidArgumentException * * @return array */ diff --git a/src/Metadata/IriConverterInterface.php b/src/Metadata/IriConverterInterface.php index 0d0b51dfa8c..2347f1c9c7d 100644 --- a/src/Metadata/IriConverterInterface.php +++ b/src/Metadata/IriConverterInterface.php @@ -15,6 +15,7 @@ use ApiPlatform\Metadata\Exception\InvalidArgumentException; use ApiPlatform\Metadata\Exception\ItemNotFoundException; +use ApiPlatform\Metadata\Exception\OperationNotFoundException; use ApiPlatform\Metadata\Exception\RuntimeException; /** @@ -40,6 +41,7 @@ public function getResourceFromIri(string $iri, array $context = [], ?Operation * @param object|class-string $resource * @param array|array{force_resource_class?: string|class-string, item_uri_template?: string, uri_variables?: array} $context * + * @throws OperationNotFoundException * @throws InvalidArgumentException * @throws RuntimeException */ diff --git a/src/Metadata/IsApiResource.php b/src/Metadata/IsApiResource.php index 0b881a6bdfa..a448db91637 100644 --- a/src/Metadata/IsApiResource.php +++ b/src/Metadata/IsApiResource.php @@ -15,6 +15,8 @@ /** * @author Kévin Dunglas + * + * @phpstan-ignore trait.unused */ trait IsApiResource { diff --git a/src/State/Pagination/Pagination.php b/src/State/Pagination/Pagination.php index 5daae171523..2544baf2917 100644 --- a/src/State/Pagination/Pagination.php +++ b/src/State/Pagination/Pagination.php @@ -88,7 +88,11 @@ public function getOffset(?Operation $operation = null, array $context = []): in return ($offset = ($context['count'] ?? 0) - $last) < 0 ? 0 : $offset; } - /** @var int|float $offset */ + /** + * @var int|float $offset + * + * @phpstan-ignore-next-line varTag.nativeType (https://github.com/phpstan/phpstan/issues/6683) + */ $offset = ($this->getPage($context) - 1) * $limit; if (!\is_int($offset)) { diff --git a/src/State/Provider/ContentNegotiationProvider.php b/src/State/Provider/ContentNegotiationProvider.php index 02f28f30121..b7f7733d8d2 100644 --- a/src/State/Provider/ContentNegotiationProvider.php +++ b/src/State/Provider/ContentNegotiationProvider.php @@ -105,7 +105,6 @@ private function getInputFormat(HttpOperation $operation, Request $request): ?st return null; } - /** @var string $contentType */ $formats = $operation->getInputFormats() ?? []; if ($format = $this->getMimeTypeFormat($contentType, $formats)) { return $format; diff --git a/src/Symfony/Doctrine/EventListener/PublishMercureUpdatesListener.php b/src/Symfony/Doctrine/EventListener/PublishMercureUpdatesListener.php index 4dce5fd3885..14763dbe96e 100644 --- a/src/Symfony/Doctrine/EventListener/PublishMercureUpdatesListener.php +++ b/src/Symfony/Doctrine/EventListener/PublishMercureUpdatesListener.php @@ -232,7 +232,7 @@ private function publishUpdate(object $object, array $options, string $type): vo // This may change in the feature, because it's not JSON Merge Patch compliant, // and I'm not a fond of this approach. $iri = $options['topics'] ?? $object->iri; - /** @var string $data */ + /** @var non-empty-string $data */ $data = json_encode(['@id' => $object->id] + ($this->includeType ? ['@type' => $object->type] : []), \JSON_THROW_ON_ERROR); } else { $resourceClass = $this->getObjectClass($object); diff --git a/src/Symfony/Routing/ApiLoader.php b/src/Symfony/Routing/ApiLoader.php index 5203af91afb..595c2b41369 100644 --- a/src/Symfony/Routing/ApiLoader.php +++ b/src/Symfony/Routing/ApiLoader.php @@ -39,7 +39,6 @@ final class ApiLoader extends Loader public function __construct(KernelInterface $kernel, private readonly ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory, private readonly ContainerInterface $container, private readonly array $formats, private readonly array $resourceClassDirectories = [], private readonly bool $graphqlEnabled = false, private readonly bool $entrypointEnabled = true, readonly bool $docsEnabled = true, private readonly bool $graphiQlEnabled = false) { - /** @var string[]|string $paths */ $paths = $kernel->locateResource('@ApiPlatformBundle/Resources/config/routing'); $this->fileLoader = new XmlFileLoader(new FileLocator($paths)); } diff --git a/tests/Behat/JsonApiContext.php b/tests/Behat/JsonApiContext.php index 3d95b2fba03..7cd50646c57 100644 --- a/tests/Behat/JsonApiContext.php +++ b/tests/Behat/JsonApiContext.php @@ -121,10 +121,6 @@ public function theJsonNodeShouldBeSorted(string $node = ''): void { $actual = (array) $this->getValueOfNode($node); - if (!\is_array($actual)) { - throw new \Exception(\sprintf('The "%s" node value is not an array', $node)); - } - $expected = $actual; ksort($expected); diff --git a/tests/Behat/OpenApiContext.php b/tests/Behat/OpenApiContext.php index 0d60eb407a5..ed196f4092c 100644 --- a/tests/Behat/OpenApiContext.php +++ b/tests/Behat/OpenApiContext.php @@ -144,8 +144,7 @@ public function assertThePropertyForTheOpenAPIClassShouldBeEqualTo(string $prope */ private function getPropertyInfo(string $propertyName, string $className): \stdClass { - /** @var iterable $properties */ - $properties = $this->getProperties($className); + $properties = (array) $this->getProperties($className); foreach ($properties as $classPropertyName => $property) { if ($classPropertyName === $propertyName) { return $property; diff --git a/tests/Fixtures/TestBundle/Security/SecuredDummyAttributeBasedVoter.php b/tests/Fixtures/TestBundle/Security/SecuredDummyAttributeBasedVoter.php index 40c692c1614..a934b4c8774 100644 --- a/tests/Fixtures/TestBundle/Security/SecuredDummyAttributeBasedVoter.php +++ b/tests/Fixtures/TestBundle/Security/SecuredDummyAttributeBasedVoter.php @@ -45,7 +45,7 @@ protected function voteOnAttribute(string $attribute, mixed $subject, TokenInter return false; } - if (!\in_array($subject, array_keys(self::RBAC), true) || !\is_array(self::RBAC[$subject])) { + if (!\array_key_exists($subject, self::RBAC)) { return false; } diff --git a/tests/OpenApi/Command/OpenApiCommandTest.php b/tests/OpenApi/Command/OpenApiCommandTest.php index 44619c3dfdf..6e9619c7df4 100644 --- a/tests/OpenApi/Command/OpenApiCommandTest.php +++ b/tests/OpenApi/Command/OpenApiCommandTest.php @@ -121,7 +121,7 @@ public function testExecuteWithYaml(): void public function testWriteToFile(): void { - /** @var string $tmpFile */ + /** @var non-falsy-string $tmpFile */ $tmpFile = tempnam(sys_get_temp_dir(), 'test_write_to_file'); $this->tester->run(['command' => 'api:openapi:export', '--output' => $tmpFile]); diff --git a/tests/State/RespondProcessorTest.php b/tests/State/RespondProcessorTest.php index decbb70287d..7f17340c7a9 100644 --- a/tests/State/RespondProcessorTest.php +++ b/tests/State/RespondProcessorTest.php @@ -18,13 +18,11 @@ use ApiPlatform\Metadata\Operation\Factory\OperationMetadataFactoryInterface; use ApiPlatform\Metadata\ResourceClassResolverInterface; use ApiPlatform\State\Processor\RespondProcessor; -use ApiPlatform\State\ProcessorInterface; use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Employee; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; class RespondProcessorTest extends TestCase @@ -74,7 +72,6 @@ class: Employee::class, return ($args[2] ?? null)?->getUriTemplate() ?? '/default'; }); - /** @var ProcessorInterface $respondProcessor */ $respondProcessor = new RespondProcessor($iriConverter->reveal(), $resourceClassResolver->reveal(), $operationMetadataFactory->reveal()); $response = $respondProcessor->process('content', $canonicalUriTemplateRedirectingOperation, context: [ @@ -106,7 +103,6 @@ public function testAddsExceptionHeaders(): void { $operation = new Get(); - /** @var ProcessorInterface $respondProcessor */ $respondProcessor = new RespondProcessor(); $req = new Request(); $req->attributes->set('exception', new TooManyRequestsHttpException(32)); @@ -121,7 +117,6 @@ public function testAddsHeaders(): void { $operation = new Get(headers: ['foo' => 'bar']); - /** @var ProcessorInterface $respondProcessor */ $respondProcessor = new RespondProcessor(); $req = new Request(); $response = $respondProcessor->process('content', $operation, context: [