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

eliashaeussler / typo3-warming / 15422445901

03 Jun 2025 04:13PM UTC coverage: 90.617% (-0.4%) from 91.04%
15422445901

push

github

github-actions[bot]
[TASK] Automatically rebuild frontend assets

1352 of 1492 relevant lines covered (90.62%)

10.07 hits per line

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

78.5
/Classes/Configuration/Configuration.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\Configuration;
25

26
use EliasHaeussler\CacheWarmup;
27
use EliasHaeussler\Typo3Warming\Crawler;
28
use EliasHaeussler\Typo3Warming\Extension;
29
use Symfony\Component\DependencyInjection;
30
use TYPO3\CMS\Core;
31
use TYPO3\CMS\Extbase;
32

33
/**
34
 * Configuration
35
 *
36
 * @author Elias Häußler <elias@haeussler.dev>
37
 * @license GPL-2.0-or-later
38
 */
39
#[DependencyInjection\Attribute\Autoconfigure(public: true)]
40
final class Configuration
41
{
42
    private const DEFAULT_CRAWLER = Crawler\ConcurrentUserAgentCrawler::class;
43
    private const DEFAULT_VERBOSE_CRAWLER = Crawler\OutputtingUserAgentCrawler::class;
44
    private const DEFAULT_LIMIT = 250;
45
    private const DEFAULT_SUPPORTED_DOKTYPES = [
46
        Core\Domain\Repository\PageRepository::DOKTYPE_DEFAULT,
47
    ];
48

49
    private readonly string $userAgent;
50
    private ?CacheWarmup\Crawler\CrawlerFactory $crawlerFactory = null;
51

52
    public function __construct(
79✔
53
        private readonly Core\Configuration\ExtensionConfiguration $configuration,
54
        private readonly CacheWarmup\Crawler\Strategy\CrawlingStrategyFactory $crawlingStrategyFactory,
55
        private readonly CacheWarmup\Config\Component\OptionsParser $optionsParser,
56
    ) {
57
        $this->userAgent = $this->generateUserAgent();
79✔
58
    }
59

60
    /**
61
     * @throws CacheWarmup\Exception\CrawlerDoesNotExist
62
     * @throws CacheWarmup\Exception\CrawlerIsInvalid
63
     * @throws CacheWarmup\Exception\OptionsAreInvalid
64
     * @throws CacheWarmup\Exception\OptionsAreMalformed
65
     */
66
    public function getCrawler(): CacheWarmup\Crawler\Crawler
9✔
67
    {
68
        $crawlerOptions = [];
9✔
69

70
        try {
71
            $crawlerClass = $this->configuration->get(Extension::KEY, 'crawler');
9✔
72

73
            if (!\is_string($crawlerClass) ||
9✔
74
                !\is_a($crawlerClass, CacheWarmup\Crawler\Crawler::class, true)
8✔
75
            ) {
76
                $crawlerClass = self::DEFAULT_CRAWLER;
3✔
77
            } else {
78
                $crawlerOptions = $this->getCrawlerOptions();
6✔
79
            }
80
        } catch (Core\Exception) {
×
81
            $crawlerClass = self::DEFAULT_VERBOSE_CRAWLER;
×
82
        }
83

84
        return $this->getCrawlerFactory()->get($crawlerClass, $crawlerOptions);
9✔
85
    }
86

87
    /**
88
     * @return array<string, mixed>
89
     * @throws CacheWarmup\Exception\OptionsAreInvalid
90
     * @throws CacheWarmup\Exception\OptionsAreMalformed
91
     */
92
    public function getCrawlerOptions(): array
10✔
93
    {
94
        try {
95
            $json = $this->configuration->get(Extension::KEY, 'crawlerOptions');
10✔
96

97
            // Early return if no crawler options are configured
98
            if (!\is_string($json) || $json === '') {
10✔
99
                return [];
8✔
100
            }
101

102
            return $this->optionsParser->parse($json);
2✔
103
        } catch (Core\Exception) {
1✔
104
            return [];
×
105
        }
106
    }
107

108
    /**
109
     * @throws CacheWarmup\Exception\CrawlerDoesNotExist
110
     * @throws CacheWarmup\Exception\CrawlerIsInvalid
111
     * @throws CacheWarmup\Exception\OptionsAreInvalid
112
     * @throws CacheWarmup\Exception\OptionsAreMalformed
113
     */
114
    public function getVerboseCrawler(): CacheWarmup\Crawler\VerboseCrawler
5✔
115
    {
116
        $crawlerOptions = [];
5✔
117

118
        try {
119
            $crawlerClass = $this->configuration->get(Extension::KEY, 'verboseCrawler');
5✔
120

121
            if (!\is_string($crawlerClass) ||
5✔
122
                !is_a($crawlerClass, CacheWarmup\Crawler\VerboseCrawler::class, true)
4✔
123
            ) {
124
                $crawlerClass = self::DEFAULT_VERBOSE_CRAWLER;
3✔
125
            } else {
126
                $crawlerOptions = $this->getVerboseCrawlerOptions();
2✔
127
            }
128
        } catch (Core\Exception) {
×
129
            $crawlerClass = self::DEFAULT_VERBOSE_CRAWLER;
×
130
        }
131

132
        /** @var CacheWarmup\Crawler\VerboseCrawler $crawler */
133
        $crawler = $this->getCrawlerFactory()->get($crawlerClass, $crawlerOptions);
5✔
134

135
        return $crawler;
5✔
136
    }
137

138
    /**
139
     * @return array<string, mixed>
140
     * @throws CacheWarmup\Exception\OptionsAreInvalid
141
     * @throws CacheWarmup\Exception\OptionsAreMalformed
142
     */
143
    public function getVerboseCrawlerOptions(): array
6✔
144
    {
145
        try {
146
            $json = $this->configuration->get(Extension::KEY, 'verboseCrawlerOptions');
6✔
147

148
            // Early return if no crawler options are configured
149
            if (!\is_string($json) || $json === '') {
6✔
150
                return [];
4✔
151
            }
152

153
            return $this->optionsParser->parse($json);
2✔
154
        } catch (Core\Exception) {
1✔
155
            return [];
×
156
        }
157
    }
158

159
    /**
160
     * @return array<string, mixed>
161
     * @throws CacheWarmup\Exception\OptionsAreInvalid
162
     * @throws CacheWarmup\Exception\OptionsAreMalformed
163
     */
164
    public function getParserOptions(): array
8✔
165
    {
166
        try {
167
            $json = $this->configuration->get(Extension::KEY, 'parserOptions');
8✔
168

169
            // Early return if no parser options are configured
170
            if (!\is_string($json) || $json === '') {
8✔
171
                return [];
6✔
172
            }
173

174
            return $this->optionsParser->parse($json);
2✔
175
        } catch (Core\Exception) {
1✔
176
            return [];
×
177
        }
178
    }
179

180
    /**
181
     * @return array<string, mixed>
182
     * @throws CacheWarmup\Exception\OptionsAreInvalid
183
     * @throws CacheWarmup\Exception\OptionsAreMalformed
184
     */
185
    public function getClientOptions(): array
18✔
186
    {
187
        try {
188
            $json = $this->configuration->get(Extension::KEY, 'clientOptions');
18✔
189

190
            // Early return if no client options are configured
191
            if (!\is_string($json) || $json === '') {
18✔
192
                return [];
16✔
193
            }
194

195
            return $this->optionsParser->parse($json);
2✔
196
        } catch (Core\Exception) {
1✔
197
            return [];
×
198
        }
199
    }
200

201
    /**
202
     * @return non-negative-int
203
     */
204
    public function getLimit(): int
12✔
205
    {
206
        try {
207
            $limit = $this->configuration->get(Extension::KEY, 'limit');
12✔
208

209
            if (!is_numeric($limit)) {
12✔
210
                return self::DEFAULT_LIMIT;
1✔
211
            }
212

213
            return max(0, (int)$limit);
11✔
214
        } catch (Core\Exception) {
×
215
            return self::DEFAULT_LIMIT;
×
216
        }
217
    }
218

219
    /**
220
     * @return list<string>
221
     */
222
    public function getExcludePatterns(): array
8✔
223
    {
224
        try {
225
            $exclude = $this->configuration->get(Extension::KEY, 'exclude');
8✔
226

227
            // Early return if no exclude patterns are configured
228
            if (!\is_string($exclude) || $exclude === '') {
8✔
229
                return [];
7✔
230
            }
231

232
            return Core\Utility\GeneralUtility::trimExplode(',', $exclude, true);
1✔
233
        } catch (Core\Exception) {
×
234
            return [];
×
235
        }
236
    }
237

238
    public function getStrategy(): ?CacheWarmup\Crawler\Strategy\CrawlingStrategy
13✔
239
    {
240
        try {
241
            $strategy = $this->configuration->get(Extension::KEY, 'strategy');
13✔
242

243
            // Early return if no crawling strategy is configured
244
            if (!\is_string($strategy) || $strategy === '') {
13✔
245
                return null;
11✔
246
            }
247

248
            // Early return if configured crawling strategy is invalid
249
            if (!$this->crawlingStrategyFactory->has($strategy)) {
2✔
250
                return null;
1✔
251
            }
252

253
            return $this->crawlingStrategyFactory->get($strategy);
1✔
254
        } catch (Core\Exception) {
×
255
            return null;
×
256
        }
257
    }
258

259
    public function isEnabledInPageTree(): bool
14✔
260
    {
261
        try {
262
            $enablePageTree = $this->configuration->get(Extension::KEY, 'enablePageTree');
14✔
263

264
            return (bool)$enablePageTree;
14✔
265
        } catch (Core\Exception) {
×
266
            return true;
×
267
        }
268
    }
269

270
    /**
271
     * @return list<int>
272
     */
273
    public function getSupportedDoktypes(): array
13✔
274
    {
275
        try {
276
            $doktypes = $this->configuration->get(Extension::KEY, 'supportedDoktypes');
13✔
277

278
            if (!\is_string($doktypes)) {
13✔
279
                return self::DEFAULT_SUPPORTED_DOKTYPES;
1✔
280
            }
281

282
            return Core\Utility\GeneralUtility::intExplode(',', $doktypes, true);
12✔
283
        } catch (Core\Exception) {
×
284
            return self::DEFAULT_SUPPORTED_DOKTYPES;
×
285
        }
286
    }
287

288
    public function isEnabledInToolbar(): bool
33✔
289
    {
290
        try {
291
            $enableToolbar = $this->configuration->get(Extension::KEY, 'enableToolbar');
33✔
292

293
            return (bool)$enableToolbar;
33✔
294
        } catch (Core\Exception) {
×
295
            return true;
×
296
        }
297
    }
298

299
    public function getUserAgent(): string
9✔
300
    {
301
        return $this->userAgent;
9✔
302
    }
303

304
    private function generateUserAgent(): string
79✔
305
    {
306
        $string = 'TYPO3/tx_warming_crawler';
79✔
307

308
        if (class_exists(Core\Crypto\HashService::class)) {
79✔
309
            return Core\Utility\GeneralUtility::makeInstance(Core\Crypto\HashService::class)->appendHmac(
79✔
310
                $string,
79✔
311
                self::class,
79✔
312
            );
79✔
313
        }
314

315
        // @todo Remove once support for TYPO3 v12 is dropped
316
        /* @phpstan-ignore classConstant.deprecatedClass, method.deprecatedClass */
317
        return Core\Utility\GeneralUtility::makeInstance(Extbase\Security\Cryptography\HashService::class)->appendHmac(
×
318
            $string,
×
319
        );
×
320
    }
321

322
    private function getCrawlerFactory(): CacheWarmup\Crawler\CrawlerFactory
14✔
323
    {
324
        // Cannot be instantiated with DI, would lead to circular dependencies
325
        return $this->crawlerFactory ??= Core\Utility\GeneralUtility::makeInstance(
14✔
326
            CacheWarmup\Crawler\CrawlerFactory::class,
14✔
327
        );
14✔
328
    }
329
}
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