Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2076,7 +2076,7 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e
->acceptAndWrap(['string'], 'base_uri')
->validate()
->ifTrue(static fn () => !class_exists(HttpClient::class))
->then(static fn () => 'HttpClient support cannot be enabled as the component is not installed. Try running "composer require symfony/http-client".')
->then(static fn () => throw new LogicException('HttpClient support cannot be enabled as the component is not installed. Try running "composer require symfony/http-client".'))
->end()
->validate()
->ifTrue(static fn ($v) => !isset($v['scope']) && !isset($v['base_uri']))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2877,39 +2877,45 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder
$retryOptions = $scopeConfig['retry_failed'] ?? ['enabled' => false];
unset($scopeConfig['retry_failed']);

$transport = $name.'.transport';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of introducing this separate service, we could register $name with the initial service (which could even be a DI alias instead of this current() hack in the factory) and then registering ScopingHttpClient as a decorator of that service, allowing to configure the decoration priority even for cases where ScopingHttpClient should not be the outer one (see UriTemplateHttpClient which is still on the outside).

$container->register($transport, HttpClientInterface::class)
->setFactory('current')
->setArguments([[new Reference('http_client.transport')]])
;

if (null === $scope) {
$baseUri = $scopeConfig['base_uri'];
unset($scopeConfig['base_uri']);

$container->register($name, ScopingHttpClient::class)
->setFactory([ScopingHttpClient::class, 'forBaseUri'])
->setArguments([new Reference('http_client.transport'), $baseUri, $scopeConfig])
->setArguments([new Reference($transport), $baseUri, $scopeConfig])
->addTag('http_client.client')
->addTag('kernel.reset', ['method' => 'reset', 'on_invalid' => 'ignore'])
;
} else {
$container->register($name, ScopingHttpClient::class)
->setArguments([new Reference('http_client.transport'), [$scope => $scopeConfig], $scope])
->setArguments([new Reference($transport), [$scope => $scopeConfig], $scope])
->addTag('http_client.client')
->addTag('kernel.reset', ['method' => 'reset', 'on_invalid' => 'ignore'])
;
}

if ($this->readConfigEnabled('http_client.scoped_clients.'.$name.'.caching', $container, $cachingOptions)) {
$this->registerCachingHttpClient($cachingOptions, $scopeConfig, $name, $container);
$this->registerCachingHttpClient($cachingOptions, $scopeConfig, $transport, $container);
}

if (null !== $rateLimiter) {
$this->registerThrottlingHttpClient($rateLimiter, $name, $container);
$this->registerThrottlingHttpClient($rateLimiter, $transport, $container);
}

if ($this->readConfigEnabled('http_client.scoped_clients.'.$name.'.retry_failed', $container, $retryOptions)) {
$this->registerRetryableHttpClient($retryOptions, $name, $container);
$this->registerRetryableHttpClient($retryOptions, $transport, $container);
}

$container
->register($name.'.uri_template', UriTemplateHttpClient::class)
->setDecoratedService($name, null, 7) // Between TraceableHttpClient (5) and RetryableHttpClient (10)
->setDecoratedService($name, null, 7) // After TraceableHttpClient (5) and on top of ScopingHttpClient
->setArguments([
new Reference($name.'.uri_template.inner'),
new Reference('http_client.uri_template_expander', ContainerInterface::NULL_ON_INVALID_REFERENCE),
Expand Down Expand Up @@ -2943,7 +2949,7 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder
private function registerCachingHttpClient(array $options, array $defaultOptions, string $name, ContainerBuilder $container): void
{
if (!class_exists(ChunkCacheItemNotFoundException::class)) {
throw new LogicException('Caching cannot be enabled as version 7.3+ of the HttpClient component is required.');
throw new LogicException('Caching cannot be enabled as version 7.4+ of the HttpClient component is required.');
}

$container
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2295,15 +2295,18 @@ public function testCachingHttpClient()
$this->assertFalse($arguments[3]);
$this->assertSame(2, $arguments[4]);

$this->assertTrue($container->hasDefinition('bar.caching'));
$definition = $container->getDefinition('bar.caching');
$this->assertTrue($container->hasDefinition('bar.transport.caching'));
$definition = $container->getDefinition('bar.transport.caching');
$this->assertSame(CachingHttpClient::class, $definition->getClass());
$this->assertSame('bar', $definition->getDecoratedService()[0]);
$arguments = $definition->getArguments();
$this->assertInstanceOf(Reference::class, $arguments[0]);
$this->assertSame('bar.caching.inner', (string) $arguments[0]);
$this->assertSame('bar.transport.caching.inner', (string) $arguments[0]);
$this->assertInstanceOf(Reference::class, $arguments[1]);
$this->assertSame('baz', (string) $arguments[1]);
$scopedClient = $container->getDefinition('bar');

$this->assertSame('bar.transport', (string) $scopedClient->getArgument(0));
$this->assertNull($scopedClient->getDecoratedService());
}

public function testHttpClientRetry()
Expand All @@ -2317,8 +2320,8 @@ public function testHttpClientRetry()
$this->assertSame(0.3, $container->getDefinition('http_client.retry_strategy')->getArgument(4));
$this->assertSame(2, $container->getDefinition('http_client.retryable')->getArgument(2));

$this->assertSame(RetryableHttpClient::class, $container->getDefinition('foo.retryable')->getClass());
$this->assertSame(4, $container->getDefinition('foo.retry_strategy')->getArgument(2));
$this->assertSame(RetryableHttpClient::class, $container->getDefinition('foo.transport.retryable')->getClass());
$this->assertSame(4, $container->getDefinition('foo.transport.retry_strategy')->getArgument(2));
}

public function testHttpClientWithQueryParameterKey()
Expand Down Expand Up @@ -2383,15 +2386,15 @@ public function testHttpClientRateLimiter()
$this->assertInstanceOf(Reference::class, $arguments[1]);
$this->assertSame('http_client.throttling.limiter', (string) $arguments[1]);

$this->assertTrue($container->hasDefinition('foo.throttling'));
$definition = $container->getDefinition('foo.throttling');
$this->assertTrue($container->hasDefinition('foo.transport.throttling'));
$definition = $container->getDefinition('foo.transport.throttling');
$this->assertSame(ThrottlingHttpClient::class, $definition->getClass());
$this->assertSame('foo', $definition->getDecoratedService()[0]);
$this->assertSame('foo.transport', $definition->getDecoratedService()[0]);
$this->assertCount(2, $arguments = $definition->getArguments());
$this->assertInstanceOf(Reference::class, $arguments[0]);
$this->assertSame('foo.throttling.inner', (string) $arguments[0]);
$this->assertSame('foo.transport.throttling.inner', (string) $arguments[0]);
$this->assertInstanceOf(Reference::class, $arguments[1]);
$this->assertSame('foo.throttling.limiter', (string) $arguments[1]);
$this->assertSame('foo.transport.throttling.limiter', (string) $arguments[1]);
}

public static function provideMailer(): iterable
Expand Down