[TASK] Use null coalescing operator where possible
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / TypoScript / TypoScriptService.php
1 <?php
2 declare(strict_types=1);
3 namespace TYPO3\CMS\Core\TypoScript;
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
18 /**
19 * Utilities to manage and convert TypoScript
20 * Also contains the functionality in TypoScript called "optionSplit"
21 */
22 class TypoScriptService
23 {
24 /**
25 * Removes all trailing dots recursively from TS settings array
26 *
27 * Extbase converts the "classical" TypoScript (with trailing dot) to a format without trailing dot,
28 * to be more future-proof and not to have any conflicts with Fluid object accessor syntax.
29 *
30 * @param array $typoScriptArray The TypoScript array (e.g. array('foo' => 'TEXT', 'foo.' => array('bar' => 'baz')))
31 * @return array e.g. array('foo' => array('_typoScriptNodeValue' => 'TEXT', 'bar' => 'baz'))
32 */
33 public function convertTypoScriptArrayToPlainArray(array $typoScriptArray): array
34 {
35 foreach ($typoScriptArray as $key => $value) {
36 if (substr((string)$key, -1) === '.') {
37 $keyWithoutDot = substr((string)$key, 0, -1);
38 $typoScriptNodeValue = $typoScriptArray[$keyWithoutDot] ?? null;
39 if (is_array($value)) {
40 $typoScriptArray[$keyWithoutDot] = $this->convertTypoScriptArrayToPlainArray($value);
41 if ($typoScriptNodeValue !== null) {
42 $typoScriptArray[$keyWithoutDot]['_typoScriptNodeValue'] = $typoScriptNodeValue;
43 }
44 unset($typoScriptArray[$key]);
45 } else {
46 $typoScriptArray[$keyWithoutDot] = null;
47 }
48 }
49 }
50 return $typoScriptArray;
51 }
52
53 /**
54 * Returns an array with Typoscript the old way (with dot).
55 *
56 * Extbase converts the "classical" TypoScript (with trailing dot) to a format without trailing dot,
57 * to be more future-proof and not to have any conflicts with Fluid object accessor syntax.
58 * However, if you want to call legacy TypoScript objects, you somehow need the "old" syntax (because this is what TYPO3 is used to).
59 * With this method, you can convert the extbase TypoScript to classical TYPO3 TypoScript which is understood by the rest of TYPO3.
60 *
61 * @param array $plainArray An TypoScript Array with Extbase Syntax (without dot but with _typoScriptNodeValue)
62 * @return array array with TypoScript as usual (with dot)
63 * @api
64 */
65 public function convertPlainArrayToTypoScriptArray(array $plainArray): array
66 {
67 $typoScriptArray = [];
68 foreach ($plainArray as $key => $value) {
69 if (is_array($value)) {
70 if (isset($value['_typoScriptNodeValue'])) {
71 $typoScriptArray[$key] = $value['_typoScriptNodeValue'];
72 unset($value['_typoScriptNodeValue']);
73 }
74 $typoScriptArray[$key . '.'] = $this->convertPlainArrayToTypoScriptArray($value);
75 } else {
76 $typoScriptArray[$key] = $value ?? '';
77 }
78 }
79 return $typoScriptArray;
80 }
81
82 /**
83 * Implementation of the "optionSplit" feature in TypoScript (used eg. for MENU objects)
84 * What it does is to split the incoming TypoScript array so that the values are exploded by certain
85 * strings ("||" and "|*|") and each part distributed into individual TypoScript arrays with a similar structure,
86 * but individualized values.
87 * The concept is known as "optionSplit" and is rather advanced to handle but quite powerful, in particular
88 * for creating menus in TYPO3.
89 *
90 * @param array $originalConfiguration A TypoScript array
91 * @param int $splitCount The number of items for which to generated individual TypoScript arrays
92 * @return array The individualized TypoScript array.
93 */
94 public function explodeConfigurationForOptionSplit(array $originalConfiguration, int $splitCount): array
95 {
96 $finalConfiguration = [];
97 if (!$splitCount) {
98 return $finalConfiguration;
99 }
100 // Initialize output to carry at least the keys
101 for ($aKey = 0; $aKey < $splitCount; $aKey++) {
102 $finalConfiguration[$aKey] = [];
103 }
104 // Recursive processing of array keys
105 foreach ($originalConfiguration as $cKey => $val) {
106 if (is_array($val)) {
107 $tempConf = $this->explodeConfigurationForOptionSplit($val, $splitCount);
108 foreach ($tempConf as $aKey => $val2) {
109 $finalConfiguration[$aKey][$cKey] = $val2;
110 }
111 } elseif (is_string($val)) {
112 // Splitting of all values on this level of the TypoScript object tree:
113 if ($cKey === 'noTrimWrap' || (!strstr($val, '|*|') && !strstr($val, '||'))) {
114 for ($aKey = 0; $aKey < $splitCount; $aKey++) {
115 $finalConfiguration[$aKey][$cKey] = $val;
116 }
117 } else {
118 $main = explode('|*|', $val);
119 $lastC = 0;
120 $middleC = 0;
121 $firstC = 0;
122 if ($main[0]) {
123 $first = explode('||', $main[0]);
124 $firstC = count($first);
125 }
126 $middle = [];
127 if ($main[1]) {
128 $middle = explode('||', $main[1]);
129 $middleC = count($middle);
130 }
131 $last = [];
132 $value = '';
133 if ($main[2]) {
134 $last = explode('||', $main[2]);
135 $lastC = count($last);
136 $value = $last[0];
137 }
138 for ($aKey = 0; $aKey < $splitCount; $aKey++) {
139 if ($firstC && isset($first[$aKey])) {
140 $value = $first[$aKey];
141 } elseif ($middleC) {
142 $value = $middle[($aKey - $firstC) % $middleC];
143 }
144 if ($lastC && $lastC >= $splitCount - $aKey) {
145 $value = $last[$lastC - ($splitCount - $aKey)];
146 }
147 $finalConfiguration[$aKey][$cKey] = trim($value);
148 }
149 }
150 }
151 }
152 return $finalConfiguration;
153 }
154 }