194 lines
8.3 KiB
PHP
194 lines
8.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/*
|
|
* This file is part of the TYPO3 CMS project.
|
|
*
|
|
* It is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License, either version 2
|
|
* of the License, or any later version.
|
|
*
|
|
* For the full copyright and license information, please read the
|
|
* LICENSE.txt file that was distributed with this source code.
|
|
*
|
|
* The TYPO3 project - inspiring people to share!
|
|
*/
|
|
|
|
namespace TYPO3\CMS\Core\TypoScript;
|
|
|
|
/**
|
|
* Helper class to manage and convert TypoScript into differently shaped arrays.
|
|
* Also contains the functionality in TypoScript called "optionSplit".
|
|
*
|
|
* @internal
|
|
*/
|
|
readonly class TypoScriptService
|
|
{
|
|
/**
|
|
* Removes all trailing dots recursively from TS settings array
|
|
*
|
|
* Extbase converts the "classical" TypoScript (with trailing dot) to a format without trailing dot,
|
|
* to be more future-proof and not to have any conflicts with Fluid object accessor syntax.
|
|
*
|
|
* @param array<string|int, mixed> $typoScriptArray for example `['foo' => 'TEXT', 'foo.' => ['bar' => 'baz']]`
|
|
* @return array<string|int, mixed> for example `['foo' => ['_typoScriptNodeValue' => 'TEXT', 'bar' => 'baz']]`
|
|
* @internal Avoid using this method. This has been invented for Extbase, which decided to move TypoScript
|
|
* arrays around in just another different way.
|
|
*/
|
|
public function convertTypoScriptArrayToPlainArray(array $typoScriptArray): array
|
|
{
|
|
foreach ($typoScriptArray as $key => $value) {
|
|
if (str_ends_with((string)$key, '.')) {
|
|
$keyWithoutDot = substr((string)$key, 0, -1);
|
|
$typoScriptNodeValue = $typoScriptArray[$keyWithoutDot] ?? null;
|
|
if (is_array($value)) {
|
|
$typoScriptArray[$keyWithoutDot] = $this->convertTypoScriptArrayToPlainArray($value);
|
|
if ($typoScriptNodeValue !== null) {
|
|
$typoScriptArray[$keyWithoutDot]['_typoScriptNodeValue'] = $typoScriptNodeValue;
|
|
}
|
|
unset($typoScriptArray[$key]);
|
|
} else {
|
|
$typoScriptArray[$keyWithoutDot] = null;
|
|
}
|
|
}
|
|
}
|
|
return $typoScriptArray;
|
|
}
|
|
|
|
/**
|
|
* Returns an array with Typoscript the old way (with dot).
|
|
*
|
|
* Extbase converts the "classical" TypoScript (with trailing dot) to a format without trailing dot,
|
|
* to be more future-proof and not to have any conflicts with Fluid object accessor syntax.
|
|
* However, if you want to call legacy TypoScript objects, you somehow need the "old" syntax (because this is what TYPO3 is used to).
|
|
* With this method, you can convert the extbase TypoScript to classical TYPO3 TypoScript which is understood by the rest of TYPO3.
|
|
*
|
|
* @param array $plainArray A TypoScript Array with Extbase Syntax (without dot but with _typoScriptNodeValue)
|
|
* @return array Array with TypoScript as usual (with dot)
|
|
* @internal Avoid using this method. This has been invented for Extbase, which decided to move TypoScript
|
|
* arrays around in just another different way.
|
|
*/
|
|
public function convertPlainArrayToTypoScriptArray(array $plainArray): array
|
|
{
|
|
$typoScriptArray = [];
|
|
foreach ($plainArray as $key => $value) {
|
|
if (is_array($value)) {
|
|
if (isset($value['_typoScriptNodeValue'])) {
|
|
$typoScriptArray[$key] = $value['_typoScriptNodeValue'];
|
|
unset($value['_typoScriptNodeValue']);
|
|
}
|
|
$typoScriptArray[$key . '.'] = $this->convertPlainArrayToTypoScriptArray($value);
|
|
} else {
|
|
$typoScriptArray[$key] = $value ?? '';
|
|
}
|
|
}
|
|
return $typoScriptArray;
|
|
}
|
|
|
|
/**
|
|
* Implementation of the "optionSplit" feature in TypoScript (used eg. for MENU objects)
|
|
* What it does is to split the incoming TypoScript array so that the values are exploded by certain
|
|
* strings ("||" and "|*|") and each part distributed into individual TypoScript arrays with a similar structure,
|
|
* but individualized values.
|
|
* The concept is known as "optionSplit" and is rather advanced to handle but quite powerful, in particular
|
|
* for creating menus in TYPO3.
|
|
*
|
|
* @param array $originalConfiguration A TypoScript array
|
|
* @param int $splitCount The number of items for which to generate individual TypoScript arrays
|
|
* @return array The individualized TypoScript array.
|
|
*/
|
|
public function explodeConfigurationForOptionSplit(array $originalConfiguration, int $splitCount): array
|
|
{
|
|
$finalConfiguration = [];
|
|
if (!$splitCount) {
|
|
return $finalConfiguration;
|
|
}
|
|
// Initialize output to carry at least the keys
|
|
for ($aKey = 0; $aKey < $splitCount; $aKey++) {
|
|
$finalConfiguration[$aKey] = [];
|
|
}
|
|
// Recursive processing of array keys
|
|
foreach ($originalConfiguration as $cKey => $val) {
|
|
if (is_array($val)) {
|
|
$tempConf = $this->explodeConfigurationForOptionSplit($val, $splitCount);
|
|
foreach ($tempConf as $aKey => $val2) {
|
|
$finalConfiguration[$aKey][$cKey] = $val2;
|
|
}
|
|
} elseif (is_string($val)) {
|
|
// Splitting of all values on this level of the TypoScript object tree:
|
|
if ($cKey === 'noTrimWrap' || (!str_contains($val, '|*|') && !str_contains($val, '||'))) {
|
|
for ($aKey = 0; $aKey < $splitCount; $aKey++) {
|
|
$finalConfiguration[$aKey][$cKey] = $val;
|
|
}
|
|
} else {
|
|
$main = explode('|*|', $val);
|
|
$lastC = 0;
|
|
$middleC = 0;
|
|
$firstC = 0;
|
|
if ($main[0]) {
|
|
$first = explode('||', $main[0]);
|
|
$firstC = count($first);
|
|
}
|
|
$middle = [];
|
|
if (!empty($main[1])) {
|
|
$middle = explode('||', $main[1]);
|
|
$middleC = count($middle);
|
|
}
|
|
$last = [];
|
|
$value = '';
|
|
if (!empty($main[2])) {
|
|
$last = explode('||', $main[2]);
|
|
$lastC = count($last);
|
|
$value = $last[0];
|
|
}
|
|
for ($aKey = 0; $aKey < $splitCount; $aKey++) {
|
|
if ($firstC && isset($first[$aKey])) {
|
|
$value = $first[$aKey];
|
|
} elseif ($middleC) {
|
|
$value = $middle[($aKey - $firstC) % $middleC];
|
|
}
|
|
if ($lastC && $lastC >= $splitCount - $aKey) {
|
|
$value = $last[$lastC - ($splitCount - $aKey)];
|
|
}
|
|
$finalConfiguration[$aKey][$cKey] = trim($value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $finalConfiguration;
|
|
}
|
|
|
|
/**
|
|
* Flatten TypoScript label array; converting a hierarchical array into a flat
|
|
* array with the keys separated by dots.
|
|
*
|
|
* Example Input: array('k1' => array('subkey1' => 'val1'))
|
|
* Example Output: array('k1.subkey1' => 'val1')
|
|
*
|
|
* @param array $labelValues Hierarchical array of labels
|
|
* @param string $parentKey the name of the parent key in the recursion; is only needed for recursion.
|
|
* @return array flattened array of labels.
|
|
*/
|
|
public function flattenTypoScriptLabelArray(array $labelValues, string $parentKey = ''): array
|
|
{
|
|
$result = [];
|
|
foreach ($labelValues as $key => $labelValue) {
|
|
if (!empty($parentKey)) {
|
|
if ($key === '_typoScriptNodeValue') {
|
|
$key = $parentKey;
|
|
} else {
|
|
$key = $parentKey . '.' . $key;
|
|
}
|
|
}
|
|
if (is_array($labelValue)) {
|
|
$labelValue = $this->flattenTypoScriptLabelArray($labelValue, $key);
|
|
$result = array_merge($result, $labelValue);
|
|
} else {
|
|
$result[$key] = $labelValue;
|
|
}
|
|
}
|
|
return $result;
|
|
}
|
|
}
|