Completed
Push — master ( e7e23d...4acac6 )
by Andrea Marco
22:12 queued 12:16
created

AbstractApi::getQueryByOptions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 6
rs 9.4285
cc 2
eloc 3
nc 2
nop 1
1
<?php
2
3
namespace Cerbero\FluentApi;
4
5
use BadMethodCallException;
6
use Cerbero\FluentApi\Clients\AsyncClientInterface;
7
use Cerbero\FluentApi\Clients\ClientInterface;
8
use Cerbero\FluentApi\Clients\GuzzleAdapter;
9
use Cerbero\FluentApi\Inflectors\Psr4ResourceInflector;
10
use Cerbero\FluentApi\Inflectors\ResourceInflectorInterface;
11
use Cerbero\FluentApi\Requests\Request;
12
use Closure;
13
use Exception;
14
use GuzzleHttp\Client;
15
16
/**
17
 * Abstract implementation of an API start point.
18
 *
19
 * @author    Andrea Marco Sartori
20
 */
21
abstract class AbstractApi extends VersionableRequestMaker
22
{
23
    /**
24
     * The HTTP client.
25
     *
26
     * @author  Andrea Marco Sartori
27
     * @var     Cerbero\FluentApi\Clients\ClientInterface
28
     */
29
    protected $client;
30
31
    /**
32
     * The resource inflector.
33
     *
34
     * @author  Andrea Marco Sartori
35
     * @var     Cerbero\FluentApi\Inflectors\ResourceInflectorInterface
36
     */
37
    protected $inflector;
38
39
    /**
40
     * The latest resource invoked.
41
     *
42
     * @var Cerbero\FluentApi\AbstractResource|null
43
     */
44
    protected $resource;
45
46
    /**
47
     * Set the dependencies.
48
     *
49
     * @author    Andrea Marco Sartori
50
     * @param    string|null    $version
51
     * @param    Cerbero\FluentApi\Clients\ClientInterface|null    $client
52
     * @param    Cerbero\FluentApi\Inflectors\ResourceInflectorInterface|null    $inflector
53
     * @return    void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
54
     */
55
    public function __construct(
56
        $version = null,
57
        ClientInterface $client = null,
58
        ResourceInflectorInterface $inflector = null
59
    ) {
60
        $client    = $client    ?: $this->defaultClient();
61
        $inflector = $inflector ?: $this->defaultInflector();
62
63
        $this->setVersion($version)->setClient($client)->setInflector($inflector);
64
    }
65
66
    /**
67
     * Dynamically resolve resources.
68
     *
69
     * @author    Andrea Marco Sartori
70
     * @param    string    $name
71
     * @param    array    $parameters
72
     * @return    $this
73
     */
74
    public function __call($name, array $parameters)
75
    {
76
        if ($this->resource && method_exists($this->resource, $name)) {
77
            $this->request = $this->getRequestFromPreviousResource($name, $parameters);
78
        } else {
79
            $this->request = $this->getRequestByResolvingResource($name, $parameters);
80
        }
81
82
        return $this;
83
    }
84
85
    /**
86
     * Retrieve the updated request from the previously resolved resource.
87
     *
88
     * @param    string    $method
89
     * @param    array    $parameters
90
     * @return    Cerbero\FluentApi\Requests\Request
91
     */
92
    private function getRequestFromPreviousResource($method, array $parameters)
93
    {
94
        call_user_func_array([$this->resource, $method], $parameters);
95
96
        return $this->resource->updateOptions($this->getRequest());
97
    }
98
99
    /**
100
     * Retrieve the request from the newly resolved resource.
101
     *
102
     * @param    string    $resource
103
     * @param    array    $parameters
104
     * @return    Cerbero\FluentApi\Requests\Request
105
     */
106
    private function getRequestByResolvingResource($resource, array $parameters)
107
    {
108
        $class = $this->inflectResource($resource);
109
110
        $this->resource = new $class(...$parameters);
111
112
        return $this->resource->fillRequest($this->getRequest());
113
    }
114
115
    /**
116
     * Retrieve the base URL.
117
     *
118
     * @author    Andrea Marco Sartori
119
     * @return    string
120
     */
121
    abstract public function getUrl();
122
123
    /**
124
     * Retrieve the resource to call by inflecting the given name.
125
     *
126
     * @author    Andrea Marco Sartori
127
     * @param    string    $name
128
     * @return    Cerbero\FluentApi\Resources\ResourceInterface
129
     */
130
    protected function inflectResource($name)
131
    {
132
        $target = $this->resource ?: $this;
133
134
        $resource = $this->getInflector()->baseNamespace(get_class($target))
135
                                         ->version($target->getVersion())
136
                                         ->inflect($name);
137
138
        if (class_exists($resource)) {
139
            return $resource;
140
        }
141
142
        throw new BadMethodCallException("The resource [$resource] does not exist.");
143
    }
144
145
    /**
146
     * Retrieve the resource inflector.
147
     *
148
     * @return    Cerbero\FluentApi\Inflectors\ResourceInflectorInterface
149
     */
150
    public function getInflector()
151
    {
152
        return $this->inflector ?: $this->defaultInflector();
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->inflector ?: $this->defaultInflector(); of type Cerbero\FluentApi\Cerber...s\Psr4ResourceInflector adds the type Cerbero\FluentApi\Inflectors\Psr4ResourceInflector to the return on line 152 which is incompatible with the return type documented by Cerbero\FluentApi\AbstractApi::getInflector of type Cerbero\FluentApi\Cerber...ourceInflectorInterface.
Loading history...
153
    }
154
155
    /**
156
     * Retrieve the default inflector.
157
     *
158
     * @author    Andrea Marco Sartori
159
     * @return    Cerbero\FluentApi\Inflectors\ResourceInflectorInterface
160
     */
161
    protected function defaultInflector()
162
    {
163
        return new Psr4ResourceInflector;
164
    }
165
166
    /**
167
     * Set the resource inflector.
168
     *
169
     * @author    Andrea Marco Sartori
170
     * @param    Cerbero\FluentApi\Inflectors\ResourceInflectorInterface    $inflector
171
     * @return    $this
172
     */
173
    public function setInflector(ResourceInflectorInterface $inflector)
174
    {
175
        $this->inflector = $inflector;
0 ignored issues
show
Documentation Bug introduced by
It seems like $inflector of type object<Cerbero\FluentApi...urceInflectorInterface> is incompatible with the declared type object<Cerbero\FluentApi...urceInflectorInterface> of property $inflector.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
176
177
        return $this;
178
    }
179
180
    /**
181
     * Perform a synchronous call to the eventual endpoint.
182
     *
183
     * @return    Psr\Http\Message\ResponseInterface
184
     */
185
    public function call()
186
    {
187
        return $this->performCall();
188
    }
189
190
    /**
191
     * Perform an HTTP call by using the client.
192
     *
193
     * @param    Closure|null    $success
194
     * @param    Closure|null    $failure
195
     * @return    mixed
196
     */
197
    private function performCall(Closure $success = null, Closure $failure = null)
198
    {
199
        $parameters = [
200
            $this->request->verb(), $this->request->endpoint(), $this->request->options(),
201
        ];
202
203
        if ($success) {
204
            array_push($parameters, $success, $failure);
205
        }
206
207
        return call_user_func_array(
208
            [$this->getClient(), $success ? 'then' : 'call'], $parameters
209
        );
210
    }
211
212
    /**
213
     * Perform an asynchronous call to the eventual endpoint.
214
     *
215
     * @param    Closure    $success
216
     * @param    Closure|null    $failure
217
     * @return    mixed
218
     */
219
    public function then(Closure $success, Closure $failure = null)
220
    {
221
        if (! $this->getClient() instanceof AsyncClientInterface) {
222
            throw new Exception("The set client can't perform asynchronous calls.");
223
        }
224
225
        return $this->performCall($success, $failure);
226
    }
227
228
    /**
229
     * Retrieve the body of the response as a JSON object.
230
     *
231
     * @return    \StdClass
232
     */
233
    public function toJson()
234
    {
235
        return $this->decodeBody();
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->decodeBody(); of type stdClass|array adds the type array to the return on line 235 which is incompatible with the return type documented by Cerbero\FluentApi\AbstractApi::toJson of type stdClass.
Loading history...
236
    }
237
238
    /**
239
     * Retrieve the decoded response body.
240
     *
241
     * @param    boolean    $toArray
242
     * @return    \StdClass|array
243
     */
244
    private function decodeBody($toArray = false)
245
    {
246
        $body = $this->call()->getBody()->getContents();
247
248
        return json_decode($body, $toArray);
249
    }
250
251
    /**
252
     * Retrieve the body of the response as an array.
253
     *
254
     * @return    array
255
     */
256
    public function toArray()
257
    {
258
        return $this->decodeBody(true);
259
    }
260
261
    /**
262
     * Retrieve the eventual API URL.
263
     *
264
     * @return    string
265
     */
266
    public function toUrl()
267
    {
268
        $options = $this->getRequest()->options();
269
270
        $query = $this->getQueryByOptions($options);
271
272
        return $this->getRequest()->endpoint() . $query;
273
    }
274
275
    /**
276
     * Retrieve the query string parameters by the given options.
277
     *
278
     * @param    array    $options
279
     * @return    string
280
     */
281
    private function getQueryByOptions(array $options)
282
    {
283
        $query = $this->getClient()->getQueryByOptions($options);
284
285
        return empty($query) ? '' : '?' . http_build_query($query);
286
    }
287
288
    /**
289
     * Retrieve the request to pass through resources.
290
     *
291
     * @return    Cerbero\FluentApi\Requests\Request
292
     */
293
    public function getRequest()
294
    {
295
        return $this->request ?: new Request($this->getUrl());
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request ?: new \C...quest($this->getUrl()); of type Cerbero\FluentApi\Cerber...entApi\Requests\Request adds the type Cerbero\FluentApi\Requests\Request to the return on line 295 which is incompatible with the return type declared by the abstract method Cerbero\FluentApi\Versio...equestMaker::getRequest of type Cerbero\FluentApi\Cerber...entApi\Requests\Request.
Loading history...
296
    }
297
298
    /**
299
     * Set the HTTP client.
300
     *
301
     * @author    Andrea Marco Sartori
302
     * @param    Cerbero\FluentApi\Clients\ClientInterface    $client
303
     * @return    $this
304
     */
305
    public function setClient(ClientInterface $client)
306
    {
307
        $this->client = $client;
0 ignored issues
show
Documentation Bug introduced by
It seems like $client of type object<Cerbero\FluentApi\Clients\ClientInterface> is incompatible with the declared type object<Cerbero\FluentApi...lients\ClientInterface> of property $client.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
308
309
        return $this;
310
    }
311
312
    /**
313
     * Retrieve the HTTP client.
314
     *
315
     * @author    Andrea Marco Sartori
316
     * @return    Cerbero\FluentApi\Clients\ClientInterface
317
     */
318
    public function getClient()
319
    {
320
        return $this->client;
321
    }
322
323
    /**
324
     * Retrieve the default HTTP client to use if none is provided.
325
     *
326
     * @author    Andrea Marco Sartori
327
     * @return    Cerbero\FluentApi\Clients\ClientInterface
328
     */
329
    protected function defaultClient()
330
    {
331
        return new GuzzleAdapter(new Client);
332
    }
333
}
334