Skip to content

Commit ce31ab9

Browse files
committed
Merge pull request #11 from Nyholm/services
Make sure you can configure clients and register them as services
2 parents 9a59cf5 + a633b0f commit ce31ab9

18 files changed

+246
-45
lines changed

.travis.yml

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
language: php
22

33
php:
4-
- 5.4
54
- 5.5
65
- 5.6
76
- 7.0
@@ -10,25 +9,22 @@ php:
109
env:
1110
global:
1211
- TEST_COMMAND="composer test"
12+
matrix:
13+
- SYMFONY_VERSION=3.0.*
14+
- SYMFONY_VERSION=2.8.*
1315
- SYMFONY_VERSION=2.7.*
1416

1517
matrix:
16-
allow_failures:
17-
- php: 7.0
1818
fast_finish: true
19-
include:
20-
- php: 5.4
21-
env:
22-
- COMPOSER_FLAGS="--prefer-stable --prefer-lowest"
23-
- COVERAGE=true
24-
- TEST_COMMAND="composer test-ci"
25-
- php: 5.6
26-
env: SYMFONY_VERSION=2.8.*
27-
- php: 5.6
28-
env: SYMFONY_VERSION=3.0.*
19+
allow_failures:
20+
- php: hhvm
21+
- env: SYMFONY_VERSION=3.0.*
22+
- php: 5.5
23+
env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" && COVERAGE=true && TEST_COMMAND="composer test-ci" && SYMFONY_VERSION=2.7.*
2924

3025
before_install:
3126
- travis_retry composer self-update
27+
- wget https://github.com/puli/cli/releases/download/1.0.0-beta9/puli.phar && chmod +x puli.phar
3228

3329
install:
3430
- composer require symfony/symfony:${SYMFONY_VERSION} --no-update
@@ -40,4 +36,3 @@ script:
4036
after_success:
4137
- if [[ "$COVERAGE" = true ]]; then wget https://scrutinizer-ci.com/ocular.phar; fi
4238
- if [[ "$COVERAGE" = true ]]; then php ocular.phar code-coverage:upload --format=php-clover build/coverage.xml; fi
43-
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Http\HttplugBundle\ClientFactory;
4+
5+
/**
6+
* @author Tobias Nyholm <[email protected]>
7+
*/
8+
interface ClientFactoryInterface
9+
{
10+
/**
11+
* Input an array of configuration to be able to create a HttpClient
12+
*
13+
* @param array $config
14+
*
15+
* @return \Http\Client\HttpClient
16+
*/
17+
public function createClient(array $config = array());
18+
}

ClientFactory/DummyClient.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace Http\HttplugBundle\ClientFactory;
4+
5+
/**
6+
* This client is used as a placeholder for the dependency injection. It will never be used.
7+
*
8+
* @author Tobias Nyholm <[email protected]>
9+
*/
10+
class DummyClient
11+
{
12+
}

ClientFactory/Guzzle5Factory.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Http\HttplugBundle\ClientFactory;
4+
5+
use GuzzleHttp\Client;
6+
use Http\Adapter\Guzzle5\Client as Adapter;
7+
8+
/**
9+
* @author Tobias Nyholm <[email protected]>
10+
*/
11+
class Guzzle5Factory
12+
{
13+
/**
14+
* {@inheritdoc}
15+
*/
16+
public function createClient(array $config = [])
17+
{
18+
if (!class_exists('Http\Adapter\Guzzle5\Client')) {
19+
throw new \LogicException('To use the Guzzle5 adapter you need to install the "php-http/guzzle5-adapter" package.');
20+
}
21+
22+
$client = new Client($config);
23+
24+
return new Adapter($client);
25+
}
26+
}

ClientFactory/Guzzle6Factory.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Http\HttplugBundle\ClientFactory;
4+
5+
use GuzzleHttp\Client;
6+
use Http\Adapter\Guzzle6\Client as Adapter;
7+
8+
/**
9+
* @author Tobias Nyholm <[email protected]>
10+
*/
11+
class Guzzle6Factory implements ClientFactoryInterface
12+
{
13+
public function createClient(array $config = [])
14+
{
15+
if (!class_exists('Http\Adapter\Guzzle6\Client')) {
16+
throw new \LogicException('To use the Guzzle6 adapter you need to install the "php-http/guzzle6-adapter" package.');
17+
}
18+
19+
$client = new Client($config);
20+
21+
return new Adapter($client);
22+
}
23+
}

DependencyInjection/Configuration.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
namespace Http\HttplugBundle\DependencyInjection;
44

5+
use Symfony\Component\Config\Definition\ArrayNode;
6+
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
57
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
68
use Symfony\Component\Config\Definition\ConfigurationInterface;
79
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
810

911
/**
10-
* This class contains the configuration information for the bundle
12+
* This class contains the configuration information for the bundle.
1113
*
1214
* This information is solely responsible for how the different configuration
1315
* sections are normalized, and merged.
@@ -24,12 +26,15 @@ public function getConfigTreeBuilder()
2426
$treeBuilder = new TreeBuilder();
2527
$rootNode = $treeBuilder->root('httplug');
2628

29+
$this->configureClients($rootNode);
30+
2731
$rootNode
2832
->validate()
2933
->ifTrue(function ($v) {
3034
return !empty($v['classes']['client'])
3135
|| !empty($v['classes']['message_factory'])
3236
|| !empty($v['classes']['uri_factory'])
37+
|| !empty($v['classes']['stream_factory'])
3338
;
3439
})
3540
->then(function ($v) {
@@ -54,6 +59,7 @@ public function getConfigTreeBuilder()
5459
->scalarNode('client')->defaultValue('httplug.client.default')->end()
5560
->scalarNode('message_factory')->defaultValue('httplug.message_factory.default')->end()
5661
->scalarNode('uri_factory')->defaultValue('httplug.uri_factory.default')->end()
62+
->scalarNode('stream_factory')->defaultValue('httplug.stream_factory.default')->end()
5763
->end()
5864
->end()
5965
->arrayNode('classes')
@@ -63,11 +69,29 @@ public function getConfigTreeBuilder()
6369
->scalarNode('client')->defaultNull()->end()
6470
->scalarNode('message_factory')->defaultNull()->end()
6571
->scalarNode('uri_factory')->defaultNull()->end()
72+
->scalarNode('stream_factory')->defaultNull()->end()
6673
->end()
6774
->end()
6875
->end()
6976
;
7077

7178
return $treeBuilder;
7279
}
80+
81+
protected function configureClients(ArrayNodeDefinition $root)
82+
{
83+
$root->children()
84+
->arrayNode('clients')
85+
->useAttributeAsKey('name')
86+
->prototype('array')
87+
->children()
88+
->scalarNode('factory')
89+
->isRequired()
90+
->cannotBeEmpty()
91+
->info('The service id of a factory to use when creating the adapter.')
92+
->end()
93+
->variableNode('config')->end()
94+
->end()
95+
->end();
96+
}
7397
}

DependencyInjection/HttplugExtension.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
namespace Http\HttplugBundle\DependencyInjection;
44

5+
use Http\HttplugBundle\ClientFactory\DummyClient;
56
use Symfony\Component\DependencyInjection\ContainerBuilder;
67
use Symfony\Component\Config\FileLocator;
78
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
9+
use Symfony\Component\DependencyInjection\Reference;
810
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
911

1012
/**
@@ -22,6 +24,7 @@ public function load(array $configs, ContainerBuilder $container)
2224

2325
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
2426

27+
$loader->load('services.xml');
2528
$loader->load('discovery.xml');
2629
foreach ($config['classes'] as $service => $class) {
2730
if (!empty($class)) {
@@ -33,5 +36,21 @@ public function load(array $configs, ContainerBuilder $container)
3336
foreach ($config['main_alias'] as $type => $id) {
3437
$container->setAlias(sprintf('httplug.%s', $type), $id);
3538
}
39+
40+
// Configure client services
41+
$first = isset($config['clients']['default']) ? 'default' : null;
42+
foreach ($config['clients'] as $name => $arguments) {
43+
if ($first === null) {
44+
$first = $name;
45+
}
46+
47+
$def = $container->register('httplug.client.'.$name, DummyClient::class);
48+
$def->setFactory([new Reference($arguments['factory']), 'createClient'])
49+
->addArgument($arguments['config']);
50+
}
51+
52+
if ($first !== null) {
53+
$container->setAlias('httplug.client.default', 'httplug.client.'.$first);
54+
}
3655
}
3756
}

Exception/InvalidConfiguration.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Http\HttplugBundle\Exception;
4+
5+
class InvalidConfiguration extends \Exception
6+
{
7+
}

HttplugBundle.php

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

33
namespace Http\HttplugBundle;
44

5+
use Symfony\Component\DependencyInjection\ContainerBuilder;
56
use Symfony\Component\HttpKernel\Bundle\Bundle;
67

78
/**

README.md

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,62 @@ For information how to write applications with the services provided by this bun
4141

4242
### Use in Applications
4343

44+
#### Custom services
45+
4446
This bundle provides 3 services:
4547

4648
* `httplug.client` a service that provides the `Http\Client\HttpClient`
4749
* `httplug.message_factory` a service that provides the `Http\Message\MessageFactory`
4850
* `httplug.uri_factory` a service that provides the `Http\Message\UriFactory`
51+
* `httplug.stream_factory` a service that provides the `Http\Message\StreamFactory`
4952

5053
These services are always an alias to another service. You can specify your own service or leave the default, which is the same name with `.default` appended. The default services in turn use the service discovery mechanism to provide the best available implementation. You can specify a class for each of the default services to use instead of discovery, as long as those classes can be instantiated without arguments.
5154

5255
If you need a more custom setup, define the services in your application configuration and specify your service in the `main_alias` section. For example, to add authentication headers, you could define a service that decorates the service `httplug.client.default` with a plugin that injects the authentication headers into the request and configure `httplug.main_alias.client` to the name of your service.
5356

5457
```yaml
5558
httplug:
56-
main_alias:
57-
client: httplug.client.default
58-
message_factory: httplug.message_factory.default
59-
uri_factory: httplug.uri_factory.default
60-
classes:
61-
client: ~ # uses discovery if not specified
62-
message_factory: ~
63-
uri_factory: ~
59+
main_alias:
60+
client: httplug.client.default
61+
message_factory: httplug.message_factory.default
62+
uri_factory: httplug.uri_factory.default
63+
stream_factory: httplug.stream_factory.default
64+
classes:
65+
# uses discovery if not specified
66+
client: ~
67+
message_factory: ~
68+
uri_factory: ~
69+
stream_factory: ~
70+
```
71+
72+
#### Configure your client
73+
74+
You can configure your clients with some good default options. The clients are later registered as services.
75+
76+
```yaml
77+
httplug:
78+
clients:
79+
my_guzzle5:
80+
factory: 'httplug.factory.guzzle5'
81+
config:
82+
# These options are given to Guzzle without validation.
83+
base_url: 'http://google.se/'
84+
defaults:
85+
verify_ssl: false
86+
timeout: 4
87+
headers:
88+
Content-Type: 'application/json'
89+
acme:
90+
factory: 'httplug.factory.guzzle6'
91+
config:
92+
base_url: 'http://google.se/'
93+
94+
```
95+
96+
```php
97+
98+
$httpClient = $this->container->get('httplug.client.my_guzzle5');
99+
$httpClient = $this->container->get('httplug.client.acme');
64100
```
65101

66102
### Use for Reusable Bundles

Resources/config/discovery.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
class="Http\Message\UriFactory">
1717
<factory class="Http\Discovery\UriFactoryDiscovery" method="find"/>
1818
</service>
19+
<service id="httplug.stream_factory.default"
20+
class="Http\Message\StreamFactory">
21+
<factory class="Http\Discovery\StreamFactoryDiscovery" method="find"/>
22+
</service>
1923

2024
</services>
2125
</container>

Resources/config/services.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<container xmlns="http://symfony.com/schema/dic/services"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
5+
6+
<services>
7+
8+
<!-- ClientFactories -->
9+
<service id="httplug.factory.guzzle5" class="Http\HttplugBundle\ClientFactory\Guzzle5Factory" public="false" />
10+
<service id="httplug.factory.guzzle6" class="Http\HttplugBundle\ClientFactory\Guzzle6Factory" public="false" />
11+
</services>
12+
</container>

Tests/Resources/Fixtures/config/full.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
'client' => 'my_client',
66
'message_factory' => 'my_message_factory',
77
'uri_factory' => 'my_uri_factory',
8+
'stream_factory' => 'my_stream_factory',
89
),
910
'classes' => array(
10-
'client' => 'Http\Adapter\Guzzle6HttpAdapter',
11-
'message_factory' => 'Http\Discovery\MessageFactory\GuzzleFactory',
12-
'uri_factory' => 'Http\Discovery\UriFactory\GuzzleFactory',
11+
'client' => 'Http\Adapter\Guzzle6\Client',
12+
'message_factory' => 'Http\Message\MessageFactory\GuzzleMessageFactory',
13+
'uri_factory' => 'Http\Message\UriFactory\GuzzleUriFactory',
14+
'stream_factory' => 'Http\Message\StreamFactory\GuzzleStreamFactory',
1315
),
1416
));

Tests/Resources/Fixtures/config/full.xml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
<client>my_client</client>
77
<message-factory>my_message_factory</message-factory>
88
<uri-factory>my_uri_factory</uri-factory>
9+
<stream-factory>my_stream_factory</stream-factory>
910
</main-alias>
1011
<classes>
11-
<client>Http\Adapter\Guzzle6HttpAdapter</client>
12-
<message-factory>Http\Discovery\MessageFactory\GuzzleFactory</message-factory>
13-
<uri-factory>Http\Discovery\UriFactory\GuzzleFactory</uri-factory>
14-
12+
<client>Http\Adapter\Guzzle6\Client</client>
13+
<message-factory>Http\Message\MessageFactory\GuzzleMessageFactory</message-factory>
14+
<uri-factory>Http\Message\UriFactory\GuzzleUriFactory</uri-factory>
15+
<stream-factory>Http\Message\StreamFactory\GuzzleStreamFactory</stream-factory>
1516
</classes>
1617
</config>
1718

0 commit comments

Comments
 (0)