PageControllerExtension::HasCacheKeyContent()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 6
rs 10
c 4
b 0
f 0
1
<?php
2
3
namespace Sunnysideup\SimpleTemplateCaching\Extensions;
4
5
use PageController;
0 ignored issues
show
Bug introduced by
The type PageController was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\Core\Extension;
8
use SilverStripe\Security\Security;
9
use SilverStripe\Versioned\Versioned;
10
11
/**
12
 * Class \Sunnysideup\SimpleTemplateCaching\Extensions\PageControllerExtension.
13
 *
14
 * @property PageController|PageControllerExtension $owner
15
 */
16
class PageControllerExtension extends Extension
17
{
18
    private static bool $unique_cache_for_each_member = true;
19
20
    /**
21
     * make sure to set unique_cache_for_each_member to false
22
     * to use this.
23
     */
24
    private static bool $unique_cache_for_each_member_group_combo = false;
25
26
    /**
27
     * @var null|string
28
     */
29
    protected static $_cache_key_any_data_object_changes;
30
31
    /**
32
     * @var null|bool
33
     */
34
    private static $_can_cache_content;
35
36
    /**
37
     * @var string
38
     */
39
    private static string $_can_cache_content_string = '';
40
41
    /**
42
     * does the page have cache keys AKA can it be cached?
43
     */
44
    public function HasCacheKeys(): bool
45
    {
46
        $owner = $this->getOwner();
47
        if (null === self::$_can_cache_content) {
48
            $canCache = true;
49
            self::$_can_cache_content_string = '';
50
            if ($owner->hasMethod('canCachePage')) {
51
                // if it can cache the page, then it the cache string will remain empty.
52
                $canCache = $owner->canCachePage();
53
                self::$_can_cache_content_string .=  $canCache ? '' : $this->getRandomKey();
54
            }
55
56
            //action
57
            $action = $owner->request->param('Action');
58
            if ($action) {
59
                self::$_can_cache_content_string .= 'UA' . $action;
60
            }
61
62
            // id
63
            $id = $owner->request->param('ID');
64
            if ($id) {
65
                self::$_can_cache_content_string .= 'UI' . $id;
66
            }
67
68
            // otherid
69
            $otherId = $owner->request->param('OtherID');
70
            if ($otherId) {
71
                self::$_can_cache_content_string .= 'OI' . $otherId;
72
            }
73
74
            //request vars
75
            $requestVars = $owner->request->requestVars();
76
            if ($requestVars) {
77
                $canCache = false;
78
                foreach ($requestVars  as $key => $item) {
79
                    if (! $item) {
80
                        $item = '';
81
                    }
82
                    self::$_can_cache_content_string .= serialize($key . '_' . serialize($item));
83
                }
84
            }
85
86
            if (Versioned::get_reading_mode() !== 'Stage.Live') {
87
                self::$_can_cache_content_string .= 'V' . Versioned::get_reading_mode();
88
                $canCache = false;
89
            }
90
91
            //member
92
            $member = Security::getCurrentUser();
93
            if ($member && $member->exists()) {
94
                if (Config::inst()->get(self::class, 'unique_cache_for_each_member')) {
95
                    self::$_can_cache_content_string .= 'UM' . $member->ID;
96
                } elseif (Config::inst()->get(self::class, 'unique_cache_for_each_member_group_combo')) {
97
                    $groupIds = $member->Groups()->columnUnique();
98
                    sort($groupIds, SORT_NUMERIC);
99
                    self::$_can_cache_content_string .= 'UG' . implode(',', $groupIds);
100
                } else {
101
                    $canCache = false;
102
                }
103
            }
104
            // crucial
105
            self::$_can_cache_content = (bool) $canCache;
106
        }
107
108
        return self::$_can_cache_content;
109
    }
110
111
    public function HasCacheKeyMeta(): bool
112
    {
113
        return $this->HasCacheKeys();
114
    }
115
116
    public function HasCacheKeyHeader(): bool
117
    {
118
        return $this->HasCacheKeys();
119
    }
120
121
    public function HasCacheKeyMenu(): bool
122
    {
123
        return $this->HasCacheKeys();
124
    }
125
126
    public function HasCacheKeyContent(): bool
127
    {
128
        if ($this->getOwner()->NeverCachePublicly) {
129
            return false;
130
        }
131
        return $this->HasCacheKeys();
132
    }
133
134
    public function HasCacheKeyFooter(): bool
135
    {
136
        return $this->HasCacheKeys();
137
    }
138
139
    public function CacheKeyMeta(?bool $includePageId = true, ?bool $forceCaching = false): string
0 ignored issues
show
Unused Code introduced by
The parameter $forceCaching 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

139
    public function CacheKeyMeta(?bool $includePageId = true, /** @scrutinizer ignore-unused */ ?bool $forceCaching = false): string

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...
Unused Code introduced by
The parameter $includePageId 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

139
    public function CacheKeyMeta(/** @scrutinizer ignore-unused */ ?bool $includePageId = true, ?bool $forceCaching = false): string

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...
140
    {
141
        return $this->CacheKeyGenerator('META', true, false);
142
    }
143
144
    public function CacheKeyHeader(?bool $includePageId = false, ?bool $forceCaching = false): string
145
    {
146
        return $this->CacheKeyGenerator('H', $includePageId, $forceCaching);
147
    }
148
149
    public function CacheKeyMenu(?bool $includePageId = true, ?bool $forceCaching = false): string
150
    {
151
        return $this->CacheKeyGenerator('M', $includePageId, $forceCaching);
152
    }
153
154
    public function CacheKeyFooter(?bool $includePageId = false, ?bool $forceCaching = false): string
155
    {
156
        return $this->CacheKeyGenerator('F', $includePageId, $forceCaching);
157
    }
158
159
    public function CacheKeyContent(?bool $forceCaching = false): string
0 ignored issues
show
Unused Code introduced by
The parameter $forceCaching 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

159
    public function CacheKeyContent(/** @scrutinizer ignore-unused */ ?bool $forceCaching = false): string

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...
160
    {
161
        $owner = $this->getOwner();
162
        if ($owner->NeverCachePublicly) {
163
            return $this->getRandomKey();
164
        }
165
        $cacheKey = $this->CacheKeyGenerator('C');
166
        if ($owner->hasMethod('CacheKeyContentCustom')) {
167
            $cacheKey .= '_' . $owner->CacheKeyContentCustom();
168
        }
169
170
        return $cacheKey;
171
    }
172
173
    public function CacheKeyGenerator(string $letter, ?bool $includePageId = true, ?bool $forceCaching = false): string
174
    {
175
        $owner = $this->getOwner();
176
        if ($this->HasCacheKeys() || $forceCaching) {
177
            $string = $letter . '_' . $this->getCanCacheContentString() . '_' . $this->cacheKeyAnyDataObjectChanges();
178
179
            if ($includePageId) {
180
                $string .= '_ID_' . $owner->ID;
181
            }
182
        } else {
183
            $string = 'NOT_CACHED__ID_' . $this->getRandomKey();
184
        }
185
186
        return $string;
187
    }
188
189
    /**
190
     * if the cache string is NOT empty then we cannot cache
191
     * as there are specific caching values that indicate the page can not be cached.
192
     */
193
    protected function canCacheCheck(): bool
194
    {
195
        // back to source
196
        return $this->HasCacheKeys();
197
    }
198
199
    protected function getRandomKey()
200
    {
201
        $uniqueId = uniqid('', true);
202
203
        // Combine it with some random data
204
        $randomData = bin2hex(random_bytes(16));
205
206
        // Create a SHA-256 hash
207
        return hash('sha256', $uniqueId . $randomData);
208
    }
209
210
    protected function getCanCacheContentString(): string
211
    {
212
        return self::$_can_cache_content_string;
213
    }
214
215
    protected function cacheKeyAnyDataObjectChanges(): string
216
    {
217
        if (null === self::$_cache_key_any_data_object_changes) {
218
            self::$_cache_key_any_data_object_changes = SimpleTemplateCachingSiteConfigExtension::site_cache_key();
219
        }
220
221
        return self::$_cache_key_any_data_object_changes;
222
    }
223
}
224