Passed
Pull Request — 1.x (#113)
by Akihito
04:06 queued 01:49
created

ResourceStorage::__construct()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 6
Bugs 0 Features 1
Metric Value
cc 4
eloc 14
c 6
b 0
f 1
nc 3
nop 8
dl 0
loc 27
ccs 11
cts 11
cp 1
crap 4
rs 9.7998

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\QueryRepository;
6
7
use BEAR\QueryRepository\Annotation\IsOptimizeCache;
8
use BEAR\RepositoryModule\Annotation\EtagPool;
9
use BEAR\RepositoryModule\Annotation\KnownTagTtl;
10
use BEAR\Resource\AbstractUri;
11
use BEAR\Resource\RequestInterface;
12
use BEAR\Resource\ResourceObject;
13
use Doctrine\Common\Cache\CacheProvider;
14
use Psr\Cache\CacheItemPoolInterface;
15
use Ray\PsrCacheModule\Annotation\Shared;
16
use Symfony\Component\Cache\Adapter\AdapterInterface;
17
use Symfony\Component\Cache\Adapter\DoctrineAdapter;
18
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
19
use Symfony\Contracts\Cache\ItemInterface;
20
21
use function array_merge;
22
use function array_unique;
23
use function assert;
24
use function explode;
25
use function implode;
26
use function is_array;
27
use function is_string;
28
use function sprintf;
29
use function strtoupper;
30
31
final class ResourceStorage implements ResourceStorageInterface
32
{
33 30
    use ResourceStorageCacheableTrait;
34
35 30
    /**
36 30
     * Resource object cache prefix
37
     */
38
    private const KEY_RO = 'ro-';
39
40
    /**
41 3
     * Resource static cache prifix
42
     */
43 3
    private const KEY_DONUT = 'donut-';
44
45
    /** @var RepositoryLoggerInterface */
46
    private $logger;
47
48
    /** @var TagAwareAdapter */
49 26
    private $roPool;
50
51 26
    /** @var TagAwareAdapter */
52 26
    private $etagPool;
53 26
54
    /** @var PurgerInterface */
55 26
    private $purger;
56
57 26
    /** @var UriTagInterface */
58
    private $uriTag;
59 26
60 26
    /** @var ResourceStorageSaver */
61
    private $saver;
62
63
    /** @var float */
64
    private $knownTagTtl;
65 27
66
    /** @var bool */
67 27
    private $isOptimizeCache;
68 27
69
    /**
70 27
     * @Shared("pool")
71 27
     * @EtagPool("etagPool")
72
     * @KnownTagTtl("knownTagTtl")
73
     * @IsOptimizeCache("isOptimizeCache")
74
     */
75
    #[Shared('pool'), EtagPool('etagPool'), KnownTagTtl('knownTagTtl'), IsOptimizeCache('isOptimizeCache')]
76 25
    public function __construct(
77
        RepositoryLoggerInterface $logger,
78 25
        PurgerInterface $etagDeleter,
79
        UriTagInterface $uriTag,
80 25
        ?CacheItemPoolInterface $pool = null,
81
        ?CacheItemPoolInterface $etagPool = null,
82
        ?CacheProvider $cache = null,
83
        float $knownTagTtl = 0.0,
84
        bool $isOptimizeCache = false
85
    ) {
86 12
        $this->logger = $logger;
87
        $this->purger = $etagDeleter;
88 12
        $this->uriTag = $uriTag;
89 12
        $this->saver = new ResourceStorageSaver();
90
        if ($pool === null && $cache instanceof CacheProvider) {
91 12
            $this->injectDoctrineCache($cache);
92
93
            return;
94
        }
95
96
        $this->knownTagTtl = $knownTagTtl;
97 24
        assert($pool instanceof AdapterInterface);
98
        $etagPool =  $etagPool instanceof AdapterInterface ? $etagPool : $pool;
99 24
        $this->roPool = new TagAwareAdapter($pool, $etagPool, $knownTagTtl);
100 24
        $this->etagPool = new TagAwareAdapter($etagPool, $etagPool, $knownTagTtl);
101 24
        $this->isOptimizeCache = $isOptimizeCache;
102
    }
103 24
104
    private function injectDoctrineCache(CacheProvider $cache): void
105
    {
106
        /** @psalm-suppress DeprecatedClass */
107
        $this->roPool = new TagAwareAdapter(new DoctrineAdapter($cache));
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Cache\Adapter\DoctrineAdapter has been deprecated: Since Symfony 5.4, use Doctrine\Common\Cache\Psr6\CacheAdapter instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

107
        $this->roPool = new TagAwareAdapter(/** @scrutinizer ignore-deprecated */ new DoctrineAdapter($cache));
Loading history...
108
        $this->etagPool = $this->roPool;
109 2
    }
110
111 2
    /**
112 2
     * {@inheritdoc}
113 2
     */
114
    public function get(AbstractUri $uri): ?ResourceState
115 2
    {
116
        $item = $this->roPool->getItem($this->getUriKey($uri, self::KEY_RO));
117
        assert($item instanceof ItemInterface);
118 26
        $state = $item->get();
119
        assert($state instanceof ResourceState || $state === null);
120 26
121 8
        return $state;
122
    }
123 18
124 18
    public function getDonut(AbstractUri $uri): ?ResourceDonut
125 2
    {
126
        $key = $this->getUriKey($uri, self::KEY_DONUT);
127
        $item = $this->roPool->getItem($key);
128
        assert($item instanceof ItemInterface);
129 18
        $donut = $item->get();
130
        assert($donut instanceof ResourceDonut || $donut === null);
131
132 27
        return $donut;
133
    }
134 27
135 18
    /**
136
     * {@inheritdoc}
137 10
     */
138 10
    public function hasEtag(string $etag): bool
139 10
    {
140 10
        return $this->etagPool->hasItem($etag);
141 10
    }
142 10
143
    /**
144
     * {@inheritdoc}
145
     */
146 10
    public function deleteEtag(AbstractUri $uri)
147
    {
148
        $uriTag = ($this->uriTag)($uri);
149
150
        return $this->invalidateTags([$uriTag]);
151
    }
152
153
    /**
154
     * {@inheritdoc}
155
     */
156
    public function invalidateTags(array $tags): bool
157
    {
158
        $tag = $tags !== [] ? implode(' ', $tags) : '';
159
        $this->logger->log('invalidate-etag tags:%s', $tag);
160
        $valid1 = $this->roPool->invalidateTags($tags);
161
        $valid2 = $this->etagPool->invalidateTags($tags);
162
        ($this->purger)(implode(' ', $tags));
163
164
        return $valid1 && $valid2;
165
    }
166
167
    /**
168
     * {@inheritdoc}
169
     *
170
     * @return bool
171
     */
172
    public function saveValue(ResourceObject $ro, int $ttl)
173
    {
174
        /** @psalm-suppress MixedAssignment $body */
175
        $body = $this->evaluateBody($ro->body);
176
        $value = ResourceState::create($ro, $body, null);
177
        $key = $this->getUriKey($ro->uri, self::KEY_RO);
178
        $tags = $this->getTags($ro);
179
        $this->logger->log('save-value uri:%s tags:%s ttl:%s', $ro->uri, $tags, $ttl);
180
181
        return $this->saver->__invoke($key, $value, $this->roPool, $tags, $ttl);
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     *
187
     * @return bool
188
     */
189
    public function saveView(ResourceObject $ro, int $ttl)
190
    {
191
        $this->logger->log('save-view uri:%s ttl:%s', $ro->uri, $ttl);
192
        /** @psalm-suppress MixedAssignment $body */
193
        $body = $this->evaluateBody($ro->body);
194
        $value = ResourceState::create($ro, $body, $ro->view);
195
        $key = $this->getUriKey($ro->uri, self::KEY_RO);
196
        $tags = $this->getTags($ro);
197
198
        return $this->saver->__invoke($key, $value, $this->roPool, $tags, $ttl);
199
    }
200
201
    public function saveDonut(AbstractUri $uri, ResourceDonut $donut, ?int $sMaxAge): void
202
    {
203
        $key = $this->getUriKey($uri, self::KEY_DONUT);
204
        $this->logger->log('save-donut uri:%s s-maxage:%s', $uri, $sMaxAge);
205
206
        $this->saver->__invoke($key, $donut, $this->roPool, [], $sMaxAge);
207
    }
208
209
    public function saveDonutView(ResourceObject $ro, ?int $ttl): bool
210
    {
211
        $body = $this->isOptimizeCache || ! is_array($ro->body) ? [] : $this->evaluateDonutBody($ro->body);
212
        $resourceState = ResourceState::create($ro, $body, $ro->view);
213
        $key = $this->getUriKey($ro->uri, self::KEY_RO);
214
        $tags = $this->getTags($ro);
215
        $this->logger->log('save-donut-view uri:%s surrogate-keys:%s s-maxage:%s', $ro->uri, $tags, $ttl);
216
217
        return $this->saver->__invoke($key, $resourceState, $this->roPool, $tags, $ttl);
218
    }
219
220
    /**
221
     * @param array<mixed> $body
222
     *
223
     * @return array<mixed>
224
     */
225
    private function evaluateDonutBody(array $body): array
226
    {
227
        foreach ($body as $key => $item) {
228
            if ($item instanceof DonutRequest) {
229
                $body[$key] = $item->getBody();
230
            }
231
        }
232
233
        return $body;
234
    }
235
236
    /**
237
     * @return list<string>
0 ignored issues
show
Bug introduced by
The type BEAR\QueryRepository\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
238
     */
239
    private function getTags(ResourceObject $ro): array
240
    {
241
        $etag = $ro->headers['ETag'];
242
        $tags = [$etag, ($this->uriTag)($ro->uri)];
243
        if (isset($ro->headers[Header::SURROGATE_KEY])) {
244
            $tags = array_merge($tags, explode(' ', $ro->headers[Header::SURROGATE_KEY]));
245
        }
246
247
        /** @var list<string> $uniqueTags */
248
        $uniqueTags = array_unique($tags);
249
250
        return $uniqueTags;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $uniqueTags returns the type BEAR\QueryRepository\list which is incompatible with the type-hinted return array.
Loading history...
251
    }
252
253
    /**
254
     * @param mixed $body
255
     *
256
     * @return mixed
257
     */
258
    private function evaluateBody($body)
259
    {
260
        if (! is_array($body)) {
261
            return $body;
262
        }
263
264
        /** @psalm-suppress MixedAssignment $item */
265
        foreach ($body as &$item) {
266
            if ($item instanceof RequestInterface) {
267
                $item = ($item)();
268
            }
269
270
            if ($item instanceof ResourceObject) {
271
                $item->body = $this->evaluateBody($item->body);
272
            }
273
        }
274
275
        return $body;
276
    }
277
278
    private function getUriKey(AbstractUri $uri, string $type): string
279
    {
280
        return $type . ($this->uriTag)($uri) . (isset($_SERVER['X_VARY']) ? $this->getVary() : '');
281
    }
282
283
    private function getVary(): string
284
    {
285
        $xvary = $_SERVER['X_VARY'];
286
        assert(is_string($xvary));
287
        $varys = explode(',', $xvary);
288
        $varyString = '';
289
        foreach ($varys as $vary) {
290
            $phpVaryKey = sprintf('X_%s', strtoupper($vary));
291
            if (isset($_SERVER[$phpVaryKey]) && is_string($_SERVER[$phpVaryKey])) {
292
                $varyString .= $_SERVER[$phpVaryKey];
293
            }
294
        }
295
296
        return $varyString;
297
    }
298
299
    public function saveEtag(AbstractUri $uri, string $etag, string $surrogateKeys, ?int $ttl): void
300
    {
301
        $tags = $surrogateKeys !== '' ? explode(' ', $surrogateKeys) : [];
302
        $tags[] = (new UriTag())($uri);
303
        /** @var list<string> $uniqueTags */
304
        $uniqueTags = array_unique($tags);
305
        $this->logger->log('save-etag uri:%s etag:%s surrogate-keys:%s', $uri, $etag, $uniqueTags);
306
        $this->saver->__invoke($etag, 'etag', $this->etagPool, $uniqueTags, $ttl);
0 ignored issues
show
Bug introduced by
$uniqueTags of type BEAR\QueryRepository\list is incompatible with the type array expected by parameter $tags of BEAR\QueryRepository\Res...torageSaver::__invoke(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

306
        $this->saver->__invoke($etag, 'etag', $this->etagPool, /** @scrutinizer ignore-type */ $uniqueTags, $ttl);
Loading history...
307
    }
308
}
309