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

ContainerInterfaceProxy::getServiceProxy()   B

Complexity

Conditions 11
Paths 7

Size

Total Lines 31
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 11.044

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 16
c 1
b 0
f 0
nc 7
nop 2
dl 0
loc 31
rs 7.3166
ccs 13
cts 14
cp 0.9286
crap 11.044

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 $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