ContainerInterfaceProxy::getServiceProxy()   B
last analyzed

Complexity

Conditions 10
Paths 6

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 10.0578

Importance

Changes 0
Metric Value
eloc 14
c 0
b 0
f 0
nc 6
nop 2
dl 0
loc 27
cc 10
rs 7.6666
ccs 11
cts 12
cp 0.9167
crap 10.0578

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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