RequestCore::set_read_file()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 6
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
require_once KS3_API_PATH.DIRECTORY_SEPARATOR."encryption".DIRECTORY_SEPARATOR."EncryptionCallBack.php";
4
/**
5
 * Handles all HTTP requests using cURL and manages the responses.
6
 *
7
 * @version 2011.06.07
8
 * @copyright 2006-2011 Ryan Parman
9
 * @copyright 2006-2010 Foleeo Inc.
10
 * @copyright 2010-2011 Amazon.com, Inc. or its affiliates.
11
 * @copyright 2008-2011 Contributors
12
 * @license http://opensource.org/licenses/bsd-license.php Simplified BSD License
13
 */
14
class RequestCore
15
{
16
	/**
17
	 * The URL being requested.
18
	 */
19
	public $request_url;
20
21
	/**
22
	 * The headers being sent in the request.
23
	 */
24
	public $request_headers;
25
26
	/**
27
	 * The body being sent in the request.
28
	 */
29
	public $request_body;
30
31
	/**
32
	 * The response returned by the request.
33
	 */
34
	public $response;
35
36
	/**
37
	 * The headers returned by the request.
38
	 */
39
	public $response_headers;
40
41
	/**
42
	 * The body returned by the request.
43
	 */
44
	public $response_body;
45
46
	/**
47
	 * The HTTP status code returned by the request.
48
	 */
49
	public $response_code;
50
51
	/**
52
	 * Additional response data.
53
	 */
54
	public $response_info;
55
56
	/**
57
	 * The handle for the cURL object.
58
	 */
59
	public $curl_handle;
60
61
	/**
62
	 * The method by which the request is being made.
63
	 */
64
	public $method;
65
66
	/**
67
	 * Stores the proxy settings to use for the request.
68
	 */
69
	public $proxy = null;
70
71
	/**
72
	 * The username to use for the request.
73
	 */
74
	public $username = null;
75
76
	/**
77
	 * The password to use for the request.
78
	 */
79
	public $password = null;
80
81
	/**
82
	 * Custom CURLOPT settings.
83
	 */
84
	public $curlopts = null;
85
86
	/**
87
	 * The state of debug mode.
88
	 */
89
	public $debug_mode = false;
90
91
	/**
92
	 * The default class to use for HTTP Requests (defaults to <RequestCore>).
93
	 */
94
	public $request_class = 'RequestCore';
95
96
	/**
97
	 * The default class to use for HTTP Responses (defaults to <ResponseCore>).
98
	 */
99
	public $response_class = 'ResponseCore';
100
101
	/**
102
	 * Default useragent string to use.
103
	 */
104
	public $useragent = 'RequestCore/1.4.3';
105
106
	/**
107
	 * File to read from while streaming up.
108
	 */
109
	public $read_file = null;
110
111
	/**
112
	 * The resource to read from while streaming up.
113
	 */
114
	public $read_stream = null;
115
116
	/**
117
	 * The size of the stream to read from.
118
	 */
119
	public $read_stream_size = null;
120
121
	/**
122
	 * The length already read from the stream.
123
	 */
124
	public $read_stream_read = 0;
125
126
	/**
127
	 * File to write to while streaming down.
128
	 */
129
	public $write_file = null;
130
131
	/**
132
	 * The resource to write to while streaming down.
133
	 */
134
	public $write_stream = null;
135
136
	/**
137
	 * Stores the intended starting seek position.
138
	 */
139
	public $seek_position = null;
140
141
	/**
142
	 * The location of the cacert.pem file to use.
143
	 */
144
	public $cacert_location = false;
145
146
	/**
147
	 * The state of SSL certificate verification.
148
	 */
149
	public $ssl_verification = true;
150
151
	/**
152
	 * The user-defined callback function to call when a stream is read from.
153
	 */
154
	public $registered_streaming_read_callback = null;
155
156
	/**
157
	 * The user-defined callback function to call when a stream is written to.
158
	 */
159
	public $registered_streaming_write_callback = null;
160
161
	/*%******************************************************************************************%*/
162
	// CONSTANTS
163
164
	/**
165
	 * GET HTTP Method
166
	 */
167
	const HTTP_GET = 'GET';
168
169
	/**
170
	 * POST HTTP Method
171
	 */
172
	const HTTP_POST = 'POST';
173
174
	/**
175
	 * PUT HTTP Method
176
	 */
177
	const HTTP_PUT = 'PUT';
178
179
	/**
180
	 * DELETE HTTP Method
181
	 */
182
	const HTTP_DELETE = 'DELETE';
183
184
	/**
185
	 * HEAD HTTP Method
186
	 */
187
	const HTTP_HEAD = 'HEAD';
188
189
190
	/*%******************************************************************************************%*/
191
	// CONSTRUCTOR/DESTRUCTOR
192
193
	/**
194
	 * Constructs a new instance of this class.
195
	 *
196
	 * @param string $url (Optional) The URL to request or service endpoint to query.
197
	 * @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`
198
	 * @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class.
199
	 * @return $this A reference to the current instance.
200
	 */
201
	public function __construct($url = null, $proxy = null, $helpers = null)
202
	{
203
		// Set some default values.
204
		$this->request_url = $url;
205
		$this->method = self::HTTP_GET;
206
		$this->request_headers = array();
207
		$this->request_body = '';
208
209
		// Set a new Request class if one was set.
210
		if (isset($helpers['request']) && !empty($helpers['request']))
211
		{
212
			$this->request_class = $helpers['request'];
213
		}
214
215
		// Set a new Request class if one was set.
216
		if (isset($helpers['response']) && !empty($helpers['response']))
217
		{
218
			$this->response_class = $helpers['response'];
219
		}
220
221
		if ($proxy)
222
		{
223
			$this->set_proxy($proxy);
224
		}
225
226
		return $this;
227
	}
228
229
	/**
230
	 * Destructs the instance. Closes opened file handles.
231
	 *
232
	 * @return $this A reference to the current instance.
233
	 */
234
	public function __destruct()
235
	{
236
		if (isset($this->read_file) && isset($this->read_stream))
237
		{
238
			fclose($this->read_stream);
239
		}
240
241
		if (isset($this->write_file) && isset($this->write_stream))
242
		{
243
			fclose($this->write_stream);
244
		}
245
246
		return $this;
247
	}
248
249
250
	/*%******************************************************************************************%*/
251
	// REQUEST METHODS
252
253
	/**
254
	 * Sets the credentials to use for authentication.
255
	 *
256
	 * @param string $user (Required) The username to authenticate with.
257
	 * @param string $pass (Required) The password to authenticate with.
258
	 * @return $this A reference to the current instance.
259
	 */
260
	public function set_credentials($user, $pass)
261
	{
262
		$this->username = $user;
263
		$this->password = $pass;
264
		return $this;
265
	}
266
267
	/**
268
	 * Adds a custom HTTP header to the cURL request.
269
	 *
270
	 * @param string $key (Required) The custom HTTP header to set.
271
	 * @param mixed $value (Required) The value to assign to the custom HTTP header.
272
	 * @return $this A reference to the current instance.
273
	 */
274
	public function add_header($key, $value)
275
	{
276
		$this->request_headers[$key] = $value;
277
		return $this;
278
	}
279
280
	/**
281
	 * Removes an HTTP header from the cURL request.
282
	 *
283
	 * @param string $key (Required) The custom HTTP header to set.
284
	 * @return $this A reference to the current instance.
285
	 */
286
	public function remove_header($key)
287
	{
288
		if (isset($this->request_headers[$key]))
289
		{
290
			unset($this->request_headers[$key]);
291
		}
292
		return $this;
293
	}
294
295
	/**
296
	 * Set the method type for the request.
297
	 *
298
	 * @param string $method (Required) One of the following constants: <HTTP_GET>, <HTTP_POST>, <HTTP_PUT>, <HTTP_HEAD>, <HTTP_DELETE>.
299
	 * @return $this A reference to the current instance.
300
	 */
301
	public function set_method($method)
302
	{
303
		$this->method = strtoupper($method);
304
		return $this;
305
	}
306
307
	/**
308
	 * Sets a custom useragent string for the class.
309
	 *
310
	 * @param string $ua (Required) The useragent string to use.
311
	 * @return $this A reference to the current instance.
312
	 */
313
	public function set_useragent($ua)
314
	{
315
		$this->useragent = $ua;
316
		return $this;
317
	}
318
319
	/**
320
	 * Set the body to send in the request.
321
	 *
322
	 * @param string $body (Required) The textual content to send along in the body of the request.
323
	 * @return $this A reference to the current instance.
324
	 */
325
	public function set_body($body)
326
	{
327
		$this->request_body = $body;
328
		return $this;
329
	}
330
331
	/**
332
	 * Set the URL to make the request to.
333
	 *
334
	 * @param string $url (Required) The URL to make the request to.
335
	 * @return $this A reference to the current instance.
336
	 */
337
	public function set_request_url($url)
338
	{
339
		$this->request_url = $url;
340
		return $this;
341
	}
342
343
	/**
344
	 * Set additional CURLOPT settings. These will merge with the default settings, and override if
345
	 * there is a duplicate.
346
	 *
347
	 * @param array $curlopts (Optional) A set of key-value pairs that set `CURLOPT` options. These will merge with the existing CURLOPTs, and ones passed here will override the defaults. Keys should be the `CURLOPT_*` constants, not strings.
348
	 * @return $this A reference to the current instance.
349
	 */
350
	public function set_curlopts($curlopts)
351
	{
352
		$this->curlopts = $curlopts;
353
		return $this;
354
	}
355
356
	/**
357
	 * Sets the length in bytes to read from the stream while streaming up.
358
	 *
359
	 * @param integer $size (Required) The length in bytes to read from the stream.
360
	 * @return $this A reference to the current instance.
361
	 */
362
	public function set_read_stream_size($size)
363
	{
364
		$this->read_stream_size = $size;
365
366
		return $this;
367
	}
368
369
	/**
370
	 * Sets the resource to read from while streaming up. Reads the stream from its current position until
371
	 * EOF or `$size` bytes have been read. If `$size` is not given it will be determined by <php:fstat()> and
372
	 * <php:ftell()>.
373
	 *
374
	 * @param resource $resource (Required) The readable resource to read from.
375
	 * @param integer $size (Optional) The size of the stream to read.
376
	 * @return $this A reference to the current instance.
377
	 */
378
	public function set_read_stream($resource, $size = null)
379
	{
380
		if (!isset($size) || $size < 0)
381
		{
382
			$stats = fstat($resource);
383
384
			if ($stats && $stats['size'] >= 0)
0 ignored issues
show
Bug Best Practice introduced by
The expression $stats of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
385
			{
386
				$position = ftell($resource);
387
388
				if ($position !== false && $position >= 0)
389
				{
390
					$size = $stats['size'] - $position;
391
				}
392
			}
393
		}
394
395
		$this->read_stream = $resource;
396
397
		return $this->set_read_stream_size($size);
398
	}
399
400
	/**
401
	 * Sets the file to read from while streaming up.
402
	 *
403
	 * @param string $location (Required) The readable location to read from.
404
	 * @return $this A reference to the current instance.
405
	 */
406
	public function set_read_file($location)
407
	{
408
		$this->read_file = $location;
409
		$read_file_handle = fopen($location, 'r');
410
411
		return $this->set_read_stream($read_file_handle);
412
	}
413
414
	/**
415
	 * Sets the resource to write to while streaming down.
416
	 *
417
	 * @param resource $resource (Required) The writeable resource to write to.
418
	 * @return $this A reference to the current instance.
419
	 */
420
	public function set_write_stream($resource)
421
	{
422
		$this->write_stream = $resource;
423
424
		return $this;
425
	}
426
427
	/**
428
	 * Sets the file to write to while streaming down.
429
	 *
430
	 * @param string $location (Required) The writeable location to write to.
431
	 * @return $this A reference to the current instance.
432
	 */
433
	public function set_write_file($location)
434
	{
435
		$this->write_file = $location;
436
		$write_file_handle = fopen($location, 'w');
437
438
		return $this->set_write_stream($write_file_handle);
439
	}
440
441
	/**
442
	 * Set the proxy to use for making requests.
443
	 *
444
	 * @param string $proxy (Required) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`
445
	 * @return $this A reference to the current instance.
446
	 */
447
	public function set_proxy($proxy)
448
	{
449
		$proxy = parse_url($proxy);
450
		$proxy['user'] = isset($proxy['user']) ? $proxy['user'] : null;
451
		$proxy['pass'] = isset($proxy['pass']) ? $proxy['pass'] : null;
452
		$proxy['port'] = isset($proxy['port']) ? $proxy['port'] : null;
453
		$this->proxy = $proxy;
454
		return $this;
455
	}
456
457
	/**
458
	 * Set the intended starting seek position.
459
	 *
460
	 * @param integer $position (Required) The byte-position of the stream to begin reading from.
461
	 * @return $this A reference to the current instance.
462
	 */
463
	public function set_seek_position($position)
464
	{
465
		$this->seek_position = isset($position) ? (integer) $position : null;
466
467
		return $this;
468
	}
469
470
	/**
471
	 * Register a callback function to execute whenever a data stream is read from using
472
	 * <CFRequest::streaming_read_callback()>.
473
	 *
474
	 * The user-defined callback function should accept three arguments:
475
	 *
476
	 * <ul>
477
	 * 	<li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
478
	 * 	<li><code>$file_handle</code> - <code>resource</code> - Required - The file handle resource that represents the file on the local file system.</li>
479
	 * 	<li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
480
	 * </ul>
481
	 *
482
	 * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
0 ignored issues
show
Bug introduced by
The type function 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...
483
	 * 	<li>The name of a global function to execute, passed as a string.</li>
484
	 * 	<li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
485
	 * 	<li>An anonymous function (PHP 5.3+).</li></ul>
486
	 * @return $this A reference to the current instance.
487
	 */
488
	public function register_streaming_read_callback($callback)
489
	{
490
		$this->registered_streaming_read_callback = $callback;
491
492
		return $this;
493
	}
494
495
	/**
496
	 * Register a callback function to execute whenever a data stream is written to using
497
	 * <CFRequest::streaming_write_callback()>.
498
	 *
499
	 * The user-defined callback function should accept two arguments:
500
	 *
501
	 * <ul>
502
	 * 	<li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
503
	 * 	<li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
504
	 * </ul>
505
	 *
506
	 * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
507
	 * 	<li>The name of a global function to execute, passed as a string.</li>
508
	 * 	<li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
509
	 * 	<li>An anonymous function (PHP 5.3+).</li></ul>
510
	 * @return $this A reference to the current instance.
511
	 */
512
	public function register_streaming_write_callback($callback)
513
	{
514
		$this->registered_streaming_write_callback = $callback;
515
516
		return $this;
517
	}
518
519
520
	/*%******************************************************************************************%*/
521
	// PREPARE, SEND, AND PROCESS REQUEST
522
523
	/**
524
	 * A callback function that is invoked by cURL for streaming up.
525
	 *
526
	 * @param resource $curl_handle (Required) The cURL handle for the request.
527
	 * @param resource $file_handle (Required) The open file handle resource.
528
	 * @param integer $length (Required) The maximum number of bytes to read.
529
	 * @return binary Binary data from a stream.
0 ignored issues
show
Bug introduced by
The type binary 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...
530
	 */
531
	public function streaming_read_callback($curl_handle, $file_handle, $length)
532
	{
533
		// Execute callback function
534
		if ($this->registered_streaming_read_callback)
535
		{
536
			return $this->registered_streaming_read_callback->streaming_read_callback($curl_handle, $file_handle, $length,$this->read_stream,$this->seek_position);
537
		}
538
		// Once we've sent as much as we're supposed to send...
539
		if ($this->read_stream_read >= $this->read_stream_size)
540
		{
541
			// Send EOF
542
			return '';
0 ignored issues
show
Bug Best Practice introduced by
The expression return '' returns the type string which is incompatible with the documented return type binary.
Loading history...
543
		}
544
545
		// If we're at the beginning of an upload and need to seek...
546
		if ($this->read_stream_read == 0 && isset($this->seek_position) && $this->seek_position !== ftell($this->read_stream))
547
		{
548
			if (fseek($this->read_stream, $this->seek_position) !== 0)
549
			{
550
				throw new RequestCore_Exception('The stream does not support seeking and is either not at the requested position or the position is unknown.');
551
			}
552
		}
553
554
		$read = fread($this->read_stream, min($this->read_stream_size - $this->read_stream_read, $length)); // Remaining upload data or cURL's requested chunk size
555
		$this->read_stream_read += strlen($read);
556
557
		$out = $read === false ? '' : $read;
558
559
		return $out;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $out returns the type string which is incompatible with the documented return type binary.
Loading history...
560
	}
561
562
	/**
563
	 * A callback function that is invoked by cURL for streaming down.
564
	 *
565
	 * @param resource $curl_handle (Required) The cURL handle for the request.
566
	 * @param binary $data (Required) The data to write.
567
	 * @return integer The number of bytes written.
568
	 */
569
	public function streaming_write_callback($curl_handle, $data)
570
	{
571
		if ($this->registered_streaming_write_callback){
572
			return $this->registered_streaming_write_callback->streaming_write_callback($curl_handle,$data,$this->write_stream);
573
		}
574
		$length = strlen($data);
575
		$written_total = 0;
576
		$written_last = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $written_last is dead and can be removed.
Loading history...
577
578
		while ($written_total < $length)
579
		{
580
			$written_last = fwrite($this->write_stream, substr($data, $written_total));
581
582
			if ($written_last === false)
583
			{
584
				return $written_total;
585
			}
586
587
			$written_total += $written_last;
588
		}
589
590
		return $written_total;
591
	}
592
593
	/**
594
	 * Prepares and adds the details of the cURL request. This can be passed along to a <php:curl_multi_exec()>
595
	 * function.
596
	 *
597
	 * @return resource The handle for the cURL object.
598
	 */
599
	public function prep_request()
600
	{
601
		$curl_handle = curl_init();
602
603
		// Set default options.
604
		curl_setopt($curl_handle, CURLOPT_URL, $this->request_url);
605
		curl_setopt($curl_handle, CURLOPT_FILETIME, true);
606
		curl_setopt($curl_handle, CURLOPT_FRESH_CONNECT, false);
607
		//为了兼容PHP 5.6,PHP 5.6把 CURLOPT_CLOSEPOLICY 这个变量删除了
608
		//curl_setopt($curl_handle, CURLOPT_CLOSEPOLICY, CURLCLOSEPOLICY_LEAST_RECENTLY_USED);
609
		curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 5);
610
		curl_setopt($curl_handle, CURLOPT_HEADER, true);
611
		curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
612
		curl_setopt($curl_handle, CURLOPT_TIMEOUT, 5184000);
613
		curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 120);
614
		curl_setopt($curl_handle, CURLOPT_NOSIGNAL, true);
615
		curl_setopt($curl_handle, CURLOPT_REFERER, $this->request_url);
616
		curl_setopt($curl_handle, CURLOPT_USERAGENT, $this->useragent);
617
		curl_setopt($curl_handle, CURLOPT_READFUNCTION, array($this, 'streaming_read_callback'));
618
619
		// Verification of the SSL cert
620
		if ($this->ssl_verification)
621
		{
622
			curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, true);
623
			curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, 2);
624
		}
625
		else
626
		{
627
			curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, false);
628
			curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, false);
629
		}
630
631
		// chmod the file as 0755
632
		if ($this->cacert_location === true)
633
		{
634
			curl_setopt($curl_handle, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');
635
		}
636
		elseif (is_string($this->cacert_location))
0 ignored issues
show
introduced by
The condition is_string($this->cacert_location) is always false.
Loading history...
637
		{
638
			curl_setopt($curl_handle, CURLOPT_CAINFO, $this->cacert_location);
639
		}
640
641
		// Debug mode
642
		if ($this->debug_mode)
643
		{
644
			curl_setopt($curl_handle, CURLOPT_VERBOSE, true);
645
		}
646
647
		// Handle open_basedir & safe mode
648
		if (!ini_get('safe_mode') && !ini_get('open_basedir'))
649
		{
650
			curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, FALSE);
651
		}
652
653
		// Enable a proxy connection if requested.
654
		if ($this->proxy)
655
		{
656
			curl_setopt($curl_handle, CURLOPT_HTTPPROXYTUNNEL, true);
657
658
			$host = $this->proxy['host'];
659
			$host .= ($this->proxy['port']) ? ':' . $this->proxy['port'] : '';
660
			curl_setopt($curl_handle, CURLOPT_PROXY, $host);
661
662
			if (isset($this->proxy['user']) && isset($this->proxy['pass']))
663
			{
664
				curl_setopt($curl_handle, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']);
665
			}
666
		}
667
668
		// Set credentials for HTTP Basic/Digest Authentication.
669
		if ($this->username && $this->password)
670
		{
671
			curl_setopt($curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
672
			curl_setopt($curl_handle, CURLOPT_USERPWD, $this->username . ':' . $this->password);
673
		}
674
675
		// Handle the encoding if we can.
676
		if (extension_loaded('zlib'))
677
		{
678
			curl_setopt($curl_handle, CURLOPT_ENCODING, '');
679
		}
680
681
		// Process custom headers
682
		if (isset($this->request_headers) && count($this->request_headers))
683
		{
684
			$temp_headers = array();
685
686
			foreach ($this->request_headers as $k => $v)
687
			{
688
				$temp_headers[] = $k . ': ' . $v;
689
			}
690
691
			curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $temp_headers);
692
		}
693
694
		switch ($this->method)
695
		{
696
			case self::HTTP_PUT:
697
				//unset($this->read_stream);
698
				curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, 'PUT');
699
				if (isset($this->read_stream))
700
				{
701
					if (!isset($this->read_stream_size) || $this->read_stream_size < 0)
702
					{
703
						throw new RequestCore_Exception('The stream size for the streaming upload cannot be determined.');
704
					}
705
706
					curl_setopt($curl_handle, CURLOPT_INFILESIZE, $this->read_stream_size);
707
					curl_setopt($curl_handle, CURLOPT_UPLOAD, true);
708
				}
709
				else
710
				{
711
					curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);
712
				}
713
				break;
714
715
			case self::HTTP_POST:
716
				curl_setopt($curl_handle, CURLOPT_POST, true);
717
				curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);
718
				break;
719
720
			case self::HTTP_HEAD:
721
				curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, self::HTTP_HEAD);
722
				curl_setopt($curl_handle, CURLOPT_NOBODY, 1);
723
				break;
724
725
			default: // Assumed GET
726
				curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, $this->method);
727
				if (isset($this->write_stream))
728
				{
729
					curl_setopt($curl_handle, CURLOPT_WRITEFUNCTION, array($this, 'streaming_write_callback'));
730
					curl_setopt($curl_handle, CURLOPT_HEADER, false);
731
				}
732
				else
733
				{
734
					curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);
735
				}
736
				break;
737
		}
738
739
		// Merge in the CURLOPTs
740
		if (isset($this->curlopts) && sizeof($this->curlopts) > 0)
741
		{
742
			foreach ($this->curlopts as $k => $v)
743
			{
744
				curl_setopt($curl_handle, $k, $v);
745
			}
746
		}
747
748
		return $curl_handle;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $curl_handle also could return the type CurlHandle which is incompatible with the documented return type resource.
Loading history...
749
	}
750
751
	/**
752
	 * Take the post-processed cURL data and break it down into useful header/body/info chunks. Uses the
753
	 * data stored in the `curl_handle` and `response` properties unless replacement data is passed in via
754
	 * parameters.
755
	 *
756
	 * @param resource $curl_handle (Optional) The reference to the already executed cURL request.
757
	 * @param string $response (Optional) The actual response content itself that needs to be parsed.
758
	 * @return ResponseCore A <ResponseCore> object containing a parsed HTTP response.
759
	 */
760
	public function process_response($curl_handle = null, $response = null)
761
	{
762
		// Accept a custom one if it's passed.
763
		if ($curl_handle && $response)
0 ignored issues
show
introduced by
$curl_handle is of type null|resource, thus it always evaluated to false.
Loading history...
764
		{
765
			$this->curl_handle = $curl_handle;
766
			$this->response = $response;
767
		}
768
769
		// As long as this came back as a valid resource...
770
		if (is_resource($this->curl_handle))
771
		{
772
			// Determine what's what.
773
			$header_size = curl_getinfo($this->curl_handle, CURLINFO_HEADER_SIZE);
774
			$this->response_headers = substr($this->response, 0, $header_size);
775
			$this->response_body = substr($this->response, $header_size);
776
			$this->response_code = curl_getinfo($this->curl_handle, CURLINFO_HTTP_CODE);
777
			$this->response_info = curl_getinfo($this->curl_handle);
778
779
			// Parse out the headers
780
			$this->response_headers = explode("\r\n\r\n", trim($this->response_headers));
781
			$this->response_headers = array_pop($this->response_headers);
782
			$this->response_headers = explode("\r\n", $this->response_headers);
783
			array_shift($this->response_headers);
784
785
			// Loop through and split up the headers.
786
			$header_assoc = array();
787
			foreach ($this->response_headers as $header)
788
			{
789
				$kv = explode(': ', $header);
790
				$header_assoc[strtolower($kv[0])] = isset($kv[1])?$kv[1]:'';
791
			}
792
793
			// Reset the headers to the appropriate property.
794
			$this->response_headers = $header_assoc;
795
			$this->response_headers['_info'] = $this->response_info;
796
			$this->response_headers['_info']['method'] = $this->method;
797
798
			if ($curl_handle && $response)
0 ignored issues
show
introduced by
$curl_handle is of type null|resource, thus it always evaluated to false.
Loading history...
799
			{
800
				return new $this->response_class($this->response_headers, $this->response_body, $this->response_code, $this->curl_handle);
801
			}
802
		}
803
804
		// Return false
805
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type ResponseCore.
Loading history...
806
	}
807
808
	/**
809
	 * Sends the request, calling necessary utility functions to update built-in properties.
810
	 *
811
	 * @param boolean $parse (Optional) Whether to parse the response with ResponseCore or not.
812
	 * @return string The resulting unparsed data from the request.
813
	 */
814
	public function send_request($parse = false)
815
	{
816
		set_time_limit(0);
817
818
		$curl_handle = $this->prep_request();
819
		$this->response = curl_exec($curl_handle);
820
821
		if ($this->response === false)
822
		{
823
			throw new RequestCore_Exception('cURL resource: ' . (string) $curl_handle . '; cURL error: ' . curl_error($curl_handle) . ' (' . curl_errno($curl_handle) . ')');
824
		}
825
826
		$parsed_response = $this->process_response($curl_handle, $this->response);
0 ignored issues
show
Bug introduced by
It seems like $this->response can also be of type true; however, parameter $response of RequestCore::process_response() 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

826
		$parsed_response = $this->process_response($curl_handle, /** @scrutinizer ignore-type */ $this->response);
Loading history...
827
828
		curl_close($curl_handle);
829
830
		if ($parse)
831
		{
832
			return $parsed_response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $parsed_response returns the type ResponseCore which is incompatible with the documented return type string.
Loading history...
833
		}
834
835
		return $this->response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->response also could return the type true which is incompatible with the documented return type string.
Loading history...
836
	}
837
838
	/**
839
	 * Sends the request using <php:curl_multi_exec()>, enabling parallel requests. Uses the "rolling" method.
840
	 *
841
	 * @param array $handles (Required) An indexed array of cURL handles to process simultaneously.
842
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
843
	 * 	<li><code>callback</code> - <code>string|array</code> - Optional - The string name of a function to pass the response data to. If this is a method, pass an array where the <code>[0]</code> index is the class and the <code>[1]</code> index is the method name.</li>
844
	 * 	<li><code>limit</code> - <code>integer</code> - Optional - The number of simultaneous requests to make. This can be useful for scaling around slow server responses. Defaults to trusting cURLs judgement as to how many to use.</li></ul>
845
	 * @return array Post-processed cURL responses.
846
	 */
847
	public function send_multi_request($handles, $opt = null)
848
	{
849
		set_time_limit(0);
850
851
		// Skip everything if there are no handles to process.
852
		if (count($handles) === 0) return array();
853
854
		if (!$opt) $opt = array();
855
856
		// Initialize any missing options
857
		$limit = isset($opt['limit']) ? $opt['limit'] : -1;
858
859
		// Initialize
860
		$handle_list = $handles;
861
		$http = new $this->request_class();
862
		$multi_handle = curl_multi_init();
863
		$handles_post = array();
864
		$added = count($handles);
865
		$last_handle = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $last_handle is dead and can be removed.
Loading history...
866
		$count = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $count is dead and can be removed.
Loading history...
867
		$i = 0;
868
869
		// Loop through the cURL handles and add as many as it set by the limit parameter.
870
		while ($i < $added)
871
		{
872
			if ($limit > 0 && $i >= $limit) break;
873
			curl_multi_add_handle($multi_handle, array_shift($handles));
0 ignored issues
show
Bug introduced by
It seems like $multi_handle can also be of type true; however, parameter $multi_handle of curl_multi_add_handle() does only seem to accept CurlMultiHandle|resource, 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

873
			curl_multi_add_handle(/** @scrutinizer ignore-type */ $multi_handle, array_shift($handles));
Loading history...
874
			$i++;
875
		}
876
877
		do
878
		{
879
			$active = false;
880
881
			// Start executing and wait for a response.
882
			while (($status = curl_multi_exec($multi_handle, $active)) === CURLM_CALL_MULTI_PERFORM)
0 ignored issues
show
Unused Code introduced by
The assignment to $status is dead and can be removed.
Loading history...
Bug introduced by
It seems like $multi_handle can also be of type true; however, parameter $multi_handle of curl_multi_exec() does only seem to accept CurlMultiHandle|resource, 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

882
			while (($status = curl_multi_exec(/** @scrutinizer ignore-type */ $multi_handle, $active)) === CURLM_CALL_MULTI_PERFORM)
Loading history...
883
			{
884
				// Start looking for possible responses immediately when we have to add more handles
885
				if (count($handles) > 0) break;
886
			}
887
888
			// Figure out which requests finished.
889
			$to_process = array();
890
891
			while ($done = curl_multi_info_read($multi_handle))
0 ignored issues
show
Bug introduced by
It seems like $multi_handle can also be of type true; however, parameter $multi_handle of curl_multi_info_read() does only seem to accept CurlMultiHandle|resource, 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

891
			while ($done = curl_multi_info_read(/** @scrutinizer ignore-type */ $multi_handle))
Loading history...
892
			{
893
				// Since curl_errno() isn't reliable for handles that were in multirequests, we check the 'result' of the info read, which contains the curl error number, (listed here http://curl.haxx.se/libcurl/c/libcurl-errors.html )
894
				if ($done['result'] > 0)
895
				{
896
					throw new RequestCore_Exception('cURL resource: ' . (string) $done['handle'] . '; cURL error: ' . curl_error($done['handle']) . ' (' . $done['result'] . ')');
897
				}
898
899
				// Because curl_multi_info_read() might return more than one message about a request, we check to see if this request is already in our array of completed requests
900
				elseif (!isset($to_process[(int) $done['handle']]))
901
				{
902
					$to_process[(int) $done['handle']] = $done;
903
				}
904
			}
905
906
			// Actually deal with the request
907
			foreach ($to_process as $pkey => $done)
908
			{
909
				$response = $http->process_response($done['handle'], curl_multi_getcontent($done['handle']));
910
				$key = array_search($done['handle'], $handle_list, true);
911
				$handles_post[$key] = $response;
912
913
				if (count($handles) > 0)
914
				{
915
					curl_multi_add_handle($multi_handle, array_shift($handles));
916
				}
917
918
				curl_multi_remove_handle($multi_handle, $done['handle']);
0 ignored issues
show
Bug introduced by
It seems like $multi_handle can also be of type true; however, parameter $multi_handle of curl_multi_remove_handle() does only seem to accept CurlMultiHandle|resource, 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

918
				curl_multi_remove_handle(/** @scrutinizer ignore-type */ $multi_handle, $done['handle']);
Loading history...
919
				curl_close($done['handle']);
920
			}
921
		}
922
		while ($active || count($handles_post) < $added);
923
924
		curl_multi_close($multi_handle);
0 ignored issues
show
Bug introduced by
It seems like $multi_handle can also be of type true; however, parameter $multi_handle of curl_multi_close() does only seem to accept CurlMultiHandle|resource, 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

924
		curl_multi_close(/** @scrutinizer ignore-type */ $multi_handle);
Loading history...
925
926
		ksort($handles_post, SORT_NUMERIC);
927
		return $handles_post;
928
	}
929
930
931
	/*%******************************************************************************************%*/
932
	// RESPONSE METHODS
933
934
	/**
935
	 * Get the HTTP response headers from the request.
936
	 *
937
	 * @param string $header (Optional) A specific header value to return. Defaults to all headers.
938
	 * @return string|array All or selected header values.
939
	 */
940
	public function get_response_header($header = null)
941
	{
942
		if ($header)
943
		{
944
			return $this->response_headers[strtolower($header)];
945
		}
946
		return $this->response_headers;
947
	}
948
949
	/**
950
	 * Get the HTTP response body from the request.
951
	 *
952
	 * @return string The response body.
953
	 */
954
	public function get_response_body()
955
	{
956
		return $this->response_body;
957
	}
958
959
	/**
960
	 * Get the HTTP response code from the request.
961
	 *
962
	 * @return string The HTTP response code.
963
	 */
964
	public function get_response_code()
965
	{
966
		return $this->response_code;
967
	}
968
}
969
970
971
/**
972
 * Container for all response-related methods.
973
 */
974
class ResponseCore
975
{
976
	/**
977
	 * Stores the HTTP header information.
978
	 */
979
	public $header;
980
981
	/**
982
	 * Stores the SimpleXML response.
983
	 */
984
	public $body;
985
986
	/**
987
	 * Stores the HTTP response code.
988
	 */
989
	public $status;
990
991
	/**
992
	 * Constructs a new instance of this class.
993
	 *
994
	 * @param array $header (Required) Associative array of HTTP headers (typically returned by <RequestCore::get_response_header()>).
995
	 * @param string $body (Required) XML-formatted response from AWS.
996
	 * @param integer $status (Optional) HTTP response status code from the request.
997
	 * @return object Contains an <php:array> `header` property (HTTP headers as an associative array), a <php:SimpleXMLElement> or <php:string> `body` property, and an <php:integer> `status` code.
998
	 */
999
	public function __construct($header, $body, $status = null)
1000
	{
1001
		$this->header = $header;
1002
		$this->body = $body;
1003
		$this->status = $status;
1004
1005
		return $this;
1006
	}
1007
1008
	/**
1009
	 * Did we receive the status code we expected?
1010
	 *
1011
	 * @param integer|array $codes (Optional) The status code(s) to expect. Pass an <php:integer> for a single acceptable value, or an <php:array> of integers for multiple acceptable values.
1012
	 * @return boolean Whether we received the expected status code or not.
1013
	 */
1014
	public function isOK($codes = array(200, 201, 204, 206))
1015
	{
1016
		if (is_array($codes))
1017
		{
1018
			return in_array($this->status, $codes);
1019
		}
1020
1021
		return $this->status === $codes;
1022
	}
1023
}
1024
1025
/**
1026
 * Default RequestCore Exception.
1027
 */
1028
class RequestCore_Exception extends Exception {}
1029