CachedContent::replaceDynamicPlaceholders()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

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