Skip to content

Commit

Permalink
ContainerDynamicReturnTypeExtension resolves constant string types (#574
Browse files Browse the repository at this point in the history
)
  • Loading branch information
mglaman authored Jun 14, 2023
1 parent d5010b7 commit 0d5be5a
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 47 deletions.
60 changes: 16 additions & 44 deletions src/Type/ContainerDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,23 @@

namespace mglaman\PHPStanDrupal\Type;

use mglaman\PHPStanDrupal\Drupal\DrupalServiceDefinition;
use mglaman\PHPStanDrupal\Drupal\ServiceMap;
use PhpParser\Node;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\VariadicPlaceholder;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use Psr\Container\ContainerInterface;

class ContainerDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
{
/**
* @var ServiceMap
*/
private $serviceMap;
private ServiceMap $serviceMap;

public function __construct(ServiceMap $serviceMap)
{
Expand All @@ -44,49 +39,26 @@ public function getTypeFromMethodCall(
MethodReflection $methodReflection,
MethodCall $methodCall,
Scope $scope
): \PHPStan\Type\Type {
): Type {
$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
if (!isset($methodCall->args[0])) {
$args = $methodCall->getArgs();
if (count($args) !== 1) {
return $returnType;
}

$arg1 = $methodCall->args[0];
if ($arg1 instanceof VariadicPlaceholder) {
throw new ShouldNotHappenException();
}
$arg1 = $arg1->value;

$serviceId = $this->getServiceId($arg1);
if ($serviceId === null) {
return $returnType;
}
$methodName = $methodReflection->getName();
$types = [];
$argType = $scope->getType($args[0]->value);

if ($methodReflection->getName() === 'get') {
foreach ($argType->getConstantStrings() as $constantStringType) {
$serviceId = $constantStringType->getValue();
$service = $this->serviceMap->getService($serviceId);
if ($service instanceof DrupalServiceDefinition) {
return $service->getType();
if ($methodName === 'get') {
$types[] = $service !== null ? $service->getType() : $returnType;
} elseif ($methodName === 'has') {
$types[] = new ConstantBooleanType($service !== null);
}
return $returnType;
}

if ($methodReflection->getName() === 'has') {
return new ConstantBooleanType($this->serviceMap->getService($serviceId) instanceof DrupalServiceDefinition);
}

throw new ShouldNotHappenException();
}

protected function getServiceId(Node $arg1): ?string
{
if ($arg1 instanceof String_) {
// @todo determine what these types are.
return $arg1->value;
}

if ($arg1 instanceof ClassConstFetch && $arg1->class instanceof FullyQualified) {
return (string) $arg1->class;
}

return null;
return TypeCombinator::union(...$types);
}
}
7 changes: 4 additions & 3 deletions tests/src/Type/DrupalContainerDynamicReturnTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ final class DrupalContainerDynamicReturnTypeTest extends TypeInferenceTestCase

public function dataFileAsserts(): iterable
{
yield from $this->gatherAssertTypes(__DIR__ . '/data/container.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/drupal-service-static.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/drupal-class-resolver.php');
yield from self::gatherAssertTypes(__DIR__ . '/data/container.php');
yield from self::gatherAssertTypes(__DIR__ . '/data/drupal-service-static.php');
yield from self::gatherAssertTypes(__DIR__ . '/data/drupal-class-resolver.php');
yield from self::gatherAssertTypes(__DIR__ . '/data/bug-563.php');
}

/**
Expand Down
15 changes: 15 additions & 0 deletions tests/src/Type/data/bug-563.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Bug563;

use Drupal\Core\Cache\CacheBackendInterface;
use function PHPStan\Testing\assertType;

function foo(): void {
$container = \Drupal::getContainer();
assert($container !== null);
$cache_bins = ['page', 'dynamic_page_cache', 'render'];
foreach ($cache_bins as $cache_bin) {
assertType(CacheBackendInterface::class, $container->get("cache.$cache_bin"));
}
}

0 comments on commit 0d5be5a

Please sign in to comment.