Passed
Push — master ( a4ffc4...615af5 )
by Alexander
03:23
created

ContainerInterfaceProxy   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 178
Duplicated Lines 0 %

Test Coverage

Coverage 90.91%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 81
c 2
b 0
f 0
dl 0
loc 178
ccs 70
cts 77
cp 0.9091
rs 9.6
wmc 35

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
B getServiceProxy() 0 23 8
A getServiceProxyFromArray() 0 16 5
A getInstance() 0 3 1
A setServiceProxyCache() 0 3 1
A getServiceProxyCache() 0 3 1
A isDecorated() 0 3 2
A withDecoratedServices() 0 5 1
A getServiceProxyFromCallable() 0 3 1
A isActive() 0 3 2
A get() 0 22 5
A has() 0 14 2
A getCommonMethodProxy() 0 15 4
A getCommonServiceProxy() 0 6 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Debug\Proxy;
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_ARGUMENTS = 1;
23
24
    public const LOG_RESULT = 2;
25
26
    public const LOG_ERROR = 4;
27
28
    protected ContainerInterface $container;
29
30
    private ProxyManager $proxyManager;
31
32
    private array $decoratedServices = [];
0 ignored issues
show
introduced by
The private property $decoratedServices is not used, and could be removed.
Loading history...
33
34
    private array $serviceProxy = [];
35
36 7
    public function __construct(ContainerInterface $container, ContainerProxyConfig $config)
37
    {
38 7
        $this->config = $config;
39 7
        $this->container = $container;
40 7
        $this->proxyManager = new ProxyManager($this->config->getProxyCachePath());
41
    }
42
43 1
    public function withDecoratedServices(array $decoratedServices): self
44
    {
45 1
        $new = clone $this;
46 1
        $new->config = $this->config->withDecoratedServices($decoratedServices);
47 1
        return $new;
48
    }
49
50
    /**
51
     * @psalm-suppress InvalidCatch
52
     */
53 6
    public function get($id)
54
    {
55 6
        $this->resetCurrentError();
56 6
        $timeStart = microtime(true);
57
        try {
58 6
            $instance = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $instance is dead and can be removed.
Loading history...
59 6
            $instance = $this->getInstance($id);
60 1
        } catch (ContainerExceptionInterface $e) {
61 1
            $this->repeatError($e);
62 5
        } finally {
63 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\Proxy\...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

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

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