Passed
Push — develop ( 542723...1a75a9 )
by M. Mikkel
04:36 queued 45s
created

TemplateCachesService::endTemplateCache()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 35
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 15
c 1
b 0
f 0
nc 6
nop 6
dl 0
loc 35
rs 9.4555
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: mmikkel
5
 * Date: 14/07/2018
6
 * Time: 11:55
7
 */
8
9
namespace mmikkel\cacheflag\services;
10
11
use Craft;
12
use craft\base\Component;
13
use craft\helpers\StringHelper;
14
use craft\services\TemplateCaches;
15
use yii\caching\TagDependency;
16
use DateTime;
17
18
/**
19
 * Class TemplateCachesService
20
 * @author    Mats Mikkel Rummelhoff
21
 * @package mmikkel\cacheflag\services
22
 * @since 1.0.0
23
 */
24
class TemplateCachesService extends Component
25
{
26
27
    /** @var bool */
28
    private $_collectElementTags = false;
29
30
    // Public Methods
31
    // =========================================================================
32
    /**
33
     * Returns a cached template by its key.
34
     *
35
     * @param string $key The template cache key
36
     * @param string|string[]|null $flags The Cache Flag flags this cache would've been flagged with
37
     * @param bool $collectElementTags Whether to cache element queries or not
38
     * @param bool $global Whether the cache would have been stored globally.
39
     * @return string|null
40
     */
41
    public function getTemplateCache(string $key, $flags, bool $collectElementTags, bool $global)
42
    {
43
        // Make sure template caching is enabled
44
        if ($this->_isTemplateCachingEnabled() === false) {
45
            return null;
46
        }
47
48
        $this->_collectElementTags = $collectElementTags;
49
50
        $cacheKey = $this->_cacheKey($key, $global);
51
        $data = Craft::$app->getCache()->get($cacheKey);
52
53
        if ($data === false) {
54
            return null;
55
        }
56
57
        list($body, $tags) = $data;
58
59
        // Make sure the cache was tagged w/ the same flags
60
        $flagTags = $this->_getTagsForFlags($flags);
61
        $cachedFlagTags = \array_filter($tags, function (string $tag) {
62
            return \strpos($tag, 'cacheflag') === 0 || $tag === 'element';
63
        });
64
65
        if (\array_diff($flagTags, $cachedFlagTags) != \array_diff($cachedFlagTags, $flagTags)) {
66
            return null;
67
        }
68
69
        // If we're actively collecting element cache tags, add this cache's tags to the collection
70
        Craft::$app->getElements()->collectCacheTags($tags);
71
        return $body;
72
    }
73
74
    /**
75
     * @param string $key
76
     */
77
    public function startTemplateCache(string $key)
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed. ( Ignorable by Annotation )

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

77
    public function startTemplateCache(/** @scrutinizer ignore-unused */ string $key)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
78
    {
79
        // Make sure template caching is enabled
80
        if ($this->_isTemplateCachingEnabled() === false) {
81
            return;
82
        }
83
84
        if ($this->_collectElementTags) {
85
            Craft::$app->getElements()->startCollectingCacheTags();
86
        }
87
    }
88
89
    /**
90
     * Ends a template cache.
91
     *
92
     * @param string $key The template cache key.
93
     * @param string|null $flags The flags this cache should be flagged with.
94
     * @param bool $global Whether the cache should be stored globally.
95
     * @param string|null $duration How long the cache should be stored for. Should be a [relative time format](http://php.net/manual/en/datetime.formats.relative.php).
96
     * @param mixed|null $expiration When the cache should expire.
97
     * @param string $body The contents of the cache.
98
     * @throws \Throwable
99
     */
100
    public function endTemplateCache(string $key, $flags, bool $global, string $duration = null, $expiration, string $body)
0 ignored issues
show
Unused Code introduced by
The parameter $expiration is not used and could be removed. ( Ignorable by Annotation )

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

100
    public function endTemplateCache(string $key, $flags, bool $global, string $duration = null, /** @scrutinizer ignore-unused */ $expiration, string $body)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
101
    {
102
103
        // Make sure template caching is enabled
104
        if ($this->_isTemplateCachingEnabled() === false) {
105
            return;
106
        }
107
108
        // If there are any transform generation URLs in the body, don't cache it.
109
        // stripslashes($body) in case the URL has been JS-encoded or something.
110
        if (StringHelper::contains(stripslashes($body), 'assets/generate-transform')) {
111
            return;
112
        }
113
114
        // Get flag tags
115
        $flagTags = $this->_getTagsForFlags($flags);
116
117
        if ($this->_collectElementTags) {
118
            // If we're collecting element tags, collect the flag tags too, and end the collection
119
            Craft::$app->getElements()->collectCacheTags($flagTags);
120
            $dep = Craft::$app->getElements()->stopCollectingCacheTags();
121
        } else {
122
            // If not, just tag it with the flags
123
            $dep = new TagDependency([
124
                'tags' => $flagTags,
125
            ]);
126
        }
127
128
        $cacheKey = $this->_cacheKey($key, $global);
129
130
        if ($duration !== null) {
131
            $duration = (new DateTime($duration))->getTimestamp() - time();
132
        }
133
134
        Craft::$app->getCache()->set($cacheKey, [$body, $dep->tags], $duration, $dep);
135
    }
136
137
    // Private Methods
138
    // =========================================================================
139
    /**
140
     * Returns whether template caching is enabled, based on the 'enableTemplateCaching' config setting.
141
     *
142
     * @return bool Whether template caching is enabled
143
     */
144
    private function _isTemplateCachingEnabled(): bool
145
    {
146
        return !!Craft::$app->getConfig()->getGeneral()->enableTemplateCaching;
147
    }
148
149
    /**
150
     * Defines a data cache key that should be used for a template cache.
151
     *
152
     * @param string $key
153
     * @param bool $global
154
     */
155
    private function _cacheKey(string $key, bool $global): string
156
    {
157
        $cacheKey = "template::$key::" . Craft::$app->getSites()->getCurrentSite()->id;
158
159
        if (!$global) {
160
            $cacheKey .= '::' . $this->_path();
161
        }
162
163
        return $cacheKey;
164
    }
165
166
    /**
167
     * Returns the current request path, including a "site:" or "cp:" prefix.
168
     *
169
     * @return string
170
     */
171
    private function _path(): string
172
    {
173
        if ($this->_path !== null) {
174
            return $this->_path;
175
        }
176
177
        if (Craft::$app->getRequest()->getIsCpRequest()) {
178
            $this->_path = 'cp:';
0 ignored issues
show
Bug Best Practice introduced by
The property _path does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
179
        } else {
180
            $this->_path = 'site:';
181
        }
182
183
        $this->_path .= Craft::$app->getRequest()->getPathInfo();
184
        if (Craft::$app->getDb()->getIsMysql()) {
185
            $this->_path = StringHelper::encodeMb4($this->_path);
186
        }
187
188
        if (($pageNum = Craft::$app->getRequest()->getPageNum()) != 1) {
189
            $this->_path .= '/' . Craft::$app->getConfig()->getGeneral()->getPageTrigger() . $pageNum;
190
        }
191
192
        return $this->_path;
193
    }
194
195
    /**
196
     * @param string|array|null $flags
197
     * @param string $delimiter
198
     * @return array
199
     */
200
    private function _getTagsForFlags($flags, string $delimiter = '|'): array
201
    {
202
        $tagsArray = ['cacheflag'];
203
        if (\is_array($flags)) {
204
            $flags = \implode(',', \array_map(function ($flag) {
205
                return \preg_replace('/\s+/', '', $flag);
206
            }, $flags));
207
        } else {
208
            $flags = \preg_replace('/\s+/', '', $flags);
209
        }
210
        $flags = \array_filter(\explode($delimiter, $flags));
211
        $tagsArray = \array_merge($tagsArray, \array_map(function (string $flag) {
212
            return "cacheflag::$flag";
213
        }, $flags));
214
        if ($this->_collectElementTags) {
215
            $tagsArray[] = 'element';
216
        }
217
        return \array_unique(($tagsArray));
218
    }
219
220
}
221