Skip to content

Commit d31922c

Browse files
lookymanondrejmirtes
authored andcommitted
Command::getHelper return type
1 parent 9e985a9 commit d31922c

File tree

5 files changed

+97
-0
lines changed

5 files changed

+97
-0
lines changed

Diff for: extension.neon

+7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ parameters:
1212
- stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
1313
- stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
1414
- stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
15+
- stubs/Symfony/Component/Console/Command.stub
16+
- stubs/Symfony/Component/Console/Helper/HelperInterface.stub
1517
- stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub
1618
- stubs/Symfony/Component/DependencyInjection/ContainerBuilder.stub
1719
- stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub
@@ -255,3 +257,8 @@ services:
255257
-
256258
factory: PHPStan\Type\Symfony\Form\FormInterfaceDynamicReturnTypeExtension
257259
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
260+
261+
# Command::getHelper() return type
262+
-
263+
factory: PHPStan\Type\Symfony\CommandGetHelperDynamicReturnTypeExtension
264+
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Symfony;
4+
5+
use PhpParser\Node\Expr\MethodCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Reflection\MethodReflection;
8+
use PHPStan\Symfony\ConsoleApplicationResolver;
9+
use PHPStan\Type\DynamicMethodReturnTypeExtension;
10+
use PHPStan\Type\ObjectType;
11+
use PHPStan\Type\Type;
12+
use PHPStan\Type\TypeCombinator;
13+
use PHPStan\Type\TypeUtils;
14+
use Throwable;
15+
use function count;
16+
use function get_class;
17+
18+
final class CommandGetHelperDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
19+
{
20+
21+
/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
22+
private $consoleApplicationResolver;
23+
24+
public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
25+
{
26+
$this->consoleApplicationResolver = $consoleApplicationResolver;
27+
}
28+
29+
public function getClass(): string
30+
{
31+
return 'Symfony\Component\Console\Command\Command';
32+
}
33+
34+
public function isMethodSupported(MethodReflection $methodReflection): bool
35+
{
36+
return $methodReflection->getName() === 'getHelper';
37+
}
38+
39+
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
40+
{
41+
$defaultReturnType = new ObjectType('Symfony\Component\Console\Helper\HelperInterface');
42+
43+
if (!isset($methodCall->getArgs()[0])) {
44+
return $defaultReturnType;
45+
}
46+
47+
$classReflection = $scope->getClassReflection();
48+
if ($classReflection === null) {
49+
return $defaultReturnType;
50+
}
51+
52+
$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
53+
if (count($argStrings) !== 1) {
54+
return $defaultReturnType;
55+
}
56+
$argName = $argStrings[0]->getValue();
57+
58+
$returnTypes = [];
59+
foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) {
60+
try {
61+
$command->mergeApplicationDefinition();
62+
$returnTypes[] = new ObjectType(get_class($command->getHelper($argName)));
63+
} catch (Throwable $e) {
64+
// no-op
65+
}
66+
}
67+
68+
return count($returnTypes) > 0 ? TypeCombinator::union(...$returnTypes) : $defaultReturnType;
69+
}
70+
71+
}

Diff for: stubs/Symfony/Component/Console/Command.stub

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Symfony\Component\Console\Command;
4+
5+
class Command
6+
{
7+
/**
8+
* @return \Symfony\Component\Console\Helper\HelperInterface
9+
*/
10+
public function getHelper(string $name);
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Symfony\Component\Console\Helper;
4+
5+
interface HelperInterface
6+
{
7+
}

Diff for: tests/Type/Symfony/data/ExampleBaseCommand.php

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
2525
assertType('array<int, string>|string', $input->getArgument('diff'));
2626
assertType('array<int, string>', $input->getArgument('arr'));
2727
assertType('string|null', $input->getArgument('both'));
28+
assertType('Symfony\Component\Console\Helper\QuestionHelper', $this->getHelper('question'));
2829
}
2930

3031
}

0 commit comments

Comments
 (0)