Passed
Push — donut_body_cache ( d3062b...89e861 )
by Akihito
02:28
created

ResourceStorage::__construct()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

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
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
    use ResourceStorageCacheableTrait;
34
35
    /**
36
     * Resource object cache prefix
37
     */
38
    private const KEY_RO = 'ro-';
39
40
    /**
41
     * Resource static cache prifix
42
     */
43
    private const KEY_DONUT = 'donut-';
44
45
    /** @var RepositoryLoggerInterface */
46
    private $logger;
47
48
    /** @var TagAwareAdapter */
49
    private $roPool;
50
51
    /** @var TagAwareAdapter */
52
    private $etagPool;
53
54
    /** @var PurgerInterface */
55
    private $purger;
56
57
    /** @var UriTagInterface */
58
    private $uriTag;
59
60
    /** @var ResourceStorageSaver */
61
    private $saver;
62
63
    /** @var float */
64
    private $knownTagTtl;
65
66
    /** @var bool */
67
    private $isOptimizeCache;
68
69
    /**
70
     * @Shared("pool")
71
     * @EtagPool("etagPool")
72
     * @KnownTagTtl("knownTagTtl")
73
     * @IsOptimizeCache("isOptimizeCache")
74
     */
75
    #[Shared('pool'), EtagPool('etagPool'), KnownTagTtl('knownTagTtl'), IsOptimizeCache('isOptimizeCache')]
76
    public function __construct(
77
        RepositoryLoggerInterface $logger,
78
        PurgerInterface $etagDeleter,
79
        UriTagInterface $uriTag,
80
        ?CacheItemPoolInterface $pool = null,
81
        ?CacheItemPoolInterface $etagPool = null,
82
        ?CacheProvider $cache = null,
83
        float $knownTagTtl = 0.0,
84
        bool $isOptimizeCache = false
85
    ) {
86
        $this->logger = $logger;
87
        $this->purger = $etagDeleter;
88
        $this->uriTag = $uriTag;
89
        $this->saver = new ResourceStorageSaver();
90
        if ($pool === null && $cache instanceof CacheProvider) {
91
            $this->injectDoctrineCache($cache);
92
93
            return;
94
        }
95
96
        $this->knownTagTtl = $knownTagTtl;
97
        assert($pool instanceof AdapterInterface);
98
        $etagPool =  $etagPool instanceof AdapterInterface ? $etagPool : $pool;
99
        $this->roPool = new TagAwareAdapter($pool, $etagPool, $knownTagTtl);
100
        $this->etagPool = new TagAwareAdapter($etagPool, $etagPool, $knownTagTtl);
101
        $this->isOptimizeCache = $isOptimizeCache;
102
    }
103
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
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114
    public function get(AbstractUri $uri): ?ResourceState
115
    {
116
        $item = $this->roPool->getItem($this->getUriKey($uri, self::KEY_RO));
117
        assert($item instanceof ItemInterface);
118
        $state = $item->get();
119
        assert($state instanceof ResourceState || $state === null);
120
121
        return $state;
122
    }
123
124
    public function getDonut(AbstractUri $uri): ?ResourceDonut
125
    {
126
        $key = $this->getUriKey($uri, self::KEY_DONUT);
127
        $item = $this->roPool->getItem($key);
128
        assert($item instanceof ItemInterface);
129
        $donut = $item->get();
130
        assert($donut instanceof ResourceDonut || $donut === null);
131
132
        return $donut;
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138
    public function hasEtag(string $etag): bool
139
    {
140
        return $this->etagPool->hasItem($etag);
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146
    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
     * @return array<mixed>
222
     */
223
    private function evaluateDonutBody(array $body): array
224
    {
225
        foreach ($body as $key => $item) {
226
            if ($item instanceof DonutRequest) {
227
                $body[$key] = $item->getBody();
228
            }
229
        }
230
231
        return $body;
232
    }
233
234
    /**
235
     * @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...
236
     */
237
    private function getTags(ResourceObject $ro): array
238
    {
239
        $etag = $ro->headers['ETag'];
240
        $tags = [$etag, ($this->uriTag)($ro->uri)];
241
        if (isset($ro->headers[Header::SURROGATE_KEY])) {
242
            $tags = array_merge($tags, explode(' ', $ro->headers[Header::SURROGATE_KEY]));
243
        }
244
245
        /** @var list<string> $uniqueTags */
246
        $uniqueTags = array_unique($tags);
247
248
        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...
249
    }
250
251
    /**
252
     * @param mixed $body
253
     *
254
     * @return mixed
255
     */
256
    private function evaluateBody($body)
257
    {
258
        if (! is_array($body)) {
259
            return $body;
260
        }
261
262
        /** @psalm-suppress MixedAssignment $item */
263
        foreach ($body as &$item) {
264
            if ($item instanceof RequestInterface) {
265
                $item = ($item)();
266
            }
267
268
            if ($item instanceof ResourceObject) {
269
                $item->body = $this->evaluateBody($item->body);
270
            }
271
        }
272
273
        return $body;
274
    }
275
276
    private function getUriKey(AbstractUri $uri, string $type): string
277
    {
278
        return $type . ($this->uriTag)($uri) . (isset($_SERVER['X_VARY']) ? $this->getVary() : '');
279
    }
280
281
    private function getVary(): string
282
    {
283
        $xvary = $_SERVER['X_VARY'];
284
        assert(is_string($xvary));
285
        $varys = explode(',', $xvary);
286
        $varyString = '';
287
        foreach ($varys as $vary) {
288
            $phpVaryKey = sprintf('X_%s', strtoupper($vary));
289
            if (isset($_SERVER[$phpVaryKey]) && is_string($_SERVER[$phpVaryKey])) {
290
                $varyString .= $_SERVER[$phpVaryKey];
291
            }
292
        }
293
294
        return $varyString;
295
    }
296
297
    public function saveEtag(AbstractUri $uri, string $etag, string $surrogateKeys, ?int $ttl): void
298
    {
299
        $tags = $surrogateKeys !== '' ? explode(' ', $surrogateKeys) : [];
300
        $tags[] = (new UriTag())($uri);
301
        /** @var list<string> $uniqueTags */
302
        $uniqueTags = array_unique($tags);
303
        $this->logger->log('save-etag uri:%s etag:%s surrogate-keys:%s', $uri, $etag, $uniqueTags);
304
        $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

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