‪TYPO3CMS  11.5
VariableProcessor.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
24 {
25  protected const ‪LEVEL_DELIMITER = '__';
26  protected const ‪ARGUMENT_SEPARATOR = '/';
27  protected const ‪VARIABLE_PATTERN = '#\{(?P<modifier>!)?(?P<name>[^}]+)\}#';
28 
32  protected ‪$hashes = [];
33 
37  protected ‪$nestedValues = [];
38 
43  protected function ‪addHash(string $value): string
44  {
45  if (strlen($value) < 31 && !preg_match('#[^\w]#', $value)) {
46  return $value;
47  }
48  // removing one bit, e.g. for enforced route prefix `{!value}`
49  $hash = substr(md5($value), 0, -1);
50  // Symfony Route Compiler requires first literal to be non-integer
51  if ($hash[0] === (string)(int)$hash[0]) {
52  $hash[0] = str_replace(
53  range('0', '9'),
54  range('o', 'x'),
55  $hash[0]
56  );
57  }
58  $this->hashes[$hash] = $value;
59  return $hash;
60  }
61 
67  protected function ‪resolveHash(string $hash): string
68  {
69  if (strlen($hash) < 31) {
70  return $hash;
71  }
72  if (!isset($this->hashes[$hash])) {
73  throw new \OutOfRangeException(
74  'Hash not resolvable',
75  1537633463
76  );
77  }
78  return $this->hashes[$hash];
79  }
80 
85  protected function ‪addNestedValue(string $value): string
86  {
87  if (!str_contains($value, static::ARGUMENT_SEPARATOR)) {
88  return $value;
89  }
90  $nestedValue = str_replace(
91  static::ARGUMENT_SEPARATOR,
92  static::LEVEL_DELIMITER,
93  $value
94  );
95  $this->nestedValues[$nestedValue] = $value;
96  return $nestedValue;
97  }
98 
103  protected function ‪resolveNestedValue(string $value): string
104  {
105  if (!str_contains($value, static::LEVEL_DELIMITER)) {
106  return $value;
107  }
108  return $this->nestedValues[$value] ?? $value;
109  }
110 
117  public function ‪deflateRoutePath(string $routePath, string $namespace = null, array $arguments = []): string
118  {
119  if (!preg_match_all(static::VARIABLE_PATTERN, $routePath, $matches)) {
120  return $routePath;
121  }
122 
123  $replace = [];
124  $search = array_values($matches[0]);
125  $deflatedNames = $this->‪deflateValues($matches['name'], $namespace, $arguments);
126  foreach ($deflatedNames as $index => $deflatedName) {
127  $modifier = $matches['modifier'][$index] ?? '';
128  $replace[] = '{' . $modifier . $deflatedName . '}';
129  }
130  return str_replace($search, $replace, $routePath);
131  }
132 
139  public function ‪inflateRoutePath(string $routePath, string $namespace = null, array $arguments = []): string
140  {
141  if (!preg_match_all(static::VARIABLE_PATTERN, $routePath, $matches)) {
142  return $routePath;
143  }
144 
145  $replace = [];
146  $search = array_values($matches[0]);
147  $inflatedNames = $this->‪inflateValues($matches['name'], $namespace, $arguments);
148  foreach ($inflatedNames as $index => $inflatedName) {
149  $modifier = $matches['modifier'][$index] ?? '';
150  $replace[] = '{' . $modifier . $inflatedName . '}';
151  }
152  return str_replace($search, $replace, $routePath);
153  }
154 
163  public function ‪deflateNamespaceParameters(array $parameters, string $namespace, array $arguments = []): array
164  {
165  if (empty($namespace) || empty($parameters[$namespace])) {
166  return $parameters;
167  }
168  // prefix items of namespace parameters and apply argument mapping
169  $namespaceParameters = $this->‪deflateKeys($parameters[$namespace], $namespace, $arguments, false);
170  // deflate those array items
171  $namespaceParameters = $this->‪deflateArray($namespaceParameters);
172  unset($parameters[$namespace]);
173  // merge with remaining array items
174  return array_merge($parameters, $namespaceParameters);
175  }
176 
185  public function ‪inflateNamespaceParameters(array $parameters, string $namespace, array $arguments = []): array
186  {
187  if (empty($namespace) || empty($parameters)) {
188  return $parameters;
189  }
190 
191  $parameters = $this->‪inflateArray($parameters, $namespace, $arguments);
192  // apply argument mapping on items of inflated namespace parameters
193  if (!empty($parameters[$namespace]) && !empty($arguments)) {
194  $parameters[$namespace] = $this->‪inflateKeys($parameters[$namespace], null, $arguments, false);
195  }
196  return $parameters;
197  }
198 
206  public function ‪deflateParameters(array $parameters, array $arguments = []): array
207  {
208  $parameters = $this->‪deflateKeys($parameters, null, $arguments, false);
209  return $this->‪deflateArray($parameters);
210  }
211 
219  public function ‪inflateParameters(array $parameters, array $arguments = []): array
220  {
221  $parameters = $this->‪inflateArray($parameters, null, $arguments);
222  return $this->‪inflateKeys($parameters, null, $arguments, false);
223  }
224 
235  public function ‪deflateKeys(array $items, string $namespace = null, array $arguments = [], bool $hash = true): array
236  {
237  if (empty($items) || empty($arguments) && empty($namespace)) {
238  return $items;
239  }
240  $keys = $this->‪deflateValues(array_keys($items), $namespace, $arguments, $hash);
241  return array_combine(
242  $keys,
243  array_values($items)
244  );
245  }
246 
257  public function ‪inflateKeys(array $items, string $namespace = null, array $arguments = [], bool $hash = true): array
258  {
259  if (empty($items) || empty($arguments) && empty($namespace)) {
260  return $items;
261  }
262  $keys = $this->‪inflateValues(array_keys($items), $namespace, $arguments, $hash);
263  return array_combine(
264  $keys,
265  array_values($items)
266  );
267  }
268 
278  protected function ‪deflateValues(array $values, string $namespace = null, array $arguments = [], bool $hash = true): array
279  {
280  if (empty($values) || empty($arguments) && empty($namespace)) {
281  return $values;
282  }
283  $namespacePrefix = $namespace ? $namespace . static::LEVEL_DELIMITER : '';
284  $arguments = array_map('strval', $arguments);
285  return array_map(
286  function (string $value) use ($arguments, $namespacePrefix, $hash) {
287  $value = $arguments[$value] ?? $value;
288  $value = $this->‪addNestedValue($value);
289  $value = $namespacePrefix . $value;
290  if (!$hash) {
291  return $value;
292  }
293  return $this->‪addHash($value);
294  },
295  $values
296  );
297  }
298 
308  protected function ‪inflateValues(array $values, string $namespace = null, array $arguments = [], bool $hash = true): array
309  {
310  if (empty($values) || empty($arguments) && empty($namespace)) {
311  return $values;
312  }
313  $arguments = array_map('strval', $arguments);
314  $namespacePrefix = $namespace ? $namespace . static::LEVEL_DELIMITER : '';
315  return array_map(
316  function (string $value) use ($arguments, $namespacePrefix, $hash) {
317  if ($hash) {
318  $value = $this->‪resolveHash($value);
319  }
320  if (!empty($namespacePrefix) && strpos($value, $namespacePrefix) === 0) {
321  $value = substr($value, strlen($namespacePrefix));
322  }
323  $value = $this->‪resolveNestedValue($value);
324  $index = array_search($value, $arguments, true);
325  return $index !== false ? $index : $value;
326  },
327  $values
328  );
329  }
330 
338  protected function ‪deflateArray(array $array, string $prefix = ''): array
339  {
340  $delimiter = static::LEVEL_DELIMITER;
341  if ($prefix !== '' && substr($prefix, -strlen($delimiter)) !== $delimiter) {
342  $prefix .= static::LEVEL_DELIMITER;
343  }
344 
345  $result = [];
346  foreach ($array as $key => $value) {
347  if (is_array($value)) {
348  $result = array_replace(
349  $result,
350  $this->‪deflateArray(
351  $value,
352  $prefix . $key . static::LEVEL_DELIMITER
353  )
354  );
355  } else {
356  $deflatedKey = $this->‪addHash($prefix . $key);
357  $result[$deflatedKey] = $value;
358  }
359  }
360  return $result;
361  }
362 
371  protected function ‪inflateArray(array $array, ?string $namespace, array $arguments): array
372  {
373  $result = [];
374  foreach ($array as $key => $value) {
375  $inflatedKey = $this->‪resolveHash((string)$key);
376  // inflate nested values `namespace__any__nested` -> `namespace__any/nested`
377  $inflatedKey = $this->‪inflateNestedValue($inflatedKey, $namespace, $arguments);
378  $steps = explode(static::LEVEL_DELIMITER, $inflatedKey);
379  $pointer = &$result;
380  foreach ($steps as $step) {
381  $pointer = &$pointer[$step];
382  }
383  $pointer = $value;
384  unset($pointer);
385  }
386  return $result;
387  }
388 
395  protected function ‪inflateNestedValue(string $value, ?string $namespace, array $arguments): string
396  {
397  $namespacePrefix = $namespace ? $namespace . static::LEVEL_DELIMITER : '';
398  if (!empty($namespace) && strpos($value, $namespacePrefix) !== 0) {
399  return $value;
400  }
401  $arguments = array_map('strval', $arguments);
402  $possibleNestedValueKey = substr($value, strlen($namespacePrefix));
403  $possibleNestedValue = $this->nestedValues[$possibleNestedValueKey] ?? null;
404  if ($possibleNestedValue === null || !in_array($possibleNestedValue, $arguments, true)) {
405  return $value;
406  }
407  return $namespacePrefix . $possibleNestedValue;
408  }
409 }
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\deflateRoutePath
‪string deflateRoutePath(string $routePath, string $namespace=null, array $arguments=[])
Definition: VariableProcessor.php:115
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\$nestedValues
‪array $nestedValues
Definition: VariableProcessor.php:35
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor
Definition: VariableProcessor.php:24
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\resolveNestedValue
‪string resolveNestedValue(string $value)
Definition: VariableProcessor.php:101
‪TYPO3\CMS\Core\Routing\Enhancer
Definition: AbstractEnhancer.php:18
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\deflateNamespaceParameters
‪array deflateNamespaceParameters(array $parameters, string $namespace, array $arguments=[])
Definition: VariableProcessor.php:161
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\LEVEL_DELIMITER
‪const LEVEL_DELIMITER
Definition: VariableProcessor.php:25
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\deflateKeys
‪array deflateKeys(array $items, string $namespace=null, array $arguments=[], bool $hash=true)
Definition: VariableProcessor.php:233
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\inflateValues
‪array inflateValues(array $values, string $namespace=null, array $arguments=[], bool $hash=true)
Definition: VariableProcessor.php:306
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\VARIABLE_PATTERN
‪const VARIABLE_PATTERN
Definition: VariableProcessor.php:27
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\inflateParameters
‪array inflateParameters(array $parameters, array $arguments=[])
Definition: VariableProcessor.php:217
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\resolveHash
‪string resolveHash(string $hash)
Definition: VariableProcessor.php:65
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\ARGUMENT_SEPARATOR
‪const ARGUMENT_SEPARATOR
Definition: VariableProcessor.php:26
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\addHash
‪string addHash(string $value)
Definition: VariableProcessor.php:41
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\inflateNestedValue
‪string inflateNestedValue(string $value, ?string $namespace, array $arguments)
Definition: VariableProcessor.php:393
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\inflateArray
‪array inflateArray(array $array, ?string $namespace, array $arguments)
Definition: VariableProcessor.php:369
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\deflateArray
‪array deflateArray(array $array, string $prefix='')
Definition: VariableProcessor.php:336
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\deflateParameters
‪array deflateParameters(array $parameters, array $arguments=[])
Definition: VariableProcessor.php:204
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\$hashes
‪array $hashes
Definition: VariableProcessor.php:31
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\deflateValues
‪array deflateValues(array $values, string $namespace=null, array $arguments=[], bool $hash=true)
Definition: VariableProcessor.php:276
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\addNestedValue
‪string addNestedValue(string $value)
Definition: VariableProcessor.php:83
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\inflateKeys
‪array inflateKeys(array $items, string $namespace=null, array $arguments=[], bool $hash=true)
Definition: VariableProcessor.php:255
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\inflateNamespaceParameters
‪array inflateNamespaceParameters(array $parameters, string $namespace, array $arguments=[])
Definition: VariableProcessor.php:183
‪TYPO3\CMS\Core\Routing\Enhancer\VariableProcessor\inflateRoutePath
‪string inflateRoutePath(string $routePath, string $namespace=null, array $arguments=[])
Definition: VariableProcessor.php:137