Passed
Pull Request — master (#225)
by Dmitriy
02:36
created

ContainerInterfaceProxy::getServiceProxyCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 1
cts 1
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
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 $serviceProxy = [];
30
31
    public function __construct(protected ContainerInterface $container, ContainerProxyConfig $config)
32
    {
33
        $this->config = $config;
34
        $this->proxyManager = new ProxyManager($this->config->getProxyCachePath());
35
    }
36 7
37
    public function withDecoratedServices(array $decoratedServices): self
38 7
    {
39 7
        $new = clone $this;
40 7
        $new->config = $this->config->withDecoratedServices($decoratedServices);
41
        return $new;
42
    }
43 1
44
    public function get($id): mixed
45 1
    {
46 1
        $this->resetCurrentError();
47 1
        $timeStart = microtime(true);
48
        $instance = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $instance is dead and can be removed.
Loading history...
49
        try {
50
            $instance = $this->getInstance($id);
51
        } catch (ContainerExceptionInterface $e) {
52
            $this->repeatError($e);
53 6
        } finally {
54
            $this->logProxy(ContainerInterface::class, $this->container, 'get', [$id], $instance, $timeStart);
0 ignored issues
show
Bug introduced by
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 ignore-type  annotation

54
            $this->logProxy(ContainerInterface::class, $this->container, 'get', [$id], $instance, /** @scrutinizer ignore-type */ $timeStart);
Loading history...
55 6
        }
56 6
57
        if (
0 ignored issues
show
Coding Style introduced by
Boolean operators between conditions must be at the beginning or end of the line, but not both
Loading history...
58 6
            is_object($instance)
59 6
            && (
60 1
                ($proxy = $this->getServiceProxyCache($id)) ||
0 ignored issues
show
Bug introduced by
The method getServiceProxyCache() does not exist on Yiisoft\Yii\Debug\Collec...ContainerInterfaceProxy. Did you maybe mean getServiceProxy()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

60
                ($proxy = $this->/** @scrutinizer ignore-call */ getServiceProxyCache($id)) ||

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
61 1
                ($proxy = $this->getServiceProxy($id, $instance))
62 5
            )
63 6
        ) {
64
            $this->setServiceProxyCache($id, $proxy);
65
            return $proxy;
66
        }
67 5
68 5
        return $instance;
69
    }
70 4
71 4
    /**
72
     * @throws ContainerExceptionInterface
73
     */
74 2
    private function getInstance(string $id): mixed
75
    {
76
        if ($id === ContainerInterface::class) {
77 6
            return $this;
78
        }
79 6
80
        return $this->container->get($id);
81
    }
82 5
83
    private function isDecorated(string $service): bool
84 5
    {
85
        return $this->isActive() && $this->config->hasDecoratedService($service);
86
    }
87 5
88
    public function isActive(): bool
89 5
    {
90
        return $this->config->getIsActive() && $this->config->getDecoratedServices() !== [];
91
    }
92 5
93
    private function getServiceProxy(string $service, object $instance): ?object
94 5
    {
95
        if (isset($this->serviceProxy[$service])) {
96
            return $this->serviceProxy[$service];
97 5
        }
98
99 5
        if (!$this->isDecorated($service)) {
100 2
            return null;
101
        }
102
103 4
        if ($this->config->hasDecoratedServiceCallableConfig($service)) {
104 1
            return $this->getServiceProxyFromCallable($this->config->getDecoratedServiceConfig($service), $instance);
105
        }
106
107 3
        if ($this->config->hasDecoratedServiceArrayConfigWithStringKeys($service)) {
108 1
            return $this->getCommonMethodProxy(
109
                interface_exists($service) || class_exists($service) ? $service : $instance::class,
110
                $instance,
111 2
                $this->config->getDecoratedServiceConfig($service)
112 1
            );
113
        }
114
115 1
        if ($this->config->hasDecoratedServiceArrayConfig($service)) {
116 1
            return $this->getServiceProxyFromArray($instance, $this->config->getDecoratedServiceConfig($service));
117
        }
118
119
        if (interface_exists($service) && ($this->config->hasCollector() || $this->config->hasDispatcher())) {
120
            return $this->getCommonServiceProxy($service, $instance);
121
        }
122 1
123
        return null;
124 1
    }
125
126
    private function getServiceProxyFromCallable(callable $callback, object $instance): ?object
127 1
    {
128
        return $callback($this, $instance);
129 1
    }
130 1
131 1
    /**
132 1
     * @psalm-param class-string $service
133
     */
134
    private function getCommonMethodProxy(string $service, object $instance, array $callbacks): ?object
135 1
    {
136
        $methods = [];
137
        foreach ($callbacks as $method => $callback) {
138 1
            if (is_string($method) && is_callable($callback)) {
139
                $methods[$method] = $callback;
140
            }
141 1
        }
142
143
        return $this->proxyManager->createObjectProxy(
144
            $service,
145 1
            ServiceMethodProxy::class,
146
            [$service, $instance, $methods, $this->config]
147
        );
148 1
    }
149 1
150 1
    private function getServiceProxyFromArray(object $instance, array $params): ?object
151
    {
152 1
        try {
153
            $proxyClass = array_shift($params);
154
            foreach ($params as $index => $param) {
155
                if (is_string($param)) {
156
                    try {
157
                        $params[$index] = $this->get($param);
158 1
                    } catch (Exception) {
159
                        //leave as is
160
                    }
161
                }
162
            }
163
            return new $proxyClass($instance, ...$params);
164 1
        } catch (Exception) {
165
            return null;
166 1
        }
167
    }
168
169 1
    /**
170
     * @psalm-param class-string $service
171
     */
172
    private function getCommonServiceProxy(string $service, object $instance): object
173 4
    {
174
        return $this->proxyManager->createObjectProxy(
175 4
            $service,
176
            ServiceProxy::class,
177
            [$service, $instance, $this->config]
178
        );
179
    }
180
181 4
    private function setServiceProxyCache(string $service, object $instance): void
182
    {
183 4
        $this->serviceProxy[$service] = $instance;
184 4
    }
185
186 4
    /**
187 4
     * @psalm-suppress InvalidCatch
188
     */
189
    public function has($id): bool
190 4
    {
191 4
        $this->resetCurrentError();
192
        $timeStart = microtime(true);
193
        $result = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
194 4
195
        try {
196
            $result = $this->container->has($id);
197
        } catch (ContainerExceptionInterface $e) {
198
            $this->repeatError($e);
199
        } finally {
200
            $this->logProxy(ContainerInterface::class, $this->container, 'has', [$id], $result, $timeStart);
0 ignored issues
show
Bug introduced by
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 ignore-type  annotation

200
            $this->logProxy(ContainerInterface::class, $this->container, 'has', [$id], $result, /** @scrutinizer ignore-type */ $timeStart);
Loading history...
201
        }
202
203
        return (bool)$result;
204
    }
205
}
206