c139ad867767e28c1fd1cca66bdf9ac1228aa310
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Page / CacheHashCalculator.php
1 <?php
2 namespace TYPO3\CMS\Frontend\Page;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\SingletonInterface;
18
19 /**
20 * Logic for cHash calculation
21 */
22 class CacheHashCalculator implements SingletonInterface
23 {
24 /**
25 * @var array Parameters that are relevant for cacheHash calculation. Optional.
26 */
27 protected $cachedParametersWhiteList = [];
28
29 /**
30 * @var array Parameters that are not relevant for cacheHash calculation.
31 */
32 protected $excludedParameters = [];
33
34 /**
35 * @var array Parameters that forces a presence of a valid cacheHash.
36 */
37 protected $requireCacheHashPresenceParameters = [];
38
39 /**
40 * @var array Parameters that need a value to be relevant for cacheHash calculation
41 */
42 protected $excludedParametersIfEmpty = [];
43
44 /**
45 * @var bool Whether to exclude all empty parameters for cacheHash calculation
46 */
47 protected $excludeAllEmptyParameters = false;
48
49 /**
50 * @var bool
51 */
52 protected $includePageId = false;
53
54 /**
55 * Initialise class properties by using the relevant TYPO3 configuration
56 */
57 public function __construct()
58 {
59 $this->setConfiguration($GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']);
60 }
61
62 /**
63 * Calculates the cHash based on the provided parameters
64 *
65 * @param array $params Array of cHash key-value pairs
66 * @return string Hash of all the values
67 */
68 public function calculateCacheHash(array $params)
69 {
70 return !empty($params) ? md5(serialize($params)) : '';
71 }
72
73 /**
74 * Returns the cHash based on provided query parameters and added values from internal call
75 *
76 * @param string $queryString Query-parameters: "&xxx=yyy&zzz=uuu
77 * @return string Hash of all the values
78 * @throws \RuntimeException
79 */
80 public function generateForParameters($queryString)
81 {
82 $cacheHashParams = $this->getRelevantParameters($queryString);
83 return $this->calculateCacheHash($cacheHashParams);
84 }
85
86 /**
87 * Checks whether a parameter of the given $queryString requires cHash calculation
88 *
89 * @param string $queryString
90 * @return bool
91 */
92 public function doParametersRequireCacheHash($queryString)
93 {
94 if (empty($this->requireCacheHashPresenceParameters)) {
95 return false;
96 }
97 $hasRequiredParameter = false;
98 $parameterNames = array_keys($this->splitQueryStringToArray($queryString));
99 foreach ($parameterNames as $parameterName) {
100 if (in_array($parameterName, $this->requireCacheHashPresenceParameters, true)) {
101 $hasRequiredParameter = true;
102 break;
103 }
104 }
105 return $hasRequiredParameter;
106 }
107
108 /**
109 * Splits the input query-parameters into an array with certain parameters filtered out.
110 * Used to create the cHash value
111 *
112 * @param string $queryString Query-parameters: "&xxx=yyy&zzz=uuu
113 * @return array Array with key/value pairs of query-parameters WITHOUT a certain list of
114 * @throws \RuntimeException
115 * @see \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::makeCacheHash(), \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::typoLink()
116 */
117 public function getRelevantParameters($queryString)
118 {
119 $parameters = $this->splitQueryStringToArray($queryString);
120 $relevantParameters = [];
121 foreach ($parameters as $parameterName => $parameterValue) {
122 if ($this->isAdminPanelParameter($parameterName) || $this->isExcludedParameter($parameterName) || $this->isCoreParameter($parameterName)) {
123 continue;
124 }
125 if ($this->hasCachedParametersWhiteList() && !$this->isInCachedParametersWhiteList($parameterName)) {
126 continue;
127 }
128 if (($parameterValue === null || $parameterValue === '') && !$this->isAllowedWithEmptyValue($parameterName)) {
129 continue;
130 }
131 $relevantParameters[$parameterName] = $parameterValue;
132 }
133 if (!empty($relevantParameters)) {
134 if ($this->includePageId) {
135 if (empty($parameters['id'])) {
136 throw new \RuntimeException('ID parameter needs to be passed for the cHash calculation! As a temporary not recommended workaround, you can set $GLOBALS[\'TYPO3_CONF_VARS\'][\'FE\'][\'cHashIncludePageId\'] to false to avoid this error.', 1467983513);
137 }
138 $relevantParameters['id'] = $parameters['id'];
139 }
140 // Finish and sort parameters array by keys:
141 $relevantParameters['encryptionKey'] = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'];
142 ksort($relevantParameters);
143 }
144 return $relevantParameters;
145 }
146
147 /**
148 * Parses the query string and converts it to an array.
149 * Unlike parse_str it only creates an array with one level.
150 *
151 * e.g. foo[bar]=baz will be array('foo[bar]' => 'baz')
152 *
153 * @param string $queryString
154 * @return array
155 */
156 protected function splitQueryStringToArray($queryString)
157 {
158 $parameters = array_filter(explode('&', ltrim($queryString, '?')));
159 $parameterArray = [];
160 foreach ($parameters as $parameter) {
161 list($parameterName, $parameterValue) = explode('=', $parameter);
162 if (trim($parameterName) === '') {
163 // This parameter cannot appear in $_GET in PHP even if its value is not empty, so it should be ignored!
164 continue;
165 }
166 $parameterArray[rawurldecode($parameterName)] = rawurldecode($parameterValue);
167 }
168 return $parameterArray;
169 }
170
171 /**
172 * Checks whether the given parameter is out of a known data-set starting
173 * with ADMCMD or starts with TSFE_ADMIN_PANEL.
174 *
175 * @param string $key
176 * @return bool
177 */
178 protected function isAdminPanelParameter($key)
179 {
180 return $key === 'ADMCMD_noBeUser' || $key === 'ADMCMD_view' || $key === 'ADMCMD_editIcons'
181 || $key === 'ADMCMD_simUser' || $key === 'ADMCMD_simTime' || $key === 'ADMCMD_previewWS'
182 || stripos($key, 'TSFE_ADMIN_PANEL') !== false && preg_match('/TSFE_ADMIN_PANEL\\[.*?\\]/', $key);
183 }
184
185 /**
186 * Checks whether the given parameter is a core parameter
187 *
188 * @param string $key
189 * @return bool
190 */
191 protected function isCoreParameter($key)
192 {
193 return $key === 'id' || $key === 'type' || $key === 'no_cache' || $key === 'cHash' || $key === 'MP' || $key === 'ftu';
194 }
195
196 /**
197 * Checks whether the given parameter should be excluded from cHash calculation
198 *
199 * @param string $key
200 * @return bool
201 */
202 protected function isExcludedParameter($key)
203 {
204 return in_array($key, $this->excludedParameters, true);
205 }
206
207 /**
208 * Checks whether the given parameter is an exclusive parameter for cHash calculation
209 *
210 * @param string $key
211 * @return bool
212 */
213 protected function isInCachedParametersWhiteList($key)
214 {
215 return in_array($key, $this->cachedParametersWhiteList, true);
216 }
217
218 /**
219 * Checks whether cachedParametersWhiteList parameters are configured
220 *
221 * @return bool
222 */
223 protected function hasCachedParametersWhiteList()
224 {
225 return !empty($this->cachedParametersWhiteList);
226 }
227
228 /**
229 * Check whether the given parameter may be used even with an empty value
230 *
231 * @param $key
232 * @return bool
233 */
234 protected function isAllowedWithEmptyValue($key)
235 {
236 return !($this->excludeAllEmptyParameters || in_array($key, $this->excludedParametersIfEmpty, true));
237 }
238
239 /**
240 * Loops through the configuration array and calls the accordant
241 * getters with the value.
242 *
243 * @param array $configuration
244 */
245 public function setConfiguration(array $configuration)
246 {
247 foreach ($configuration as $name => $value) {
248 $setterName = 'set' . ucfirst($name);
249 if (method_exists($this, $setterName)) {
250 $this->{$setterName}($value);
251 }
252 }
253 }
254
255 /**
256 * @param array $cachedParametersWhiteList
257 */
258 protected function setCachedParametersWhiteList(array $cachedParametersWhiteList)
259 {
260 $this->cachedParametersWhiteList = $cachedParametersWhiteList;
261 }
262
263 /**
264 * @param bool $includePageId
265 */
266 protected function setIncludePageId($includePageId)
267 {
268 $this->includePageId = $includePageId;
269 }
270
271 /**
272 * @param bool $excludeAllEmptyParameters
273 */
274 protected function setExcludeAllEmptyParameters($excludeAllEmptyParameters)
275 {
276 $this->excludeAllEmptyParameters = $excludeAllEmptyParameters;
277 }
278
279 /**
280 * @param array $excludedParameters
281 */
282 protected function setExcludedParameters(array $excludedParameters)
283 {
284 $this->excludedParameters = $excludedParameters;
285 }
286
287 /**
288 * @param array $excludedParametersIfEmpty
289 */
290 protected function setExcludedParametersIfEmpty(array $excludedParametersIfEmpty)
291 {
292 $this->excludedParametersIfEmpty = $excludedParametersIfEmpty;
293 }
294
295 /**
296 * @param array $requireCacheHashPresenceParameters
297 */
298 protected function setRequireCacheHashPresenceParameters(array $requireCacheHashPresenceParameters)
299 {
300 $this->requireCacheHashPresenceParameters = $requireCacheHashPresenceParameters;
301 }
302 }