diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc6796e5..2f82761b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@ The change log describes what is "Added", "Removed", "Changed" or "Fixed" betwee
# Version 2
+# 2.2.0 - 2025-12-08
+- Replaced XML configuration with PHP configuration.
+
# 2.1.0 - 2024-11-24
- Added [PluginConfigurator](https://docs.php-http.org/en/latest/integrations/symfony-bundle.html#configure-a-custom-plugin)
diff --git a/src/DependencyInjection/HttplugExtension.php b/src/DependencyInjection/HttplugExtension.php
index d8c913c3..403676b0 100644
--- a/src/DependencyInjection/HttplugExtension.php
+++ b/src/DependencyInjection/HttplugExtension.php
@@ -33,7 +33,7 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Extension\Extension;
-use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
+use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\RateLimiter\LimiterInterface;
use Twig\Environment as TwigEnvironment;
@@ -56,12 +56,12 @@ public function load(array $configs, ContainerBuilder $container): void
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
- $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
+ $loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
- $loader->load('services.xml');
- $loader->load('plugins.xml');
+ $loader->load('services.php');
+ $loader->load('plugins.php');
if (\class_exists(MockClient::class)) {
- $loader->load('mock-client.xml');
+ $loader->load('mock-client.php');
}
// Register default services
@@ -79,7 +79,7 @@ public function load(array $configs, ContainerBuilder $container): void
// Configure toolbar
$profilingEnabled = $this->isConfigEnabled($container, $config['profiling']);
if ($profilingEnabled) {
- $loader->load('data-collector.xml');
+ $loader->load('data-collector.php');
if (!empty($config['profiling']['formatter'])) {
// Add custom formatter
@@ -117,7 +117,7 @@ public function load(array $configs, ContainerBuilder $container): void
throw new InvalidConfigurationException('You need to require the VCR plugin to be able to use it: "composer require --dev php-http/vcr-plugin".');
}
- $loader->load('vcr-plugin.xml');
+ $loader->load('vcr-plugin.php');
}
}
diff --git a/src/Resources/config/data-collector.php b/src/Resources/config/data-collector.php
new file mode 100644
index 00000000..46f25c7f
--- /dev/null
+++ b/src/Resources/config/data-collector.php
@@ -0,0 +1,146 @@
+services();
+
+ $services->set('httplug.formatter.full_http_message', FullHttpMessageFormatter::class);
+
+ $services->set('httplug.collector.formatter', Formatter::class)
+ ->args([
+ service('httplug.formatter.full_http_message'),
+ inline_service(CurlCommandFormatter::class),
+ ]);
+
+ $services->set('httplug.collector.collector', Collector::class)
+ ->tag('data_collector', [
+ 'template' => '@Httplug/webprofiler.html.twig',
+ 'priority' => 200,
+ 'id' => 'httplug',
+ ])
+ ->tag('kernel.reset', ['method' => 'reset']);
+
+ $services->set('httplug.plugin.stack', StackPlugin::class)
+ ->args([
+ service('httplug.collector.collector'),
+ service('httplug.collector.formatter'),
+ ])
+ ->abstract();
+
+ $services->set('httplug.collector.twig.http_message', HttpMessageMarkupExtension::class)
+ ->args([
+ service('var_dumper.cloner')->nullOnInvalid(),
+ service('var_dumper.html_dumper')->nullOnInvalid(),
+ ])
+ ->tag('twig.extension');
+
+ // Discovered clients
+ $services->set('httplug.collector.auto_discovered_client', ProfileClient::class)
+ ->decorate('httplug.auto_discovery.auto_discovered_client')
+ ->args([
+ service('httplug.collector.auto_discovered_client.inner'),
+ service('httplug.collector.collector'),
+ service('httplug.collector.formatter'),
+ service('debug.stopwatch'),
+ ]);
+
+ $services->set('httplug.collector.auto_discovered_async', ProfileClient::class)
+ ->decorate('httplug.auto_discovery.auto_discovered_async')
+ ->args([
+ service('httplug.collector.auto_discovered_async.inner'),
+ service('httplug.collector.collector'),
+ service('httplug.collector.formatter'),
+ service('debug.stopwatch'),
+ ]);
+
+ // ClientFactories
+ $services->set('httplug.collector.factory.auto', ProfileClientFactory::class)
+ ->decorate('httplug.factory.auto')
+ ->args([
+ service('httplug.collector.factory.auto.inner'),
+ service('httplug.collector.collector'),
+ service('httplug.collector.formatter'),
+ service('debug.stopwatch'),
+ ]);
+
+ $services->set('httplug.collector.factory.buzz', ProfileClientFactory::class)
+ ->decorate('httplug.factory.buzz')
+ ->args([
+ service('httplug.collector.factory.buzz.inner'),
+ service('httplug.collector.collector'),
+ service('httplug.collector.formatter'),
+ service('debug.stopwatch'),
+ ]);
+
+ $services->set('httplug.collector.factory.curl', ProfileClientFactory::class)
+ ->decorate('httplug.factory.curl')
+ ->args([
+ service('httplug.collector.factory.curl.inner'),
+ service('httplug.collector.collector'),
+ service('httplug.collector.formatter'),
+ service('debug.stopwatch'),
+ ]);
+
+ $services->set('httplug.collector.factory.guzzle6', ProfileClientFactory::class)
+ ->decorate('httplug.factory.guzzle6')
+ ->args([
+ service('httplug.collector.factory.guzzle6.inner'),
+ service('httplug.collector.collector'),
+ service('httplug.collector.formatter'),
+ service('debug.stopwatch'),
+ ]);
+
+ $services->set('httplug.collector.factory.guzzle7', ProfileClientFactory::class)
+ ->decorate('httplug.factory.guzzle7')
+ ->args([
+ service('httplug.collector.factory.guzzle7.inner'),
+ service('httplug.collector.collector'),
+ service('httplug.collector.formatter'),
+ service('debug.stopwatch'),
+ ]);
+
+ $services->set('httplug.collector.factory.react', ProfileClientFactory::class)
+ ->decorate('httplug.factory.react')
+ ->args([
+ service('httplug.collector.factory.react.inner'),
+ service('httplug.collector.collector'),
+ service('httplug.collector.formatter'),
+ service('debug.stopwatch'),
+ ]);
+
+ $services->set('httplug.collector.factory.socket', ProfileClientFactory::class)
+ ->decorate('httplug.factory.socket')
+ ->args([
+ service('httplug.collector.factory.socket.inner'),
+ service('httplug.collector.collector'),
+ service('httplug.collector.formatter'),
+ service('debug.stopwatch'),
+ ]);
+
+ $services->set(\Http\Client\Common\PluginClientFactory::class, PluginClientFactory::class)
+ ->args([
+ service('httplug.collector.collector'),
+ service('httplug.collector.formatter'),
+ service('debug.stopwatch'),
+ ]);
+
+ $services->set(PluginClientFactoryListener::class)
+ ->args([
+ service(\Http\Client\Common\PluginClientFactory::class),
+ ])
+ ->tag('kernel.event_subscriber');
+};
diff --git a/src/Resources/config/data-collector.xml b/src/Resources/config/data-collector.xml
deleted file mode 100644
index c34e41bc..00000000
--- a/src/Resources/config/data-collector.xml
+++ /dev/null
@@ -1,103 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Resources/config/mock-client.php b/src/Resources/config/mock-client.php
new file mode 100644
index 00000000..c8fd11bd
--- /dev/null
+++ b/src/Resources/config/mock-client.php
@@ -0,0 +1,23 @@
+services();
+
+ $services->set('httplug.client.mock', Client::class)
+ ->public();
+
+ $services->alias(Client::class, 'httplug.client.mock')
+ ->public();
+
+ $services->set('httplug.factory.mock', MockFactory::class)
+ ->call('setClient', [
+ service('httplug.client.mock'),
+ ]);
+};
diff --git a/src/Resources/config/mock-client.xml b/src/Resources/config/mock-client.xml
deleted file mode 100644
index 5a5e2640..00000000
--- a/src/Resources/config/mock-client.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Resources/config/plugins.php b/src/Resources/config/plugins.php
new file mode 100644
index 00000000..35614664
--- /dev/null
+++ b/src/Resources/config/plugins.php
@@ -0,0 +1,124 @@
+services();
+
+ $services->set('httplug.plugin.cache', CachePlugin::class)
+ ->args([
+ null,
+ null,
+ null,
+ ])
+ ->abstract();
+
+ $services->set('httplug.plugin.content_length', ContentLengthPlugin::class);
+
+ $services->set('httplug.plugin.cookie', CookiePlugin::class)
+ ->args([null]);
+
+ $services->set('httplug.plugin.decoder', DecoderPlugin::class);
+
+ $services->set('httplug.plugin.error', ErrorPlugin::class);
+
+ $services->set('httplug.plugin.history', HistoryPlugin::class)
+ ->args([null]);
+
+ $services->set('httplug.plugin.logger', LoggerPlugin::class)
+ ->args([
+ null,
+ null,
+ ])
+ ->tag('monolog.logger', ['channel' => 'httplug'])
+ ->abstract();
+
+ $services->set('httplug.plugin.redirect', RedirectPlugin::class);
+
+ $services->set('httplug.plugin.retry', RetryPlugin::class);
+
+ $services->set('httplug.plugin.stopwatch', StopwatchPlugin::class)
+ ->args([null])
+ ->abstract();
+
+ $services->set('httplug.plugin.throttle', ThrottlePlugin::class)
+ ->args([null])
+ ->abstract();
+
+ // client specific plugin definition prototypes
+
+ $services->set('httplug.plugin.add_host', AddHostPlugin::class)
+ ->args([
+ null,
+ null,
+ ])
+ ->abstract();
+
+ $services->set('httplug.plugin.add_path', AddPathPlugin::class)
+ ->args([null])
+ ->abstract();
+
+ $services->set('httplug.plugin.base_uri', BaseUriPlugin::class)
+ ->args([
+ null,
+ null,
+ ])
+ ->abstract();
+
+ $services->set('httplug.plugin.content_type', ContentTypePlugin::class)
+ ->args([null])
+ ->abstract();
+
+ $services->set('httplug.plugin.header_append', HeaderAppendPlugin::class)
+ ->args([null])
+ ->abstract();
+
+ $services->set('httplug.plugin.header_defaults', HeaderDefaultsPlugin::class)
+ ->args([null])
+ ->abstract();
+
+ $services->set('httplug.plugin.header_set', HeaderSetPlugin::class)
+ ->args([null])
+ ->abstract();
+
+ $services->set('httplug.plugin.header_remove', HeaderRemovePlugin::class)
+ ->args([null])
+ ->abstract();
+
+ $services->set('httplug.plugin.query_defaults', QueryDefaultsPlugin::class)
+ ->args([null])
+ ->abstract();
+
+ $services->set('httplug.plugin.request_seekable_body', RequestSeekableBodyPlugin::class)
+ ->args([null])
+ ->abstract();
+
+ $services->set('httplug.plugin.response_seekable_body', ResponseSeekableBodyPlugin::class)
+ ->args([null])
+ ->abstract();
+};
diff --git a/src/Resources/config/plugins.xml b/src/Resources/config/plugins.xml
deleted file mode 100644
index 9f9c0ec1..00000000
--- a/src/Resources/config/plugins.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- null
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Resources/config/services.php b/src/Resources/config/services.php
new file mode 100644
index 00000000..ed597732
--- /dev/null
+++ b/src/Resources/config/services.php
@@ -0,0 +1,130 @@
+services();
+
+ $services->set('httplug.strategy', ConfiguredClientsStrategy::class)
+ ->args([
+ service('httplug.auto_discovery.auto_discovered_client')->nullOnInvalid(),
+ service('httplug.auto_discovery.auto_discovered_async')->nullOnInvalid(),
+ ]);
+
+ $services->set('httplug.strategy_listener', ConfiguredClientsStrategyListener::class)
+ ->tag('kernel.event_subscriber');
+
+ $services->set('httplug.auto_discovery.auto_discovered_client', ClientInterface::class)
+ ->factory([Psr18ClientDiscovery::class, 'find']);
+
+ $services->set('httplug.auto_discovery.auto_discovered_async', HttpAsyncClient::class)
+ ->factory([HttpAsyncClientDiscovery::class, 'find']);
+
+ // Discovery with autowiring support
+ $services->set('httplug.async_client.default', HttpAsyncClient::class)
+ ->factory([HttpAsyncClientDiscovery::class, 'find']);
+
+ $services->alias(HttpAsyncClient::class, 'httplug.async_client.default');
+
+ $services->set('httplug.client.default', ClientInterface::class)
+ ->factory([Psr18ClientDiscovery::class, 'find']);
+
+ $services->alias(ClientInterface::class, 'httplug.client');
+
+ // Discovery for PSR-18
+ $services->set('httplug.psr18_client.default', ClientInterface::class)
+ ->factory([Psr18ClientDiscovery::class, 'find']);
+
+ // Discovery for PSR-17
+ $services->set('httplug.psr17_request_factory.default', RequestFactoryInterface::class)
+ ->factory([Psr17FactoryDiscovery::class, 'findRequestFactory']);
+
+ $services->alias(RequestFactoryInterface::class, 'httplug.psr17_request_factory.default')
+ ->public();
+
+ $services->set('httplug.psr17_response_factory.default', ResponseFactoryInterface::class)
+ ->factory([Psr17FactoryDiscovery::class, 'findResponseFactory']);
+
+ $services->alias(ResponseFactoryInterface::class, 'httplug.psr17_response_factory.default')
+ ->public();
+
+ $services->set('httplug.psr17_stream_factory.default', StreamFactoryInterface::class)
+ ->factory([Psr17FactoryDiscovery::class, 'findStreamFactory']);
+
+ $services->alias(StreamFactoryInterface::class, 'httplug.psr17_stream_factory.default')
+ ->public();
+
+ $services->set('httplug.psr17_uri_factory.default', UriFactoryInterface::class)
+ ->factory([Psr17FactoryDiscovery::class, 'findUrlFactory']);
+
+ $services->alias(UriFactoryInterface::class, 'httplug.psr17_uri_factory.default')
+ ->public();
+
+ $services->set('httplug.psr17_uploaded_file_factory.default', UploadedFileFactoryInterface::class)
+ ->factory([Psr17FactoryDiscovery::class, 'findUploadedFileFactory']);
+
+ $services->alias(UploadedFileFactoryInterface::class, 'httplug.psr17_uploaded_file_factory.default')
+ ->public();
+
+ $services->set('httplug.psr17_server_request_factory.default', ServerRequestFactoryInterface::class)
+ ->factory([Psr17FactoryDiscovery::class, 'findServerRequestFactory']);
+
+ $services->alias(ServerRequestFactoryInterface::class, 'httplug.psr17_server_request_factory.default')
+ ->public();
+
+ // PluginClientFactory
+ $services->set(PluginClientFactory::class);
+
+ // ClientFactories
+ $services->set('httplug.factory.auto', AutoDiscoveryFactory::class);
+
+ $services->set('httplug.factory.buzz', BuzzFactory::class)
+ ->args([
+ service('httplug.psr17_response_factory'),
+ ]);
+
+ $services->set('httplug.factory.curl', CurlFactory::class)
+ ->args([
+ service('httplug.psr17_response_factory'),
+ service('httplug.psr17_stream_factory'),
+ ]);
+
+ $services->set('httplug.factory.guzzle6', Guzzle6Factory::class);
+
+ $services->set('httplug.factory.guzzle7', Guzzle7Factory::class);
+
+ $services->set('httplug.factory.react', ReactFactory::class);
+
+ $services->set('httplug.factory.socket', SocketFactory::class);
+
+ $services->set('httplug.factory.symfony', SymfonyFactory::class)
+ ->args([
+ service('httplug.psr17_response_factory'),
+ service('httplug.psr17_stream_factory'),
+ ]);
+};
diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml
deleted file mode 100644
index c0db9ef5..00000000
--- a/src/Resources/config/services.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Resources/config/vcr-plugin.php b/src/Resources/config/vcr-plugin.php
new file mode 100644
index 00000000..49a5b0ac
--- /dev/null
+++ b/src/Resources/config/vcr-plugin.php
@@ -0,0 +1,26 @@
+services();
+
+ // Recorders
+ $services->set('httplug.plugin.vcr.recorder.filesystem', 'Http\Client\Plugin\Vcr\Recorder\FilesystemRecorder')
+ ->args([
+ null,
+ service('filesystem')->nullOnInvalid(),
+ ])
+ ->call('setLogger', [
+ service('logger')->nullOnInvalid(),
+ ])
+ ->abstract();
+
+ $services->set('httplug.plugin.vcr.recorder.in_memory', 'Http\Client\Plugin\Vcr\Recorder\InMemoryRecorder');
+
+ // Naming strategies
+ $services->set('httplug.plugin.vcr.naming_strategy.path', 'Http\Client\Plugin\Vcr\NamingStrategy\PathNamingStrategy')
+ ->abstract();
+};
diff --git a/src/Resources/config/vcr-plugin.xml b/src/Resources/config/vcr-plugin.xml
deleted file mode 100644
index 68c8bd69..00000000
--- a/src/Resources/config/vcr-plugin.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-