Skip to content

Commit 5da43bc

Browse files
committed
Add FQCN support to implementsInterface
1 parent c8cd404 commit 5da43bc

8 files changed

+96
-8
lines changed

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
],
88
"require": {
99
"php": "^7.2 || ^8.0",
10-
"phpstan/phpstan": "^1.8.0"
10+
"phpstan/phpstan": "^1.8.1"
1111
},
1212
"require-dev": {
1313
"nikic/php-parser": "^4.13.0",

src/Type/WebMozartAssert/AssertTypeSpecifyingExtension.php

+10-4
Original file line numberDiff line numberDiff line change
@@ -451,10 +451,16 @@ private static function getExpressionResolvers(): array
451451
return null;
452452
}
453453

454-
return new Instanceof_(
455-
$expr->value,
456-
new Name($classType->getValue())
457-
);
454+
$classReflection = (new ObjectType($classType->getValue()))->getClassReflection();
455+
if ($classReflection === null) {
456+
return null;
457+
}
458+
459+
if (!$classReflection->isInterface()) {
460+
return new ConstFetch(new Name('false'));
461+
}
462+
463+
return self::$resolvers['subclassOf']($scope, $expr, $class);
458464
},
459465
'keyExists' => static function (Scope $scope, Arg $array, Arg $key): Expr {
460466
return new FuncCall(

tests/Type/WebMozartAssert/AssertTypeSpecifyingExtensionTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public function dataFileAsserts(): iterable
1818
yield from $this->gatherAssertTypes(__DIR__ . '/data/object.php');
1919
yield from $this->gatherAssertTypes(__DIR__ . '/data/string.php');
2020
yield from $this->gatherAssertTypes(__DIR__ . '/data/type.php');
21+
22+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-18.php');
2123
}
2224

2325
/**

tests/Type/WebMozartAssert/ImpossibleCheckTypeMethodCallRuleTest.php

+27
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,18 @@ public function testExtension(): void
8484
'Call to static method Webmozart\Assert\Assert::allContains() with array<non-empty-string> and \'foo\' will always evaluate to true.',
8585
98,
8686
],
87+
[
88+
'Call to static method Webmozart\Assert\Assert::implementsInterface() with class-string<WebmozartAssertImpossibleCheck\Bar>|WebmozartAssertImpossibleCheck\Bar and \'WebmozartAssertImpossibleCheck\\\Bar\' will always evaluate to true.',
89+
105,
90+
],
91+
[
92+
'Call to static method Webmozart\Assert\Assert::implementsInterface() with class-string<WebmozartAssertImpossibleCheck\Bar> and \'WebmozartAssertImpossibleCheck\\\Bar\' will always evaluate to true.',
93+
108,
94+
],
95+
[
96+
'Call to static method Webmozart\Assert\Assert::implementsInterface() with mixed and \'WebmozartAssertImpossibleCheck\\\Foo\' will always evaluate to false.',
97+
111,
98+
],
8799
]);
88100
}
89101

@@ -175,6 +187,21 @@ public function testBug8(): void
175187
]);
176188
}
177189

190+
public function testBug17(): void
191+
{
192+
$this->analyse([__DIR__ . '/data/bug-17.php'], [
193+
[
194+
'Call to static method Webmozart\Assert\Assert::implementsInterface() with \'DateTime\' and \'DateTimeInterface\' will always evaluate to true.',
195+
9,
196+
],
197+
]);
198+
}
199+
200+
public function testBug18(): void
201+
{
202+
$this->analyse([__DIR__ . '/data/bug-18.php'], []);
203+
}
204+
178205
public function testBug32(): void
179206
{
180207
$this->analyse([__DIR__ . '/data/bug-32.php'], []);
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Bug17;
4+
5+
use DateTimeInterface;
6+
use Webmozart\Assert\Assert;
7+
8+
(function () {
9+
Assert::implementsInterface(\DateTime::class, DateTimeInterface::class);
10+
Assert::implementsInterface(\DateTimeZone::class, DateTimeInterface::class);
11+
})();
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Bug18;
4+
5+
use Webmozart\Assert\Assert;
6+
use function PHPStan\Testing\assertType;
7+
8+
class MyThingFactory
9+
{
10+
public function make(string $thing)
11+
{
12+
Assert::implementsInterface($thing, SomeDto::class);
13+
14+
assertType('class-string<Bug18\SomeDto>', $thing);
15+
}
16+
}
17+
18+
interface SomeDto {}

tests/Type/WebMozartAssert/data/impossible-check.php

+12
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,18 @@ public function nonEmptyStringAndSomethingUnknownNarrow($a, string $b, array $c,
9999
Assert::allContains($d, 'bar');
100100
}
101101

102+
public function implementsInterface($a, string $b, $c): void
103+
{
104+
Assert::implementsInterface($a, Bar::class);
105+
Assert::implementsInterface($a, Bar::class);
106+
107+
Assert::implementsInterface($b, Bar::class);
108+
Assert::implementsInterface($b, Bar::class);
109+
110+
Assert::implementsInterface($c, Unknown::class);
111+
Assert::implementsInterface($c, self::class);
112+
}
113+
102114
}
103115

104116
interface Bar {};

tests/Type/WebMozartAssert/data/object.php

+15-3
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,25 @@ public function interfaceExists($a, $b): void
3535
assertType('class-string|null', $b);
3636
}
3737

38-
public function implementsInterface($a, $b): void
38+
public function implementsInterface($a, $b, string $c, object $d, $e, $f): void
3939
{
4040
Assert::implementsInterface($a, ObjectFoo::class);
41-
assertType('PHPStan\Type\WebMozartAssert\ObjectFoo', $a);
41+
assertType('class-string<PHPStan\Type\WebMozartAssert\ObjectFoo>|PHPStan\Type\WebMozartAssert\ObjectFoo', $a);
4242

4343
Assert::nullOrImplementsInterface($b, ObjectFoo::class);
44-
assertType('PHPStan\Type\WebMozartAssert\ObjectFoo|null', $b);
44+
assertType('class-string<PHPStan\Type\WebMozartAssert\ObjectFoo>|PHPStan\Type\WebMozartAssert\ObjectFoo|null', $b);
45+
46+
Assert::implementsInterface($c, ObjectFoo::class);
47+
assertType('class-string<PHPStan\Type\WebMozartAssert\ObjectFoo>', $c);
48+
49+
Assert::implementsInterface($d, ObjectFoo::class);
50+
assertType('PHPStan\Type\WebMozartAssert\ObjectFoo', $d);
51+
52+
Assert::implementsInterface($e, self::class);
53+
assertType('mixed', $e);
54+
55+
Assert::implementsInterface($f, Unknown::class);
56+
assertType('mixed', $f);
4557
}
4658

4759
public function propertyExists(object $a): void

0 commit comments

Comments
 (0)