• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eliashaeussler / typo3-warming / 19520077371

19 Nov 2025 11:42PM UTC coverage: 94.592% (+1.6%) from 92.98%
19520077371

push

github

web-flow
[TASK] Update rollup to v4.53.3

| datasource | package | from   | to     |
| ---------- | ------- | ------ | ------ |
| npm        | rollup  | 4.53.2 | 4.53.3 |

1679 of 1775 relevant lines covered (94.59%)

12.27 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.56
/Classes/DataProcessing/ExtensionConfigurationProcessor.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the TYPO3 CMS extension "warming".
7
 *
8
 * Copyright (C) 2021-2025 Elias Häußler <elias@haeussler.dev>
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, either version 2 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22
 */
23

24
namespace EliasHaeussler\Typo3Warming\DataProcessing;
25

26
use EliasHaeussler\CacheWarmup;
27
use EliasHaeussler\Typo3Warming\Extension;
28
use EliasHaeussler\Typo3Warming\View;
29
use GuzzleHttp\RequestOptions;
30
use TYPO3\CMS\Backend;
31
use TYPO3\CMS\Core;
32
use TYPO3\CMS\Fluid;
33

34
/**
35
 * ExtensionConfigurationProcessor
36
 *
37
 * @author Elias Häußler <elias@haeussler.dev>
38
 * @license GPL-2.0-or-later
39
 * @internal
40
 *
41
 * @phpstan-type ExtensionConfiguration array{fieldName: string, fieldValue: string|null}
42
 */
43
final class ExtensionConfigurationProcessor
44
{
45
    private const EXPECTED_INTERFACES = [
46
        'crawler' => CacheWarmup\Crawler\Crawler::class,
47
        'verboseCrawler' => CacheWarmup\Crawler\VerboseCrawler::class,
48
    ];
49
    private const TAG_LIST_VALIDATIONS = [
50
        'exclude' => 'tx_warming_validate_exclude_pattern',
51
    ];
52

53
    /**
54
     * @var array{type: 'object', properties: array<string, array{}>, additionalProperties: false}|null
55
     */
56
    private static ?array $guzzleRequestOptionsSchema = null;
57
    private static bool $codeEditorLoaded = false;
58

59
    private readonly CacheWarmup\Crawler\Strategy\CrawlingStrategyFactory $crawlingStrategyFactory;
60
    private readonly Core\Configuration\ExtensionConfiguration $extensionConfiguration;
61
    private readonly View\TemplateRenderer $templateRenderer;
62

63
    public function __construct()
6✔
64
    {
65
        // DI is not possible here because we're in context of the failsafe container
66
        $this->crawlingStrategyFactory = new CacheWarmup\Crawler\Strategy\CrawlingStrategyFactory();
6✔
67
        $this->extensionConfiguration = Core\Utility\GeneralUtility::makeInstance(Core\Configuration\ExtensionConfiguration::class);
6✔
68
        $this->templateRenderer = new View\TemplateRenderer(
6✔
69
            Core\Utility\GeneralUtility::makeInstance(Fluid\Core\Rendering\RenderingContextFactory::class),
6✔
70
            Core\Utility\GeneralUtility::makeInstance(Core\View\ViewFactoryInterface::class),
6✔
71
        );
6✔
72
    }
73

74
    /**
75
     * @param ExtensionConfiguration $params
76
     */
77
    public function processJson(array $params): string
6✔
78
    {
79
        $fieldName = $params['fieldName'];
6✔
80
        $variables = [
6✔
81
            'fieldName' => $fieldName,
6✔
82
            'fieldValue' => $this->extensionConfiguration->get(Extension::KEY, $fieldName),
6✔
83
            'codeEditorLoaded' => self::$codeEditorLoaded,
6✔
84
        ];
6✔
85

86
        // Set flag to avoid loading code editor multiple times
87
        self::$codeEditorLoaded = true;
6✔
88

89
        if (class_exists(Backend\CodeEditor\CodeEditor::class)) {
6✔
90
            $this->resolveCodeEditorVariables($fieldName, $variables);
6✔
91
        }
92

93
        return $this->templateRenderer->render('ExtensionConfiguration/JsonValue', $variables);
6✔
94
    }
95

96
    /**
97
     * @param ExtensionConfiguration $params
98
     */
99
    public function processCrawlerFqcn(array $params): string
6✔
100
    {
101
        $fieldName = $params['fieldName'];
6✔
102
        $fieldValue = $this->extensionConfiguration->get(Extension::KEY, $fieldName);
6✔
103

104
        return $this->templateRenderer->render('ExtensionConfiguration/CrawlerFqcnValue', [
6✔
105
            'fieldName' => $fieldName,
6✔
106
            'fieldValue' => $fieldValue,
6✔
107
            'expectedInterface' => self::EXPECTED_INTERFACES[$fieldName] ?? null,
6✔
108
        ]);
6✔
109
    }
110

111
    /**
112
     * @param ExtensionConfiguration $params
113
     */
114
    public function processCrawlingStrategy(array $params): string
6✔
115
    {
116
        $fieldName = $params['fieldName'];
6✔
117
        $fieldValue = trim((string)$this->extensionConfiguration->get(Extension::KEY, $fieldName));
6✔
118
        $strategies = $this->crawlingStrategyFactory->getAll();
6✔
119

120
        // Make sure at least currently selected strategy is selectable in install tool
121
        // (in backend context, all available strategies are injected using JavaScript)
122
        if ($fieldValue !== '' && !in_array($fieldValue, $strategies, true)) {
6✔
123
            $strategies[] = $fieldValue;
×
124
        }
125

126
        return $this->templateRenderer->render('ExtensionConfiguration/CrawlingStrategyValue', [
6✔
127
            'fieldName' => $fieldName,
6✔
128
            'fieldValue' => $fieldValue,
6✔
129
            'strategies' => $strategies,
6✔
130
        ]);
6✔
131
    }
132

133
    /**
134
     * @param ExtensionConfiguration $params
135
     */
136
    public function processTagList(array $params): string
6✔
137
    {
138
        $fieldName = $params['fieldName'];
6✔
139
        $fieldValue = $this->extensionConfiguration->get(Extension::KEY, $fieldName);
6✔
140

141
        return $this->templateRenderer->render('ExtensionConfiguration/TagListValue', [
6✔
142
            'fieldName' => $fieldName,
6✔
143
            'fieldValue' => $fieldValue,
6✔
144
            'validation' => self::TAG_LIST_VALIDATIONS[$fieldName] ?? null,
6✔
145
        ]);
6✔
146
    }
147

148
    /**
149
     * @param array<string, mixed> $variables
150
     */
151
    private function resolveCodeEditorVariables(string $fieldName, array &$variables): void
6✔
152
    {
153
        $codeEditor = Core\Utility\GeneralUtility::makeInstance(Backend\CodeEditor\CodeEditor::class);
6✔
154
        $codeEditor->registerConfiguration();
6✔
155

156
        $addonRegistry = Core\Utility\GeneralUtility::makeInstance(Backend\CodeEditor\Registry\AddonRegistry::class);
6✔
157
        $addons = [];
6✔
158

159
        $modeRegistry = Core\Utility\GeneralUtility::makeInstance(Backend\CodeEditor\Registry\ModeRegistry::class);
6✔
160
        $mode = $modeRegistry->getByFileExtension('json')->getModule();
6✔
161

162
        foreach ($addonRegistry->getAddons() as $addon) {
6✔
163
            $module = $addon->getModule();
6✔
164
            if ($module !== null) {
6✔
165
                $addons[] = $module;
6✔
166
            }
167
        }
168

169
        $jsonSchema = $this->buildJsonSchemaForField($fieldName);
6✔
170

171
        if ($jsonSchema !== null) {
6✔
172
            $addons[] = Core\Page\JavaScriptModuleInstruction::create(
6✔
173
                '@eliashaeussler/typo3-warming/backend/extension-configuration.js',
6✔
174
            )->invoke('jsonSchema', $jsonSchema);
6✔
175
        }
176

177
        $variables['codeEditor'] = [
6✔
178
            'mode' => Core\Utility\GeneralUtility::jsonEncodeForHtmlAttribute($mode, false),
6✔
179
            'addons' => Core\Utility\GeneralUtility::jsonEncodeForHtmlAttribute($addons, false),
6✔
180
            'jsModule' => '@typo3/backend/code-editor/element/code-mirror-element.js',
6✔
181
        ];
6✔
182
    }
183

184
    private function buildJsonSchemaForField(string $fieldName): ?string
6✔
185
    {
186
        $filename = sprintf('EXT:warming/Resources/Private/JsonSchema/%s.json', $fieldName);
6✔
187
        $filename = Core\Utility\GeneralUtility::getFileAbsFileName($filename);
6✔
188

189
        if ($filename === '' || !file_exists($filename)) {
6✔
190
            return null;
×
191
        }
192

193
        try {
194
            $json = json_decode((string)file_get_contents($filename), true, 10, JSON_THROW_ON_ERROR);
6✔
195
            $json['definitions']['requestOptions'] = $this->createGuzzleRequestOptionsSchema();
6✔
196

197
            return json_encode($json, JSON_THROW_ON_ERROR);
6✔
198
        } catch (\JsonException) {
×
199
            return null;
×
200
        }
201
    }
202

203
    /**
204
     * @return array{type: 'object', properties: array<string, array{}>, additionalProperties: false}
205
     */
206
    private function createGuzzleRequestOptionsSchema(): array
6✔
207
    {
208
        if (self::$guzzleRequestOptionsSchema !== null) {
6✔
209
            return self::$guzzleRequestOptionsSchema;
6✔
210
        }
211

212
        $reflection = new \ReflectionClass(RequestOptions::class);
6✔
213
        $constantReflections = $reflection->getReflectionConstants();
6✔
214
        $schema = [
6✔
215
            'type' => 'object',
6✔
216
            'properties' => [],
6✔
217
            'additionalProperties' => false,
6✔
218
        ];
6✔
219

220
        foreach ($constantReflections as $constantReflection) {
6✔
221
            $schema['properties'][$constantReflection->getValue()] = [];
6✔
222
        }
223

224
        ksort($schema['properties']);
6✔
225

226
        return self::$guzzleRequestOptionsSchema = $schema;
6✔
227
    }
228
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc