RequestContext::getCurlOptions()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace GinoPane\NanoRest\Request;
4
5
use GinoPane\NanoRest\Supplemental\HeadersProperty;
6
use GinoPane\NanoRest\Response\ResponseContextAbstract;
7
use GinoPane\NanoRest\Exceptions\RequestContextException;
8
9
/**
10
 * Class RequestContext
11
 *
12
 * @author Sergey <Gino Pane> Karavay
13
 */
14
class RequestContext
15
{
16
    /**
17
     * Default values for timeouts
18
     */
19
    const TIMEOUT_DEFAULT               = 10;
20
    const CONNECTION_TIMEOUT_DEFAULT    = 5;
21
22
    /**
23
     * Default values for charsets
24
     */
25
    const CHARSET_UTF8      = 'UTF-8';
26
    const CHARSET_ISO88591  = 'ISO-8859-1';
27
28
    /**
29
     * Sample HTTP Methods
30
     */
31
    const METHOD_OPTIONS = 'OPTIONS';
32
    const METHOD_GET     = 'GET';
33
    const METHOD_HEAD    = 'HEAD';
34
    const METHOD_POST    = 'POST';
35
    const METHOD_PUT     = 'PUT';
36
    const METHOD_DELETE  = 'DELETE';
37
    const METHOD_TRACE   = 'TRACE';
38
    const METHOD_CONNECT = 'CONNECT';
39
    const METHOD_PATCH   = 'PATCH';
40
41
    /**
42
     * Sample content types
43
     */
44
    const CONTENT_TYPE_FORM         = 'multipart/form-data';
45
    const CONTENT_TYPE_FORM_URLENCODED  = 'application/x-www-form-urlencoded';
46
    const CONTENT_TYPE_TEXT_PLAIN   = 'text/plain';
47
    const CONTENT_TYPE_JSON         = 'application/json';
48
    const CONTENT_TYPE_JAVASCRIPT   = 'application/javascript';
49
    const CONTENT_TYPE_APP_XML      = 'application/xml';
50
    const CONTENT_TYPE_TEXT_XML     = 'text/xml';
51
    const CONTENT_TYPE_TEXT_HTML    = 'text/html';
52
53
    /**
54
     * The list of supported HTTP methods
55
     *
56
     * @var array
57
     */
58
    private static $availableMethods = array(
59
         self::METHOD_OPTIONS,
60
         self::METHOD_GET,
61
         self::METHOD_HEAD,
62
         self::METHOD_POST,
63
         self::METHOD_PUT,
64
         self::METHOD_DELETE,
65
         self::METHOD_TRACE,
66
         self::METHOD_CONNECT,
67
         self::METHOD_PATCH
68
    );
69
70
    /**
71
     * Default content type for requests
72
     */
73
    private $contentType = self::CONTENT_TYPE_TEXT_PLAIN;
74
75
    /**
76
     * Default charset for requests
77
     *
78
     * @var string
79
     */
80
    private $charset = self::CHARSET_UTF8;
81
82
    /**
83
     * Preferred HTTP method
84
     *
85
     * @var string
86
     */
87
    private $method = self::METHOD_GET;
88
89
    /**
90
     * Generic data to be sent
91
     *
92
     * @var mixed
93
     */
94
    private $data = null;
95
96
    /**
97
     * Parameters that should be appended to request URL
98
     *
99
     * @var array
100
     */
101
    private $requestParameters = [];
102
103
    /**
104
     * Options for transport
105
     *
106
     * @var array
107
     */
108
    private $curlOptions = [];
109
110
    /**
111
     * URL string for request
112
     *
113
     * @var string
114
     */
115
    private $url = '';
116
117
    /**
118
     * Address of proxy server
119
     *
120
     * @var string
121
     */
122
    private $proxy = '';
123
124
    /**
125
     * Connection timeout
126
     *
127
     * @var int
128
     */
129
    private $connectionTimeout = self::CONNECTION_TIMEOUT_DEFAULT;
130
131
    /**
132
     * General timeout value to be used with the request
133
     *
134
     * @var
135
     */
136
    private $timeout = self::TIMEOUT_DEFAULT;
137
138
    /**
139
     * The name of the class of desired ResponseContext
140
     *
141
     * @var string
142
     */
143
    private $responseContextClass = '';
144
145
    use HeadersProperty;
146
    use HttpBuildQueryBehavior;
147
148
    /**
149
     * RequestContext constructor
150
     *
151
     * @param string $url
152
     *
153
     * @throws RequestContextException
154
     */
155
    public function __construct(string $url = '')
156
    {
157
        if ($url) {
158
            $this->setUrl($url);
159
        }
160
    }
161
162
    /**
163
     * Fluent setter for consistency with other methods
164
     *
165
     * @param array $headers
166
     *
167
     * @return RequestContext
168
     */
169
    public function setHeaders(array $headers = []): RequestContext
170
    {
171
        $this->headers()->setHeaders($headers);
172
173
        return $this;
174
    }
175
176
    /**
177
     * Get headers prepared for request with Content-type assigned if it was not already set
178
     *
179
     * @return array
180
     */
181
    public function getRequestHeaders(): array
182
    {
183
        $headers = clone $this->headers();
184
185
        if (!$headers->headerExists('Content-type')) {
186
            if ($contentType = $this->getContentType()) {
187
                if (($charset = $this->getCharset()) && (stripos($contentType, 'charset=') === false)) {
188
                    $contentType .= "; charset={$charset}";
189
                }
190
191
                $headers->setHeader('Content-type', $contentType);
192
            }
193
        }
194
195
        return $headers->getHeadersForRequest();
196
    }
197
198
    /**
199
     * Set data for request
200
     *
201
     * @param mixed $data
202
     *
203
     * @return RequestContext
204
     */
205
    public function setData($data): RequestContext
206
    {
207
        $this->data = $data;
208
209
        return $this;
210
    }
211
212
    /**
213
     * Get previously set data
214
     *
215
     * @return mixed
216
     */
217
    public function getData()
218
    {
219
        return $this->data;
220
    }
221
222
    /**
223
     * Get previously set data encoded for request
224
     *
225
     * @return string
226
     */
227
    public function getRequestData(): string
228
    {
229
        $requestData = $this->getData();
230
231
        $requestData = is_array($requestData) ? $this->httpBuildQuery($requestData) : (string)$requestData;
232
233
        return $requestData;
234
    }
235
236
    /**
237
     * Get HTTP method
238
     *
239
     * @return string
240
     */
241
    public function getMethod(): string
242
    {
243
        return $this->method;
244
    }
245
246
    /**
247
     * Override default HTTP method
248
     *
249
     * @param string $method
250
     *
251
     * @throws RequestContextException
252
     *
253
     * @return RequestContext
254
     */
255
    public function setMethod(string $method): RequestContext
256
    {
257
        $method = strtoupper($method);
258
259
        if (!in_array($method, self::$availableMethods)) {
260
            throw new RequestContextException('Supplied HTTP method is not supported');
261
        }
262
263
        $this->method = $method;
264
265
        return $this;
266
    }
267
268
    /**
269
     * Get URL string
270
     *
271
     * @return string
272
     */
273
    public function getUrl(): string
274
    {
275
        return $this->url;
276
    }
277
278
    /**
279
     * Set URL string
280
     *
281
     * @param string $url
282
     *
283
     * @throws RequestContextException
284
     *
285
     * @return RequestContext
286
     */
287
    public function setUrl(string $url): RequestContext
288
    {
289
        $this->assertValidUrl($url);
290
291
        $this->url = $url;
292
293
        return $this;
294
    }
295
296
    /**
297
     * Get URL string with request parameters applied
298
     *
299
     * @return string
300
     */
301
    public function getRequestUrl(): string
302
    {
303
        $url = $this->getUrl();
304
305
        if ($this->getRequestParameters()) {
306
            $url = $this->attachQueryToUrl($url, $this->httpBuildQuery($this->getRequestParameters()));
307
        }
308
309
        return $url;
310
    }
311
312
    /**
313
     * Attach request query to URL
314
     *
315
     * @param $url
316
     * @param $query
317
     *
318
     * @return string
319
     */
320
    public function attachQueryToUrl($url, $query): string
321
    {
322
        return $url . (strpos($url, '?') === false ? '?' : '') . $query;
323
    }
324
325
    /**
326
     * Get request params
327
     *
328
     * @return array
329
     */
330
    public function getRequestParameters(): array
331
    {
332
        return $this->requestParameters;
333
    }
334
335
    /**
336
     * Set an array of request params
337
     *
338
     * @param array $requestParameters
339
     *
340
     * @return RequestContext
341
     */
342
    public function setRequestParameters(array $requestParameters = []): RequestContext
343
    {
344
        $this->requestParameters = $requestParameters;
345
346
        return $this;
347
    }
348
349
    /**
350
     * Get cURL options
351
     *
352
     * @return array
353
     */
354
    public function getCurlOptions(): array
355
    {
356
        return $this->curlOptions;
357
    }
358
359
    /**
360
     * Set a single CURL option for context
361
     *
362
     * @param int $optionName
363
     * @param mixed $optionValue
364
     *
365
     * @throws RequestContextException
366
     *
367
     * @return RequestContext
368
     */
369
    public function setCurlOption(int $optionName, $optionValue): RequestContext
370
    {
371
        if (@curl_setopt(curl_init(), $optionName, $optionValue)) {
372
            $this->curlOptions[$optionName] = $optionValue;
373
        } else {
374
            throw new RequestContextException(
375
                "Curl option is invalid: '$optionName' => " . var_export($optionValue, true)
376
            );
377
        }
378
379
        return $this;
380
    }
381
382
    /**
383
     * Set an array of CURL options for context. Please note, that old options would be removed or overwritten
384
     *
385
     * @param array $curlOptions
386
     *
387
     * @throws RequestContextException
388
     *
389
     * @return RequestContext
390
     */
391
    public function setCurlOptions(array $curlOptions = []): RequestContext
392
    {
393
        $this->curlOptions = [];
394
395
        foreach ($curlOptions as $name => $value) {
396
            $this->setCurlOption($name, $value);
397
        }
398
399
        return $this;
400
    }
401
402
    /**
403
     * @return mixed
404
     */
405
    public function getContentType()
406
    {
407
        return $this->contentType;
408
    }
409
410
    /**
411
     * @param mixed $contentType
412
     *
413
     * @return RequestContext
414
     */
415
    public function setContentType($contentType): RequestContext
416
    {
417
        $this->contentType = $contentType;
418
419
        return $this;
420
    }
421
422
    /**
423
     * Get charset for current request
424
     *
425
     * @return string
426
     */
427
    public function getCharset(): string
428
    {
429
        return $this->charset;
430
    }
431
432
    /**
433
     * Set charset for current request
434
     *
435
     * @param string $charset
436
     *
437
     * @return RequestContext
438
     */
439
    public function setCharset(string $charset): RequestContext
440
    {
441
        $this->charset = $charset;
442
443
        return $this;
444
    }
445
446
    /**
447
     * @return string
448
     */
449
    public function getProxy(): string
450
    {
451
        return $this->proxy;
452
    }
453
454
    /**
455
     * @param string $proxy
456
     *
457
     * @throws RequestContextException
458
     *
459
     * @return RequestContext
460
     */
461
    public function setProxy(string $proxy): RequestContext
462
    {
463
        $this->assertValidUrl($proxy);
464
465
        $this->proxy = $proxy;
466
467
        return $this;
468
    }
469
470
    /**
471
     * @return int|float
472
     */
473
    public function getConnectionTimeout()
474
    {
475
        return $this->connectionTimeout;
476
    }
477
478
    /**
479
     * @param int|float $connectionTimeout
480
     *
481
     * @return RequestContext
482
     */
483
    public function setConnectionTimeout($connectionTimeout): RequestContext
484
    {
485
        $this->connectionTimeout = $connectionTimeout;
486
487
        return $this;
488
    }
489
490
    /**
491
     * @return int|float
492
     */
493
    public function getTimeout()
494
    {
495
        return $this->timeout;
496
    }
497
498
    /**
499
     * @param int|float $timeout
500
     *
501
     * @return RequestContext
502
     */
503
    public function setTimeout($timeout): RequestContext
504
    {
505
        $this->timeout = $timeout;
506
507
        return $this;
508
    }
509
510
    /**
511
     * Sets desired type of response context
512
     *
513
     * @param string $responseContextClass
514
     *
515
     * @throws RequestContextException
516
     *
517
     * @return RequestContext
518
     */
519
    public function setResponseContextClass(string $responseContextClass): RequestContext
520
    {
521
        if (!is_a($responseContextClass, ResponseContextAbstract::class, true)) {
522
            throw new RequestContextException(
523
                sprintf(
524
                    "Class %s must have %s as one of its parents",
525
                    $responseContextClass, //@codeCoverageIgnore
526
                    ResponseContextAbstract::class
527
                )
528
            );
529
        }
530
531
        $this->responseContextClass = $responseContextClass;
532
533
        return $this;
534
    }
535
536
    /**
537
     * Returns current response context object of desired type
538
     *
539
     * @return ResponseContextAbstract
540
     */
541
    public function getResponseContextObject(): ResponseContextAbstract
542
    {
543
        return $this->responseContextClass ? new $this->responseContextClass() : ResponseContextAbstract::getByType('');
544
    }
545
546
    /**
547
     * Get string representation of RequestContext object
548
     *
549
     * @return string
550
     */
551
    public function __toString(): string
552
    {
553
        $headers = $this->getRequestHeaders()
554
            ? print_r($this->getRequestHeaders(), true)
555
            : "No headers were set";
556
557
        $data = $this->getData() ? print_r($this->getData(), true) : "No data was set";
558
559
        $requestParameters = $this->getRequestParameters()
560
            ? print_r($this->getRequestParameters(), true)
561
            : "No request parameters were set";
562
563
        return <<<DEBUG
564
===================
565
Method: {$this->getMethod()}
566
Request URL: {$this->getRequestUrl()}
567
===================
568
Headers:
569
570
{$headers}
571
===================
572
Data:
573
574
{$data}
575
===================
576
Request Parameters:
577
578
{$requestParameters}
579
===================
580
DEBUG;
581
    }
582
583
    /**
584
     * Throw exception on invalid URL
585
     *
586
     * @param string $url
587
     *
588
     * @throws RequestContextException
589
     */
590
    private function assertValidUrl(string $url): void
591
    {
592
        if (!(filter_var($url, FILTER_VALIDATE_URL) || filter_var($url, FILTER_VALIDATE_IP))) {
593
            throw new RequestContextException("Failed to set invalid URL: $url");
594
        }
595
    }
596
}
597