Test Failed
Pull Request — master (#133)
by Rustam
03:14
created

ContainerInterfaceProxy::isDecorated()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 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_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
    public function __construct(ContainerInterface $container, ContainerProxyConfig $config)
37
    {
38
        $this->config = $config;
39
        $this->container = $container;
40
        $this->proxyManager = new ProxyManager($this->config->getProxyCachePath());
41
    }
42
43
    public function withDecoratedServices(array $decoratedServices): self
44
    {
45
        $new = clone $this;
46
        $new->config = $this->config->withDecoratedServices($decoratedServices);
47
        return $new;
48
    }
49
50
    /**
51
     * @psalm-suppress InvalidCatch
52
     */
53
    public function get($id)
54
    {
55
        $this->resetCurrentError();
56
        $timeStart = microtime(true);
57
        try {
58
            $instance = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $instance is dead and can be removed.
Loading history...
59
            $instance = $this->getInstance($id);
60
        } catch (ContainerExceptionInterface $e) {
61
            $this->repeatError($e);
62
        } finally {
63
            $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

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

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