Skip to content

Commit d9acb80

Browse files
[!!!][FEATURE] Introduce ClientFactory and clientOptions config
1 parent 823b5f9 commit d9acb80

33 files changed

+982
-106
lines changed

composer.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@
4646
"phpstan/phpstan-phpunit": "^2.0",
4747
"phpstan/phpstan-symfony": "^2.0",
4848
"phpunit/phpunit": "^11.5.1",
49+
"symfony/config": "^5.4.35 || ^6.4.3 || ^7.0.3",
4950
"symfony/event-dispatcher-contracts": "^2.5.3 || ^3.4.2",
50-
"symfony/string": "^6.4.3 || ^7.0.3"
51+
"symfony/string": "^5.4.35 || ^6.4.3 || ^7.0.3"
5152
},
5253
"conflict": {
5354
"cuyz/valinor": "1.14.0"
@@ -97,7 +98,10 @@
9798
"sca": [
9899
"@sca:php"
99100
],
100-
"sca:php": "phpstan analyse -c phpstan.php",
101+
"sca:php": [
102+
"@php tests/build/service-container.php",
103+
"phpstan analyse -c phpstan.php"
104+
],
101105
"test": [
102106
"@test:e2e",
103107
"@test:unit"

composer.lock

Lines changed: 76 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/.vitepress/config.mts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ export default defineConfig({
103103
{text: 'Endless mode', link: '/config-reference/repeat-after'},
104104
],
105105
},
106+
{
107+
text: 'Client',
108+
collapsed: true,
109+
items: [
110+
{text: 'Client options', link: '/config-reference/client-options'},
111+
],
112+
},
106113
{
107114
text: 'Crawling',
108115
collapsed: true,
@@ -163,6 +170,10 @@ export default defineConfig({
163170
{text: 'Configurable Parser', link: '/api/configurable-parser'},
164171
],
165172
},
173+
{
174+
text: 'Dependency injection',
175+
link: '/api/dependency-injection',
176+
},
166177
{
167178
text: 'Events',
168179
link: '/api/events',

docs/api/crawler.md

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,6 @@ final class MyCustomCrawler implements CacheWarmup\Crawler\Crawler
2020
}
2121
```
2222

23-
## Dependency Injection <Badge type="tip" text="3.2+" />
24-
25-
When a crawler is created using
26-
[`EliasHaeussler\CacheWarmup\Crawler\CrawlerFactory`](../../src/Crawler/CrawlerFactory.php),
27-
a limited service container is built to instantiate the crawler.
28-
This allows custom crawlers to define dependencies to a limited
29-
set of services, including:
30-
31-
* Current output (implementation of `OutputInterface`)
32-
* Current logger (implementation of `LoggerInterface`), only
33-
available if the [`--log-file`](../config-reference/log-file.md)
34-
command option is passed
35-
* Current event dispatcher (implementation of `EventDispatcherInterface`)
36-
3723
## Method Reference
3824

3925
The default crawler describes the following method:

docs/api/dependency-injection.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Dependency injection <Badge type="tip" text="3.2+" />
2+
3+
A limited service container is built to instantiate crawlers and
4+
parsers. This allows custom crawlers to define dependencies to a
5+
limited set of services.
6+
7+
## Included services
8+
9+
The container includes the following runtime services:
10+
11+
| Service | Description | Added&nbsp;in |
12+
|-----------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|
13+
| [`OutputInterface`](https://github.com/symfony/console/blob/v5.4.35/Output/OutputInterface.php) | Current output, either extracted from console application or constructed as `ConsoleOutput` | <Badge type="tip" text="3.2.0" /> |
14+
| [`LoggerInterface`](https://github.com/php-fig/log/blob/2.0.0/src/LoggerInterface.php) | Current logger, only available if the [`logFile`](../config-reference/log-file.md) configuration option is passed | <Badge type="tip" text="3.2.0" /> |
15+
| [`EventDispatcherInterface`](https://github.com/php-fig/event-dispatcher/blob/1.0.0/src/EventDispatcherInterface.php) | Current event dispatcher instance, either extracted from console application or constructed as new instance | <Badge type="tip" text="3.2.0" /> |
16+
| [`ClientFactory`](../../src/Http/Client/ClientFactory.php) | Factory to create Guzzle client with defaults from [`clientOptions`](../config-reference/client-options.md) configuration option | <Badge type="tip" text="4.0.0" /> |
17+
| [`ClientInterface`](https://github.com/guzzle/guzzle/blob/7.8.2/src/ClientInterface.php) | Shared Guzzle client, constructed with [`clientOptions`](../config-reference/client-options.md) configuration option | <Badge type="tip" text="4.0.0" /> |
18+
19+
## Supported factories
20+
21+
The service container is built when creating crawlers and parsers
22+
through their respective factories, including:
23+
24+
* [`EliasHaeussler\CacheWarmup\Crawler\CrawlerFactory`](../../src/Crawler/CrawlerFactory.php)
25+
* [`EliasHaeussler\CacheWarmup\Xml\ParserFactory`](../../src/Xml/ParserFactory.php)
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
outline: [2,3]
3+
---
4+
5+
# Client options <Badge type="tip" text="4.0+" />
6+
7+
<small>📝&nbsp;Name: `clientOptions` &middot; 🖥️&nbsp;Option: `-t`, `--client-options`</small>
8+
9+
> Additional [configuration](https://docs.guzzlephp.org/en/stable/quickstart.html#creating-a-client)
10+
> for shared Guzzle client.
11+
12+
::: info
13+
The shared Guzzle client is used in default crawlers and default parser. Custom
14+
crawlers and parsers may also use it, if properly configured via
15+
[dependency injection](../api/dependency-injection.md).
16+
:::
17+
18+
## Example
19+
20+
Pass client options in the expected input format.
21+
22+
::: warning IMPORTANT
23+
When passing client options as **command parameter** or **environment variable**,
24+
make sure to pass them as **JSON-encoded string**.
25+
:::
26+
27+
::: code-group
28+
29+
```bash [CLI]
30+
./cache-warmup.phar --client-options '{"auth": ["username", "password"]}'
31+
```
32+
33+
```json [JSON]
34+
{
35+
"clientOptions": {
36+
"auth": ["username", "password"]
37+
}
38+
}
39+
```
40+
41+
```php [PHP]
42+
use EliasHaeussler\CacheWarmup;
43+
44+
return static function (CacheWarmup\Config\CacheWarmupConfig $config) {
45+
$config->setClientOption('auth', ['username', 'password']);
46+
47+
return $config;
48+
};
49+
```
50+
51+
```yaml [YAML]
52+
clientOptions:
53+
auth: ['username', 'password']
54+
```
55+
56+
```bash [.env]
57+
CACHE_WARMUP_CLIENT_OPTIONS='{"auth": ["username", "password"]}'
58+
```
59+
60+
:::

docs/config-reference/index.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ Defines how to format cache warmup progress and result.
4444
* [Progress](progress.md)
4545
* [Endless mode](repeat-after.md)
4646

47+
## Client
48+
49+
Describes options to customize the shared Guzzle client.
50+
51+
* [Client options](client-options.md)
52+
4753
## Crawling
4854

4955
Describes options to modify and customize the crawling behavior.

phpstan.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
$symfonySet = PHPStanConfig\Set\SymfonySet::create()
2727
->withConsoleApplicationLoader('tests/build/console-application.php')
28+
->withContainerXmlPath('.build/container.xml')
2829
;
2930

3031
return PHPStanConfig\Config\Config::create(__DIR__)

src/Command/CacheWarmupCommand.php

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@
2626
use EliasHaeussler\CacheWarmup\CacheWarmer;
2727
use EliasHaeussler\CacheWarmup\Config;
2828
use EliasHaeussler\CacheWarmup\Crawler;
29+
use EliasHaeussler\CacheWarmup\DependencyInjection;
2930
use EliasHaeussler\CacheWarmup\Event;
3031
use EliasHaeussler\CacheWarmup\Exception;
3132
use EliasHaeussler\CacheWarmup\Formatter;
3233
use EliasHaeussler\CacheWarmup\Helper;
34+
use EliasHaeussler\CacheWarmup\Http;
3335
use EliasHaeussler\CacheWarmup\Log;
3436
use EliasHaeussler\CacheWarmup\Result;
3537
use EliasHaeussler\CacheWarmup\Sitemap;
@@ -66,20 +68,19 @@ final class CacheWarmupCommand extends Console\Command\Command
6668

6769
private readonly Crawler\Strategy\CrawlingStrategyFactory $crawlingStrategyFactory;
6870
private readonly Config\Component\OptionsParser $optionsParser;
69-
private readonly Xml\ParserFactory $parserFactory;
7071
private readonly Time\TimeTracker $timeTracker;
7172
private Config\CacheWarmupConfig $config;
7273
private Console\Style\SymfonyStyle $io;
7374
private Formatter\Formatter $formatter;
7475
private Crawler\CrawlerFactory $crawlerFactory;
76+
private Xml\ParserFactory $parserFactory;
7577
private bool $firstRun = true;
7678

7779
public function __construct(
7880
private readonly EventDispatcherInterface $eventDispatcher = new EventDispatcher\EventDispatcher(),
7981
) {
8082
$this->crawlingStrategyFactory = new Crawler\Strategy\CrawlingStrategyFactory();
8183
$this->optionsParser = new Config\Component\OptionsParser();
82-
$this->parserFactory = new Xml\ParserFactory();
8384
$this->timeTracker = new Time\TimeTracker();
8485

8586
parent::__construct('cache-warmup');
@@ -171,6 +172,13 @@ protected function configure(): void
171172
172173
<comment>%command.full_name% --limit 50</comment>
173174
175+
<info>Client options</info>
176+
<info>==============</info>
177+
Parsing and crawling of XML sitemaps and URLs is done by a shared Guzzle client.
178+
Use the <comment>--client-options</comment> option to provide additional config for the client:
179+
180+
<comment>%command.full_name% --client-options '{"auth": ["username", "password"]}'</comment>
181+
174182
<info>Crawler</info>
175183
<info>=======</info>
176184
By default, cache warmup will be done using concurrent HEAD requests.
@@ -283,6 +291,12 @@ protected function configure(): void
283291
Console\Input\InputOption::VALUE_NONE,
284292
'Show progress bar during cache warmup',
285293
);
294+
$this->addOption(
295+
'client-options',
296+
't',
297+
Console\Input\InputOption::VALUE_REQUIRED,
298+
'Additional config for client used for parsing and crawling XML sitemaps',
299+
);
286300
$this->addOption(
287301
'crawler',
288302
'c',
@@ -407,13 +421,17 @@ protected function initialize(Console\Input\InputInterface $input, Console\Outpu
407421
}
408422
}
409423

410-
$this->crawlerFactory = new Crawler\CrawlerFactory(
424+
// Create container factory for crawler and parser factory
425+
$containerFactory = new DependencyInjection\ContainerFactory(
411426
$output,
412427
$logger,
413-
$logLevel,
414-
$stopOnFailure,
415428
$this->eventDispatcher,
429+
new Http\Client\ClientFactory($this->config->getClientOptions()),
416430
);
431+
432+
// Create factories
433+
$this->crawlerFactory = new Crawler\CrawlerFactory($containerFactory, $logLevel, $stopOnFailure);
434+
$this->parserFactory = new Xml\ParserFactory($containerFactory);
417435
}
418436

419437
protected function interact(Console\Input\InputInterface $input, Console\Output\OutputInterface $output): void
@@ -472,6 +490,13 @@ protected function execute(Console\Input\InputInterface $input, Console\Output\O
472490
$this->firstRun = false;
473491
}
474492

493+
// Print client options
494+
if ($this->formatter->isVerbose() && $this->io->isVerbose() && [] !== $this->config->getClientOptions()) {
495+
$this->io->section('Using custom client options:');
496+
$this->io->writeln((string) json_encode($this->config->getClientOptions(), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
497+
$this->io->newLine();
498+
}
499+
475500
// Initialize components
476501
$crawler = $this->initializeCrawler();
477502
$cacheWarmer = $this->timeTracker->track(fn () => $this->initializeCacheWarmer($crawler));

0 commit comments

Comments
 (0)