Skip to content

Commit 3b3083d

Browse files
committed
Merge remote-tracking branch 'origin/1.3.x' into 1.4.x
2 parents 4cc4788 + 34b3c43 commit 3b3083d

File tree

3 files changed

+73
-0
lines changed

3 files changed

+73
-0
lines changed

extension.neon

+5
Original file line numberDiff line numberDiff line change
@@ -336,3 +336,8 @@ services:
336336
tags:
337337
- phpstan.properties.readWriteExtension
338338
- phpstan.additionalConstructorsExtension
339+
340+
# CacheInterface::get() return type
341+
-
342+
factory: PHPStan\Type\Symfony\CacheInterfaceGetDynamicReturnTypeExtension
343+
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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\Reflection\ParametersAcceptorSelector;
9+
use PHPStan\Type\DynamicMethodReturnTypeExtension;
10+
use PHPStan\Type\GeneralizePrecision;
11+
use PHPStan\Type\Type;
12+
13+
final class CacheInterfaceGetDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
14+
{
15+
16+
public function getClass(): string
17+
{
18+
return 'Symfony\Contracts\Cache\CacheInterface';
19+
}
20+
21+
public function isMethodSupported(MethodReflection $methodReflection): bool
22+
{
23+
return $methodReflection->getName() === 'get';
24+
}
25+
26+
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
27+
{
28+
if (!isset($methodCall->getArgs()[1])) {
29+
return null;
30+
}
31+
32+
$callbackReturnType = $scope->getType($methodCall->getArgs()[1]->value);
33+
if ($callbackReturnType->isCallable()->yes()) {
34+
$parametersAcceptor = ParametersAcceptorSelector::selectFromArgs(
35+
$scope,
36+
$methodCall->getArgs(),
37+
$callbackReturnType->getCallableParametersAcceptors($scope)
38+
);
39+
$returnType = $parametersAcceptor->getReturnType();
40+
41+
// generalize template parameters
42+
return $returnType->generalize(GeneralizePrecision::templateArgument());
43+
}
44+
45+
return null;
46+
}
47+
48+
}

tests/Type/Symfony/data/cache.php

+20
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,26 @@ function testCacheCallable(\Symfony\Contracts\Cache\CacheInterface $cache): voi
1212
assertType('string', $result);
1313
};
1414

15+
/**
16+
* @param callable():string $fn
17+
*/
18+
function testNonScalarCacheCallable(\Symfony\Contracts\Cache\CacheInterface $cache, callable $fn): void {
19+
$result = $cache->get('foo', $fn);
20+
21+
assertType('string', $result);
22+
};
23+
24+
25+
/**
26+
* @param callable():non-empty-string $fn
27+
*/
28+
function testCacheCallableReturnTypeGeneralization(\Symfony\Contracts\Cache\CacheInterface $cache, callable $fn): void {
29+
$result = $cache->get('foo', $fn);
30+
31+
assertType('string', $result);
32+
};
33+
34+
1535
/**
1636
* @param \Symfony\Contracts\Cache\CallbackInterface<\stdClass> $cb
1737
*/

0 commit comments

Comments
 (0)