These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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\Client\Promise\HttpFulfilledPromise; |
||
10 | use Http\Client\Promise\HttpRejectedPromise; |
||
11 | use Psr\Http\Client\ClientInterface; |
||
12 | use Psr\Http\Message\RequestInterface; |
||
13 | use Symfony\Component\OptionsResolver\OptionsResolver; |
||
14 | |||
15 | /** |
||
16 | * The client managing plugins and providing a decorator around HTTP Clients. |
||
17 | * |
||
18 | * @author Joel Wurtz <[email protected]> |
||
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 | * @var Plugin[] $debug_plugins an array of plugins that are injected between each normal plugin |
||
50 | * } |
||
51 | * |
||
52 | * @throws \RuntimeException if client is not an instance of HttpClient or HttpAsyncClient |
||
53 | */ |
||
54 | 11 | public function __construct($client, array $plugins = [], array $options = []) |
|
55 | { |
||
56 | 11 | if ($client instanceof HttpAsyncClient) { |
|
57 | 3 | $this->client = $client; |
|
58 | 8 | } elseif ($client instanceof HttpClient || $client instanceof ClientInterface) { |
|
0 ignored issues
–
show
|
|||
59 | 8 | $this->client = new EmulatedHttpAsyncClient($client); |
|
60 | } else { |
||
61 | throw new \RuntimeException('Client must be an instance of Http\\Client\\HttpClient or Http\\Client\\HttpAsyncClient'); |
||
62 | } |
||
63 | |||
64 | 11 | $this->plugins = $plugins; |
|
65 | 11 | $this->options = $this->configure($options); |
|
66 | 11 | } |
|
67 | |||
68 | /** |
||
69 | * {@inheritdoc} |
||
70 | */ |
||
71 | 5 | public function sendRequest(RequestInterface $request) |
|
72 | { |
||
73 | // If we don't have an http client, use the async call |
||
74 | 5 | if (!($this->client instanceof HttpClient)) { |
|
75 | 1 | return $this->sendAsyncRequest($request)->wait(); |
|
76 | } |
||
77 | |||
78 | // Else we want to use the synchronous call of the underlying client, and not the async one in the case |
||
79 | // we have both an async and sync call |
||
80 | $pluginChain = $this->createPluginChain($this->plugins, function (RequestInterface $request) { |
||
81 | try { |
||
82 | 3 | return new HttpFulfilledPromise($this->client->sendRequest($request)); |
|
83 | } catch (HttplugException $exception) { |
||
84 | return new HttpRejectedPromise($exception); |
||
85 | } |
||
86 | 4 | }); |
|
87 | |||
88 | 4 | return $pluginChain($request)->wait(); |
|
89 | } |
||
90 | |||
91 | /** |
||
92 | * {@inheritdoc} |
||
93 | */ |
||
94 | 2 | public function sendAsyncRequest(RequestInterface $request) |
|
95 | { |
||
96 | $pluginChain = $this->createPluginChain($this->plugins, function (RequestInterface $request) { |
||
97 | 2 | return $this->client->sendAsyncRequest($request); |
|
98 | 2 | }); |
|
99 | |||
100 | 2 | return $pluginChain($request); |
|
101 | } |
||
102 | |||
103 | /** |
||
104 | * Configure the plugin client. |
||
105 | * |
||
106 | * @param array $options |
||
107 | * |
||
108 | * @return array |
||
109 | */ |
||
110 | 11 | private function configure(array $options = []) |
|
111 | { |
||
112 | 11 | if (isset($options['debug_plugins'])) { |
|
113 | 1 | @trigger_error('The "debug_plugins" option is deprecated since 1.5 and will be removed in 2.0.', E_USER_DEPRECATED); |
|
114 | } |
||
115 | |||
116 | 11 | $resolver = new OptionsResolver(); |
|
117 | 11 | $resolver->setDefaults([ |
|
118 | 11 | 'max_restarts' => 10, |
|
119 | 'debug_plugins' => [], |
||
120 | ]); |
||
121 | |||
122 | $resolver |
||
123 | 11 | ->setAllowedTypes('debug_plugins', 'array') |
|
124 | ->setAllowedValues('debug_plugins', function (array $plugins) { |
||
125 | 11 | foreach ($plugins as $plugin) { |
|
126 | // Make sure each object passed with the `debug_plugins` is an instance of Plugin. |
||
127 | 1 | if (!$plugin instanceof Plugin) { |
|
128 | 1 | return false; |
|
129 | } |
||
130 | } |
||
131 | |||
132 | 11 | return true; |
|
133 | 11 | }); |
|
134 | |||
135 | 11 | return $resolver->resolve($options); |
|
136 | } |
||
137 | |||
138 | /** |
||
139 | * Create the plugin chain. |
||
140 | * |
||
141 | * @param Plugin[] $pluginList A list of plugins |
||
142 | * @param callable $clientCallable Callable making the HTTP call |
||
143 | * |
||
144 | * @return callable |
||
145 | */ |
||
146 | 6 | private function createPluginChain($pluginList, callable $clientCallable) |
|
147 | { |
||
148 | 6 | $firstCallable = $lastCallable = $clientCallable; |
|
149 | |||
150 | /* |
||
151 | * Inject debug plugins between each plugin. |
||
152 | */ |
||
153 | 6 | $pluginListWithDebug = $this->options['debug_plugins']; |
|
154 | 6 | foreach ($pluginList as $plugin) { |
|
155 | 2 | $pluginListWithDebug[] = $plugin; |
|
156 | 2 | $pluginListWithDebug = array_merge($pluginListWithDebug, $this->options['debug_plugins']); |
|
157 | } |
||
158 | |||
159 | 6 | while ($plugin = array_pop($pluginListWithDebug)) { |
|
160 | $lastCallable = function (RequestInterface $request) use ($plugin, $lastCallable, &$firstCallable) { |
||
161 | 2 | return $plugin->handleRequest($request, $lastCallable, $firstCallable); |
|
162 | 2 | }; |
|
163 | |||
164 | 2 | $firstCallable = $lastCallable; |
|
165 | } |
||
166 | |||
167 | 6 | $firstCalls = 0; |
|
168 | 6 | $firstCallable = function (RequestInterface $request) use ($lastCallable, &$firstCalls) { |
|
169 | 6 | if ($firstCalls > $this->options['max_restarts']) { |
|
170 | 1 | throw new LoopException('Too many restarts in plugin client', $request); |
|
171 | } |
||
172 | |||
173 | 6 | ++$firstCalls; |
|
174 | |||
175 | 6 | return $lastCallable($request); |
|
176 | 6 | }; |
|
177 | |||
178 | 6 | return $firstCallable; |
|
179 | } |
||
180 | } |
||
181 |
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.