CURL::open()   B
last analyzed

Complexity

Conditions 8
Paths 16

Size

Total Lines 36
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 22
dl 0
loc 36
rs 8.4444
c 0
b 0
f 0
cc 8
nc 16
nop 0
1
<?php
2
3
/**
4
 * This file is part of the alphaz Framework.
5
 *
6
 * @author Muhammad Umer Farooq (Malik) <[email protected]>
7
 *
8
 * @link https://github.com/alphazframework/framework
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 *  file that was distributed with this source code.
12
 * @since 1.0.0
13
 *
14
 * @license MIT
15
 */
16
17
namespace alphaz\http\Clients;
18
19
class CURL extends AbstractClient
20
{
21
    /**
22
     * __construct.
23
     *
24
     * @param (string) $url
25
     *                      (string) $method
26
     *                      (array) $options
27
     *
28
     * @since 1.0.0
29
     */
30
    public function __construct($url, $method = 'GET', array $options = null)
31
    {
32
        if (!function_exists('curl_init')) {
33
            throw new \Exception('Error: cURL is not available.');
34
        }
35
        $this->resource = curl_init();
0 ignored issues
show
Documentation Bug introduced by
It seems like curl_init() can also be of type resource. However, the property $resource is declared as type object. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
36
37
        $this->setUrl($url);
38
        $this->setMethod($method);
39
        $this->setOption(CURLOPT_URL, $this->url);
40
        $this->setOption(CURLOPT_HEADER, true);
41
        $this->setOption(CURLOPT_RETURNTRANSFER, true);
42
43
        if ($options !== null) {
44
            $this->setOptions($options);
0 ignored issues
show
Bug introduced by
The method setOptions() does not exist on alphaz\http\Clients\CURL. Did you maybe mean setOption()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

44
            $this->/** @scrutinizer ignore-call */ 
45
                   setOptions($options);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
45
        }
46
    }
47
48
    /**
49
     * Set the method.
50
     *
51
     * @param (string) $method
52
     *
53
     * @since 1.0.0
54
     *
55
     * @return object
56
     */
57
    public function setMethod($method)
58
    {
59
        parent::setMethod($method);
60
        if ($method !== 'GET') {
61
            switch ($method) {
62
                case 'POST':
63
                    $this->setOption(CURLOPT_POST, true);
64
                    break;
65
                default:
66
                    $this->setOption(CURLOPT_CUSTOMREQUEST, $this->method);
67
                    break;
68
            }
69
        }
70
71
        return $this;
72
    }
73
74
    /**
75
     * Return cURL resource (alias to $this->getResource()).
76
     *
77
     * @since 1.0.0
78
     *
79
     * @return resource
80
     */
81
    public function curl()
82
    {
83
        return $this->resource;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->resource returns the type object which is incompatible with the documented return type resource.
Loading history...
84
    }
85
86
    /**
87
     * Create and open cURL resource.
88
     *
89
     * @since 1.0.0
90
     *
91
     * @return object
92
     */
93
    public function open()
94
    {
95
        $url = $this->url;
96
        $headers = [];
97
98
        // Set query data if there is any
99
        if (count($this->fields) > 0) {
100
            if ($this->method == 'GET') {
101
                $url = $this->options[CURLOPT_URL].'?'.$this->getQuery();
102
                $this->setOption(CURLOPT_URL, $url);
103
            } else {
104
                if (isset($this->requestHeaders['Content-Type']) && ($this->requestHeaders['Content-Type'] != 'multipart/form-data')) {
105
                    $this->setOption(CURLOPT_POSTFIELDS, $this->getQuery());
106
                    $this->setRequestHeader('Content-Length', $this->getQueryLength());
107
                } else {
108
                    $this->setOption(CURLOPT_POSTFIELDS, $this->fields);
109
                }
110
                $this->setOption(CURLOPT_POSTFIELDS, $this->fields);
111
                $this->setOption(CURLOPT_URL, $url);
112
            }
113
        }
114
115
        if ($this->hasRequestHeaders()) {
116
            foreach ($this->requestHeaders as $header => $value) {
117
                $headers[] = $header.': '.$value;
118
            }
119
            $this->setOption(CURLOPT_HTTPHEADER, $headers);
120
        }
121
122
        $this->response = curl_exec($this->resource);
0 ignored issues
show
Documentation Bug introduced by
It seems like curl_exec($this->resource) can also be of type true. However, the property $response is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
123
124
        if ($this->response === false) {
125
            $this->throwError('Error: '.curl_errno($this->resource).' => '.curl_error($this->resource).'.');
0 ignored issues
show
Bug introduced by
The method throwError() does not exist on alphaz\http\Clients\CURL. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

125
            $this->/** @scrutinizer ignore-call */ 
126
                   throwError('Error: '.curl_errno($this->resource).' => '.curl_error($this->resource).'.');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
126
        }
127
128
        return $this;
129
    }
130
131
    /**
132
     * Set curl session option.
133
     *
134
     * @param (mixed) $option
135
     *                        (mixed) $value
136
     *
137
     * @since 1.0.0
138
     *
139
     * @return object
140
     */
141
    public function setOption($option, $value)
142
    {
143
        $this->options[$option] = $value;
144
        curl_setopt($this->resource, $option, $value);
145
146
        return $this;
147
    }
148
149
    /**
150
     * Set curl session options.
151
     *
152
     * @param (array) $options
153
     *
154
     * @since 1.0.0
155
     *
156
     * @return object
157
     */
158
    public function setOptios($options)
159
    {
160
        foreach ($options as $option => $value) {
161
            $this->setOption($option, $value);
162
        }
163
        curl_setopt_array($this->resource, $options);
164
165
        return $this;
166
    }
167
168
    /**
169
     * Set curl return header.
170
     *
171
     * @param (bool) $header
172
     *
173
     * @since 1.0.0
174
     *
175
     * @return object
176
     */
177
    public function setReturnHeader($header = true)
178
    {
179
        $this->setOption(CURLOPT_HEADER, (bool) $header);
180
181
        return $this;
182
    }
183
184
    /**
185
     * Set curl return tranfer header.
186
     *
187
     * @param (bool) $header
188
     *
189
     * @since 1.0.0
190
     *
191
     * @return object
192
     */
193
    public function setReturnTransfer($header = true)
194
    {
195
        $this->setOption(CURLOPT_RETURNTRANSFER, (bool) $header);
196
197
        return $this;
198
    }
199
200
    /**
201
     * Set curl return header.
202
     *
203
     * @since 1.0.0
204
     *
205
     * @return bool
206
     */
207
    public function isReturnHeader()
208
    {
209
        return isset($this->options[CURLOPT_HEADER]) && ($this->options[CURLOPT_HEADER] == true);
210
    }
211
212
    /**
213
     * Set curl return tranfer header.
214
     *
215
     * @since 1.0.0
216
     *
217
     * @return bool
218
     */
219
    public function isReturnTransfer()
220
    {
221
        return isset($this->options[CURLOPT_RETURNTRANSFER]) && ($this->options[CURLOPT_RETURNTRANSFER] == true);
222
    }
223
224
    /**
225
     * Get curl session option.
226
     *
227
     * @param (mixes) $option
0 ignored issues
show
Bug introduced by
The type alphaz\http\Clients\mixes was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
228
     *
229
     * @since 1.0.0
230
     *
231
     * @return mixes
232
     */
233
    public function getOption($option)
234
    {
235
        return (isset($this->options[$option])) ? $this->options[$option] : false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return IssetNode ? $this...ptions[$option] : false could also return false which is incompatible with the documented return type alphaz\http\Clients\mixes. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
236
    }
237
238
    /**
239
     * Get curl last session info.
240
     *
241
     * @param (mixes) $option
242
     *
243
     * @since 1.0.0
244
     *
245
     * @return mixes
246
     */
247
    public function getInfo($option = null)
248
    {
249
        return ($option !== null) ? curl_getinfo($this->resource, $option) : curl_getinfo($this->resource);
0 ignored issues
show
Bug introduced by
$option of type alphaz\http\Clients\mixes is incompatible with the type integer|null expected by parameter $option of curl_getinfo(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

249
        return ($option !== null) ? curl_getinfo($this->resource, /** @scrutinizer ignore-type */ $option) : curl_getinfo($this->resource);
Loading history...
250
    }
251
252
    /**
253
     * Method to send the request and get the response.
254
     *
255
     * @since 1.0.0
256
     *
257
     * @return void
258
     */
259
    public function send()
260
    {
261
        $this->open();
262
263
        if ($this->response === false) {
0 ignored issues
show
introduced by
The condition $this->response === false is always false.
Loading history...
264
            throw new \Exception('Error: '.curl_errno($this->resource).' => '.curl_error($this->resource).'.');
265
        }
266
        // If the CURLOPT_RETURNTRANSFER option is set to true, get the response body and parse the headers.
267
        if (isset($this->options[CURLOPT_RETURNTRANSFER]) && ($this->options[CURLOPT_RETURNTRANSFER] == true)) {
268
            $headerSize = $this->getInfo(CURLINFO_HEADER_SIZE);
0 ignored issues
show
Bug introduced by
alphaz\http\Clients\CURLINFO_HEADER_SIZE of type integer is incompatible with the type alphaz\http\Clients\mixes expected by parameter $option of alphaz\http\Clients\CURL::getInfo(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

268
            $headerSize = $this->getInfo(/** @scrutinizer ignore-type */ CURLINFO_HEADER_SIZE);
Loading history...
269
            if ($this->options[CURLOPT_HEADER]) {
270
                $this->responseHeader = substr($this->response, 0, $headerSize);
0 ignored issues
show
Bug introduced by
It seems like $this->response can also be of type true; however, parameter $string of substr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

270
                $this->responseHeader = substr(/** @scrutinizer ignore-type */ $this->response, 0, $headerSize);
Loading history...
Bug introduced by
$headerSize of type alphaz\http\Clients\mixes is incompatible with the type integer|null expected by parameter $length of substr(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

270
                $this->responseHeader = substr($this->response, 0, /** @scrutinizer ignore-type */ $headerSize);
Loading history...
271
                $this->body = substr($this->response, $headerSize);
0 ignored issues
show
Bug introduced by
$headerSize of type alphaz\http\Clients\mixes is incompatible with the type integer expected by parameter $offset of substr(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

271
                $this->body = substr($this->response, /** @scrutinizer ignore-type */ $headerSize);
Loading history...
272
                $this->parseResponseHeaders();
273
            } else {
274
                $this->body = $this->response;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->response can also be of type true. However, the property $body is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
Documentation Bug introduced by
It seems like $this->response can also be of type true. However, the property $response is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
275
            }
276
        }
277
278
        if (array_key_exists('Content-Encoding', $this->responseHeaders)) {
279
            $this->decodeBody();
280
        }
281
    }
282
283
    /**
284
     * Return the cURL version.
285
     *
286
     * @since 1.0.0
287
     *
288
     * @return array
289
     */
290
    public function version()
291
    {
292
        return curl_version();
293
    }
294
295
    /**
296
     * Close the cURL connection.
297
     *
298
     * @since 1.0.0
299
     *
300
     * @return void
301
     */
302
    public function close()
303
    {
304
        if ($this->hasResource()) {
305
            curl_close($this->resource);
306
        }
307
    }
308
309
    /**
310
     * Parse headers.
311
     *
312
     * @since 1.0.0
313
     *
314
     * @return void
315
     */
316
    protected function parseResponseHeaders()
317
    {
318
        if ($this->responseHeader !== null) {
319
            $headers = explode("\n", $this->responseHeader);
320
            foreach ($headers as $header) {
321
                if (strpos($header, 'HTTP') !== false) {
322
                    $this->version = substr($header, 0, strpos($header, ' '));
323
                    $this->version = substr($this->version, strpos($this->version, '/') + 1);
324
                    preg_match('/\d\d\d/', trim($header), $match);
325
                    $this->code = $match[0];
326
                    $this->message = trim(str_replace('HTTP/'.$this->version.' '.$this->code.' ', '', $header));
327
                } elseif (strpos($header, ':') !== false) {
328
                    $name = substr($header, 0, strpos($header, ':'));
329
                    $value = substr($header, strpos($header, ':') + 1);
330
                    $this->responseHeaders[trim($name)] = trim($value);
331
                }
332
            }
333
        }
334
    }
335
}
336