Passed
Pull Request — master (#159)
by Evgeniy
07:24
created

CacheContent::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 7
ccs 6
cts 6
cp 1
rs 10
cc 1
nc 1
nop 4
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\View;
6
7
use DateInterval;
8
use InvalidArgumentException;
9
use Yiisoft\Cache\CacheInterface;
10
use Yiisoft\Cache\CacheKeyNormalizer;
11
use Yiisoft\Cache\Dependency\Dependency;
12
13
use function array_merge;
14
use function get_class;
15
use function gettype;
16
use function is_string;
17
use function is_object;
18
use function sprintf;
19
use function strtr;
20
21
/**
22
 * CacheContent caches content, supports the use of dynamic content {@see DynamicContent} inside cached content.
23
 */
24
final class CacheContent
25
{
26
    private string $id;
27
    private CacheInterface $cache;
28
    private CacheKeyNormalizer $cacheKeyNormalizer;
29
30
    /**
31
     * @var array<string, DynamicContent>
32
     */
33
    private array $dynamicContents = [];
34
35
    /**
36
     * @var string[]
37
     */
38
    private array $variations = [];
39
40
    /**
41
     * @param string $id The unique identifier of the cached content.
42
     * @param CacheInterface $cache The cache instance.
43
     * @param array $dynamicContents The dynamic content instances.
44
     * @param array $variations List of string factors that would cause the variation of the content being cached.
45
     */
46 5
    public function __construct(string $id, CacheInterface $cache, array $dynamicContents = [], array $variations = [])
47
    {
48 5
        $this->id = $id;
49 5
        $this->cache = $cache;
50 5
        $this->cacheKeyNormalizer = new CacheKeyNormalizer();
51 5
        $this->setDynamicContents($dynamicContents);
52 5
        $this->setVariations($variations);
53 5
    }
54
55
    /**
56
     * Caches, replaces placeholders with actual dynamic content, and returns the full actual content.
57
     *
58
     * @param string $content The content of the item to cache store.
59
     * @param DateInterval|int|null $ttl The TTL of the cached content.
60
     * @param Dependency|null $dependency The dependency of the cached content.
61
     * @param float $beta The value for calculating the range that is used for "Probably early expiration".
62
     *
63
     * @see CacheInterface::getOrSet()
64
     *
65
     * @return string The rendered cached content.
66
     */
67 5
    public function cache(string $content, $ttl = 60, Dependency $dependency = null, float $beta = 1.0): string
68
    {
69 5
        return $this->replaceDynamicPlaceholders(
70 5
            $this->cache->getOrSet($this->cacheKey(), static fn (): string => $content, $ttl, $dependency, $beta),
71
        );
72
    }
73
74
    /**
75
     * Returns cached content with placeholders replaced with actual dynamic content.
76
     *
77
     * @return string|null The cached content. Null is returned if valid content is not found in the cache.
78
     */
79 5
    public function cachedContent(): ?string
80
    {
81 5
        $content = $this->cache->psr()->get($this->cacheKey());
82
83 5
        if ($content === null) {
84 5
            return null;
85
        }
86
87 4
        return $this->replaceDynamicPlaceholders($content);
88
    }
89
90
    /**
91
     * Generates a unique key used for storing the content in cache.
92
     *
93
     * @return string A valid cache key.
94
     */
95 5
    private function cacheKey(): string
96
    {
97 5
        return $this->cacheKeyNormalizer->normalize(array_merge([__CLASS__, $this->id], $this->variations));
98
    }
99
100
    /**
101
     * Replaces placeholders with actual dynamic content.
102
     *
103
     * @param string $content The content to be replaced.
104
     *
105
     * @return string The content with replaced placeholders.
106
     */
107 5
    private function replaceDynamicPlaceholders(string $content): string
108
    {
109 5
        $dynamicContents = [];
110
111 5
        foreach ($this->dynamicContents as $dynamicContent) {
112 5
            $dynamicContents[$dynamicContent->placeholder()] = $dynamicContent->content();
113
        }
114
115 5
        if (!empty($dynamicContents)) {
116 5
            $content = strtr($content, $dynamicContents);
117
        }
118
119 5
        return $content;
120
    }
121
122
    /**
123
     * Sets dynamic content instances.
124
     *
125
     * @param array $dynamicContents The dynamic content instances to set.
126
     */
127 5
    private function setDynamicContents(array $dynamicContents): void
128
    {
129 5
        foreach ($dynamicContents as $dynamicContent) {
130 5
            if (!($dynamicContent instanceof DynamicContent)) {
131
                throw new InvalidArgumentException(sprintf(
132
                    'Invalid dynamic content "%s" specified. It must be a "%s" instance.',
133
                    is_object($dynamicContent) ? get_class($dynamicContent) : gettype($dynamicContent),
134
                    DynamicContent::class,
135
                ));
136
            }
137
138 5
            $this->dynamicContents[$dynamicContent->id()] = $dynamicContent;
139
        }
140 5
    }
141
142
    /**
143
     * Sets variations.
144
     *
145
     * @param array $variations The variations to set.
146
     */
147 5
    private function setVariations(array $variations): void
148
    {
149 5
        foreach ($variations as $variation) {
150 1
            if (!is_string($variation)) {
151
                throw new InvalidArgumentException(sprintf(
152
                    'Invalid variation "%s" specified. It must be a string type.',
153
                    is_object($variation) ? get_class($variation) : gettype($variation),
154
                ));
155
            }
156
157 1
            $this->variations[] = $variation;
158
        }
159 5
    }
160
}
161