Skip to content

Commit

Permalink
Command::getHelper return type
Browse files Browse the repository at this point in the history
  • Loading branch information
lookyman authored and ondrejmirtes committed Jan 8, 2022
1 parent 9e985a9 commit d31922c
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 0 deletions.
7 changes: 7 additions & 0 deletions extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ parameters:
- stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
- stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
- stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
- stubs/Symfony/Component/Console/Command.stub
- stubs/Symfony/Component/Console/Helper/HelperInterface.stub
- stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub
- stubs/Symfony/Component/DependencyInjection/ContainerBuilder.stub
- stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub
Expand Down Expand Up @@ -255,3 +257,8 @@ services:
-
factory: PHPStan\Type\Symfony\Form\FormInterfaceDynamicReturnTypeExtension
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]

# Command::getHelper() return type
-
factory: PHPStan\Type\Symfony\CommandGetHelperDynamicReturnTypeExtension
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
71 changes: 71 additions & 0 deletions src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Symfony;

use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Symfony\ConsoleApplicationResolver;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeUtils;
use Throwable;
use function count;
use function get_class;

final class CommandGetHelperDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
{

/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
private $consoleApplicationResolver;

public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
{
$this->consoleApplicationResolver = $consoleApplicationResolver;
}

public function getClass(): string
{
return 'Symfony\Component\Console\Command\Command';
}

public function isMethodSupported(MethodReflection $methodReflection): bool
{
return $methodReflection->getName() === 'getHelper';
}

public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
{
$defaultReturnType = new ObjectType('Symfony\Component\Console\Helper\HelperInterface');

if (!isset($methodCall->getArgs()[0])) {
return $defaultReturnType;
}

$classReflection = $scope->getClassReflection();
if ($classReflection === null) {
return $defaultReturnType;
}

$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
if (count($argStrings) !== 1) {
return $defaultReturnType;
}
$argName = $argStrings[0]->getValue();

$returnTypes = [];
foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) {
try {
$command->mergeApplicationDefinition();
$returnTypes[] = new ObjectType(get_class($command->getHelper($argName)));
} catch (Throwable $e) {
// no-op
}
}

return count($returnTypes) > 0 ? TypeCombinator::union(...$returnTypes) : $defaultReturnType;
}

}
11 changes: 11 additions & 0 deletions stubs/Symfony/Component/Console/Command.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Symfony\Component\Console\Command;

class Command
{
/**
* @return \Symfony\Component\Console\Helper\HelperInterface
*/
public function getHelper(string $name);
}
7 changes: 7 additions & 0 deletions stubs/Symfony/Component/Console/Helper/HelperInterface.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Symfony\Component\Console\Helper;

interface HelperInterface
{
}
1 change: 1 addition & 0 deletions tests/Type/Symfony/data/ExampleBaseCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
assertType('array<int, string>|string', $input->getArgument('diff'));
assertType('array<int, string>', $input->getArgument('arr'));
assertType('string|null', $input->getArgument('both'));
assertType('Symfony\Component\Console\Helper\QuestionHelper', $this->getHelper('question'));
}

}

0 comments on commit d31922c

Please sign in to comment.