Completed
Pull Request — master (#31)
by Joel
02:06
created

PluginClient::sendAsyncRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4286
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Http\Client\Plugin;
4
5
use Http\Client\HttpAsyncClient;
6
use Http\Client\HttpClient;
7
use Http\Client\Plugin\Exception\LoopException;
8
use Psr\Http\Message\RequestInterface;
9
use Symfony\Component\OptionsResolver\OptionsResolver;
10
11
/**
12
 * The client managing plugins and providing a decorator around HTTP Clients.
13
 *
14
 * @author Joel Wurtz <[email protected]>
15
 */
16
class PluginClient implements HttpClient, HttpAsyncClient
17
{
18
    /**
19
     * An HTTP async client.
20
     *
21
     * @var HttpAsyncClient
22
     */
23
    protected $client;
24
25
    /**
26
     * The plugin chain.
27
     *
28
     * @var Plugin[]
29
     */
30
    protected $plugins;
31
32
    /**
33
     * A list of options.
34
     *
35
     * @var array
36
     */
37
    protected $options;
38
39
    /**
40
     * @param HttpClient|HttpAsyncClient $client
41
     * @param Plugin[]                   $plugins
42
     * @param array                      $options
43
     *
44
     * @throws \RuntimeException if client is not an instance of HttpClient or HttpAsyncClient
45
     */
46 6
    public function __construct($client, array $plugins = [], array $options = [])
47
    {
48 6
        if ($client instanceof HttpAsyncClient) {
49 1
            $this->client = $client;
50 6
        } elseif ($client instanceof HttpClient) {
51 5
            $this->client = new EmulateAsyncClient($client);
52 5
        } else {
53
            throw new \RuntimeException('Client must be an instance of Http\\Client\\HttpClient or Http\\Client\\HttpAsyncClient');
54
        }
55
56 6
        $this->plugins = $plugins;
57 6
        $this->options = $this->configure($options);
58 6
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63 2
    public function sendRequest(RequestInterface $request)
64
    {
65 2
        $promise = $this->sendAsyncRequest($request);
66
67 1
        return $promise->wait();
68
    }
69
70
    /**
71
     * {@inheritdoc}
72
     */
73 3
    public function sendAsyncRequest(RequestInterface $request)
74
    {
75 3
        $pluginChain = $this->createPluginChain($this->plugins);
76
77 3
        return $pluginChain($request);
78
    }
79
80
    /**
81
     * Configure the plugin client.
82
     *
83
     * @param array $options
84
     *
85
     * @return array
86
     */
87 6
    protected function configure(array $options = [])
88
    {
89 6
        $resolver = new OptionsResolver();
90 6
        $resolver->setDefaults([
91 6
            'max_restarts' => 10,
92 6
        ]);
93
94 6
        return $resolver->resolve($options);
95
    }
96
97
    /**
98
     * @param Plugin[] $pluginList
99
     *
100
     * @return callable
101
     */
102 3
    private function createPluginChain($pluginList)
103
    {
104 3
        $client = $this->client;
105 3
        $options = $this->options;
106
107
        $lastCallable = function (RequestInterface $request) use ($client) {
108 2
            return $client->sendAsyncRequest($request);
109 3
        };
110
111 3
        $firstCallable = $lastCallable;
112 3
        while ($plugin = array_pop($pluginList)) {
113
            $lastCallable = function (RequestInterface $request) use ($plugin, $lastCallable, &$firstCallable) {
114 1
                return $plugin->handleRequest($request, $lastCallable, $firstCallable);
115 1
            };
116
117 1
            $firstCallable = $lastCallable;
118 1
        }
119
120 3
        $firstCalls = 0;
121 3
        $firstCallable = function (RequestInterface $request) use ($options, $lastCallable, &$firstCalls) {
122 3
            if ($firstCalls > $options['max_restarts']) {
123 1
                throw new LoopException('Too many restarts in plugin client', $request);
124
            }
125
126 3
            ++$firstCalls;
127
128 3
            return $lastCallable($request);
129 3
        };
130
131 3
        return $firstCallable;
132
    }
133
}
134