| Current Path : /var/www/surf/TYPO3/vendor/typo3/cms-core/Classes/Configuration/Parser/ |
| Current File : /var/www/surf/TYPO3/vendor/typo3/cms-core/Classes/Configuration/Parser/PageTsConfigParser.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\Configuration\Parser;
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
use TYPO3\CMS\Core\Configuration\TypoScript\ConditionMatching\ConditionMatcherInterface;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser;
/**
* A TS-Config parsing class which performs condition evaluation.
*
* This class does parsing of a compiled TSconfig string, and applies matching() based on the
* Context (FE or BE) in it, allowing to be fully agnostic to the outside world.
*
* @deprecated since TYPO3 v12, will be removed with v13. Use PageTsConfigFactory instead.
* When removing, also remove entries in core Services.yaml.
*/
class PageTsConfigParser
{
protected TypoScriptParser $typoScriptParser;
protected FrontendInterface $cache;
public function __construct(TypoScriptParser $typoScriptParser, FrontendInterface $cache)
{
trigger_error('Class ' . __CLASS__ . ' will be removed with TYPO3 v13.0. Use PageTsConfigFactory instead.', E_USER_DEPRECATED);
$this->typoScriptParser = $typoScriptParser;
$this->cache = $cache;
}
/**
* Parses and matches a given string
* Adds entries to the cache:
* - when an exact on the conditions are there
* - when a parse is there, then matches are happening anyway, and it is checked if this can be cached as well.
*
* If a site is provided the settings stored in the site's configuration is available as constants for the TSconfig.
*
* @param string $content page TSconfig, usually accumulated by the PageTsConfigLoader
* @param ConditionMatcherInterface $matcher an instance to match strings
* @param Site|null $site The current site the page TSconfig is parsed for
* @return array the
*/
public function parse(string $content, ConditionMatcherInterface $matcher, ?Site $site = null): array
{
if ($site) {
$siteSettings = $site->getSettings();
if (!$siteSettings->isEmpty()) {
$siteSettings = $siteSettings->getAllFlat();
// Recursive substitution of site settings (up to 10 nested levels)
for ($i = 0; $i < 10; $i++) {
$beforeSubstitution = $content;
$content = preg_replace_callback(
'/\\{\\$(.[^}]*)\\}/',
static function (array $matches) use ($siteSettings): string {
return isset($siteSettings[$matches[1]]) && !is_array($siteSettings[$matches[1]])
? (string)$siteSettings[$matches[1]] : $matches[0];
},
$content
);
if ($beforeSubstitution === $content) {
break;
}
}
}
}
$hashOfContent = md5('PAGES:' . $content);
$cachedContent = $this->cache->get($hashOfContent);
// Something about this content has been cached before, lets verify the matchings, if they also apply
if (is_array($cachedContent) && is_array($cachedContent[0])) {
// Cache entry found, see if the "matching" section matches with the matcher
$storedData = $cachedContent[0];
$storedMD5 = $cachedContent[1];
$storedData['match'] = $this->matching($storedData['sections'] ?? [], $matcher);
$hashOfDataWithMatches = md5(json_encode($storedData));
// The matches are the same, so nothing to do here
if ($hashOfDataWithMatches === $storedMD5) {
$result = $storedData['TSconfig'];
} else {
// Create a hash out of the content-hash PLUS the matching information and try again
$shash = md5($hashOfDataWithMatches . $hashOfContent);
$storedData = $this->cache->get($shash);
if (is_array($storedData)) {
$result = $storedData['TSconfig'];
} else {
// Create a new content with the matcher, and cache it as a new entry
$parsedAndMatchedData = $this->parseAndMatch($content, $matcher);
// Now store the full data from the parser (with matches)
$this->cache->set($shash, $parsedAndMatchedData, ['pageTSconfig'], 0);
$result = $parsedAndMatchedData['TSconfig'];
}
}
return $result;
}
// Nothing found in cache for this content string, let's do everything.
$parsedAndMatchedData = $this->parseAndMatch($content, $matcher);
// ALL parts, including the matching part is cached.
$md5 = md5(json_encode($parsedAndMatchedData));
$this->cache->set($hashOfContent, [$parsedAndMatchedData, $md5], ['pageTSconfig'], 0);
return $parsedAndMatchedData['TSconfig'];
}
/**
* Does the actual parsing using the TypoScriptParser "parse" method by applying a condition matcher.
*
* @param string $content The TSconfig being parsed
* @return array Array containing the parsed TSconfig, the encountered sections, the matched sections. This is stored in cache.
*/
protected function parseAndMatch(string $content, ConditionMatcherInterface $matcher): array
{
$this->typoScriptParser->parse($content, $matcher);
return [
'TSconfig' => $this->typoScriptParser->setup,
'sections' => $this->typoScriptParser->sections,
'match' => $this->typoScriptParser->sectionsMatch,
];
}
/**
* Is just going through an array of conditions to determine which are matching (for getting correct cache entry)
*
* @param array $sectionsToMatch An array containing the sections to match
* @return array The input array with matching sections to be filled into the "match" key
*/
protected function matching(array $sectionsToMatch, ConditionMatcherInterface $matcher): array
{
$matches = [];
foreach ($sectionsToMatch ?? [] as $key => $pre) {
if ($matcher->match($pre)) {
$matches[$key] = $pre;
}
}
return $matches;
}
}