ContainerInterfaceProxy   A
last analyzed

Complexity

Total Complexity 38

Size/Duplication

Total Lines 190
Duplicated Lines 0 %

Test Coverage

Coverage 90.91%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 85
dl 0
loc 190
rs 9.36
c 1
b 0
f 0
ccs 70
cts 77
cp 0.9091
wmc 38

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A withDecoratedServices() 0 5 1
A isActive() 0 3 2
A getInstance() 0 7 2
A getServiceProxyCache() 0 3 1
A isDecorated() 0 3 2
A setServiceProxyCache() 0 3 1
A has() 0 15 2
A getCommonMethodProxy() 0 13 4
A getServiceProxyFromArray() 0 16 5
B getServiceProxy() 0 27 10
A getCommonServiceProxy() 0 6 1
A get() 0 25 5
A getServiceProxyFromCallable() 0 3 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 $decoratedServices = [];
0 ignored issues
show
introduced by
The private property $decoratedServices is not used, and could be removed.
Loading history...
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
Unused Code introduced by
The assignment to $instance is dead and can be removed.
Loading history...
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
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

56
            $this->logProxy(ContainerInterface::class, $this->container, 'get', [$id], $instance, /** @scrutinizer ignore-type */ $timeStart);
Loading history...
57
        }
58 6
59 6
        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...
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
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
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
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

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