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

eliashaeussler / typo3-warming / 14534307929

18 Apr 2025 11:15AM UTC coverage: 91.827% (-0.2%) from 92.065%
14534307929

Pull #769

github

eliashaeussler
[!!!][TASK] Drop support for PHP 8.1
Pull Request #769: [!!!][FEATURE] Require `eliashaeussler/cache-warmup` ^4.0

85 of 90 new or added lines in 12 files covered. (94.44%)

2 existing lines in 1 file now uncovered.

1191 of 1297 relevant lines covered (91.83%)

9.29 hits per line

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

76.04
/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

51
    public function __construct(
67✔
52
        private readonly Core\Configuration\ExtensionConfiguration $configuration,
53
        private readonly CacheWarmup\Crawler\CrawlerFactory $crawlerFactory,
54
        private readonly CacheWarmup\Crawler\Strategy\CrawlingStrategyFactory $crawlingStrategyFactory,
55
        private readonly CacheWarmup\Config\Component\OptionsParser $optionsParser,
56
    ) {
57
        $this->userAgent = $this->generateUserAgent();
67✔
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\VerboseCrawler>|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
            }
UNCOV
81
        } catch (Core\Exception) {
×
NEW
82
            $crawlerClass = self::DEFAULT_VERBOSE_CRAWLER;
×
83
        }
84

85
        return $this->crawlerFactory->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
            }
UNCOV
130
        } catch (Core\Exception) {
×
NEW
131
            $crawlerClass = self::DEFAULT_VERBOSE_CRAWLER;
×
132
        }
133

134
        /** @var CacheWarmup\Crawler\VerboseCrawler $crawler */
135
        $crawler = $this->crawlerFactory->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 client 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 non-negative-int
184
     */
185
    public function getLimit(): int
12✔
186
    {
187
        try {
188
            $limit = $this->configuration->get(Extension::KEY, 'limit');
12✔
189

190
            if (!is_numeric($limit)) {
12✔
191
                return self::DEFAULT_LIMIT;
1✔
192
            }
193

194
            return max(0, (int)$limit);
11✔
195
        } catch (Core\Exception) {
×
196
            return self::DEFAULT_LIMIT;
×
197
        }
198
    }
199

200
    /**
201
     * @return list<string>
202
     */
203
    public function getExcludePatterns(): array
8✔
204
    {
205
        try {
206
            $exclude = $this->configuration->get(Extension::KEY, 'exclude');
8✔
207

208
            // Early return if no exclude patterns are configured
209
            if (!\is_string($exclude) || $exclude === '') {
8✔
210
                return [];
7✔
211
            }
212

213
            return Core\Utility\GeneralUtility::trimExplode(',', $exclude, true);
1✔
214
        } catch (Core\Exception) {
×
215
            return [];
×
216
        }
217
    }
218

219
    public function getStrategy(): ?CacheWarmup\Crawler\Strategy\CrawlingStrategy
13✔
220
    {
221
        try {
222
            $strategy = $this->configuration->get(Extension::KEY, 'strategy');
13✔
223

224
            // Early return if no crawling strategy is configured
225
            if (!\is_string($strategy) || $strategy === '') {
13✔
226
                return null;
11✔
227
            }
228

229
            // Early return if configured crawling strategy is invalid
230
            if (!$this->crawlingStrategyFactory->has($strategy)) {
2✔
231
                return null;
1✔
232
            }
233

234
            return $this->crawlingStrategyFactory->get($strategy);
1✔
235
        } catch (Core\Exception) {
×
236
            return null;
×
237
        }
238
    }
239

240
    public function isEnabledInPageTree(): bool
14✔
241
    {
242
        try {
243
            $enablePageTree = $this->configuration->get(Extension::KEY, 'enablePageTree');
14✔
244

245
            return (bool)$enablePageTree;
14✔
246
        } catch (Core\Exception) {
×
247
            return true;
×
248
        }
249
    }
250

251
    /**
252
     * @return list<int>
253
     */
254
    public function getSupportedDoktypes(): array
13✔
255
    {
256
        try {
257
            $doktypes = $this->configuration->get(Extension::KEY, 'supportedDoktypes');
13✔
258

259
            if (!\is_string($doktypes)) {
13✔
260
                return self::DEFAULT_SUPPORTED_DOKTYPES;
1✔
261
            }
262

263
            return array_values(Core\Utility\GeneralUtility::intExplode(',', $doktypes, true));
12✔
264
        } catch (Core\Exception) {
×
265
            return self::DEFAULT_SUPPORTED_DOKTYPES;
×
266
        }
267
    }
268

269
    public function isEnabledInToolbar(): bool
25✔
270
    {
271
        try {
272
            $enableToolbar = $this->configuration->get(Extension::KEY, 'enableToolbar');
25✔
273

274
            return (bool)$enableToolbar;
25✔
275
        } catch (Core\Exception) {
×
276
            return true;
×
277
        }
278
    }
279

280
    public function getUserAgent(): string
9✔
281
    {
282
        return $this->userAgent;
9✔
283
    }
284

285
    private function generateUserAgent(): string
67✔
286
    {
287
        $string = 'TYPO3/tx_warming_crawler';
67✔
288

289
        if (class_exists(Core\Crypto\HashService::class)) {
67✔
290
            return Core\Utility\GeneralUtility::makeInstance(Core\Crypto\HashService::class)->appendHmac(
×
291
                $string,
×
292
                self::class,
×
293
            );
×
294
        }
295

296
        // @todo Remove once support for TYPO3 v12 is dropped
297
        return Core\Utility\GeneralUtility::makeInstance(Extbase\Security\Cryptography\HashService::class)->appendHmac(
67✔
298
            $string,
67✔
299
        );
67✔
300
    }
301
}
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

© 2025 Coveralls, Inc