Passed
Pull Request — master (#225)
by Dmitriy
06:09 queued 03:43
created

ContainerInterfaceProxy::getServiceProxyCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
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
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
    /**
45 1
     * @psalm-suppress InvalidCatch
46 1
     */
47 1
    public function get($id)
48
    {
49
        $this->resetCurrentError();
50
        $timeStart = microtime(true);
51
        try {
52
            $instance = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $instance is dead and can be removed.
Loading history...
53 6
            $instance = $this->getInstance($id);
54
        } catch (ContainerExceptionInterface $e) {
55 6
            $this->repeatError($e);
56 6
        } finally {
57
            $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

57
            $this->logProxy(ContainerInterface::class, $this->container, 'get', [$id], $instance, /** @scrutinizer ignore-type */ $timeStart);
Loading history...
58 6
        }
59 6
60 1
        if (is_object($instance) && ($proxy = $this->getServiceProxy($id, $instance))) {
61 1
            $this->setServiceProxyCache($id, $proxy);
62 5
            return $proxy;
63 6
        }
64
65
        return $instance;
66
    }
67 5
68 5
    private function getInstance(string $id)
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($service, $instance, $this->config->getDecoratedServiceConfig($service));
103 4
        }
104 1
105
        if ($this->config->hasDecoratedServiceArrayConfig($service)) {
106
            return $this->getServiceProxyFromArray($instance, $this->config->getDecoratedServiceConfig($service));
107 3
        }
108 1
109
        if (interface_exists($service) && ($this->config->hasCollector() || $this->config->hasDispatcher())) {
110
            return $this->getCommonServiceProxy($service, $instance);
111 2
        }
112 1
113
        return null;
114
    }
115 1
116 1
    private function getServiceProxyFromCallable(callable $callback, object $instance): ?object
117
    {
118
        return $callback($this, $instance);
119
    }
120
121
    private function getCommonMethodProxy(string $service, object $instance, array $callbacks): ?object
122 1
    {
123
        $methods = [];
124 1
        while ($callback = current($callbacks)) {
125
            $method = key($callbacks);
126
            if (is_string($method) && is_callable($callback)) {
127 1
                $methods[$method] = $callback;
128
            }
129 1
            next($callbacks);
130 1
        }
131 1
132 1
        return $this->proxyManager->createObjectProxy(
133
            $service,
134
            ServiceMethodProxy::class,
135 1
            [$service, $instance, $methods, $this->config]
136
        );
137
    }
138 1
139
    private function getServiceProxyFromArray(object $instance, array $params): ?object
140
    {
141 1
        try {
142
            $proxyClass = array_shift($params);
143
            foreach ($params as $index => $param) {
144
                if (is_string($param)) {
145 1
                    try {
146
                        $params[$index] = $this->get($param);
147
                    } catch (Exception) {
148 1
                        //leave as is
149 1
                    }
150 1
                }
151
            }
152 1
            return new $proxyClass($instance, ...$params);
153
        } catch (Exception) {
154
            return null;
155
        }
156
    }
157
158 1
    private function getCommonServiceProxy(string $service, object $instance): object
159
    {
160
        return $this->proxyManager->createObjectProxy(
161
            $service,
162
            ServiceProxy::class,
163
            [$service, $instance, $this->config]
164 1
        );
165
    }
166 1
167
    private function setServiceProxyCache(string $service, object $instance): void
168
    {
169 1
        $this->serviceProxy[$service] = $instance;
170
    }
171
172
    /**
173 4
     * @psalm-suppress InvalidCatch
174
     */
175 4
    public function has($id): bool
176
    {
177
        $this->resetCurrentError();
178
        $timeStart = microtime(true);
179
        $result = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
180
181 4
        try {
182
            $result = $this->container->has($id);
183 4
        } catch (ContainerExceptionInterface $e) {
184 4
            $this->repeatError($e);
185
        } finally {
186 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

186
            $this->logProxy(ContainerInterface::class, $this->container, 'has', [$id], $result, /** @scrutinizer ignore-type */ $timeStart);
Loading history...
187 4
        }
188
189
        return (bool)$result;
190 4
    }
191
}
192