Skip to content

Make sure you can configure clients and register them as services #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 5, 2016
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
23 changes: 9 additions & 14 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
language: php

php:
- 5.4
- 5.5
- 5.6
- 7.0
Expand All @@ -10,25 +9,22 @@ php:
env:
global:
- TEST_COMMAND="composer test"
matrix:
- SYMFONY_VERSION=3.0.*
- SYMFONY_VERSION=2.8.*
Copy link
Collaborator

Choose a reason for hiding this comment

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

i agree that we should change this, but maybe to 3.0 right away. we need to adjust the symfony version for the matrix lines below as well so we still test 2.7, 2.8 and 3.0

Copy link
Member Author

Choose a reason for hiding this comment

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

Should I also use PHP7 as standard?

Copy link
Collaborator

Choose a reason for hiding this comment

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

yeah lets do that!

- SYMFONY_VERSION=2.7.*

matrix:
allow_failures:
- php: 7.0
fast_finish: true
include:
- php: 5.4
env:
- COMPOSER_FLAGS="--prefer-stable --prefer-lowest"
- COVERAGE=true
- TEST_COMMAND="composer test-ci"
- php: 5.6
env: SYMFONY_VERSION=2.8.*
- php: 5.6
env: SYMFONY_VERSION=3.0.*
allow_failures:
- php: hhvm
- env: SYMFONY_VERSION=3.0.*
- php: 5.5
env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" && COVERAGE=true && TEST_COMMAND="composer test-ci" && SYMFONY_VERSION=2.7.*

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

install:
- composer require symfony/symfony:${SYMFONY_VERSION} --no-update
Expand All @@ -40,4 +36,3 @@ script:
after_success:
- if [[ "$COVERAGE" = true ]]; then wget https://scrutinizer-ci.com/ocular.phar; fi
- if [[ "$COVERAGE" = true ]]; then php ocular.phar code-coverage:upload --format=php-clover build/coverage.xml; fi

18 changes: 18 additions & 0 deletions ClientFactory/ClientFactoryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Http\HttplugBundle\ClientFactory;

/**
* @author Tobias Nyholm <[email protected]>
*/
interface ClientFactoryInterface
{
/**
* Input an array of configuration to be able to create a HttpClient
*
* @param array $config
*
* @return \Http\Client\HttpClient
*/
public function createClient(array $config = array());
}
12 changes: 12 additions & 0 deletions ClientFactory/DummyClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Http\HttplugBundle\ClientFactory;

/**
* This client is used as a placeholder for the dependency injection. It will never be used.
*
* @author Tobias Nyholm <[email protected]>
*/
class DummyClient
{
}
26 changes: 26 additions & 0 deletions ClientFactory/Guzzle5Factory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Http\HttplugBundle\ClientFactory;

use GuzzleHttp\Client;
use Http\Adapter\Guzzle5\Client as Adapter;

/**
* @author Tobias Nyholm <[email protected]>
*/
class Guzzle5Factory
{
/**
* {@inheritdoc}
*/
public function createClient(array $config = [])
{
if (!class_exists('Http\Adapter\Guzzle5\Client')) {
throw new \LogicException('To use the Guzzle5 adapter you need to install the "php-http/guzzle5-adapter" package.');
}

$client = new Client($config);

return new Adapter($client);
}
}
23 changes: 23 additions & 0 deletions ClientFactory/Guzzle6Factory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Http\HttplugBundle\ClientFactory;

use GuzzleHttp\Client;
use Http\Adapter\Guzzle6\Client as Adapter;

/**
* @author Tobias Nyholm <[email protected]>
*/
class Guzzle6Factory implements ClientFactoryInterface
{
public function createClient(array $config = [])
{
if (!class_exists('Http\Adapter\Guzzle6\Client')) {
throw new \LogicException('To use the Guzzle6 adapter you need to install the "php-http/guzzle6-adapter" package.');
}

$client = new Client($config);

return new Adapter($client);
}
}
26 changes: 25 additions & 1 deletion DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

namespace Http\HttplugBundle\DependencyInjection;

use Symfony\Component\Config\Definition\ArrayNode;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;

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

$this->configureClients($rootNode);

$rootNode
->validate()
->ifTrue(function ($v) {
return !empty($v['classes']['client'])
|| !empty($v['classes']['message_factory'])
|| !empty($v['classes']['uri_factory'])
|| !empty($v['classes']['stream_factory'])
;
})
->then(function ($v) {
Expand All @@ -54,6 +59,7 @@ public function getConfigTreeBuilder()
->scalarNode('client')->defaultValue('httplug.client.default')->end()
->scalarNode('message_factory')->defaultValue('httplug.message_factory.default')->end()
->scalarNode('uri_factory')->defaultValue('httplug.uri_factory.default')->end()
->scalarNode('stream_factory')->defaultValue('httplug.stream_factory.default')->end()
->end()
->end()
->arrayNode('classes')
Expand All @@ -63,11 +69,29 @@ public function getConfigTreeBuilder()
->scalarNode('client')->defaultNull()->end()
->scalarNode('message_factory')->defaultNull()->end()
->scalarNode('uri_factory')->defaultNull()->end()
->scalarNode('stream_factory')->defaultNull()->end()
->end()
->end()
->end()
;

return $treeBuilder;
}

protected function configureClients(ArrayNodeDefinition $root)
{
$root->children()
->arrayNode('clients')
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('factory')
->isRequired()
->cannotBeEmpty()
->info('The service id of a factory to use when creating the adapter.')
->end()
->variableNode('config')->end()
->end()
->end();
}
}
19 changes: 19 additions & 0 deletions DependencyInjection/HttplugExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

namespace Http\HttplugBundle\DependencyInjection;

use Http\HttplugBundle\ClientFactory\DummyClient;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

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

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

$loader->load('services.xml');
$loader->load('discovery.xml');
foreach ($config['classes'] as $service => $class) {
if (!empty($class)) {
Expand All @@ -33,5 +36,21 @@ public function load(array $configs, ContainerBuilder $container)
foreach ($config['main_alias'] as $type => $id) {
$container->setAlias(sprintf('httplug.%s', $type), $id);
}

// Configure client services
$first = isset($config['clients']['default']) ? 'default' : null;
foreach ($config['clients'] as $name => $arguments) {
if ($first === null) {
$first = $name;
}

$def = $container->register('httplug.client.'.$name, DummyClient::class);
$def->setFactory([new Reference($arguments['factory']), 'createClient'])
->addArgument($arguments['config']);
}

if ($first !== null) {
$container->setAlias('httplug.client.default', 'httplug.client.'.$first);
}
}
}
7 changes: 7 additions & 0 deletions Exception/InvalidConfiguration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Http\HttplugBundle\Exception;

class InvalidConfiguration extends \Exception
{
}
1 change: 1 addition & 0 deletions HttplugBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Http\HttplugBundle;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

/**
Expand Down
52 changes: 44 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,62 @@ For information how to write applications with the services provided by this bun

### Use in Applications

#### Custom services

This bundle provides 3 services:

* `httplug.client` a service that provides the `Http\Client\HttpClient`
* `httplug.message_factory` a service that provides the `Http\Message\MessageFactory`
* `httplug.uri_factory` a service that provides the `Http\Message\UriFactory`
* `httplug.stream_factory` a service that provides the `Http\Message\StreamFactory`

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.

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.

```yaml
httplug:
main_alias:
client: httplug.client.default
message_factory: httplug.message_factory.default
uri_factory: httplug.uri_factory.default
classes:
client: ~ # uses discovery if not specified
message_factory: ~
uri_factory: ~
main_alias:
client: httplug.client.default
message_factory: httplug.message_factory.default
uri_factory: httplug.uri_factory.default
stream_factory: httplug.stream_factory.default
classes:
# uses discovery if not specified
client: ~
message_factory: ~
uri_factory: ~
stream_factory: ~
```

#### Configure your client

You can configure your clients with some good default options. The clients are later registered as services.

```yaml
httplug:
clients:
my_guzzle5:
factory: 'httplug.factory.guzzle5'
config:
# These options are given to Guzzle without validation.
base_url: 'http://google.se/'
defaults:
verify_ssl: false
timeout: 4
headers:
Content-Type: 'application/json'
acme:
factory: 'httplug.factory.guzzle6'
config:
base_url: 'http://google.se/'

```

```php

$httpClient = $this->container->get('httplug.client.my_guzzle5');
$httpClient = $this->container->get('httplug.client.acme');
```

### Use for Reusable Bundles
Expand Down
4 changes: 4 additions & 0 deletions Resources/config/discovery.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
class="Http\Message\UriFactory">
<factory class="Http\Discovery\UriFactoryDiscovery" method="find"/>
</service>
<service id="httplug.stream_factory.default"
class="Http\Message\StreamFactory">
<factory class="Http\Discovery\StreamFactoryDiscovery" method="find"/>
</service>

</services>
</container>
12 changes: 12 additions & 0 deletions Resources/config/services.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>

<!-- ClientFactories -->
<service id="httplug.factory.guzzle5" class="Http\HttplugBundle\ClientFactory\Guzzle5Factory" public="false" />
<service id="httplug.factory.guzzle6" class="Http\HttplugBundle\ClientFactory\Guzzle6Factory" public="false" />
</services>
</container>
8 changes: 5 additions & 3 deletions Tests/Resources/Fixtures/config/full.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
'client' => 'my_client',
'message_factory' => 'my_message_factory',
'uri_factory' => 'my_uri_factory',
'stream_factory' => 'my_stream_factory',
),
'classes' => array(
'client' => 'Http\Adapter\Guzzle6HttpAdapter',
'message_factory' => 'Http\Discovery\MessageFactory\GuzzleFactory',
'uri_factory' => 'Http\Discovery\UriFactory\GuzzleFactory',
'client' => 'Http\Adapter\Guzzle6\Client',
'message_factory' => 'Http\Message\MessageFactory\GuzzleMessageFactory',
'uri_factory' => 'Http\Message\UriFactory\GuzzleUriFactory',
'stream_factory' => 'Http\Message\StreamFactory\GuzzleStreamFactory',
),
));
9 changes: 5 additions & 4 deletions Tests/Resources/Fixtures/config/full.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
<client>my_client</client>
<message-factory>my_message_factory</message-factory>
<uri-factory>my_uri_factory</uri-factory>
<stream-factory>my_stream_factory</stream-factory>
</main-alias>
<classes>
<client>Http\Adapter\Guzzle6HttpAdapter</client>
<message-factory>Http\Discovery\MessageFactory\GuzzleFactory</message-factory>
<uri-factory>Http\Discovery\UriFactory\GuzzleFactory</uri-factory>

<client>Http\Adapter\Guzzle6\Client</client>
<message-factory>Http\Message\MessageFactory\GuzzleMessageFactory</message-factory>
<uri-factory>Http\Message\UriFactory\GuzzleUriFactory</uri-factory>
<stream-factory>Http\Message\StreamFactory\GuzzleStreamFactory</stream-factory>
</classes>
</config>

Expand Down
Loading