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

eliashaeussler / typo3-warming / 12587343620

02 Jan 2025 06:59PM UTC coverage: 90.268%. First build
12587343620

Pull #769

github

web-flow
Merge dfcf7a38c into 4c89ccdb8
Pull Request #769: [!!!][FEATURE] Require `eliashaeussler/cache-warmup` ^4.0

69 of 81 new or added lines in 11 files covered. (85.19%)

1076 of 1192 relevant lines covered (90.27%)

8.43 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
            }
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
            }
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
    public function getLimit(): int
12✔
183
    {
184
        try {
185
            $limit = $this->configuration->get(Extension::KEY, 'limit');
12✔
186

187
            if (!is_numeric($limit)) {
12✔
188
                return self::DEFAULT_LIMIT;
1✔
189
            }
190

191
            return abs((int)$limit);
11✔
192
        } catch (Core\Exception) {
×
193
            return self::DEFAULT_LIMIT;
×
194
        }
195
    }
196

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

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

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

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

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

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

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

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

242
            return (bool)$enablePageTree;
14✔
243
        } catch (Core\Exception) {
×
244
            return true;
×
245
        }
246
    }
247

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

256
            if (!\is_string($doktypes)) {
13✔
257
                return self::DEFAULT_SUPPORTED_DOKTYPES;
1✔
258
            }
259

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

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

271
            return (bool)$enableToolbar;
25✔
272
        } catch (Core\Exception) {
×
273
            return true;
×
274
        }
275
    }
276

277
    public function getUserAgent(): string
9✔
278
    {
279
        return $this->userAgent;
9✔
280
    }
281

282
    private function generateUserAgent(): string
67✔
283
    {
284
        $string = 'TYPO3/tx_warming_crawler';
67✔
285

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

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