HTTP::get()   D
last analyzed

Complexity

Conditions 9
Paths 12

Size

Total Lines 32
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 16
nc 12
nop 4
dl 0
loc 32
rs 4.909
c 0
b 0
f 0
1
<?php
2
3
/*
4
This file is part of Peachy MediaWiki Bot API
5
6
Peachy is free software: you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation, either version 3 of the License, or
9
(at your option) any later version.
10
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
GNU General Public License for more details.
15
16
You should have received a copy of the GNU General Public License
17
along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
*/
19
20
/**
21
 * @file
22
 * HTTP object
23
 * Stores all cURL functions
24
 */
25
26
/**
27
 * HTTP Class, stores cURL functions
28
 */
29
class HTTP {
30
31
	/**
32
	 * Curl object
33
	 *
34
	 * @var resource a cURL handle
35
	 * @access private
36
	 */
37
	private $curl_instance;
38
39
	/**
40
	 * Hash to use for cookies
41
	 *
42
	 * @var string
43
	 * @access private
44
	 */
45
	private $cookie_hash;
46
47
	/**
48
	 * Whether or not to enable GET:, POST:, and DLOAD: messages being sent to the terminal.
49
	 *
50
	 * @var bool
51
	 * @access private
52
	 */
53
	private $echo;
54
55
	/**
56
	 * Useragent
57
	 *
58
	 * @var mixed
59
	 * @access private
60
	 */
61
	private $user_agent;
62
63
	/**
64
	 * Temporary file where cookies are stored
65
	 *
66
	 * @var mixed
67
	 * @access private
68
	 */
69
	private $cookie_jar;
70
71
	/**
72
	 * @var string|null
73
	 */
74
	private $lastHeader = null;
75
76
	/**
77
	 * Construction method for the HTTP class
78
	 *
79
	 * @access public
80
	 *
81
	 * @param bool $echo Whether or not to enable GET:, POST:, and DLOAD: messages being sent to the terminal. Default false;
82
	 *
83
	 * @note please consider using HTTP::getDefaultInstance() instead
84
	 *
85
	 * @throws RuntimeException
86
	 * @throws DependencyError
87
	 *
88
	 * @return HTTP
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...
89
	 */
90
	public function __construct($echo = false)
91
	{
92
		if( !function_exists( 'curl_init' ) ) {
93
			throw new DependencyError( "cURL", "http://us2.php.net/manual/en/curl.requirements.php" );
0 ignored issues
show
Documentation introduced by
'http://us2.php.net/manu.../curl.requirements.php' is of type string, but the function expects a boolean.

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...
94
		}
95
96
		$this->echo = $echo;
97
		$this->curl_instance = curl_init();
98
		if( $this->curl_instance === false ) {
99
			throw new RuntimeException( 'Failed to initialize curl' );
100
		}
101
		$this->cookie_hash = md5( time() . '-' . rand( 0, 999 ) );
102
		$this->cookie_jar = sys_get_temp_dir() . 'peachy.cookies.' . $this->cookie_hash . '.dat';
103
104
		$userAgent = 'Peachy MediaWiki Bot API';
105
		if( defined( 'PEACHYVERSION' ) ) $userAgent .= ' Version ' . PEACHYVERSION;
106
		$this->setUserAgent( $userAgent );
107
108
		Hooks::runHook( 'HTTPNewCURLInstance', array( &$this, &$echo ) );
109
110
		$this->setCookieJar( $this->cookie_jar );
111
112
		curl_setopt( $this->curl_instance, CURLOPT_MAXCONNECTS, 100 );
113
		curl_setopt( $this->curl_instance, CURLOPT_MAXREDIRS, 10 );
114
		$this->setCurlHeaders();
115
		curl_setopt( $this->curl_instance, CURLOPT_ENCODING, 'gzip' );
116
		curl_setopt( $this->curl_instance, CURLOPT_RETURNTRANSFER, 1 );
117
		curl_setopt( $this->curl_instance, CURLOPT_HEADER, 1 );
118
		curl_setopt( $this->curl_instance, CURLOPT_TIMEOUT, 10 );
119
		curl_setopt( $this->curl_instance, CURLOPT_CONNECTTIMEOUT, 10 );
120
121
		global $pgProxy;
122
		if( isset( $pgProxy ) && count( $pgProxy ) ) {
123
			curl_setopt( $this->curl_instance, CURLOPT_PROXY, $pgProxy['addr'] );
124
			if( isset( $pgProxy['type'] ) ) {
125
				curl_setopt( $this->curl_instance, CURLOPT_PROXYTYPE, $pgProxy['type'] );
126
			}
127
			if( isset( $pgProxy['userpass'] ) ) {
128
				curl_setopt( $this->curl_instance, CURLOPT_PROXYUSERPWD, $pgProxy['userpass'] );
129
			}
130
			if( isset( $pgProxy['port'] ) ) {
131
				curl_setopt( $this->curl_instance, CURLOPT_PROXYPORT, $pgProxy['port'] );
132
			}
133
		}
134
	}
135
136
	/**
137
	 * @param array $extraHeaders
138
     */
139
	private function setCurlHeaders( $extraHeaders = array() ) {
140
		curl_setopt( $this->curl_instance, CURLOPT_HTTPHEADER, array_merge( array( 'Expect:' ), $extraHeaders ) );
141
	}
142
143
	/**
144
	 * @param boolean $verifyssl
145
	 */
146
	private function setVerifySSL( $verifyssl = null ) {
147
		if( is_null( $verifyssl ) ) {
148
			global $verifyssl;
149
		}
150
		if( !$verifyssl ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $verifyssl of type null|boolean is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
151
			curl_setopt( $this->curl_instance, CURLOPT_SSL_VERIFYPEER, false );
152
			curl_setopt( $this->curl_instance, CURLOPT_SSL_VERIFYHOST, 0 );
153
		} else {
154
			curl_setopt( $this->curl_instance, CURLOPT_SSL_VERIFYPEER, true );
155
			//support for value of 1 will be removed in cURL 7.28.1
156
			curl_setopt( $this->curl_instance, CURLOPT_SSL_VERIFYHOST, 2 );
157
		}
158
	}
159
160
	/**
161
	 * @param string $cookie_file
162
	 */
163
	public function setCookieJar($cookie_file)
164
	{
165
		$this->cookie_jar = $cookie_file;
166
167
		Hooks::runHook( 'HTTPSetCookieJar', array( &$cookie_file ) );
168
169
		curl_setopt( $this->curl_instance, CURLOPT_COOKIEJAR, $cookie_file );
170
		curl_setopt( $this->curl_instance, CURLOPT_COOKIEFILE, $cookie_file );
171
	}
172
173
	/**
174
	 * @param null $user_agent
175
	 * @throws BadEntryError
176
	 * @throws HookError
177
     */
178
	public function setUserAgent($user_agent = null)
179
	{
180
		$this->user_agent = $user_agent;
181
182
		Hooks::runHook( 'HTTPSetUserAgent', array( &$user_agent ) );
183
184
		curl_setopt( $this->curl_instance, CURLOPT_USERAGENT, $user_agent );
185
	}
186
187
	/**
188
	 * @return string|bool Data. False on failure.
189
	 * @throws CURLError
190
	 */
191
	private function doCurlExecWithRetrys() {
192
		$data = false;
193
		for( $i = 0; $i <= 20; $i++ ){
194
			try{
195
				$response = curl_exec( $this->curl_instance );
196
				$header_size = curl_getinfo( $this->curl_instance, CURLINFO_HEADER_SIZE );
197
				$this->lastHeader = substr( $response, 0, $header_size );
198
				$data = substr( $response, $header_size );
199
			} catch( Exception $e ){
200
				if( curl_errno( $this->curl_instance ) != 0 ) {
201
					throw new CURLError( curl_errno( $this->curl_instance ), curl_error( $this->curl_instance ) );
202
				}
203
				if( $i == 20 ) {
204
					pecho( "Warning: A CURL error occurred.  Attempted 20 times.  Terminating attempts.", PECHO_WARN );
205
					return false;
206
				} else {
207
					pecho( "Warning: A CURL error occurred.  Details can be found in the PHP error log.  Retrying...", PECHO_WARN );
208
				}
209
				continue;
210
			}
211
			if( !is_null( $data ) && $data !== false ) {
212
				break;
213
			}
214
		}
215
		return $data;
216
	}
217
218
	/**
219
	 * Get an url with HTTP GET
220
	 *
221
	 * @access public
222
	 *
223
	 * @param string $url URL to get
224
	 * @param array|null $data Array of data to pass. Gets transformed into the URL inside the function. Default null.
225
	 * @param array $headers Array of headers to pass to curl
226
	 * @param bool $verifyssl override for the global verifyssl value
227
	 *
228
	 * @return bool|string Result
229
	 */
230
	public function get( $url, $data = null, $headers = array(), $verifyssl = null ) {
231
		global $argv, $displayGetOutData;
232
233
		if( is_string( $headers ) ) curl_setopt( $this->curl_instance, CURLOPT_HTTPHEADER, array( $headers ) );
234
		else $this->setCurlHeaders( $headers );
235
		$this->setVerifySSL( $verifyssl );
236
237
		curl_setopt( $this->curl_instance, CURLOPT_FOLLOWLOCATION, 1 );
238
		curl_setopt( $this->curl_instance, CURLOPT_HTTPGET, 1 );
239
		curl_setopt( $this->curl_instance, CURLOPT_POST, 0 );
240
241
		/*if( !is_null( $this->use_cookie ) ) {
242
			curl_setopt($this->curl_instance,CURLOPT_COOKIE, $this->use_cookie);
243
		}*/
244
245
		if( !is_null( $data ) && is_array( $data ) && !empty( $data ) ) {
246
			$url .= '?' . http_build_query( $data );
247
		}
248
249
		curl_setopt( $this->curl_instance, CURLOPT_URL, $url );
250
251
		if( ( !is_null( $argv ) && in_array( 'peachyecho', $argv ) ) || $this->echo ) {
252
			if( $displayGetOutData ) {
253
				pecho( "GET: $url\n", PECHO_NORMAL );
254
			}
255
		}
256
257
		Hooks::runHook( 'HTTPGet', array( &$this, &$url, &$data ) );
258
259
		return $this->doCurlExecWithRetrys();
260
261
	}
262
263
	/**
264
	 * Returns the HTTP code of the last request
265
	 *
266
	 * @access public
267
	 * @return int HTTP code
268
	 */
269
	public function get_HTTP_code()
270
	{
271
		$ci = curl_getinfo( $this->curl_instance );
272
		return $ci['http_code'];
273
	}
274
275
	/**
276
	 * Sends data via HTTP POST
277
	 *
278
	 * @access public
279
	 *
280
	 * @param string $url URL to send
281
	 * @param array $data Array of data to pass.
282
	 * @param array $headers Array of headers to pass to curl
283
	 *
284
	 * @param bool|null $verifyssl override for global verifyssl value
285
	 *
286
	 * @return bool|string Result
287
	 */
288
	public function post($url, $data, $headers = array(), $verifyssl = null)
289
	{
290
		global $argv, $displayPostOutData;
291
292
		if( is_string( $headers ) ) curl_setopt( $this->curl_instance, CURLOPT_HTTPHEADER, array( $headers ) );
293
		else $this->setCurlHeaders( $headers );
294
		$this->setVerifySSL( $verifyssl );
295
296
		curl_setopt( $this->curl_instance, CURLOPT_FOLLOWLOCATION, 0 );
297
		curl_setopt( $this->curl_instance, CURLOPT_HTTPGET, 0 );
298
		curl_setopt( $this->curl_instance, CURLOPT_POST, 1 );
299
		curl_setopt( $this->curl_instance, CURLOPT_POSTFIELDS, $data );
300
301
		/*if( !is_null( $this->use_cookie ) ) {
302
			curl_setopt($this->curl_instance,CURLOPT_COOKIE, $this->use_cookie);
303
		}*/
304
305
		curl_setopt( $this->curl_instance, CURLOPT_URL, $url );
306
307
		if( ( !is_null( $argv ) && in_array( 'peachyecho', $argv ) ) || $this->echo ) {
308
			if( $displayPostOutData ) {
309
				pecho( "POST: $url\n", PECHO_NORMAL );
310
			}
311
		}
312
313
		Hooks::runHook( 'HTTPPost', array( &$this, &$url, &$data ) );
314
315
		return $this->doCurlExecWithRetrys();
316
	}
317
318
	/**
319
	 * Downloads an URL to the local disk
320
	 *
321
	 * @access public
322
	 *
323
	 * @param string $url URL to get
324
	 * @param string $local Local filename to download to
325
	 * @param array $headers Array of headers to pass to curl
326
	 * @param bool|null $verifyssl
327
	 *
328
	 * @return bool
329
	 */
330
	function download( $url, $local, $headers = array(), $verifyssl = null ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Comprehensibility Best Practice introduced by
It is recommend to declare an explicit visibility for download.

Generally, we recommend to declare visibility for all methods in your source code. This has the advantage of clearly communication to other developers, and also yourself, how this method should be consumed.

If you are not sure which visibility to choose, it is a good idea to start with the most restrictive visibility, and then raise visibility as needed, i.e. start with private, and only raise it to protected if a sub-class needs to have access, or public if an external class needs access.

Loading history...
331
		global $argv;
332
333
		$out = fopen( $local, 'wb' );
334
335
		if( is_string( $headers ) ) curl_setopt( $this->curl_instance, CURLOPT_HTTPHEADER, array( $headers ) );
336
		else $this->setCurlHeaders( $headers );
337
		$this->setVerifySSL( $verifyssl );
338
339
		// curl_setopt($this->curl_instance, CURLOPT_FILE, $out);
340
		curl_setopt( $this->curl_instance, CURLOPT_HTTPGET, 1 );
341
		curl_setopt( $this->curl_instance, CURLOPT_POST, 0 );
342
		curl_setopt( $this->curl_instance, CURLOPT_URL, $url );
343
		curl_setopt( $this->curl_instance, CURLOPT_HEADER, 0 );
344
345
		if( ( !is_null( $argv ) && in_array( 'peachyecho', $argv ) ) || $this->echo ) {
346
			pecho( "DLOAD: $url\n", PECHO_NORMAL );
347
		}
348
349
		Hooks::runHook( 'HTTPDownload', array( &$this, &$url, &$local ) );
350
351
		fwrite( $out, $this->doCurlExecWithRetrys() );
352
		fclose( $out );
353
354
		return true;
355
356
	}
357
358
	/**
359
	 * Gets the Header for the last request made
360
	 * @return null|string
361
	 */
362
	public function getLastHeader() {
363
		return $this->lastHeader;
364
	}
365
366
	/**
367
	 * Destructor, deletes cookies and closes cURL class
368
	 *
369
	 * @access public
370
	 * @return void
371
	 */
372
	public function __destruct()
373
	{
374
		Hooks::runHook( 'HTTPClose', array( &$this ) );
375
376
		curl_close( $this->curl_instance );
377
378
		//@unlink($this->cookie_jar);
379
	}
380
381
	/**
382
	 * The below allows us to only have one instance of this class
383
	 */
384
	private static $defaultInstance = null;
385
	private static $defaultInstanceWithEcho = null;
386
387
	/**
388
	 * @param bool|false $echo
389
	 * @return HTTP|null
390
     */
391
	public static function getDefaultInstance($echo = false)
392
	{
393
		if( $echo ) {
394
			if( is_null( self::$defaultInstanceWithEcho ) ) {
395
				self::$defaultInstanceWithEcho = new Http( $echo );
396
			}
397
			return self::$defaultInstanceWithEcho;
398
		} else {
399
			if( is_null( self::$defaultInstance ) ) {
400
				self::$defaultInstance = new Http( $echo );
401
			}
402
			return self::$defaultInstance;
403
		}
404
	}
405
406
}