Passed
Push — master ( 99f331...02d9ba )
by Alexander
02:17
created

FragmentCache::begin()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 0
dl 0
loc 9
ccs 7
cts 7
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Widgets;
6
7
use function array_merge;
8
use function ob_get_clean;
9
use function ob_implicit_flush;
10
use function ob_start;
11
use Yiisoft\Cache\CacheInterface;
12
use Yiisoft\Cache\Dependency\Dependency;
13
14
use Yiisoft\View\DynamicContentAwareInterface;
15
use Yiisoft\View\DynamicContentAwareTrait;
16
use Yiisoft\View\WebView;
17
use Yiisoft\Widget\Widget;
18
19
final class FragmentCache extends Widget implements DynamicContentAwareInterface
20
{
21
    use DynamicContentAwareTrait;
22
23
    private string $id;
24
    private CacheInterface $cache;
25
    private int $duration = 60;
26
    private ?Dependency $dependency = null;
27
    private $variations;
28
    private ?string $content = null;
29
30
    private WebView $webView;
31
32 5
    public function __construct(CacheInterface $cache, WebView $webview)
33
    {
34 5
        $this->cache = $cache;
35 5
        $this->webView = $webview;
36 5
    }
37
38
    /**
39
     * Initializes the FragmentCache object.
40
     */
41 5
    public function begin(): ?string
42
    {
43 5
        parent::begin();
44 5
        if ($this->getCachedContent() === null) {
45 5
            $this->webView->pushDynamicContent($this);
46 5
            ob_start();
47 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

47
            PHP_VERSION_ID >= 80000 ? ob_implicit_flush(/** @scrutinizer ignore-type */ false) : ob_implicit_flush(0);
Loading history...
48
        }
49 5
        return null;
50
    }
51
52
    /**
53
     * Marks the end of content to be cached.
54
     *
55
     * Content displayed before this method call and after {@see init()} will be captured and saved in cache.
56
     *
57
     * This method does nothing if valid content is already found in cache.
58
     *
59
     * @return string the result of widget execution to be outputted.
60
     */
61 5
    public function run(): string
62
    {
63 5
        $content = $this->getCachedContent();
64
65 5
        if ($content !== null) {
66 4
            return $content;
67
        }
68
69 5
        $this->webView->popDynamicContent();
70
71 5
        $content = ob_get_clean();
72
73 5
        if ($content === false || $content === '') {
74
            return '';
75
        }
76
77 5
        $data = [$content, $this->getDynamicPlaceholders()];
78
79 5
        $this->cache->set($this->calculateKey(), $data, $this->duration, $this->dependency);
80
81 5
        return $this->updateDynamicContent($content, $this->getDynamicPlaceholders());
82
    }
83
84
    /**
85
     * Returns the cached content if available.
86
     *
87
     * @return string|null the cached content. False is returned if valid content is not found in the cache.
88
     */
89 5
    public function getCachedContent(): ?string
90
    {
91 5
        $key = $this->calculateKey();
92
93 5
        if (!$this->cache->has($key)) {
94 5
            return null;
95
        }
96
97 4
        $data = $this->cache->get($key);
98
99 4
        [$this->content, $placeholders] = $data;
100
101 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

101
        $this->content = $this->updateDynamicContent(/** @scrutinizer ignore-type */ $this->content, $placeholders, true);
Loading history...
102
103 4
        return $this->content;
104
    }
105
106
    /**
107
     * Generates a unique key used for storing the content in cache.
108
     *
109
     * The key generated depends on both {@see id} and {@see variations}.
110
     *
111
     * @return mixed a valid cache key
112
     */
113 5
    public function calculateKey()
114
    {
115 5
        $key = array_merge([__CLASS__, $this->id], (array) $this->variations);
116
117 5
        $jsonKey = json_encode($key);
118
119 5
        return md5($jsonKey);
120
    }
121
122
    /**
123
     * @param Dependency|null $value the dependency that the cached content depends on.
124
     *
125
     * This can be either a {@see Dependency} object or a configuration array for creating the dependency object.
126
     *
127
     * Would make the output cache depends on the last modified time of all posts. If any post has its modification time
128
     * changed, the cached content would be invalidated.
129
     *
130
     * @return $this
131
     */
132
    public function dependency(?Dependency $value): self
133
    {
134
        $this->dependency = $value;
135
136
        return $this;
137
    }
138
139
    /**
140
     * @param int $value number of seconds that the data can remain valid in cache.
141
     *
142
     * @return $this
143
     */
144
    public function duration(int $value): self
145
    {
146
        $this->duration = $value;
147
148
        return $this;
149
    }
150
151 5
    public function id(string $value): self
152
    {
153 5
        $this->id = $value;
154
155 5
        return $this;
156
    }
157
158
    /**
159
     * @param array|string $value list of factors that would cause the variation of the content being cached.
160
     *
161
     * Each factor is a string representing a variation (e.g. the language, a GET parameter). The following variation
162
     * setting will cause the content to be cached in different versions according to the current application language:
163
     *
164
     * @return $this
165
     */
166 1
    public function variations($value): self
167
    {
168 1
        $this->variations = $value;
169
170 1
        return $this;
171
    }
172
173 3
    protected function getView(): WebView
174
    {
175 3
        return $this->webView;
176
    }
177
}
178