Skip to content

Commit 900b1e7

Browse files
committed
feat: add CollectionHasSpecifyingExtension
1 parent 9ac546b commit 900b1e7

5 files changed

Lines changed: 125 additions & 0 deletions

File tree

rules.neon

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,9 @@ rules:
66
- Shopware\PhpStan\Rule\SetForeignKeyRule
77
- Shopware\PhpStan\Rule\MethodBecomesAbstractRule
88
- Shopware\PhpStan\Rule\ScheduledTaskTooLowIntervalRule
9+
10+
services:
11+
-
12+
class: Shopware\PhpStan\Type\CollectionHasSpecifyingExtension
13+
tags:
14+
- phpstan.typeSpecifier.methodTypeSpecifyingExtension
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
namespace Shopware\PhpStan\Type;
4+
5+
use PhpParser\Node\Expr\MethodCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Analyser\SpecifiedTypes;
8+
use PHPStan\Analyser\TypeSpecifier;
9+
use PHPStan\Analyser\TypeSpecifierAwareExtension;
10+
use PHPStan\Analyser\TypeSpecifierContext;
11+
use PHPStan\Reflection\MethodReflection;
12+
use PHPStan\Type\MethodTypeSpecifyingExtension;
13+
use PHPStan\Type\NullType;
14+
use PHPStan\Type\TypeCombinator;
15+
use Shopware\Core\Framework\Struct\Collection;
16+
17+
/**
18+
* @internal
19+
*/
20+
class CollectionHasSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
21+
{
22+
private TypeSpecifier $typeSpecifier;
23+
24+
public function getClass(): string
25+
{
26+
return Collection::class;
27+
}
28+
29+
public function isMethodSupported(MethodReflection $methodReflection, MethodCall $node, TypeSpecifierContext $context): bool
30+
{
31+
$declaringClass = $methodReflection->getDeclaringClass();
32+
33+
return (
34+
$declaringClass->getName() === Collection::class
35+
|| $declaringClass->isSubclassOf(Collection::class)
36+
)
37+
&& $methodReflection->getName() === 'has' && !$context->null();
38+
}
39+
40+
public function specifyTypes(
41+
MethodReflection $methodReflection,
42+
MethodCall $node,
43+
Scope $scope,
44+
TypeSpecifierContext $context,
45+
): SpecifiedTypes {
46+
$getExpr = new MethodCall($node->var, 'get', $node->args);
47+
48+
$getterTypes = $this->typeSpecifier->create(
49+
$getExpr,
50+
TypeCombinator::removeNull($scope->getType($getExpr)),
51+
$context,
52+
$scope,
53+
);
54+
55+
return $getterTypes->unionWith(
56+
$this->typeSpecifier->create(
57+
$getExpr,
58+
new NullType(),
59+
$context->negate(),
60+
$scope,
61+
),
62+
);
63+
}
64+
65+
public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
66+
{
67+
$this->typeSpecifier = $typeSpecifier;
68+
}
69+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace Shopware\PhpStan\Tests\Type;
4+
5+
use PHPStan\Testing\TypeInferenceTestCase;
6+
7+
class CollectionHasSpecifyingExtensionTest extends TypeInferenceTestCase
8+
{
9+
public function testCollectionHas(): void
10+
{
11+
foreach (static::gatherAssertTypes(__DIR__ . '/../data/CollectionHasSpecifyingExtension/collection_has.php') as $args) {
12+
// because of the autoload issue we can not use data providers as phpstan does itself,
13+
// therefore we need to rely on this hacks
14+
$assertType = array_shift($args);
15+
$file = array_shift($args);
16+
17+
$this->assertFileAsserts($assertType, $file, ...$args);
18+
}
19+
}
20+
21+
public static function getAdditionalConfigFiles(): array
22+
{
23+
return [
24+
__DIR__ . '/../data/CollectionHasSpecifyingExtension/extension.neon',
25+
];
26+
}
27+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Shopware\Core\Framework\DataAbstractionLayer\Entity;
6+
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
7+
8+
use function PHPStan\Testing\assertType;
9+
10+
$collection = new EntityCollection(['foo' => new Entity()]);
11+
12+
if ($collection->has('foo')) {
13+
assertType(Entity::class, $collection->get('foo'));
14+
assertType(Entity::class . '|null', $collection->get('bar'));
15+
} else {
16+
assertType('null', $collection->get('foo'));
17+
assertType(Entity::class . '|null', $collection->get('bar'));
18+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
services:
2+
-
3+
class: Shopware\PhpStan\Type\CollectionHasSpecifyingExtension
4+
tags:
5+
- phpstan.typeSpecifier.methodTypeSpecifyingExtension

0 commit comments

Comments
 (0)