1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace Yiisoft\Yii\Debug\Collector; |
||||
6 | |||||
7 | use Exception; |
||||
8 | use Psr\Container\ContainerExceptionInterface; |
||||
9 | use Psr\Container\ContainerInterface; |
||||
10 | use Yiisoft\Proxy\ProxyManager; |
||||
11 | use Yiisoft\Proxy\ProxyTrait; |
||||
12 | |||||
13 | use function is_callable; |
||||
14 | use function is_object; |
||||
15 | use function is_string; |
||||
16 | |||||
17 | final class ContainerInterfaceProxy implements ContainerInterface |
||||
18 | { |
||||
19 | use ProxyLogTrait; |
||||
20 | use ProxyTrait; |
||||
21 | |||||
22 | public const LOG_NOTHING = 0; |
||||
23 | public const LOG_ARGUMENTS = 1; |
||||
24 | public const LOG_RESULT = 2; |
||||
25 | public const LOG_ERROR = 4; |
||||
26 | |||||
27 | private ProxyManager $proxyManager; |
||||
28 | |||||
29 | private array $decoratedServices = []; |
||||
0 ignored issues
–
show
introduced
by
![]() |
|||||
30 | |||||
31 | private array $serviceProxy = []; |
||||
32 | |||||
33 | public function __construct(protected ContainerInterface $container, ContainerProxyConfig $config) |
||||
34 | { |
||||
35 | $this->config = $config; |
||||
36 | 7 | $this->proxyManager = new ProxyManager($this->config->getProxyCachePath()); |
|||
37 | } |
||||
38 | 7 | ||||
39 | 7 | public function withDecoratedServices(array $decoratedServices): self |
|||
40 | 7 | { |
|||
41 | $new = clone $this; |
||||
42 | $new->config = $this->config->withDecoratedServices($decoratedServices); |
||||
43 | 1 | return $new; |
|||
44 | } |
||||
45 | 1 | ||||
46 | 1 | public function get($id): mixed |
|||
47 | 1 | { |
|||
48 | $this->resetCurrentError(); |
||||
49 | $timeStart = microtime(true); |
||||
50 | $instance = null; |
||||
0 ignored issues
–
show
|
|||||
51 | try { |
||||
52 | $instance = $this->getInstance($id); |
||||
53 | 6 | } catch (ContainerExceptionInterface $e) { |
|||
54 | $this->repeatError($e); |
||||
55 | 6 | } finally { |
|||
56 | 6 | $this->logProxy(ContainerInterface::class, $this->container, 'get', [$id], $instance, $timeStart); |
|||
0 ignored issues
–
show
It seems like
$timeStart can also be of type string ; however, parameter $timeStart of Yiisoft\Yii\Debug\Collec...erfaceProxy::logProxy() does only seem to accept double , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
57 | } |
||||
58 | 6 | ||||
59 | 6 | if ( |
|||
0 ignored issues
–
show
|
|||||
60 | 1 | is_object($instance) |
|||
61 | 1 | && ( |
|||
62 | 5 | ($proxy = $this->getServiceProxyCache($id)) || |
|||
63 | 6 | ($proxy = $this->getServiceProxy($id, $instance)) |
|||
64 | ) |
||||
65 | ) { |
||||
66 | $this->setServiceProxyCache($id, $proxy); |
||||
67 | 5 | return $proxy; |
|||
68 | 5 | } |
|||
69 | |||||
70 | 4 | return $instance; |
|||
71 | 4 | } |
|||
72 | |||||
73 | /** |
||||
74 | 2 | * @throws ContainerExceptionInterface |
|||
75 | */ |
||||
76 | private function getInstance(string $id): mixed |
||||
77 | 6 | { |
|||
78 | if ($id === ContainerInterface::class) { |
||||
79 | 6 | return $this; |
|||
80 | } |
||||
81 | |||||
82 | 5 | return $this->container->get($id); |
|||
83 | } |
||||
84 | 5 | ||||
85 | private function isDecorated(string $service): bool |
||||
86 | { |
||||
87 | 5 | return $this->isActive() && $this->config->hasDecoratedService($service); |
|||
88 | } |
||||
89 | 5 | ||||
90 | public function isActive(): bool |
||||
91 | { |
||||
92 | 5 | return $this->config->getIsActive() && $this->config->getDecoratedServices() !== []; |
|||
93 | } |
||||
94 | 5 | ||||
95 | private function getServiceProxyCache(string $service): ?object |
||||
96 | { |
||||
97 | 5 | return $this->serviceProxy[$service] ?? null; |
|||
98 | } |
||||
99 | 5 | ||||
100 | 2 | private function getServiceProxy(string $service, object $instance): ?object |
|||
101 | { |
||||
102 | if (!$this->isDecorated($service)) { |
||||
103 | 4 | return null; |
|||
104 | 1 | } |
|||
105 | |||||
106 | if ($this->config->hasDecoratedServiceCallableConfig($service)) { |
||||
107 | 3 | return $this->getServiceProxyFromCallable($this->config->getDecoratedServiceConfig($service), $instance); |
|||
108 | 1 | } |
|||
109 | |||||
110 | if ($this->config->hasDecoratedServiceArrayConfigWithStringKeys($service)) { |
||||
111 | 2 | return $this->getCommonMethodProxy( |
|||
112 | 1 | interface_exists($service) || class_exists($service) ? $service : $instance::class, |
|||
113 | $instance, |
||||
114 | $this->config->getDecoratedServiceConfig($service) |
||||
115 | 1 | ); |
|||
116 | 1 | } |
|||
117 | |||||
118 | if ($this->config->hasDecoratedServiceArrayConfig($service)) { |
||||
119 | return $this->getServiceProxyFromArray($instance, $this->config->getDecoratedServiceConfig($service)); |
||||
120 | } |
||||
121 | |||||
122 | 1 | if (interface_exists($service) && ($this->config->hasCollector() || $this->config->hasDispatcher())) { |
|||
123 | return $this->getCommonServiceProxy($service, $instance); |
||||
124 | 1 | } |
|||
125 | |||||
126 | return null; |
||||
127 | 1 | } |
|||
128 | |||||
129 | 1 | private function getServiceProxyFromCallable(callable $callback, object $instance): ?object |
|||
130 | 1 | { |
|||
131 | 1 | return $callback($this, $instance); |
|||
132 | 1 | } |
|||
133 | |||||
134 | /** |
||||
135 | 1 | * @psalm-param class-string $service |
|||
136 | */ |
||||
137 | private function getCommonMethodProxy(string $service, object $instance, array $callbacks): ?object |
||||
138 | 1 | { |
|||
139 | $methods = []; |
||||
140 | foreach ($callbacks as $method => $callback) { |
||||
141 | 1 | if (is_string($method) && is_callable($callback)) { |
|||
142 | $methods[$method] = $callback; |
||||
143 | } |
||||
144 | } |
||||
145 | 1 | ||||
146 | return $this->proxyManager->createObjectProxy( |
||||
147 | $service, |
||||
148 | 1 | ServiceMethodProxy::class, |
|||
149 | 1 | [$service, $instance, $methods, $this->config] |
|||
150 | 1 | ); |
|||
151 | } |
||||
152 | 1 | ||||
153 | private function getServiceProxyFromArray(object $instance, array $params): ?object |
||||
154 | { |
||||
155 | try { |
||||
156 | $proxyClass = array_shift($params); |
||||
157 | foreach ($params as $index => $param) { |
||||
158 | 1 | if (is_string($param)) { |
|||
159 | try { |
||||
160 | $params[$index] = $this->get($param); |
||||
161 | } catch (Exception) { |
||||
162 | //leave as is |
||||
163 | } |
||||
164 | 1 | } |
|||
165 | } |
||||
166 | 1 | return new $proxyClass($instance, ...$params); |
|||
167 | } catch (Exception) { |
||||
168 | return null; |
||||
169 | 1 | } |
|||
170 | } |
||||
171 | |||||
172 | /** |
||||
173 | 4 | * @psalm-param class-string $service |
|||
174 | */ |
||||
175 | 4 | private function getCommonServiceProxy(string $service, object $instance): object |
|||
176 | { |
||||
177 | return $this->proxyManager->createObjectProxy( |
||||
178 | $service, |
||||
179 | ServiceProxy::class, |
||||
180 | [$service, $instance, $this->config] |
||||
181 | 4 | ); |
|||
182 | } |
||||
183 | 4 | ||||
184 | 4 | private function setServiceProxyCache(string $service, object $instance): void |
|||
185 | { |
||||
186 | 4 | $this->serviceProxy[$service] = $instance; |
|||
187 | 4 | } |
|||
188 | |||||
189 | /** |
||||
190 | 4 | * @psalm-suppress InvalidCatch |
|||
191 | 4 | */ |
|||
192 | public function has($id): bool |
||||
193 | { |
||||
194 | 4 | $this->resetCurrentError(); |
|||
195 | $timeStart = microtime(true); |
||||
196 | $result = null; |
||||
0 ignored issues
–
show
|
|||||
197 | |||||
198 | try { |
||||
199 | $result = $this->container->has($id); |
||||
200 | } catch (ContainerExceptionInterface $e) { |
||||
201 | $this->repeatError($e); |
||||
202 | } finally { |
||||
203 | $this->logProxy(ContainerInterface::class, $this->container, 'has', [$id], $result, $timeStart); |
||||
0 ignored issues
–
show
It seems like
$timeStart can also be of type string ; however, parameter $timeStart of Yiisoft\Yii\Debug\Collec...erfaceProxy::logProxy() does only seem to accept double , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
204 | } |
||||
205 | |||||
206 | return (bool)$result; |
||||
207 | } |
||||
208 | } |
||||
209 |