/guzzle-bundle

The Mapudo GuzzleBundle allows integrating Guzzle into Symfony projects.

Primary LanguagePHPMIT LicenseMIT

Latest Version on Packagist Software License Build Status Total Downloads

Requirements | Install | Usage | Contributing | Security | License | About

GuzzleBundle

The Mapudo GuzzleBundle allows integrating Guzzle into Symfony projects.

The main features of this bundle are:

  • Easy addition of custom middleware and event listeners
  • Close to 100% test coverage
  • Written from the ground up for PHP 7
  • Built-in middleware for
    • Logging via Monolog on a separate guzzle channel
    • Request debugging via Symfony Profiler (styled with Bootstrap) and DebugBar
    • Event dispatching via Symfony Event Framework

Why yet another Guzzle Bundle? We simply weren't happy with the code quality and middleware integration of existing bundles and our issues were not addressed by their maintainers.

Requirements

  • PHP 7.0 and newer
  • Symfony ~2.8|~3.0|~4.0

Install

Via Composer

$ composer require mapudo/guzzle-bundle

Usage

Activating the bundle

Register the bundle in your AppKernel.php.

// app/AppKernel.php
...
new Mapudo\Bundle\GuzzleBundle\GuzzleBundle(), 
...

Configuration

Configure the bundle via app/config.yml. You can define multiple clients and set basic options.

guzzle:
    clients:
        test_client:
            base_uri: 'https://example.com/path'
            headers:
                Accept: 'application/json'
                Accept-Language: 'de'
            request_options:
                allow_redirects: true
                cert:
                    - '/path/to/cert.pem'
                    - 'password'
        another_client:
            base_uri: 'https://another.example.com/root/child'
            headers:
                X-Auth: 'token'

All request_options documented in the Guzzle documentation can be configured for clients, except for the following which can only be set on individual requests:

  • body
  • cookies
  • debug
  • form_params
  • json
  • multipart
  • on_headers
  • on_stats
  • progress
  • sink

Clients defined in the configuration are then registered by the bundle's CompilerPass and you can afterwards access them from the container by name. For the above cases, we would have two clients which we can access with guzzle.client.test_client and guzzle.client.another_client.

Making requests

Making requests is easy and essentially the same as using Guzzle directly. As described above, you can also set request_options here that are not available to configure via the config.

<?php
$client = $container->get('guzzle.client.test_client');
$moreOptions = ['on_stats' => function (\GuzzleHttp\TransferStats $stats) {
    echo $stats->getEffectiveUri() . "\n";
    echo $stats->getTransferTime() . "\n";
}];
$client->requestAsync(\Symfony\Component\HttpFoundation\Request::METHOD_GET, '/path/without/base_uri', $moreOptions);

Note: Clear your cache after adding a new log handler or a middleware so that the CompilerPass is run again.

Logging

The CompilerPass registers a logging middleware which defines a guzzle logging channel. By default all Monolog handlers defined in your config will log these requests. To disable logging for specific handlers add !guzzle to the channel list of the handler.

monolog:
   handlers:
       main:
           channels: [!guzzle]

It is also possible to log request data into the Symfony profiler and debug toolbar. To do to this, activate the built-in profiler handler with the following configuration:

monolog:
    handlers:
        my_debug_handler:
            type: service
            id: mapudo_bundle_guzzle.log_handler.symfony_profiler_handler
            level: debug
            channels: [guzzle]
        ## add additional handlers as needed
        # my_other_handler:
            # ...
            # channels: [guzzle]

After adding this handler you need to rebuild the assets so your Profiler is styled, e.g. with assets:install. Here you can select which client's request to be shown. Symfony Profiler

Log into specific channels

Per default the LogMiddleware logs into the "guzzle" channel. If you want that a different handler is used (dependent on the channel) you can define a service tag for this.

YAML

mapudo_frontend.mapudo_api.log_middleware:
    class: Mapudo\Bundle\GuzzleBundle\Middleware\LogMiddleware
    tags:
        - { name: guzzle.middleware, method: log, client: mapudo_api, channel: timings }

As you can see, the tag now contain a "channel" node. If configured like this, the guzzle client mapudo_api will have the LogMiddleware as a middleware injected that uses a/multiple logger which log into the timings channel.

Important

Please note, that the service id of the LogMiddleware must contain the name log_middleware since the compiler pass checks if the service definition contains this name to retrieve the channel.

Add your own Logging

This bundle uses Symfony's normalizer to normalize the request and response objects before passing them to the logger. This allows you to handle the requests and responses as an array. If you want to work with objects instead, the bundle provides a request and response denormalizer which morphs the arrays into corresponding objects.

For example, to write your own handler, take the response and request array out of the given context and denormalize them.

<?php
if ($record['context']) {
    $response = null;
    if (!empty($record['context']['response'])) {
        $response = $this->responseDenormalizer->denormalize($record['context']['response'], Response::class);
    }
    if (!empty($record['context']['request'])) {
        /** @var Request $request */
        $request = $this->requestDenormalizer->denormalize($record['context']['request'], Request::class);
        $request->setResponse($response);
    }
}

or just do what you want.

Middleware

This bundle comes with two middleware services already implemented. One to dispatch events and one to log with given handlers.

Add your own Middleware

The bundle supports registering Middlewares by using __invoke() or creating a custom method.

The CompilerPass searches for services which are tagged with guzzle.middleware. A tag method is optional to define which method should be executed if you don't use __invoke() in your middleware. You need to add the tag client with the name of the client. However, if you want to create a middleware for all clients you can omit the tag.

YAML

services:
    guzzle.middleware.my_middleware:
        class: exampleClass
        tags:
            - { name: guzzle.middleware, method: addMyMiddleware, client: test_client }

XML

<services>
    <service id="guzzle.middleware.my_middleware" class="exampleClass">
        <tag name="guzzle.middleware" method="addMyMiddleware" client="test_client"/>
    </service>
</services>

Events

Add your own Event Listeners

The CompilerPass searches for services tagged with kernel.event_listener. A tag event is also required to define on which event to listen to. You are allowed to use either guzzle_event.pre_transaction or guzzle_event.post_transaction. Unlike middleware, a client name is required here.

YAML

services:
    guzzle.event.my_event:
        class: exampleClass
        tags:
            - { name: kernel.event_listener, event: guzzle_event.pre_transaction, client: test_client }

XML

<services>
    <service id="guzzle.event.my_event" class="exampleClass">
        <tag name="kernel.event_listener" event="guzzle_event.pre_transaction" client="test_client"/>
    </service>
</services>

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email mailto:security@mapudo.com instead of using the issue tracker.

License

The MIT License (MIT). Please see License File for more information.

About

This bundle was authored at Mapudo, the online materials marketplace. We're hiring!