Skip to content

Commit 925fa8e

Browse files
authored
Merge pull request #112 from php-http/client-extra-plugins
Allow to configure plugins specifically for a client
2 parents ec48ab0 + 11eeafc commit 925fa8e

File tree

12 files changed

+598
-124
lines changed

12 files changed

+598
-124
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
### Added
1111

12+
- You can now also configure client specific plugins in the `plugins` option of a client.
13+
Some plugins that you previously had to define in your own services can now be configured on the client.
1214
- Support for BatchClient
1315
- The stopwatch plugin in included by default when using profiling.
1416

DependencyInjection/Configuration.php

+253-99
Large diffs are not rendered by default.

DependencyInjection/HttplugExtension.php

+87-24
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
use Http\Message\Authentication\BasicAuth;
1515
use Http\Message\Authentication\Bearer;
1616
use Http\Message\Authentication\Wsse;
17+
use Psr\Http\Message\UriInterface;
1718
use Symfony\Component\Config\FileLocator;
1819
use Symfony\Component\DependencyInjection\ContainerBuilder;
20+
use Symfony\Component\DependencyInjection\ContainerInterface;
1921
use Symfony\Component\DependencyInjection\Definition;
2022
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
2123
use Symfony\Component\DependencyInjection\Reference;
@@ -53,7 +55,7 @@ public function load(array $configs, ContainerBuilder $container)
5355
}
5456

5557
// Configure toolbar
56-
if ($config['profiling']['enabled']) {
58+
if ($this->isConfigEnabled($container, $config['profiling'])) {
5759
$loader->load('data-collector.xml');
5860

5961
if (!empty($config['profiling']['formatter'])) {
@@ -70,8 +72,8 @@ public function load(array $configs, ContainerBuilder $container)
7072
;
7173
}
7274

73-
$this->configurePlugins($container, $config['plugins']);
7475
$this->configureClients($container, $config);
76+
$this->configureSharedPlugins($container, $config['plugins']); // must be after clients, as clients.X.plugins might use plugins as templates that will be removed
7577
$this->configureAutoDiscoveryClients($container, $config);
7678
}
7779

@@ -91,7 +93,7 @@ private function configureClients(ContainerBuilder $container, array $config)
9193
$first = $name;
9294
}
9395

94-
$this->configureClient($container, $name, $arguments, $config['profiling']['enabled']);
96+
$this->configureClient($container, $name, $arguments, $this->isConfigEnabled($container, $config['profiling']));
9597
}
9698

9799
// If we have clients configured
@@ -108,7 +110,7 @@ private function configureClients(ContainerBuilder $container, array $config)
108110
* @param ContainerBuilder $container
109111
* @param array $config
110112
*/
111-
private function configurePlugins(ContainerBuilder $container, array $config)
113+
private function configureSharedPlugins(ContainerBuilder $container, array $config)
112114
{
113115
if (!empty($config['authentication'])) {
114116
$this->configureAuthentication($container, $config['authentication']);
@@ -118,21 +120,23 @@ private function configurePlugins(ContainerBuilder $container, array $config)
118120
foreach ($config as $name => $pluginConfig) {
119121
$pluginId = 'httplug.plugin.'.$name;
120122

121-
if ($pluginConfig['enabled']) {
123+
if ($this->isConfigEnabled($container, $pluginConfig)) {
122124
$def = $container->getDefinition($pluginId);
123-
$this->configurePluginByName($name, $def, $pluginConfig);
125+
$this->configurePluginByName($name, $def, $pluginConfig, $container, $pluginId);
124126
} else {
125127
$container->removeDefinition($pluginId);
126128
}
127129
}
128130
}
129131

130132
/**
131-
* @param string $name
132-
* @param Definition $definition
133-
* @param array $config
133+
* @param string $name
134+
* @param Definition $definition
135+
* @param array $config
136+
* @param ContainerBuilder $container In case we need to add additional services for this plugin
137+
* @param string $serviceId Service id of the plugin, in case we need to add additional services for this plugin.
134138
*/
135-
private function configurePluginByName($name, Definition $definition, array $config)
139+
private function configurePluginByName($name, Definition $definition, array $config, ContainerInterface $container, $serviceId)
136140
{
137141
switch ($name) {
138142
case 'cache':
@@ -173,6 +177,23 @@ private function configurePluginByName($name, Definition $definition, array $con
173177
$definition->replaceArgument(0, new Reference($config['stopwatch']));
174178
break;
175179

180+
/* client specific plugins */
181+
182+
case 'add_host':
183+
$uriService = $serviceId.'.host_uri';
184+
$this->createUri($container, $uriService, $config['host']);
185+
$definition->replaceArgument(0, new Reference($uriService));
186+
$definition->replaceArgument(1, [
187+
'replace' => $config['replace'],
188+
]);
189+
break;
190+
case 'header_append':
191+
case 'header_defaults':
192+
case 'header_set':
193+
case 'header_remove':
194+
$definition->replaceArgument(0, $config['headers']);
195+
break;
196+
176197
default:
177198
throw new \InvalidArgumentException(sprintf('Internal exception: Plugin %s is not handled', $name));
178199
}
@@ -181,11 +202,15 @@ private function configurePluginByName($name, Definition $definition, array $con
181202
/**
182203
* @param ContainerBuilder $container
183204
* @param array $config
205+
*
206+
* @return array List of service ids for the authentication plugins.
184207
*/
185-
private function configureAuthentication(ContainerBuilder $container, array $config)
208+
private function configureAuthentication(ContainerBuilder $container, array $config, $servicePrefix = 'httplug.plugin.authentication')
186209
{
210+
$pluginServices = [];
211+
187212
foreach ($config as $name => $values) {
188-
$authServiceKey = sprintf('httplug.plugin.authentication.%s.auth', $name);
213+
$authServiceKey = sprintf($servicePrefix.'.%s.auth', $name);
189214
switch ($values['type']) {
190215
case 'bearer':
191216
$container->register($authServiceKey, Bearer::class)
@@ -208,33 +233,54 @@ private function configureAuthentication(ContainerBuilder $container, array $con
208233
throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type']));
209234
}
210235

211-
$container->register('httplug.plugin.authentication.'.$name, AuthenticationPlugin::class)
212-
->addArgument(new Reference($authServiceKey));
236+
$pluginServiceKey = $servicePrefix.'.'.$name;
237+
$container->register($pluginServiceKey, AuthenticationPlugin::class)
238+
->addArgument(new Reference($authServiceKey))
239+
;
240+
$pluginServices[] = $pluginServiceKey;
213241
}
242+
243+
return $pluginServices;
214244
}
215245

216246
/**
217247
* @param ContainerBuilder $container
218-
* @param string $name
248+
* @param string $clientName
219249
* @param array $arguments
220250
* @param bool $profiling
221251
*/
222-
private function configureClient(ContainerBuilder $container, $name, array $arguments, $profiling)
252+
private function configureClient(ContainerBuilder $container, $clientName, array $arguments, $profiling)
223253
{
224-
$serviceId = 'httplug.client.'.$name;
254+
$serviceId = 'httplug.client.'.$clientName;
255+
256+
$plugins = [];
257+
foreach ($arguments['plugins'] as $plugin) {
258+
list($pluginName, $pluginConfig) = each($plugin);
259+
if ('reference' === $pluginName) {
260+
$plugins[] = $pluginConfig['id'];
261+
} elseif ('authentication' === $pluginName) {
262+
$plugins = array_merge($plugins, $this->configureAuthentication($container, $pluginConfig, $serviceId.'.authentication'));
263+
} else {
264+
$pluginServiceId = $serviceId.'.plugin.'.$pluginName;
265+
$def = clone $container->getDefinition('httplug.plugin'.'.'.$pluginName);
266+
$def->setAbstract(false);
267+
$this->configurePluginByName($pluginName, $def, $pluginConfig, $container, $pluginServiceId);
268+
$container->setDefinition($pluginServiceId, $def);
269+
$plugins[] = $pluginServiceId;
270+
}
271+
}
225272

226273
$pluginClientOptions = [];
227-
228274
if ($profiling) {
275+
// Add the stopwatch plugin
229276
if (!in_array('httplug.plugin.stopwatch', $arguments['plugins'])) {
230-
// Add the stopwatch plugin
231-
array_unshift($arguments['plugins'], 'httplug.plugin.stopwatch');
277+
array_unshift($plugins, 'httplug.plugin.stopwatch');
232278
}
233279

234280
// Tell the plugin journal what plugins we used
235281
$container
236282
->getDefinition('httplug.collector.plugin_journal')
237-
->addMethodCall('setPlugins', [$name, $arguments['plugins']])
283+
->addMethodCall('setPlugins', [$clientName, $plugins])
238284
;
239285

240286
$debugPluginServiceId = $this->registerDebugPlugin($container, $serviceId);
@@ -250,7 +296,7 @@ private function configureClient(ContainerBuilder $container, $name, array $argu
250296
function ($id) {
251297
return new Reference($id);
252298
},
253-
$arguments['plugins']
299+
$plugins
254300
)
255301
)
256302
->addArgument(new Reference($arguments['factory']))
@@ -290,6 +336,23 @@ function ($id) {
290336
}
291337
}
292338

339+
/**
340+
* Create a URI object with the default URI factory.
341+
*
342+
* @param ContainerBuilder $container
343+
* @param string $serviceId Name of the private service to create
344+
* @param string $uri String representation of the URI
345+
*/
346+
private function createUri(ContainerBuilder $container, $serviceId, $uri)
347+
{
348+
$container
349+
->register($serviceId, UriInterface::class)
350+
->setPublic(false)
351+
->setFactory([new Reference('httplug.uri_factory'), 'createUri'])
352+
->addArgument($uri)
353+
;
354+
}
355+
293356
/**
294357
* Make the user can select what client is used for auto discovery. If none is provided, a service will be created
295358
* by finding a client using auto discovery.
@@ -307,7 +370,7 @@ private function configureAutoDiscoveryClients(ContainerBuilder $container, arra
307370
$container,
308371
'auto_discovered_client',
309372
[HttpClientDiscovery::class, 'find'],
310-
$config['profiling']['enabled']
373+
$this->isConfigEnabled($container, $config['profiling'])
311374
);
312375
}
313376

@@ -322,7 +385,7 @@ private function configureAutoDiscoveryClients(ContainerBuilder $container, arra
322385
$container,
323386
'auto_discovered_async',
324387
[HttpAsyncClientDiscovery::class, 'find'],
325-
$config['profiling']['enabled']
388+
$this->isConfigEnabled($container, $config['profiling'])
326389
);
327390
}
328391

Resources/config/plugins.xml

+20
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,25 @@
2727
<service id="httplug.plugin.stopwatch" class="Http\Client\Common\Plugin\StopwatchPlugin" public="false">
2828
<argument />
2929
</service>
30+
31+
<!-- client specific plugin definition prototypes -->
32+
33+
<service id="httplug.plugin.add_host" class="Http\Client\Common\Plugin\AddHostPlugin" public="false" abstract="true">
34+
<argument/>
35+
<argument/>
36+
</service>
37+
<service id="httplug.plugin.header_append" class="Http\Client\Common\Plugin\HeaderAppendPlugin" public="false" abstract="true">
38+
<argument/>
39+
</service>
40+
<service id="httplug.plugin.header_defaults" class="Http\Client\Common\Plugin\HeaderDefaultsPlugin" public="false" abstract="true">
41+
<argument/>
42+
</service>
43+
<service id="httplug.plugin.header_set" class="Http\Client\Common\Plugin\HeaderSetPlugin" public="false" abstract="true">
44+
<argument/>
45+
</service>
46+
<service id="httplug.plugin.header_remove" class="Http\Client\Common\Plugin\HeaderRemovePlugin" public="false" abstract="true">
47+
<argument/>
48+
</service>
49+
3050
</services>
3151
</container>

Tests/Functional/ServiceInstantiationTest.php

+3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ public function testDebugToolbar()
4343
$plugins = $journal->getPlugins('acme');
4444
$this->assertEquals([
4545
'httplug.plugin.stopwatch',
46+
'httplug.client.acme.plugin.decoder',
4647
'httplug.plugin.redirect',
48+
'httplug.client.acme.plugin.add_host',
49+
'httplug.client.acme.authentication.my_basic',
4750
], $plugins);
4851
}
4952
}

Tests/Resources/Fixtures/config/full.php

+37
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,43 @@
1313
'uri_factory' => 'Http\Message\UriFactory\GuzzleUriFactory',
1414
'stream_factory' => 'Http\Message\StreamFactory\GuzzleStreamFactory',
1515
],
16+
'clients' => [
17+
'test' => [
18+
'factory' => 'httplug.factory.guzzle6',
19+
'http_methods_client' => true,
20+
'plugins' => [
21+
'httplug.plugin.redirect',
22+
[
23+
'add_host' => [
24+
'host' => 'http://localhost',
25+
],
26+
],
27+
[
28+
'header_set' => [
29+
'headers' => [
30+
'X-FOO' => 'bar',
31+
],
32+
],
33+
],
34+
[
35+
'header_remove' => [
36+
'headers' => [
37+
'X-FOO',
38+
],
39+
],
40+
],
41+
[
42+
'authentication' => [
43+
'my_basic' => [
44+
'type' => 'basic',
45+
'username' => 'foo',
46+
'password' => 'bar',
47+
],
48+
],
49+
]
50+
],
51+
],
52+
],
1653
'profiling' => [
1754
'enabled' => true,
1855
'formatter' => 'my_toolbar_formatter',

Tests/Resources/Fixtures/config/full.xml

+21
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,27 @@
1414
<uri-factory>Http\Message\UriFactory\GuzzleUriFactory</uri-factory>
1515
<stream-factory>Http\Message\StreamFactory\GuzzleStreamFactory</stream-factory>
1616
</classes>
17+
<client name="test" factory="httplug.factory.guzzle6" http-methods-client="true">
18+
<plugin>httplug.plugin.redirect</plugin>
19+
<plugin>
20+
<add-host host="http://localhost"/>
21+
</plugin>
22+
<plugin>
23+
<header-set>
24+
<header name="X-FOO">bar</header>
25+
</header-set>
26+
</plugin>
27+
<plugin>
28+
<header-remove>
29+
<header>X-FOO</header>
30+
</header-remove>
31+
</plugin>
32+
<plugin>
33+
<authentication>
34+
<my_basic type="basic" username="foo" password="bar"/>
35+
</authentication>
36+
</plugin>
37+
</client>
1738
<profiling enabled="true" formatter="my_toolbar_formatter" captured_body_length="0"/>
1839
<plugins>
1940
<authentication>

Tests/Resources/Fixtures/config/full.yml

+22
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,28 @@ httplug:
99
message_factory: Http\Message\MessageFactory\GuzzleMessageFactory
1010
uri_factory: Http\Message\UriFactory\GuzzleUriFactory
1111
stream_factory: Http\Message\StreamFactory\GuzzleStreamFactory
12+
clients:
13+
test:
14+
factory: httplug.factory.guzzle6
15+
http_methods_client: true
16+
plugins:
17+
- 'httplug.plugin.redirect'
18+
-
19+
add_host:
20+
host: http://localhost
21+
-
22+
header_set:
23+
headers:
24+
X-FOO: bar
25+
-
26+
header_remove:
27+
headers: [X-FOO]
28+
-
29+
authentication:
30+
my_basic:
31+
type: basic
32+
username: foo
33+
password: bar
1234
profiling:
1335
enabled: true
1436
formatter: my_toolbar_formatter
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
httplug:
2+
clients:
3+
acme:
4+
plugins:
5+
- foobar:
6+
baz: ~

0 commit comments

Comments
 (0)