Skip to content

Commit a2bb3bf

Browse files
authored
Merge pull request #2071 from dunglas/phpstan
Upgrade PHPStan to 0.10 and fix new violations
2 parents e6b18f3 + 6b785a0 commit a2bb3bf

32 files changed

+104
-190
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ jobs:
110110
- *update-project-dependencies
111111
- run:
112112
name: Install PHPStan
113-
command: composer global require phpstan/phpstan:^0.8
113+
command: composer global require phpstan/phpstan:^0.10
114114
- *save-composer-cache-by-revision
115115
- *save-composer-cache-by-branch
116116
- run:

phpstan.neon

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,35 @@ parameters:
33
- tests/Fixtures/app/AppKernel.php
44
excludes_analyse:
55
- tests/Fixtures/app/cache
6+
# The Symfony Configuration API isn't good enough to be analysed
7+
- src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php
68
ignoreErrors:
7-
- '#Call to an undefined method Symfony\\Component\\Routing\\Exception\\ExceptionInterface::getCode\(\)#'
8-
- '#Call to an undefined method Prophecy\\Prophecy\\ObjectProphecy::[a-zA-Z0-9_]+\(\)#'
9-
- '#Access to an undefined property Prophecy\\Prophecy\\ObjectProphecy::\$[a-zA-Z0-9_]+#'
10-
- '#Call to an undefined method PHPUnit\\Framework\\MockObject\\MockObject::[a-zA-Z0-9_]+\(\)#'
11-
- '#Call to an undefined method Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata::getAssociationMappings\(\)#'
9+
# Real problems, hard to fix
10+
- '#Parameter \#2 \$dqlPart of method Doctrine\\ORM\\QueryBuilder::add\(\) expects array\|object, string given\.#'
1211

1312
# False positives
13+
- '#Access to an undefined property Prophecy\\Prophecy\\ObjectProphecy::\$[a-zA-Z0-9_]+#'
14+
- '#Access to an undefined property object::\$isIdentifierComposite.#'
1415
- '#Call to an undefined method Doctrine\\Common\\Persistence\\ObjectManager::getConnection\(\)#'
15-
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Extension\\QueryResult(Item|Collection)ExtensionInterface::supportsResult\(\) invoked with 3 parameters, 1-2 required\.#'
16+
- '#Call to an undefined method PHPUnit\\Framework\\MockObject\\MockObject::[a-zA-Z0-9_]+\(\)#'
17+
- '#Call to an undefined method Prophecy\\Prophecy\\ObjectProphecy::[a-zA-Z0-9_]+\(\)#'
18+
- '#Method ApiPlatform\\Core\\Tests\\Bridge\\Doctrine\\Orm\\ItemDataProviderTest::getManagerRegistry\(\) should return Doctrine\\Common\\Persistence\\ManagerRegistry but returns object\.#'
19+
- '#Method ApiPlatform\\Core\\Tests\\Bridge\\Doctrine\\Util\\IdentifierManagerTraitTest::getObjectManager\(\) should return Doctrine\\Common\\Persistence\\ObjectManager but returns object\.#'
20+
# Temporary fix while the PHPStan extension for Prophecy isn't compatible with 0.10
21+
- '#Parameter .* expects .*, .*object.* given\.#'
22+
- '#Parameter \#[0-9] \$filterLocator of class ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Extension\\FilterExtension constructor expects ApiPlatform\\Core\\Api\\FilterCollection|Psr\\Container\\ContainerInterface(\|null)?, ArrayObject given\.#'
23+
- '#Property ApiPlatform\\Core\\Test\\DoctrineOrmFilterTestCase::\$managerRegistry \(Doctrine\\Common\\Persistence\\ManagerRegistry\) does not accept object\.#'
24+
- '#Property ApiPlatform\\Core\\Test\\DoctrineOrmFilterTestCase::\$repository \(Doctrine\\ORM\\EntityRepository\) does not accept Doctrine\\Common\\Persistence\\ObjectRepository\.#'
25+
# https://github.com/doctrine/doctrine2/pull/7298/files
26+
- '#Strict comparison using === between null and int will always evaluate to false\.#'
27+
- '#Strict comparison using !== between null and null will always evaluate to false\.#'
28+
29+
# Expected, due to deprecations
1630
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Extension\\QueryResult(Item|Collection)ExtensionInterface::getResult\(\) invoked with 4 parameters, 1 required\.#'
31+
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Extension\\QueryResult(Item|Collection)ExtensionInterface::supportsResult\(\) invoked with 3 parameters, 1-2 required\.#'
32+
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Filter\\AbstractFilter::apply\(\) invoked with 5 parameters, 3-4 required\.#'
1733
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Filter\\AbstractFilter::filterProperty\(\) invoked with 7 parameters, 5-6 required\.#'
34+
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Filter\\FilterInterface::apply\(\) invoked with 5 parameters, 3-4 required\.#'
1835
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Filter\\OrderFilter::filterProperty\(\) invoked with 7 parameters, 5-6 required\.#'
36+
- '#Method ApiPlatform\\Core\\DataProvider\\CollectionDataProviderInterface::getCollection\(\) invoked with 3 parameters, 1-2 required.#'
37+
- '#PHPDoc tag @param references unknown parameter \$operationName#'

src/Annotation/ApiFilter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ final class ApiFilter
5353

5454
public function __construct($options = [])
5555
{
56-
if (!isset($options['value'])) {
56+
if (!\is_string($options['value'] ?? null)) {
5757
throw new InvalidArgumentException('This annotation needs a value representing the filter class.');
5858
}
5959

60-
if (!is_subclass_of($options['value'], FilterInterface::class)) {
60+
if (!is_a($options['value'], FilterInterface::class, true)) {
6161
throw new InvalidArgumentException(sprintf('The filter class "%s" does not implement "%s".', $options['value'], FilterInterface::class));
6262
}
6363

src/Api/ResourceClassResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public function getResourceClass($value, string $resourceClass = null, bool $str
5151
$typeToFind = $type = $resourceClass;
5252
}
5353

54-
if (($strict && isset($type) && $resourceClass !== $type) || false === $isResourceClass = $this->isResourceClass($typeToFind)) {
54+
if (($strict && $resourceClass !== $type) || false === $isResourceClass = $this->isResourceClass($typeToFind)) {
5555
if (is_subclass_of($type, $resourceClass) && $this->isResourceClass($resourceClass)) {
5656
return $type;
5757
}

src/Bridge/Doctrine/Orm/Extension/FilterExtension.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,11 @@ public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGenerator
6161
}
6262

6363
foreach ($resourceFilters as $filterId) {
64-
if (!($filter = $this->getFilter($filterId)) instanceof FilterInterface) {
65-
continue;
64+
$filter = $this->getFilter($filterId);
65+
if ($filter instanceof FilterInterface) {
66+
$context['filters'] = $context['filters'] ?? [];
67+
$filter->apply($queryBuilder, $queryNameGenerator, $resourceClass, $operationName, $context);
6668
}
67-
68-
$context['filters'] = $context['filters'] ?? [];
69-
70-
$filter->apply($queryBuilder, $queryNameGenerator, $resourceClass, $operationName, $context);
7169
}
7270
}
7371
}

src/Bridge/Doctrine/Orm/Extension/OrderExtension.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGenerator
6060
$order = 'ASC';
6161
}
6262

63-
if (false === ($pos = \strpos($field, '.'))
64-
|| isset($classMetaData->embeddedClasses[\substr($field, 0, $pos)])) {
63+
$pos = strpos($field, '.');
64+
if (false === $pos || isset($classMetaData->embeddedClasses[\substr($field, 0, $pos)])) {
6565
// Configure default filter with property
6666
$field = "{$rootAlias}.{$field}";
6767
} else {

src/Bridge/Doctrine/Orm/Filter/AbstractFilter.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ protected function isPropertyNested(string $property/*, string $resourceClass*/)
164164
$resourceClass = null;
165165
}
166166

167-
if (false === $pos = strpos($property, '.')) {
167+
$pos = strpos($property, '.');
168+
if (false === $pos) {
168169
return false;
169170
}
170171

src/Bridge/Doctrine/Orm/Util/IdentifierManagerTrait.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ private function normalizeIdentifiers($id, ObjectManager $manager, string $resou
4949
$identifiersMap = [];
5050

5151
// first transform identifiers to a proper key/value array
52-
foreach (explode(';', $id) as $identifier) {
52+
foreach (explode(';', (string) $id) as $identifier) {
5353
if (!$identifier) {
5454
continue;
5555
}

src/Bridge/Doctrine/Orm/Util/QueryChecker.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,9 @@ public static function hasOrderByOnToManyJoin(QueryBuilder $queryBuilder, Manage
127127
$parts = QueryJoinParser::getOrderByParts($orderBy);
128128

129129
foreach ($parts as $part) {
130-
if (false !== ($pos = strpos($part, '.'))) {
131-
$alias = substr($part, 0, $pos);
132-
133-
$orderByAliases[$alias] = true;
130+
$pos = strpos($part, '.');
131+
if (false !== $pos) {
132+
$orderByAliases[substr($part, 0, $pos)] = true;
134133
}
135134
}
136135
}

src/Bridge/Symfony/Bundle/DependencyInjection/Compiler/AnnotationFilterPass.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ final class AnnotationFilterPass implements CompilerPassInterface
3939
public function process(ContainerBuilder $container)
4040
{
4141
$resourceClassDirectories = $container->getParameter('api_platform.resource_class_directories');
42+
/**
43+
* @var Reader
44+
*/
4245
$reader = $container->get('annotation_reader');
4346

4447
foreach (ReflectionClassRecursiveIterator::getReflectionClassesFromDirectories($resourceClassDirectories) as $className => $reflectionClass) {

src/Bridge/Symfony/Bundle/EventListener/SwaggerUiListener.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ final class SwaggerUiListener
1919
{
2020
/**
2121
* Sets SwaggerUiAction as controller if the requested format is HTML.
22-
*
23-
* @param $event GetResponseEvent
2422
*/
2523
public function onKernelRequest(GetResponseEvent $event)
2624
{

src/DataProvider/OperationDataProviderTrait.php

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@
1414
namespace ApiPlatform\Core\DataProvider;
1515

1616
use ApiPlatform\Core\Exception\InvalidArgumentException;
17-
use ApiPlatform\Core\Exception\InvalidIdentifierException;
1817
use ApiPlatform\Core\Exception\PropertyNotFoundException;
1918
use ApiPlatform\Core\Exception\RuntimeException;
20-
use ApiPlatform\Core\Identifier\Normalizer\ChainIdentifierDenormalizer;
2119

2220
/**
2321
* @internal
@@ -35,15 +33,10 @@ trait OperationDataProviderTrait
3533
private $itemDataProvider;
3634

3735
/**
38-
* @var SubresourceDataProviderInterface
36+
* @var SubresourceDataProviderInterface|null
3937
*/
4038
private $subresourceDataProvider;
4139

42-
/**
43-
* @var ChainIdentifierDenormalizer
44-
*/
45-
private $identifierDenormalizer;
46-
4740
/**
4841
* Retrieves data for a collection operation.
4942
*
@@ -57,7 +50,7 @@ private function getCollectionData(array $attributes, array $context)
5750
/**
5851
* Gets data for an item operation.
5952
*
60-
* @throws NotFoundHttpException
53+
* @throws PropertyNotFoundException
6154
*
6255
* @return object|null
6356
*/
@@ -73,14 +66,13 @@ private function getItemData($identifiers, array $attributes, array $context)
7366
/**
7467
* Gets data for a nested operation.
7568
*
76-
* @throws NotFoundHttpException
7769
* @throws RuntimeException
7870
*
7971
* @return object|null
8072
*/
8173
private function getSubresourceData($identifiers, array $attributes, array $context)
8274
{
83-
if (!$this->subresourceDataProvider) {
75+
if (null === $this->subresourceDataProvider) {
8476
throw new RuntimeException('Subresources not supported');
8577
}
8678

@@ -89,8 +81,6 @@ private function getSubresourceData($identifiers, array $attributes, array $cont
8981

9082
/**
9183
* @param array $parameters - usually comes from $request->attributes->all()
92-
*
93-
* @throws InvalidIdentifierException
9484
*/
9585
private function extractIdentifiers(array $parameters, array $attributes)
9686
{
@@ -104,12 +94,10 @@ private function extractIdentifiers(array $parameters, array $attributes)
10494

10595
$identifiers = [];
10696

107-
foreach ($attributes['subresource_context']['identifiers'] as $key => list($id, $resourceClass, $hasIdentifier)) {
108-
if (false === $hasIdentifier) {
109-
continue;
97+
foreach ($attributes['subresource_context']['identifiers'] as $key => list($id, , $hasIdentifier)) {
98+
if (false !== $hasIdentifier) {
99+
$identifiers[$id] = $parameters[$id];
110100
}
111-
112-
$identifiers[$id] = $parameters[$id];
113101
}
114102

115103
return $identifiers;

src/EventListener/DeserializeListener.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ public function onKernelRequest(GetResponseEvent $event)
8484
*/
8585
private function getFormat(Request $request): string
8686
{
87+
/**
88+
* @var string|null
89+
*/
8790
$contentType = $request->headers->get('CONTENT_TYPE');
8891
if (null === $contentType) {
8992
throw new NotAcceptableHttpException('The "Content-Type" header must exist.');

src/JsonApi/Serializer/ItemNormalizer.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
use ApiPlatform\Core\Serializer\AbstractItemNormalizer;
2626
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
2727
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
28+
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
29+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
2830

2931
/**
3032
* Converts between objects and array.
@@ -161,7 +163,10 @@ protected function denormalizeRelation(string $attributeName, PropertyMetadata $
161163
if (!$this->resourceClassResolver->isResourceClass($className)) {
162164
$context['resource_class'] = $className;
163165

164-
return $this->serializer->denormalize($value, $className, $format, $context);
166+
if ($this->serializer instanceof DenormalizerInterface) {
167+
return $this->serializer->denormalize($value, $className, $format, $context);
168+
}
169+
throw new InvalidArgumentException(sprintf('The injected serializer must be an instance of "%s".', DenormalizerInterface::class));
165170
}
166171

167172
if (!\is_array($value) || !isset($value['id'], $value['type'])) {
@@ -188,7 +193,10 @@ protected function normalizeRelation(PropertyMetadata $propertyMetadata, $relate
188193
} else {
189194
unset($context['resource_class']);
190195

191-
return $this->serializer->normalize($relatedObject, $format, $context);
196+
if ($this->serializer instanceof NormalizerInterface) {
197+
return $this->serializer->normalize($relatedObject, $format, $context);
198+
}
199+
throw new InvalidArgumentException(sprintf('The injected serializer must be an instance of "%s".', NormalizerInterface::class));
192200
}
193201
} else {
194202
$iri = $this->iriConverter->getIriFromItem($relatedObject);

src/JsonLd/Action/ContextAction.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,7 @@ public function __construct(ContextBuilderInterface $contextBuilder, ResourceNam
4444
/**
4545
* Generates a context according to the type requested.
4646
*
47-
* @param $shortName string
48-
*
4947
* @throws NotFoundHttpException
50-
*
51-
* @return array
5248
*/
5349
public function __invoke(string $shortName): array
5450
{

src/JsonLd/ContextBuilder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ final class ContextBuilder implements ContextBuilderInterface
3636
private $urlGenerator;
3737

3838
/**
39-
* @var NameConverterInterface
39+
* @var NameConverterInterface|null
4040
*/
4141
private $nameConverter;
4242

src/Metadata/Extractor/XmlExtractor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ private function getProperties(\SimpleXMLElement $resource): array
133133
/**
134134
* Transforms an XML attribute's value in a PHP value.
135135
*
136-
* @return bool|string|null
136+
* @return string|int|bool|null
137137
*/
138138
private function phpize(\SimpleXMLElement $array, string $key, string $type)
139139
{

src/Serializer/AbstractCollectionNormalizer.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,10 @@ public function normalize($object, $format = null, array $context = [])
9191
protected function getPaginationConfig($object, array $context = []): array
9292
{
9393
$currentPage = $lastPage = $itemsPerPage = $pageTotalItems = $totalItems = null;
94+
$paginated = $paginator = false;
9495

95-
if ($paginated = $paginator = $object instanceof PartialPaginatorInterface) {
96+
if ($object instanceof PartialPaginatorInterface) {
97+
$paginated = $paginator = true;
9698
if ($object instanceof PaginatorInterface) {
9799
$paginated = 1. !== $lastPage = $object->getLastPage();
98100
$totalItems = $object->getTotalItems();

src/Serializer/AbstractItemNormalizer.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
3232
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
3333
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
34+
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
35+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
3436

3537
/**
3638
* Base item normalizer.
@@ -306,7 +308,10 @@ protected function denormalizeRelation(string $attributeName, PropertyMetadata $
306308
$context['api_allow_update'] = true;
307309

308310
try {
309-
return $this->serializer->denormalize($value, $className, $format, $context);
311+
if ($this->serializer instanceof DenormalizerInterface) {
312+
return $this->serializer->denormalize($value, $className, $format, $context);
313+
}
314+
throw new InvalidArgumentException(sprintf('The injected serializer must be an instance of "%s".', DenormalizerInterface::class));
310315
} catch (InvalidValueException $e) {
311316
if (!$this->allowPlainIdentifiers || null === $this->itemDataProvider) {
312317
throw $e;
@@ -437,7 +442,10 @@ protected function getAttributeValue($object, $attribute, $format = null, array
437442

438443
unset($context['resource_class']);
439444

440-
return $this->serializer->normalize($attributeValue, $format, $context);
445+
if ($this->serializer instanceof NormalizerInterface) {
446+
return $this->serializer->normalize($attributeValue, $format, $context);
447+
}
448+
throw new InvalidArgumentException(sprintf('The injected serializer must be an instance of "%s".', NormalizerInterface::class));
441449
}
442450

443451
/**
@@ -478,7 +486,10 @@ protected function normalizeRelation(PropertyMetadata $propertyMetadata, $relate
478486
$context['resource_class'] = $resourceClass;
479487
}
480488

481-
return $this->serializer->normalize($relatedObject, $format, $context);
489+
if ($this->serializer instanceof NormalizerInterface) {
490+
return $this->serializer->normalize($relatedObject, $format, $context);
491+
}
492+
throw new InvalidArgumentException(sprintf('The injected serializer must be an instance of "%s".', NormalizerInterface::class));
482493
}
483494

484495
$iri = $this->iriConverter->getIriFromItem($relatedObject);

src/Swagger/Serializer/DocumentationNormalizer.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -757,11 +757,6 @@ public function supportsNormalization($data, $format = null)
757757
}
758758

759759
/**
760-
* @param string $operationType
761-
* @param bool $denormalization
762-
* @param ResourceMetadata $resourceMetadata
763-
* @param string $operationType
764-
*
765760
* @return array|null
766761
*/
767762
private function getSerializerContext(string $operationType, bool $denormalization, ResourceMetadata $resourceMetadata, string $operationName)

src/Util/AnnotationFilterExtractorTrait.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ trait AnnotationFilterExtractorTrait
3131
*
3232
* @param array $miscAnnotations class or property annotations
3333
*
34-
* @return array only ApiFilter annotations
34+
* @return \Iterator only ApiFilter annotations
3535
*/
3636
private function getFilterAnnotations(array $miscAnnotations): \Iterator
3737
{

tests/Annotation/ApiResourceTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public function testAssignation()
4444
public function testApiResourceAnnotation()
4545
{
4646
$reader = new AnnotationReader();
47+
/**
48+
* @var ApiResource
49+
*/
4750
$resource = $reader->getClassAnnotation(new \ReflectionClass(AnnotatedClass::class), ApiResource::class);
4851

4952
$this->assertSame('shortName', $resource->shortName);

0 commit comments

Comments
 (0)