Passed
Push — master ( b3e21b...2e544e )
by Alexander
01:42
created

FragmentCache::getCachedContent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 7
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 15
ccs 8
cts 8
cp 1
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Widgets;
6
7
use Yiisoft\Cache\CacheInterface;
8
use Yiisoft\Cache\Dependency\Dependency;
9
use Yiisoft\View\DynamicContentAwareInterface;
10
use Yiisoft\View\DynamicContentAwareTrait;
11
use Yiisoft\View\WebView;
12
use Yiisoft\Widget\Widget;
13
14
use function array_merge;
15
use function ob_get_clean;
16
use function ob_implicit_flush;
17
use function ob_start;
18
19
/**
20
 * FragmentCache is used by {@see \Yiisoft\View\View} to provide caching of page fragments.
21
 *
22
 * @property-read string|false $cachedContent The cached content. False is returned if valid content is not found in the
23
 * cache. This property is read-only.
24
 */
25
final class FragmentCache extends Widget implements DynamicContentAwareInterface
26
{
27
    use DynamicContentAwareTrait;
28
29
    private string $id;
30
31
    /**
32
     * @var CacheInterface|null the cache object or the application component ID of the cache object.
33
     *
34
     * After the FragmentCache object is created, if you want to change this property, you should only assign it with
35
     * a cache object.
36
     */
37
    private CacheInterface $cache;
38
39
    /**
40
     * @var int number of seconds that the data can remain valid in cache.
41
     *
42
     * Use 0 to indicate that the cached data will never expire.
43
     */
44
    private int $duration = 60;
45
46
    /**
47
     * @var Dependency|null the dependency that the cached content depends on.
48
     *
49
     * This can be either a {@see Dependency} object or a configuration array for creating the dependency object.
50
     *
51
     * Would make the output cache depends on the last modified time of all posts. If any post has its modification time
52
     * changed, the cached content would be invalidated.
53
     */
54
    private ?Dependency $dependency = null;
55
56
    /**
57
     * @var string[]|string list of factors that would cause the variation of the content being cached.
58
     *
59
     * Each factor is a string representing a variation (e.g. the language, a GET parameter). The following variation
60
     * setting will cause the content to be cached in different versions according to the current application language:
61
     */
62
    private $variations;
63
64
    /**
65
     * @var string|null the cached content. Null if the content is not cached.
66
     */
67
    private ?string $content = null;
68
69
    private WebView $webView;
70
71 5
    public function __construct(CacheInterface $cache, WebView $webview)
72
    {
73 5
        $this->cache = $cache;
74 5
        $this->webView = $webview;
75 5
    }
76
77
    /**
78
     * Initializes the FragmentCache object.
79
     */
80 5
    public function start(): void
81
    {
82 5
        if ($this->getCachedContent() === null) {
83 5
            $this->webView->pushDynamicContent($this);
84 5
            ob_start();
85 5
            PHP_VERSION_ID >= 80000 ? ob_implicit_flush(false) : ob_implicit_flush(0);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type integer expected by parameter $flag of ob_implicit_flush(). ( Ignorable by Annotation )

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

85
            PHP_VERSION_ID >= 80000 ? ob_implicit_flush(/** @scrutinizer ignore-type */ false) : ob_implicit_flush(0);
Loading history...
86
        }
87 5
    }
88
89
    /**
90
     * Marks the end of content to be cached.
91
     *
92
     * Content displayed before this method call and after {@see init()} will be captured and saved in cache.
93
     *
94
     * This method does nothing if valid content is already found in cache.
95
     *
96
     * @return string the result of widget execution to be outputted.
97
     */
98 5
    public function run(): string
99
    {
100 5
        if (($content = $this->getCachedContent()) !== null) {
101 4
            return $content;
102
        }
103
104 5
        $this->webView->popDynamicContent();
105
106 5
        $content = ob_get_clean();
107
108 5
        if ($content === false || $content === '') {
109
            return '';
110
        }
111
112 5
        $data = [$content, $this->getDynamicPlaceholders()];
113 5
        $this->cache->set($this->calculateKey(), $data, $this->duration, $this->dependency);
114
115 5
        return $this->updateDynamicContent($content, $this->getDynamicPlaceholders());
116
    }
117
118
    /**
119
     * Returns the cached content if available.
120
     *
121
     * @return string|null the cached content. False is returned if valid content is not found in the cache.
122
     */
123 5
    public function getCachedContent(): ?string
124
    {
125 5
        $key = $this->calculateKey();
126
127 5
        if (!$this->cache->has($key)) {
0 ignored issues
show
Bug introduced by
$key of type array is incompatible with the type string expected by parameter $key of Psr\SimpleCache\CacheInterface::has(). ( Ignorable by Annotation )

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

127
        if (!$this->cache->has(/** @scrutinizer ignore-type */ $key)) {
Loading history...
128 5
            return null;
129
        }
130
131 4
        $data = $this->cache->get($key);
0 ignored issues
show
Bug introduced by
$key of type array is incompatible with the type string expected by parameter $key of Psr\SimpleCache\CacheInterface::get(). ( Ignorable by Annotation )

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

131
        $data = $this->cache->get(/** @scrutinizer ignore-type */ $key);
Loading history...
132
133 4
        [$this->content, $placeholders] = $data;
134
135 4
        $this->content = $this->updateDynamicContent($this->content, $placeholders, true);
0 ignored issues
show
Bug introduced by
It seems like $this->content can also be of type null; however, parameter $content of Yiisoft\Yii\Widgets\Frag...:updateDynamicContent() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

135
        $this->content = $this->updateDynamicContent(/** @scrutinizer ignore-type */ $this->content, $placeholders, true);
Loading history...
136
137 4
        return $this->content;
138
    }
139
140
    /**
141
     * Generates a unique key used for storing the content in cache.
142
     *
143
     * The key generated depends on both {@see id} and {@see variations}.
144
     *
145
     * @return mixed a valid cache key
146
     */
147 5
    public function calculateKey()
148
    {
149 5
        return array_merge([__CLASS__, $this->id], (array) $this->variations);
150
    }
151
152
    /**
153
     * {@see $content}
154
     *
155
     * @param string|null $value
156
     *
157
     * @return $this
158
     */
159 1
    public function content(?string $value): self
160
    {
161 1
        $this->content = $value;
162
163 1
        return $this;
164
    }
165
166
    /**
167
     * {@see $dependency}
168
     *
169
     * @param Dependency $value
170
     *
171
     * @return $this
172
     */
173
    public function dependency(Dependency $value): self
174
    {
175
        $this->dependency = $value;
176
177
        return $this;
178
    }
179
180
    /**
181
     * {@see $duration}
182
     *
183
     * @param int $value
184
     *
185
     * @return $this
186
     */
187
    public function duration(int $value): self
188
    {
189
        $this->duration = $value;
190
191
        return $this;
192
    }
193
194
    /**
195
     * @see $id
196
     *
197
     * @param string $value
198
     *
199
     * @return $this
200
     */
201 5
    public function id(string $value): self
202
    {
203 5
        $this->id = $value;
204
205 5
        return $this;
206
    }
207
208
    /**
209
     * {@see $variations}
210
     *
211
     * @param array|string $value
212
     *
213
     * @return $this
214
     */
215 1
    public function variations($value): self
216
    {
217 1
        $this->variations = $value;
218
219 1
        return $this;
220
    }
221
222 3
    protected function getView(): WebView
223
    {
224 3
        return $this->webView;
225
    }
226
}
227