-
Notifications
You must be signed in to change notification settings - Fork 72
/
Copy pathContainerResolver.php
97 lines (80 loc) · 2.67 KB
/
ContainerResolver.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<?php
declare(strict_types=1);
namespace Psalm\LaravelPlugin\Util;
use PhpParser\Node\Arg;
use Psalm\LaravelPlugin\Providers\ApplicationProvider;
use Psalm\NodeTypeProvider;
use Psalm\Type\Atomic\TLiteralString;
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Union;
use function array_key_exists;
use function class_exists;
use function count;
use function get_class;
use function is_null;
use function is_object;
use function is_string;
final class ContainerResolver
{
/**
* map of abstract to concrete class fqn
* @var array
* @psalm-var array<string, class-string|string>
*/
private static $cache = [];
/**
* @psalm-return class-string|string|null
*/
private static function resolveFromApplicationContainer(string $abstract): ?string
{
if (array_key_exists($abstract, static::$cache)) {
return static::$cache[$abstract];
}
// dynamic analysis to resolve the actual type from the container
try {
$concrete = ApplicationProvider::getApp()->make($abstract);
} catch (\Throwable $e) {
return null;
}
if (is_string($concrete)) {
// some of the path helpers actually return a string when being resolved
$concreteClass = $concrete;
} elseif (is_object($concrete)) {
// normally we have an object resolved
$concreteClass = get_class($concrete);
} else {
// not sure how to handle this yet
return null;
}
static::$cache[$abstract] = $concreteClass;
return $concreteClass;
}
/**
* @param array<Arg> $call_args
*/
public static function resolvePsalmTypeFromApplicationContainerViaArgs(NodeTypeProvider $nodeTypeProvider, array $call_args): ?Union
{
if (! count($call_args)) {
return null;
}
$firstArgType = $nodeTypeProvider->getType($call_args[0]->value);
if ($firstArgType && $firstArgType->isSingleStringLiteral()) {
$abstract = $firstArgType->getSingleStringLiteral()->value;
$concrete = static::resolveFromApplicationContainer($abstract);
if (is_null($concrete)) {
return null;
}
// todo: is there a better way to check if this is a literal class string?
if (class_exists($concrete)) {
return new Union([
new TNamedObject($concrete),
]);
}
// the likes of publicPath, which returns a literal string
return new Union([
new TLiteralString($concrete),
]);
}
return null;
}
}