Passed
Push — master ( 8a8b7b...c348f9 )
by Evgeniy
03:48
created

FragmentCache   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 146
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 39
c 2
b 1
f 0
dl 0
loc 146
ccs 42
cts 42
cp 1
rs 10
wmc 14

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A id() 0 5 1
A dynamicContents() 0 8 2
A dependency() 0 5 1
A variations() 0 5 1
A ttl() 0 5 1
A run() 0 22 5
A begin() 0 7 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Widgets;
6
7
use RuntimeException;
8
use Yiisoft\Cache\CacheInterface;
9
use Yiisoft\Cache\Dependency\Dependency;
10
use Yiisoft\View\Cache\CachedContent;
11
use Yiisoft\View\Cache\DynamicContent;
12
use Yiisoft\Widget\Widget;
13
14
use function ob_end_clean;
15
use function ob_get_clean;
16
use function ob_implicit_flush;
17
use function ob_start;
18
19
/**
20
 * FragmentCache caches a fragment of content.
21
 *
22
 * @see CachedContent
23
 * @see DynamicContent
24
 *
25
 * Example of use:
26
 *
27
 * ```php
28
 * $dynamicContent = new Yiisoft\View\Cache\DynamicContent('dynamic-id', static function (array $parameters): string {
29
 *     return strtoupper("{$parameters['a']} - {$parameters['b']}");
30
 * }, ['a' => 'string-a', 'b' => 'string-b']);
31
 *
32
 * FragmentCache::widget()->id('cache-id')->ttl(30)->dynamicContents($dynamicContent)->begin();
33
 *     echo 'Content to be cached ...';
34
 *     echo $dynamicContent->placeholder();
35
 *     echo 'Content to be cached ...';
36
 * FragmentCache::end();
37
 * ```
38
 */
39
final class FragmentCache extends Widget
40
{
41
    private ?string $id = null;
42
    private CacheInterface $cache;
43
    private ?Dependency $dependency = null;
44
    private int $ttl = 60;
45
    private array $variations = [];
46
47
    /**
48
     * @var array<string, DynamicContent>
49
     */
50
    private array $dynamicContents = [];
51
52 8
    public function __construct(CacheInterface $cache)
53
    {
54 8
        $this->cache = $cache;
55 8
    }
56
57
    /**
58
     * Returns a new instance with the specified Widget ID.
59
     *
60
     * @param string $value The unique identifier of the cache fragment.
61
     *
62
     * @return self
63
     */
64 7
    public function id(string $value): self
65
    {
66 7
        $new = clone $this;
67 7
        $new->id = $value;
68 7
        return $new;
69
    }
70
71
    /**
72
     * Returns a new instance with the specified dependency.
73
     *
74
     * @param Dependency $value The dependency that the cached content depends on.
75
     *
76
     * This can be either a {@see Dependency} object or a configuration array for creating the dependency object.
77
     *
78
     * Would make the output cache depends on the last modified time of all posts. If any post has its modification time
79
     * changed, the cached content would be invalidated.
80
     *
81
     * @return self
82
     */
83 2
    public function dependency(Dependency $value): self
84
    {
85 2
        $new = clone $this;
86 2
        $new->dependency = $value;
87 2
        return $new;
88
    }
89
90
    /**
91
     * Returns a new instance with the specified TTL.
92
     *
93
     * @param int $value The number of seconds that the data can remain valid in cache.
94
     *
95
     * @return self
96
     */
97 3
    public function ttl(int $value): self
98
    {
99 3
        $new = clone $this;
100 3
        $new->ttl = $value;
101 3
        return $new;
102
    }
103
104
    /**
105
     * Returns a new instance with the specified dynamic contents.
106
     *
107
     * @param DynamicContent ...$value The dynamic content instances.
108
     *
109
     * @return self
110
     */
111 4
    public function dynamicContents(DynamicContent ...$value): self
112
    {
113 4
        $new = clone $this;
114
115 4
        foreach ($value as $dynamicContent) {
116 4
            $new->dynamicContents[$dynamicContent->id()] = $dynamicContent;
117
        }
118 4
        return $new;
119
    }
120
121
    /**
122
     * Returns a new instance with the specified variations.
123
     *
124
     * @param string ...$value The factors that would cause the variation of the content being cached.
125
     *
126
     * Each factor is a string representing a variation (e.g. the language, a GET parameter). The following variation
127
     * setting will cause the content to be cached in different versions according to the current application language:
128
     *
129
     * ```php
130
     * $fragmentCache->variations('en');
131
     * ```
132
     *
133
     * @return self
134
     */
135 2
    public function variations(string ...$value): self
136
    {
137 2
        $new = clone $this;
138 2
        $new->variations = $value;
139 2
        return $new;
140
    }
141
142
    /**
143
     * Starts recording a fragment cache.
144
     */
145 7
    public function begin(): ?string
146
    {
147 7
        parent::begin();
148 7
        ob_start();
149
        /** @psalm-suppress PossiblyFalseArgument */
150 7
        PHP_VERSION_ID >= 80000 ? ob_implicit_flush(false) : ob_implicit_flush(0);
151 7
        return null;
152
    }
153
154
    /**
155
     * Marks the end of content to be cached.
156
     *
157
     * Content displayed before this method call and after {@see begin()} will be captured and saved in cache.
158
     *
159
     * This method does nothing if valid content is already found in cache.
160
     *
161
     * @return string The result of widget execution to be outputted.
162
     */
163 7
    protected function run(): string
164
    {
165 7
        if ($this->id === null) {
166 1
            ob_end_clean();
167 1
            throw new RuntimeException('You must assign the "id" using the "id()" setter.');
168
        }
169
170 6
        $cachedContent = new CachedContent($this->id, $this->cache, $this->dynamicContents, $this->variations);
171 6
        $content = $cachedContent->get();
172
173 6
        if ($content !== null) {
174 4
            ob_end_clean();
175 4
            return $content;
176
        }
177
178 6
        $content = ob_get_clean();
179
180 6
        if ($content === false || $content === '') {
181 1
            return '';
182
        }
183
184 5
        return $cachedContent->cache($content, $this->ttl, $this->dependency);
185
    }
186
}
187