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

eliashaeussler / typo3-warming / 14774326292

01 May 2025 11:01AM UTC coverage: 92.067% (+0.08%) from 91.991%
14774326292

push

github

web-flow
[TASK] Update all dependencies (#847)

* [TASK] Update all dependencies

* [TASk] Update PHPStan ignore annotations for TYPO3 v13

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Elias Häußler <elias@haeussler.dev>

1 of 1 new or added line in 1 file covered. (100.0%)

3 existing lines in 1 file now uncovered.

1207 of 1311 relevant lines covered (92.07%)

9.34 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(
71✔
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();
71✔
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
            /** @var class-string<CacheWarmup\Crawler\Crawler>|null $crawlerClass */
72
            $crawlerClass = $this->configuration->get(Extension::KEY, 'crawler');
9✔
73

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

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

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

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

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

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

119
        try {
120
            /** @var class-string<CacheWarmup\Crawler\VerboseCrawler>|null $crawlerClass */
121
            $crawlerClass = $this->configuration->get(Extension::KEY, 'verboseCrawler');
5✔
122

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

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

137
        return $crawler;
5✔
138
    }
139

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

295
            return (bool)$enableToolbar;
25✔
296
        } catch (Core\Exception) {
×
297
            return true;
×
298
        }
299
    }
300

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

306
    private function generateUserAgent(): string
71✔
307
    {
308
        $string = 'TYPO3/tx_warming_crawler';
71✔
309

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

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

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