Complex classes like Client often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Client, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
17 | class Client implements ClientInterface, \Psr\Http\Client\ClientInterface |
||
18 | { |
||
19 | use ClientTrait; |
||
20 | |||
21 | /** |
||
22 | * @var array Default request options |
||
23 | */ |
||
24 | private $config; |
||
25 | |||
26 | /** |
||
27 | * Clients accept an array of constructor parameters. |
||
28 | * |
||
29 | * Here's an example of creating a client using a base_uri and an array of |
||
30 | * default request options to apply to each request: |
||
31 | * |
||
32 | * $client = new Client([ |
||
33 | * 'base_uri' => 'http://www.foo.com/1.0/', |
||
34 | * 'timeout' => 0, |
||
35 | * 'allow_redirects' => false, |
||
36 | * 'proxy' => '192.168.16.1:10' |
||
37 | * ]); |
||
38 | * |
||
39 | * Client configuration settings include the following options: |
||
40 | * |
||
41 | * - handler: (callable) Function that transfers HTTP requests over the |
||
42 | * wire. The function is called with a Psr7\Http\Message\RequestInterface |
||
43 | * and array of transfer options, and must return a |
||
44 | * GuzzleHttp\Promise\PromiseInterface that is fulfilled with a |
||
45 | * Psr7\Http\Message\ResponseInterface on success. |
||
46 | * If no handler is provided, a default handler will be created |
||
47 | * that enables all of the request options below by attaching all of the |
||
48 | * default middleware to the handler. |
||
49 | * - base_uri: (string|UriInterface) Base URI of the client that is merged |
||
50 | * into relative URIs. Can be a string or instance of UriInterface. |
||
51 | * - **: any request option |
||
52 | * |
||
53 | * @param array $config Client configuration settings. |
||
54 | * |
||
55 | * @see \GuzzleHttp\RequestOptions for a list of available request options. |
||
56 | */ |
||
57 | public function __construct(array $config = []) |
||
58 | { |
||
59 | if (!isset($config['handler'])) { |
||
60 | $config['handler'] = HandlerStack::create(); |
||
61 | } elseif (!\is_callable($config['handler'])) { |
||
62 | throw new InvalidArgumentException('handler must be a callable'); |
||
63 | } |
||
64 | |||
65 | // Convert the base_uri to a UriInterface |
||
66 | if (isset($config['base_uri'])) { |
||
67 | $config['base_uri'] = Psr7\Utils::uriFor($config['base_uri']); |
||
|
|||
68 | } |
||
69 | |||
70 | $this->configureDefaults($config); |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * @param string $method |
||
75 | * @param array $args |
||
76 | * |
||
77 | * @return PromiseInterface|ResponseInterface |
||
78 | * |
||
79 | * @deprecated Client::__call will be removed in guzzlehttp/guzzle:8.0. |
||
80 | */ |
||
81 | public function __call($method, $args) |
||
82 | { |
||
83 | if (\count($args) < 1) { |
||
84 | throw new InvalidArgumentException('Magic request methods require a URI and optional options array'); |
||
85 | } |
||
86 | |||
87 | $uri = $args[0]; |
||
88 | $opts = $args[1] ?? []; |
||
89 | |||
90 | return \substr($method, -5) === 'Async' |
||
91 | ? $this->requestAsync(\substr($method, 0, -5), $uri, $opts) |
||
92 | : $this->request($method, $uri, $opts); |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * Asynchronously send an HTTP request. |
||
97 | * |
||
98 | * @param array $options Request options to apply to the given |
||
99 | * request and to the transfer. See \GuzzleHttp\RequestOptions. |
||
100 | */ |
||
101 | public function sendAsync(RequestInterface $request, array $options = []): PromiseInterface |
||
102 | { |
||
103 | // Merge the base URI into the request URI if needed. |
||
104 | $options = $this->prepareDefaults($options); |
||
105 | |||
106 | return $this->transfer( |
||
107 | $request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')), |
||
108 | $options |
||
109 | ); |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * Send an HTTP request. |
||
114 | * |
||
115 | * @param array $options Request options to apply to the given |
||
116 | * request and to the transfer. See \GuzzleHttp\RequestOptions. |
||
117 | * |
||
118 | * @throws GuzzleException |
||
119 | */ |
||
120 | public function send(RequestInterface $request, array $options = []): ResponseInterface |
||
125 | |||
126 | /** |
||
127 | * The HttpClient PSR (PSR-18) specify this method. |
||
128 | * |
||
129 | * @inheritDoc |
||
130 | */ |
||
131 | public function sendRequest(RequestInterface $request): ResponseInterface |
||
132 | { |
||
133 | $options[RequestOptions::SYNCHRONOUS] = true; |
||
134 | $options[RequestOptions::ALLOW_REDIRECTS] = false; |
||
135 | $options[RequestOptions::HTTP_ERRORS] = false; |
||
136 | |||
137 | return $this->sendAsync($request, $options)->wait(); |
||
138 | } |
||
139 | |||
140 | /** |
||
141 | * Create and send an asynchronous HTTP request. |
||
142 | * |
||
143 | * Use an absolute path to override the base path of the client, or a |
||
144 | * relative path to append to the base path of the client. The URL can |
||
145 | * contain the query string as well. Use an array to provide a URL |
||
146 | * template and additional variables to use in the URL template expansion. |
||
147 | * |
||
148 | * @param string $method HTTP method |
||
149 | * @param string|UriInterface $uri URI object or string. |
||
150 | * @param array $options Request options to apply. See \GuzzleHttp\RequestOptions. |
||
151 | */ |
||
152 | public function requestAsync(string $method, $uri = '', array $options = []): PromiseInterface |
||
153 | { |
||
154 | $options = $this->prepareDefaults($options); |
||
155 | // Remove request modifying parameter because it can be done up-front. |
||
156 | $headers = $options['headers'] ?? []; |
||
157 | $body = $options['body'] ?? null; |
||
158 | $version = $options['version'] ?? '1.1'; |
||
159 | // Merge the URI into the base URI. |
||
160 | $uri = $this->buildUri(Psr7\Utils::uriFor($uri), $options); |
||
161 | if (\is_array($body)) { |
||
162 | throw $this->invalidBody(); |
||
163 | } |
||
164 | $request = new Psr7\Request($method, $uri, $headers, $body, $version); |
||
165 | // Remove the option so that they are not doubly-applied. |
||
166 | unset($options['headers'], $options['body'], $options['version']); |
||
167 | |||
168 | return $this->transfer($request, $options); |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * Create and send an HTTP request. |
||
173 | * |
||
174 | * Use an absolute path to override the base path of the client, or a |
||
175 | * relative path to append to the base path of the client. The URL can |
||
176 | * contain the query string as well. |
||
177 | * |
||
178 | * @param string $method HTTP method. |
||
179 | * @param string|UriInterface $uri URI object or string. |
||
180 | * @param array $options Request options to apply. See \GuzzleHttp\RequestOptions. |
||
181 | * |
||
182 | * @throws GuzzleException |
||
183 | */ |
||
184 | public function request(string $method, $uri = '', array $options = []): ResponseInterface |
||
185 | { |
||
186 | $options[RequestOptions::SYNCHRONOUS] = true; |
||
187 | return $this->requestAsync($method, $uri, $options)->wait(); |
||
188 | } |
||
189 | |||
190 | /** |
||
191 | * Get a client configuration option. |
||
192 | * |
||
193 | * These options include default request options of the client, a "handler" |
||
194 | * (if utilized by the concrete client), and a "base_uri" if utilized by |
||
195 | * the concrete client. |
||
196 | * |
||
197 | * @param string|null $option The config option to retrieve. |
||
198 | * |
||
199 | * @return mixed |
||
200 | * |
||
201 | * @deprecated Client::getConfig will be removed in guzzlehttp/guzzle:8.0. |
||
202 | */ |
||
203 | public function getConfig(?string $option = null) |
||
204 | { |
||
205 | return $option === null |
||
206 | ? $this->config |
||
207 | : ($this->config[$option] ?? null); |
||
208 | } |
||
209 | |||
210 | private function buildUri(UriInterface $uri, array $config): UriInterface |
||
211 | { |
||
212 | if (isset($config['base_uri'])) { |
||
213 | $uri = Psr7\UriResolver::resolve(Psr7\Utils::uriFor($config['base_uri']), $uri); |
||
214 | } |
||
215 | |||
216 | if (isset($config['idn_conversion']) && ($config['idn_conversion'] !== false)) { |
||
217 | $idnOptions = ($config['idn_conversion'] === true) ? \IDNA_DEFAULT : $config['idn_conversion']; |
||
218 | $uri = Utils::idnUriConvert($uri, $idnOptions); |
||
219 | } |
||
220 | |||
221 | return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri; |
||
222 | } |
||
223 | |||
224 | /** |
||
225 | * Configures the default options for a client. |
||
226 | */ |
||
227 | private function configureDefaults(array $config): void |
||
228 | { |
||
229 | $defaults = [ |
||
230 | 'allow_redirects' => RedirectMiddleware::$defaultSettings, |
||
231 | 'http_errors' => true, |
||
232 | 'decode_content' => true, |
||
233 | 'verify' => true, |
||
234 | 'cookies' => false, |
||
235 | 'idn_conversion' => false, |
||
236 | ]; |
||
237 | |||
238 | // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set. |
||
239 | |||
240 | // We can only trust the HTTP_PROXY environment variable in a CLI |
||
241 | // process due to the fact that PHP has no reliable mechanism to |
||
242 | // get environment variables that start with "HTTP_". |
||
243 | if (\PHP_SAPI === 'cli' && ($proxy = Utils::getenv('HTTP_PROXY'))) { |
||
244 | $defaults['proxy']['http'] = $proxy; |
||
245 | } |
||
246 | |||
247 | if ($proxy = Utils::getenv('HTTPS_PROXY')) { |
||
248 | $defaults['proxy']['https'] = $proxy; |
||
249 | } |
||
250 | |||
251 | if ($noProxy = Utils::getenv('NO_PROXY')) { |
||
252 | $cleanedNoProxy = \str_replace(' ', '', $noProxy); |
||
253 | $defaults['proxy']['no'] = \explode(',', $cleanedNoProxy); |
||
254 | } |
||
255 | |||
256 | $this->config = $config + $defaults; |
||
257 | |||
258 | if (!empty($config['cookies']) && $config['cookies'] === true) { |
||
259 | $this->config['cookies'] = new CookieJar(); |
||
260 | } |
||
261 | |||
262 | // Add the default user-agent header. |
||
263 | if (!isset($this->config['headers'])) { |
||
264 | $this->config['headers'] = ['User-Agent' => Utils::defaultUserAgent()]; |
||
265 | } else { |
||
266 | // Add the User-Agent header if one was not already set. |
||
267 | foreach (\array_keys($this->config['headers']) as $name) { |
||
268 | if (\strtolower($name) === 'user-agent') { |
||
269 | return; |
||
270 | } |
||
271 | } |
||
272 | $this->config['headers']['User-Agent'] = Utils::defaultUserAgent(); |
||
273 | } |
||
274 | } |
||
275 | |||
276 | /** |
||
277 | * Merges default options into the array. |
||
278 | * |
||
279 | * @param array $options Options to modify by reference |
||
280 | */ |
||
281 | private function prepareDefaults(array $options): array |
||
315 | |||
316 | /** |
||
317 | * Transfers the given request and applies request options. |
||
318 | * |
||
319 | * The URI of the request is not modified and the request options are used |
||
320 | * as-is without merging in default options. |
||
321 | * |
||
322 | * @param array $options See \GuzzleHttp\RequestOptions. |
||
323 | */ |
||
324 | private function transfer(RequestInterface $request, array $options): PromiseInterface |
||
336 | |||
337 | /** |
||
338 | * Applies the array of request options to a request. |
||
339 | */ |
||
340 | private function applyOptions(RequestInterface $request, array &$options): RequestInterface |
||
462 | |||
463 | /** |
||
464 | * Return an InvalidArgumentException with pre-set message. |
||
465 | */ |
||
466 | private function invalidBody(): InvalidArgumentException |
||
474 | } |
||
475 |
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: