1<?php
2
3declare(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
18namespace TYPO3\CMS\Core\Configuration\Loader;
19
20use Psr\EventDispatcher\EventDispatcherInterface;
21use TYPO3\CMS\Core\Configuration\Event\ModifyLoadedPageTsConfigEvent;
22use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser;
23use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
24use TYPO3\CMS\Core\Utility\GeneralUtility;
25use TYPO3\CMS\Core\Utility\PathUtility;
26
27/**
28 * Traverses a root line of a pagetree up and includes all available TSconfig settings, including default
29 * setup. Then include lines are checked, and merged together into one string, ready to be parsed.
30 *
31 * Can be used in Frontend or Backend.
32 *
33 * Have a look at the PageTsConfigParser which can then parse (and cache) this information based on the.
34 *
35 * Currently, this accumulated information of the pages is NOT cached, as it would need to be tagged with any
36 * page, also including external files.
37 */
38class PageTsConfigLoader
39{
40    /**
41     * @var EventDispatcherInterface
42     */
43    protected $eventDispatcher;
44
45    public function __construct(EventDispatcherInterface $eventDispatcher)
46    {
47        $this->eventDispatcher = $eventDispatcher;
48    }
49
50    /**
51     * Main method to get all PageTSconfig from the rootline including the defaultTSconfig settings.
52     * @param array $rootLine
53     * @return string
54     */
55    public function load(array $rootLine): string
56    {
57        // Verifying includes, and melt the inclusions together into one string
58        $tsData = $this->collect($rootLine);
59        return implode("\n[GLOBAL]\n", $tsData);
60    }
61
62    /**
63     * Same as "load()" but returns an array of all parts. Only useful in the TYPO3 Backend for inspection purposes.
64     *
65     * @param array $rootLine
66     * @return array
67     * @internal
68     */
69    public function collect(array $rootLine): array
70    {
71        $tsData = [
72            'default' => $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPageTSconfig'] ?? ''
73        ];
74        foreach ($rootLine as $page) {
75            // Can happen when the rootline is given from BE context, we skip this
76            if ((int)$page['uid'] === 0) {
77                continue;
78            }
79            if (trim($page['tsconfig_includes'] ?? '')) {
80                $includeTsConfigFileList = GeneralUtility::trimExplode(',', $page['tsconfig_includes'], true);
81                // Traversing list
82                foreach ($includeTsConfigFileList as $key => $includeTsConfigFile) {
83                    if (strpos($includeTsConfigFile, 'EXT:') === 0) {
84                        [$includeTsConfigFileExtensionKey, $includeTsConfigFilename] = explode(
85                            '/',
86                            substr($includeTsConfigFile, 4),
87                            2
88                        );
89                        if ((string)$includeTsConfigFileExtensionKey !== ''
90                            && ExtensionManagementUtility::isLoaded($includeTsConfigFileExtensionKey)
91                            && (string)$includeTsConfigFilename !== ''
92                        ) {
93                            $extensionPath = ExtensionManagementUtility::extPath($includeTsConfigFileExtensionKey);
94                            $includeTsConfigFileAndPath = PathUtility::getCanonicalPath($extensionPath . $includeTsConfigFilename);
95                            if (strpos($includeTsConfigFileAndPath, $extensionPath) === 0 && file_exists($includeTsConfigFileAndPath)) {
96                                $tsData['page_' . $page['uid'] . '_includes_' . $key] = (string)file_get_contents($includeTsConfigFileAndPath);
97                            }
98                        }
99                    }
100                }
101            }
102            $tsData['page_' . $page['uid']] = $page['TSconfig'] ?? '';
103        }
104
105        $event = $this->eventDispatcher->dispatch(new ModifyLoadedPageTsConfigEvent($tsData, $rootLine));
106
107        // Apply includes
108        return TypoScriptParser::checkIncludeLines_array($event->getTsConfig());
109    }
110}
111