Files
cms-core/Classes/TypoScript/TypoScriptService.php
T

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;
}
}