Completed
Pull Request — master (#14)
by Márk
03:58
created

PluginClient::configure()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.2098

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 12
ccs 5
cts 7
cp 0.7143
rs 9.4285
cc 3
eloc 6
nc 2
nop 1
crap 3.2098
1
<?php
2
3
namespace Http\Client\Common;
4
5
use Http\Client\Common\Exception\LoopException;
6
use Http\Client\Exception as HttplugException;
7
use Http\Client\HttpAsyncClient;
8
use Http\Client\HttpClient;
9
use Http\Promise\FulfilledPromise;
10
use Http\Promise\RejectedPromise;
11
use Psr\Http\Message\RequestInterface;
12
13
/**
14
 * The client managing plugins and providing a decorator around HTTP Clients.
15
 *
16
 * @author Joel Wurtz <[email protected]>
17
 *
18
 * TODO: make class final in version 2.0, once plugins does not extend it anymore.
19
 */
20
/*final*/ class PluginClient implements HttpClient, HttpAsyncClient
21
{
22
    /**
23
     * An HTTP async client.
24
     *
25
     * @var HttpAsyncClient
26
     */
27
    private $client;
28
29
    /**
30
     * The plugin chain.
31
     *
32
     * @var Plugin[]
33
     */
34
    private $plugins;
35
36
    /**
37
     * A list of options.
38
     *
39
     * @var array
40
     */
41
    private $options;
42
43
    /**
44
     * @param HttpClient|HttpAsyncClient $client
45
     * @param Plugin[]                   $plugins
46
     * @param array                      $options {
47
     *
48
     *     @var int $max_restarts
49
     * }
50
     *
51
     * @throws \RuntimeException if client is not an instance of HttpClient or HttpAsyncClient
52
     */
53 8
    public function __construct($client, array $plugins = [], array $options = [])
54
    {
55 8
        if ($client instanceof HttpAsyncClient) {
56 3
            $this->client = $client;
57 8
        } elseif ($client instanceof HttpClient) {
58 5
            $this->client = new EmulatedHttpAsyncClient($client);
59 5
        } else {
60
            throw new \RuntimeException('Client must be an instance of Http\\Client\\HttpClient or Http\\Client\\HttpAsyncClient');
61
        }
62
63 8
        $this->plugins = $plugins;
64 8
        $this->options = $this->configure($options);
65 8
    }
66
67
    /**
68
     * {@inheritdoc}
69
     */
70 4
    public function sendRequest(RequestInterface $request)
71
    {
72
        // If we don't have an http client, use the async call
73 4
        if (!($this->client instanceof HttpClient)) {
74 1
            return $this->sendAsyncRequest($request)->wait();
75
        }
76
77
        // Else we want to use the synchronous call of the underlying client, and not the async one in the case
78
        // we have both an async and sync call
79
        $pluginChain = $this->createPluginChain($this->plugins, function (RequestInterface $request) {
80
            try {
81 2
                return new FulfilledPromise($this->client->sendRequest($request));
82
            } catch (HttplugException $exception) {
83
                return new RejectedPromise($exception);
0 ignored issues
show
Documentation introduced by
$exception is of type object<Http\Client\Exception>, but the function expects a object<Exception>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
84
            }
85 3
        });
86
87 3
        return $pluginChain($request)->wait();
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93 2
    public function sendAsyncRequest(RequestInterface $request)
94
    {
95
        $pluginChain = $this->createPluginChain($this->plugins, function (RequestInterface $request) {
96 2
            return $this->client->sendAsyncRequest($request);
97 2
        });
98
99 2
        return $pluginChain($request);
100
    }
101
102
    /**
103
     * Configure the plugin client.
104
     *
105
     * @param array $options
106
     *
107
     * @return array
108
     */
109 8
    private function configure(array $options = [])
110
    {
111
        $defaultOptions = [
112 8
            'max_restarts' => 10,
113 8
        ];
114
115 8
        if (isset($options['max_restarts']) && false === is_int($options['max_restarts'])) {
116
            unset($options['max_restarts']);
117
        }
118
119 8
        return array_merge($defaultOptions, $options);
120
    }
121
122
    /**
123
     * Create the plugin chain.
124
     *
125
     * @param Plugin[] $pluginList     A list of plugins
126
     * @param callable $clientCallable Callable making the HTTP call
127
     *
128
     * @return callable
129
     */
130 5
    private function createPluginChain($pluginList, callable $clientCallable)
131
    {
132 5
        $firstCallable = $lastCallable = $clientCallable;
133
134 5
        while ($plugin = array_pop($pluginList)) {
135
            $lastCallable = function (RequestInterface $request) use ($plugin, $lastCallable, &$firstCallable) {
136 1
                return $plugin->handleRequest($request, $lastCallable, $firstCallable);
137 1
            };
138
139 1
            $firstCallable = $lastCallable;
140 1
        }
141
142 5
        $firstCalls = 0;
143 5
        $firstCallable = function (RequestInterface $request) use ($lastCallable, &$firstCalls) {
144 5
            if ($firstCalls > $this->options['max_restarts']) {
145 1
                throw $this->createLoopException($request);
146
            }
147
148 5
            ++$firstCalls;
149
150 5
            return $lastCallable($request);
151 5
        };
152
153 5
        return $firstCallable;
154
    }
155
156
    /**
157
     * Creates a new loop exception.
158
     *
159
     * @param RequestInterface $request
160
     *
161
     * @return LoopException
162
     *
163
     * TODO: Remove once plugin client is removed from plugins.
164
     */
165 1
    protected function createLoopException(RequestInterface $request)
166
    {
167 1
        return new LoopException('Too many restarts in plugin client', $request);
168
    }
169
}
170