Completed
Push — master ( 4a1992...3a4f99 )
by Vladimir
06:33
created

UrlQuery::executeCurl()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 5
cts 6
cp 0.8333
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 0
crap 2.0185
1
<?php
2
3
/**
4
 * @copyright 2017 Vladimir Jimenez
5
 * @license   https://github.com/allejo/PhpPulse/blob/master/LICENSE.md MIT
6
 */
7
8
namespace allejo\DaPulse\Utilities;
9
10
use allejo\DaPulse\Exceptions\CurlException;
11
use allejo\DaPulse\Exceptions\HttpException;
12
13
/**
14
 * A wrapper class for working with cURL requests.
15
 *
16
 * This class configures cURL with all of the appropriate authentication information and proper cURL configuration for
17
 * processing requests.
18
 *
19
 * This class is provided as a convenience for all of the URL requests made by PhpPulse. This class may also be used
20
 * by external tools to make custom requests.
21
 *
22
 * @api
23
 * @package allejo\DaPulse\Utilities
24
 * @since   0.1.0
25
 */
26
class UrlQuery
27
{
28
    /**
29
     * Send a POST or PUT body as JSON instead of URL encoded values
30
     */
31
    const BODY_AS_JSON = 0x1;
32
33
    /**
34
     * The API endpoint that will be used in all requests
35
     *
36
     * @var string
37
     */
38
    private $url;
39
40
    /**
41
     * The cURL object this class is a wrapper for
42
     *
43
     * @var resource
44
     */
45
    private $cURL;
46
47
    /**
48
     * Configure all of the authentication needed for cURL requests and the API endpoint
49
     *
50
     * @param string $url       The API endpoint this instance will be calling
51
     * @param array  $urlParams Parameters that will be appended to the URL as GET parameters
52
     *
53
     * @since 0.1.0
54
     */
55 106
    public function __construct ($url, $urlParams)
56
    {
57 106
        $this->url  = $url . "?" . self::formatParameters($urlParams);
58 106
        $this->cURL = curl_init();
59
60 106
        $this->configureCurl();
61 106
    }
62
63
    /**
64
     * Clean up after ourselves; clean up the cURL object.
65
     */
66 106
    public function __destruct ()
67
    {
68 106
        curl_close($this->cURL);
69 106
    }
70
71
    /**
72
     * Set the credentials for basic authentication
73
     *
74
     * @param string $username The username basic authentication
75
     * @param string $password The password basic authentication
76
     *
77
     * @since 0.1.0
78
     *
79
     * @throws \InvalidArgumentException Either the username or the password was an empty or null string
80
     */
81
    public function setAuthentication ($username, $password)
82
    {
83
        if (StringUtilities::isNullOrEmpty($username) || StringUtilities::isNullOrEmpty($password))
84
        {
85
            throw new \InvalidArgumentException("Both the username and password must be non-empty strings.");
86
        }
87
88
        curl_setopt_array($this->cURL, [
89
            CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
90
            CURLOPT_USERPWD  => $username . ":" . $password
91
        ]);
92
    }
93
94
    /**
95
     * Set cURL headers
96
     *
97
     * @param array $headers The headers that will be sent with cURL
98
     *
99
     * @since 0.1.0
100
     *
101
     * @throws \InvalidArgumentException The $headers parameter was not an array or it was an empty array
102
     */
103
    public function setHeaders ($headers)
104
    {
105
        if (empty($headers) || !is_array($headers))
106
        {
107
            throw new \InvalidArgumentException("The headers parameter must be a non-empty array");
108
        }
109
110
        curl_setopt_array($this->cURL, [
111
            CURLOPT_HEADER     => true,
112
            CURLOPT_HTTPHEADER => $headers
113
        ]);
114
    }
115
116
    /**
117
     * Send a GET request
118
     *
119
     * @since  0.1.0
120
     *
121
     * @return mixed  An associative array matching the returned JSON result
122
     */
123 103
    public function sendGet ()
124
    {
125 103
        return $this->handleQuery();
126
    }
127
128
    /**
129
     * Send a POST request
130
     *
131
     * @param  array    $postArray The data that will be sent to DaPulse
132
     * @param  null|int $flags     Available flags: BODY_AS_JSON
133
     *
134
     * @since  0.1.0
135
     *
136
     * @throws HttpException
137
     *
138
     * @return mixed An associative array matching the returned JSON result
139
     */
140 9
    public function sendPost ($postArray, $flags = null)
141
    {
142 9
        $this->setPostFields($postArray, $flags);
0 ignored issues
show
Documentation introduced by
$postArray is of type array, but the function expects a string.

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:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
143
144 9
        curl_setopt_array($this->cURL, [
145 9
            CURLOPT_POST          => true,
146 9
            CURLOPT_CUSTOMREQUEST => "POST"
147 9
        ]);
148
149 9
        return $this->handleQuery();
150
    }
151
152
    /**
153
     * Send a PUT request
154
     *
155
     * @param  array    $postArray The data that will be sent to DaPulse
156
     * @param  null|int $flags     Available flags: BODY_AS_JSON
157
     *
158
     * @since  0.1.0
159
     *
160
     * @return mixed  An associative array matching the returned JSON result
161
     */
162 10
    public function sendPut ($postArray, $flags = null)
163
    {
164 10
        $this->setPostFields($postArray, $flags);
0 ignored issues
show
Documentation introduced by
$postArray is of type array, but the function expects a string.

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:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
165
166 10
        curl_setopt($this->cURL, CURLOPT_CUSTOMREQUEST, "PUT");
167
168 10
        return $this->handleQuery();
169
    }
170
171
    /**
172
     * Send a DELETE request
173
     *
174
     * @since  0.1.0
175
     *
176
     * @return mixed  An associative array matching the returned JSON result
177
     */
178 6
    public function sendDelete ()
179
    {
180 6
        curl_setopt($this->cURL, CURLOPT_CUSTOMREQUEST, "DELETE");
181
182 6
        return $this->handleQuery();
183
    }
184
185
    /**
186
     * Set the POST fields that will be submitted in the cURL request
187
     *
188
     * @param string   $postArray The POST fields that will be sent to DaPulse
189
     * @param null|int $flags     Available flags: BODY_AS_JSON
190
     *
191
     * @since 0.1.0
192
     */
193 19
    private function setPostFields ($postArray, $flags)
194
    {
195 19
        if ($flags & self::BODY_AS_JSON)
196 19
        {
197
            $postData = json_encode($postArray);
198
        }
199
        else
200
        {
201 19
            $postData = self::formatParameters($postArray);
0 ignored issues
show
Documentation introduced by
$postArray is of type string, but the function expects a array.

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:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
202
        }
203
204 19
        curl_setopt($this->cURL, CURLOPT_POSTFIELDS, $postData);
205 19
    }
206
207
    /**
208
     * Handle the execution of the cURL request. This function will also save the returned HTTP headers and handle them
209
     * appropriately.
210
     *
211
     * @since  0.1.0
212
     *
213
     * @throws CurlException If cURL is misconfigured or encounters an error
214
     * @throws HttpException An HTTP status of something other 200 is returned
215
     *
216
     * @return mixed
217
     */
218 106
    private function handleQuery ()
219
    {
220 106
        $result   = $this->executeCurl();
221 106
        $httpCode = curl_getinfo($this->cURL, CURLINFO_HTTP_CODE);
222
223 106
        if ($httpCode != 200 && $httpCode != 201)
224 106
        {
225 1
            throw new HttpException($httpCode, $result);
226
        }
227
228 106
        return json_decode($result, true);
229
    }
230
231
    /**
232
     * Configure the cURL instance and its credentials for Basic Authentication that this instance will be working with
233
     *
234
     * @since 0.1.0
235
     */
236 106
    private function configureCurl ()
237
    {
238 106
        curl_setopt_array($this->cURL, [
239 106
            CURLOPT_URL            => $this->url,
240 106
            CURLOPT_RETURNTRANSFER => true
241 106
        ]);
242 106
    }
243
244
    /**
245
     * Execute the finalized cURL object that has already been configured
246
     *
247
     * @since  0.1.0
248
     *
249
     * @throws \allejo\DaPulse\Exceptions\CurlException If cURL is misconfigured or encounters an error
250
     *
251
     * @return mixed
252
     */
253 106
    private function executeCurl ()
254
    {
255 106
        $result = curl_exec($this->cURL);
256
257 106
        if (!$result)
258 106
        {
259
            throw new CurlException($this->cURL);
260
        }
261
262 106
        return $result;
263
    }
264
265
    /**
266
     * Format an array into a URL encoded values to be submitted in cURL requests
267
     *
268
     * **Input**
269
     *
270
     * ```php
271
     * array(
272
     *     "foo"   => "bar",
273
     *     "param" => "value"
274
     * )
275
     * ```
276
     *
277
     * **Output**
278
     *
279
     * ```php
280
     * array(
281
     *     "foo=bar",
282
     *     "param=value"
283
     * )
284
     * ```
285
     *
286
     * @param  array $params An array containing parameter names as keys and parameter values as values in the array.
287
     *
288
     * @since  0.1.0
289
     *
290
     * @return string         A URL encoded and combined array of GET or POST parameters to be sent
291
     */
292 106
    private static function formatParameters ($params)
293
    {
294 106
        $parameters = [];
295
296 106
        foreach ($params as $key => $value)
297
        {
298 106
            if (is_null($value))
299 106
            {
300 1
                continue;
301
            }
302 106
            else if (is_bool($value))
303 106
            {
304 2
                $value = StringUtilities::booleanLiteral($value);
305 2
            }
306 106
            else if (is_array($value))
307 106
            {
308 2
                $formattedArray = self::formatArray($key, $value);
309 2
                $parameters[]   = self::formatParameters($formattedArray);
310
311 2
                continue;
312
            }
313
314 106
            $parameters[] = rawurlencode($key) . "=" . rawurlencode($value);
315 106
        }
316
317 106
        return implode("&", $parameters);
318
    }
319
320
    /**
321
     * Convert an indexed array into an array that can be feed to `formatParameters()` to be formatted to an acceptable
322
     * structure to be sent via a GET or POST request.
323
     *
324
     * **Input**
325
     *
326
     * ```php
327
     * array(
328
     *     "first",
329
     *     "second",
330
     *     "third"
331
     * )
332
     * ```
333
     *
334
     * **Output**
335
     *
336
     * ```php
337
     * array(
338
     *     "prefix[0]" => "first",
339
     *     "prefix[1]" => "second",
340
     *     "prefix[2]" => "third",
341
     * )
342
     * ```
343
     *
344
     * @param  string   $prefix The name of the
345
     * @param  string[] $array
346
     *
347
     * @see    formatParameters()
348
     *
349
     * @since  0.1.0
350
     *
351
     * @return array
352
     */
353 2
    private static function formatArray ($prefix, $array)
354
    {
355 2
        $parameters = [];
356
357 2
        foreach ($array as $key => $value)
358
        {
359 2
            $arrayKey              = sprintf("%s[%s]", $prefix, $key);
360 2
            $parameters[$arrayKey] = $value;
361 2
        }
362
363 2
        return $parameters;
364
    }
365
}
366