Completed
Push — master ( 2f5651...57762f )
by Joel
03:26
created

PluginClient::sendRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 6
ccs 0
cts 5
cp 0
rs 9.4286
cc 1
eloc 3
nc 1
nop 1
crap 2
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
    public function __construct($client, array $plugins = [], array $options = [])
47
    {
48
        if ($client instanceof HttpAsyncClient) {
49
            $this->client = $client;
50
        } elseif ($client instanceof HttpClient) {
51
            $this->client = new EmulateAsyncClient($client);
52
        } else {
53
            throw new \RuntimeException('Client must be an instance of Http\\Client\\HttpClient or Http\\Client\\HttpAsyncClient');
54
        }
55
56
        $this->plugins = $plugins;
57
        $this->options = $this->configure($options);
58
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63
    public function sendRequest(RequestInterface $request)
64
    {
65
        $promise = $this->sendAsyncRequest($request);
66
67
        return $promise->wait();
68
    }
69
70
    /**
71
     * {@inheritdoc}
72
     */
73
    public function sendAsyncRequest(RequestInterface $request)
74
    {
75
        $pluginChain = $this->createPluginChain($this->plugins);
76
77
        return $pluginChain($request);
78
    }
79
80
    /**
81
     * Configure the plugin client.
82
     *
83
     * @param array $options
84
     *
85
     * @return array
86
     */
87
    protected function configure(array $options = [])
88
    {
89
        $resolver = new OptionsResolver();
90
        $resolver->setDefaults([
91
            'max_restarts' => 10,
92
        ]);
93
94
        return $resolver->resolve($options);
95
    }
96
97
    /**
98
     * @param Plugin[] $pluginList
99
     *
100
     * @return callable
101
     */
102
    private function createPluginChain($pluginList)
103
    {
104
        $client = $this->client;
105
        $options = $this->options;
106
107
        $lastCallable = function (RequestInterface $request) use ($client) {
108
            return $client->sendAsyncRequest($request);
109
        };
110
111
        $firstCallable = $lastCallable;
112
        while ($plugin = array_pop($pluginList)) {
113
            $lastCallable = function (RequestInterface $request) use ($plugin, $lastCallable, &$firstCallable) {
114
                return $plugin->handleRequest($request, $lastCallable, $firstCallable);
115
            };
116
117
            $firstCallable = $lastCallable;
118
        }
119
120
        $firstCalls = 0;
121
        $firstCallable = function (RequestInterface $request) use ($options, $lastCallable, &$firstCalls) {
122
            if ($firstCalls > $options['max_restarts']) {
123
                throw new LoopException('Too many restarts in plugin client', $request);
124
            }
125
126
            ++$firstCalls;
127
128
            return $lastCallable($request);
129
        };
130
131
        return $firstCallable;
132
    }
133
}
134