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

194
            $this->logProxy(ContainerInterface::class, $this->container, 'has', [$id], $result, /** @scrutinizer ignore-type */ $timeStart);
Loading history...
195
        }
196
197
        return (bool)$result;
198
    }
199
}
200