Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit a4d8502

Browse files
committed
Support for InputInterface::hasArgument and ::hasOption
1 parent 0cb3c0b commit a4d8502

File tree

5 files changed

+166
-2
lines changed

5 files changed

+166
-2
lines changed

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ This extension provides following features:
1515
* Provides correct return type for `Envelope::all()` method based on the `$stampFqcn` parameter.
1616
* Notifies you when you try to get an unregistered service from the container.
1717
* Notifies you when you try to get a private service from the container.
18-
* Optionally correct return types for `InputInterface::getArgument()`and `::getOption`
18+
* Optionally correct return types for `InputInterface::getArgument()`, `::getOption`, `::hasArgument`, and `::hasOption`.
1919

2020
## Usage
2121

‎extension.neon‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ services:
7676
factory: PHPStan\Type\Symfony\ArgumentTypeSpecifyingExtension
7777
tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension]
7878

79+
# InputInterface::hasArgument() return type
80+
-
81+
factory: PHPStan\Type\Symfony\InputInterfaceHasArgumentDynamicReturnTypeExtension
82+
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
83+
7984
# InputInterface::getOption() return type
8085
-
8186
factory: PHPStan\Type\Symfony\InputInterfaceGetOptionDynamicReturnTypeExtension
@@ -85,3 +90,8 @@ services:
8590
-
8691
factory: PHPStan\Type\Symfony\OptionTypeSpecifyingExtension
8792
tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension]
93+
94+
# InputInterface::hasOption() return type
95+
-
96+
factory: PHPStan\Type\Symfony\InputInterfaceHasOptionDynamicReturnTypeExtension
97+
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Symfony;
4+
5+
use InvalidArgumentException;
6+
use PhpParser\Node\Expr\MethodCall;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Reflection\MethodReflection;
9+
use PHPStan\ShouldNotHappenException;
10+
use PHPStan\Symfony\ConsoleApplicationResolver;
11+
use PHPStan\Type\BooleanType;
12+
use PHPStan\Type\Constant\ConstantBooleanType;
13+
use PHPStan\Type\DynamicMethodReturnTypeExtension;
14+
use PHPStan\Type\Type;
15+
use PHPStan\Type\TypeUtils;
16+
use function array_unique;
17+
use function count;
18+
19+
final class InputInterfaceHasArgumentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
20+
{
21+
22+
/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
23+
private $consoleApplicationResolver;
24+
25+
public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
26+
{
27+
$this->consoleApplicationResolver = $consoleApplicationResolver;
28+
}
29+
30+
public function getClass(): string
31+
{
32+
return 'Symfony\Component\Console\Input\InputInterface';
33+
}
34+
35+
public function isMethodSupported(MethodReflection $methodReflection): bool
36+
{
37+
return $methodReflection->getName() === 'hasArgument';
38+
}
39+
40+
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
41+
{
42+
$defaultReturnType = new BooleanType();
43+
44+
if (!isset($methodCall->args[0])) {
45+
return $defaultReturnType;
46+
}
47+
48+
$classReflection = $scope->getClassReflection();
49+
if ($classReflection === null) {
50+
throw new ShouldNotHappenException();
51+
}
52+
53+
$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value));
54+
if (count($argStrings) !== 1) {
55+
return $defaultReturnType;
56+
}
57+
$argName = $argStrings[0]->getValue();
58+
59+
$returnTypes = [];
60+
foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) {
61+
try {
62+
$command->getDefinition()->getArgument($argName);
63+
$returnTypes[] = true;
64+
} catch (InvalidArgumentException $e) {
65+
$returnTypes[] = false;
66+
}
67+
}
68+
69+
if (count($returnTypes) === 0) {
70+
return $defaultReturnType;
71+
}
72+
73+
$returnTypes = array_unique($returnTypes);
74+
return count($returnTypes) === 1 ? new ConstantBooleanType($returnTypes[0]) : $defaultReturnType;
75+
}
76+
77+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Symfony;
4+
5+
use InvalidArgumentException;
6+
use PhpParser\Node\Expr\MethodCall;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Reflection\MethodReflection;
9+
use PHPStan\ShouldNotHappenException;
10+
use PHPStan\Symfony\ConsoleApplicationResolver;
11+
use PHPStan\Type\BooleanType;
12+
use PHPStan\Type\Constant\ConstantBooleanType;
13+
use PHPStan\Type\DynamicMethodReturnTypeExtension;
14+
use PHPStan\Type\Type;
15+
use PHPStan\Type\TypeUtils;
16+
use function array_unique;
17+
use function count;
18+
19+
final class InputInterfaceHasOptionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
20+
{
21+
22+
/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
23+
private $consoleApplicationResolver;
24+
25+
public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
26+
{
27+
$this->consoleApplicationResolver = $consoleApplicationResolver;
28+
}
29+
30+
public function getClass(): string
31+
{
32+
return 'Symfony\Component\Console\Input\InputInterface';
33+
}
34+
35+
public function isMethodSupported(MethodReflection $methodReflection): bool
36+
{
37+
return $methodReflection->getName() === 'hasOption';
38+
}
39+
40+
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
41+
{
42+
$defaultReturnType = new BooleanType();
43+
44+
if (!isset($methodCall->args[0])) {
45+
return $defaultReturnType;
46+
}
47+
48+
$classReflection = $scope->getClassReflection();
49+
if ($classReflection === null) {
50+
throw new ShouldNotHappenException();
51+
}
52+
53+
$optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value));
54+
if (count($optStrings) !== 1) {
55+
return $defaultReturnType;
56+
}
57+
$optName = $optStrings[0]->getValue();
58+
59+
$returnTypes = [];
60+
foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) {
61+
try {
62+
$command->getDefinition()->getOption($optName);
63+
$returnTypes[] = true;
64+
} catch (InvalidArgumentException $e) {
65+
$returnTypes[] = false;
66+
}
67+
}
68+
69+
if (count($returnTypes) === 0) {
70+
return $defaultReturnType;
71+
}
72+
73+
$returnTypes = array_unique($returnTypes);
74+
return count($returnTypes) === 1 ? new ConstantBooleanType($returnTypes[0]) : $defaultReturnType;
75+
}
76+
77+
}

‎tests/Symfony/NeonTest.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public function testExtensionNeon(): void
4141
], $container->getParameters());
4242

4343
self::assertCount(6, $container->findByTag('phpstan.rules.rule'));
44-
self::assertCount(9, $container->findByTag('phpstan.broker.dynamicMethodReturnTypeExtension'));
44+
self::assertCount(11, $container->findByTag('phpstan.broker.dynamicMethodReturnTypeExtension'));
4545
self::assertCount(5, $container->findByTag('phpstan.typeSpecifier.methodTypeSpecifyingExtension'));
4646
self::assertInstanceOf(ServiceMap::class, $container->getByType(ServiceMap::class));
4747
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /