GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( bb6361...a2a740 )
by Brad
04:28
created

Freemius_Api_WordPress::MakeStaticRequest()   F

Complexity

Conditions 24
Paths 7920

Size

Total Lines 131
Code Lines 68

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 24
eloc 68
nc 7920
nop 6
dl 0
loc 131
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 21 and the first side effect is on line 40.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
	/**
3
	 * Copyright 2016 Freemius, Inc.
4
	 *
5
	 * Licensed under the GPL v2 (the "License"); you may
6
	 * not use this file except in compliance with the License. You may obtain
7
	 * a copy of the License at
8
	 *
9
	 *     http://choosealicense.com/licenses/gpl-v2/
10
	 *
11
	 * Unless required by applicable law or agreed to in writing, software
12
	 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
	 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
	 * License for the specific language governing permissions and limitations
15
	 * under the License.
16
	 */
17
18
	require_once dirname( __FILE__ ) . '/FreemiusBase.php';
19
20
	if ( ! defined( 'FS_SDK__USER_AGENT' ) ) {
21
		define( 'FS_SDK__USER_AGENT', 'fs-php-' . Freemius_Api_Base::VERSION );
22
	}
23
24
	if ( ! defined( 'FS_SDK__SIMULATE_NO_CURL' ) ) {
25
		define( 'FS_SDK__SIMULATE_NO_CURL', false );
26
	}
27
28
	if ( ! defined( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE' ) ) {
29
		define( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE', false );
30
	}
31
32
	if ( ! defined( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL' ) ) {
33
		define( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL', false );
34
	}
35
36
	if ( ! defined( 'FS_SDK__HAS_CURL' ) ) {
37
		if ( FS_SDK__SIMULATE_NO_CURL ) {
38
			define( 'FS_SDK__HAS_CURL', false );
39
		} else {
40
			$curl_required_methods = array(
41
				'curl_version',
42
				'curl_exec',
43
				'curl_init',
44
				'curl_close',
45
				'curl_setopt',
46
				'curl_setopt_array',
47
				'curl_error',
48
			);
49
50
			$has_curl = true;
51
			foreach ( $curl_required_methods as $m ) {
52
				if ( ! function_exists( $m ) ) {
53
					$has_curl = false;
54
					break;
55
				}
56
			}
57
58
			define( 'FS_SDK__HAS_CURL', $has_curl );
59
		}
60
	}
61
62
	$curl_version = FS_SDK__HAS_CURL ?
63
		curl_version() :
64
		array( 'version' => '7.37' );
65
66
	if ( ! defined( 'FS_API__PROTOCOL' ) ) {
67
		define( 'FS_API__PROTOCOL', version_compare( $curl_version['version'], '7.37', '>=' ) ? 'https' : 'http' );
68
	}
69
70
	if ( ! defined( 'FS_API__LOGGER_ON' ) ) {
71
		define( 'FS_API__LOGGER_ON', false );
72
	}
73
74
	if ( ! defined( 'FS_API__ADDRESS' ) ) {
75
		define( 'FS_API__ADDRESS', '://api.freemius.com' );
76
	}
77
	if ( ! defined( 'FS_API__SANDBOX_ADDRESS' ) ) {
78
		define( 'FS_API__SANDBOX_ADDRESS', '://sandbox-api.freemius.com' );
79
	}
80
81
	if ( class_exists( 'Freemius_Api_WordPress' ) ) {
82
		return;
83
	}
84
85
	class Freemius_Api_WordPress extends Freemius_Api_Base {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
86
		private static $_logger = array();
87
88
		/**
89
		 * @param string      $pScope   'app', 'developer', 'user' or 'install'.
90
		 * @param number      $pID      Element's id.
91
		 * @param string      $pPublic  Public key.
92
		 * @param string|bool $pSecret  Element's secret key.
93
		 * @param bool        $pSandbox Whether or not to run API in sandbox mode.
94
		 */
95
		public function __construct( $pScope, $pID, $pPublic, $pSecret = false, $pSandbox = false ) {
96
			// If secret key not provided, use public key encryption.
97
			if ( is_bool( $pSecret ) ) {
98
				$pSecret = $pPublic;
99
			}
100
101
			parent::Init( $pScope, $pID, $pPublic, $pSecret, $pSandbox );
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (Init() instead of __construct()). Are you sure this is correct? If so, you might want to change this to $this->Init().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
102
		}
103
104
		public static function GetUrl( $pCanonizedPath = '', $pIsSandbox = false ) {
105
			$address = ( $pIsSandbox ? FS_API__SANDBOX_ADDRESS : FS_API__ADDRESS );
106
107
			if ( ':' === $address[0] ) {
108
				$address = self::$_protocol . $address;
109
			}
110
111
			return $address . $pCanonizedPath;
112
		}
113
114
		#----------------------------------------------------------------------------------
115
		#region Servers Clock Diff
116
		#----------------------------------------------------------------------------------
117
118
		/**
119
		 * @var int Clock diff in seconds between current server to API server.
120
		 */
121
		private static $_clock_diff = 0;
122
123
		/**
124
		 * Set clock diff for all API calls.
125
		 *
126
		 * @since 1.0.3
127
		 *
128
		 * @param $pSeconds
129
		 */
130
		public static function SetClockDiff( $pSeconds ) {
131
			self::$_clock_diff = $pSeconds;
132
		}
133
134
		/**
135
		 * Find clock diff between current server to API server.
136
		 *
137
		 * @since 1.0.2
138
		 * @return int Clock diff in seconds.
139
		 */
140
		public static function FindClockDiff() {
141
			$time = time();
142
			$pong = self::Ping();
143
144
			return ( $time - strtotime( $pong->timestamp ) );
145
		}
146
147
		#endregion
148
149
		/**
150
		 * @var string http or https
151
		 */
152
		private static $_protocol = FS_API__PROTOCOL;
153
154
		/**
155
		 * Set API connection protocol.
156
		 *
157
		 * @since 1.0.4
158
		 */
159
		public static function SetHttp() {
160
			self::$_protocol = 'http';
161
		}
162
163
		/**
164
		 * @since 1.0.4
165
		 *
166
		 * @return bool
167
		 */
168
		public static function IsHttps() {
169
			return ( 'https' === self::$_protocol );
170
		}
171
172
		/**
173
		 * Sign request with the following HTTP headers:
174
		 *      Content-MD5: MD5(HTTP Request body)
175
		 *      Date: Current date (i.e Sat, 14 Feb 2016 20:24:46 +0000)
176
		 *      Authorization: FS {scope_entity_id}:{scope_entity_public_key}:base64encode(sha256(string_to_sign,
177
		 *      {scope_entity_secret_key}))
178
		 *
179
		 * @param string $pResourceUrl
180
		 * @param array  $pWPRemoteArgs
181
		 *
182
		 * @return array
183
		 */
184
		function SignRequest( $pResourceUrl, $pWPRemoteArgs ) {
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...
185
			$auth = $this->GenerateAuthorizationParams(
186
				$pResourceUrl,
187
				$pWPRemoteArgs['method'],
188
				! empty( $pWPRemoteArgs['body'] ) ? $pWPRemoteArgs['body'] : ''
189
			);
190
191
			$pWPRemoteArgs['headers']['Date']          = $auth['date'];
192
			$pWPRemoteArgs['headers']['Authorization'] = $auth['authorization'];
193
194
			if ( ! empty( $auth['content_md5'] ) ) {
195
				$pWPRemoteArgs['headers']['Content-MD5'] = $auth['content_md5'];
196
			}
197
198
			return $pWPRemoteArgs;
199
		}
200
201
		/**
202
		 * Generate Authorization request headers:
203
		 *
204
		 *      Content-MD5: MD5(HTTP Request body)
205
		 *      Date: Current date (i.e Sat, 14 Feb 2016 20:24:46 +0000)
206
		 *      Authorization: FS {scope_entity_id}:{scope_entity_public_key}:base64encode(sha256(string_to_sign,
207
		 *      {scope_entity_secret_key}))
208
		 *
209
		 * @author Vova Feldman
210
		 *
211
		 * @param string $pResourceUrl
212
		 * @param string $pMethod
213
		 * @param string $pPostParams
214
		 *
215
		 * @return array
216
		 * @throws Freemius_Exception
217
		 */
218
		function GenerateAuthorizationParams(
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...
219
			$pResourceUrl,
220
			$pMethod = 'GET',
221
			$pPostParams = ''
222
		) {
223
			$pMethod = strtoupper( $pMethod );
224
225
			$eol          = "\n";
226
			$content_md5  = '';
227
			$content_type = '';
228
			$now          = ( time() - self::$_clock_diff );
229
			$date         = date( 'r', $now );
230
231
			if ( in_array( $pMethod, array( 'POST', 'PUT' ) ) && ! empty( $pPostParams ) ) {
232
				$content_md5  = md5( $pPostParams );
233
				$content_type = 'application/json';
234
			}
235
236
			$string_to_sign = implode( $eol, array(
237
				$pMethod,
238
				$content_md5,
239
				$content_type,
240
				$date,
241
				$pResourceUrl
242
			) );
243
244
			// If secret and public keys are identical, it means that
245
			// the signature uses public key hash encoding.
246
			$auth_type = ( $this->_secret !== $this->_public ) ? 'FS' : 'FSP';
247
248
			$auth = array(
249
				'date'          => $date,
250
				'authorization' => $auth_type . ' ' . $this->_id . ':' .
251
				                   $this->_public . ':' .
252
				                   self::Base64UrlEncode( hash_hmac(
253
					                   'sha256', $string_to_sign, $this->_secret
254
				                   ) )
255
			);
256
257
			if ( ! empty( $content_md5 ) ) {
258
				$auth['content_md5'] = $content_md5;
259
			}
260
261
			return $auth;
262
		}
263
264
		/**
265
		 * Get API request URL signed via query string.
266
		 *
267
		 * @param string $pPath
268
		 *
269
		 * @throws Freemius_Exception
270
		 *
271
		 * @return string
272
		 */
273
		function GetSignedUrl( $pPath ) {
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...
274
			$resource     = explode( '?', $this->CanonizePath( $pPath ) );
275
			$pResourceUrl = $resource[0];
276
277
			$auth = $this->GenerateAuthorizationParams( $pResourceUrl );
278
279
			return Freemius_Api_WordPress::GetUrl(
280
				$pResourceUrl . '?' .
281
				( 1 < count( $resource ) && ! empty( $resource[1] ) ? $resource[1] . '&' : '' ) .
282
				http_build_query( array(
283
					'auth_date'     => $auth['date'],
284
					'authorization' => $auth['authorization']
285
				) ), $this->_isSandbox );
286
		}
287
288
		/**
289
		 * @author Vova Feldman
290
		 *
291
		 * @param string $pUrl
292
		 * @param array  $pWPRemoteArgs
293
		 *
294
		 * @return mixed
295
		 */
296
		private static function ExecuteRequest( $pUrl, &$pWPRemoteArgs ) {
297
			$start = microtime( true );
298
299
			$response = wp_remote_request( $pUrl, $pWPRemoteArgs );
300
301
			if ( FS_API__LOGGER_ON ) {
302
				$end = microtime( true );
303
304
				$has_body      = ( isset( $pWPRemoteArgs['body'] ) && ! empty( $pWPRemoteArgs['body'] ) );
305
				$is_http_error = is_wp_error( $response );
306
307
				self::$_logger[] = array(
308
					'id'        => count( self::$_logger ),
309
					'start'     => $start,
310
					'end'       => $end,
311
					'total'     => ( $end - $start ),
312
					'method'    => $pWPRemoteArgs['method'],
313
					'path'      => $pUrl,
314
					'body'      => $has_body ? $pWPRemoteArgs['body'] : null,
315
					'result'    => ! $is_http_error ?
316
						$response['body'] :
317
						json_encode( $response->get_error_messages() ),
318
					'code'      => ! $is_http_error ? $response['response']['code'] : null,
319
					'backtrace' => debug_backtrace(),
320
				);
321
			}
322
323
			return $response;
324
		}
325
326
		/**
327
		 * @return array
328
		 */
329
		static function GetLogger() {
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...
330
			return self::$_logger;
331
		}
332
333
		/**
334
		 * @param string        $pCanonizedPath
335
		 * @param string        $pMethod
336
		 * @param array         $pParams
337
		 * @param null|array    $pWPRemoteArgs
338
		 * @param bool          $pIsSandbox
339
		 * @param null|callable $pBeforeExecutionFunction
340
		 *
341
		 * @return object[]|object|null
342
		 *
343
		 * @throws \Freemius_Exception
344
		 */
345
		private static function MakeStaticRequest(
346
			$pCanonizedPath,
347
			$pMethod = 'GET',
348
			$pParams = array(),
349
			$pWPRemoteArgs = null,
350
			$pIsSandbox = false,
351
			$pBeforeExecutionFunction = null
352
		) {
353
			// Connectivity errors simulation.
354
			if ( FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE ) {
355
				self::ThrowCloudFlareDDoSException();
356
			} else if ( FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL ) {
357
				self::ThrowSquidAclException();
358
			}
359
360
			if ( empty( $pWPRemoteArgs ) ) {
361
				$user_agent = 'Freemius/WordPress-SDK/' . Freemius_Api_Base::VERSION . '; ' .
362
				              home_url();
363
364
				$pWPRemoteArgs = array(
365
					'method'           => strtoupper( $pMethod ),
366
					'connect_timeout'  => 10,
367
					'timeout'          => 60,
368
					'follow_redirects' => true,
369
					'redirection'      => 5,
370
					'user-agent'       => $user_agent,
371
					'blocking'         => true,
372
				);
373
			}
374
375
			if ( ! isset( $pWPRemoteArgs['headers'] ) ||
376
			     ! is_array( $pWPRemoteArgs['headers'] )
377
			) {
378
				$pWPRemoteArgs['headers'] = array();
379
			}
380
381
			if ( in_array( $pMethod, array( 'POST', 'PUT' ) ) ) {
382
				if ( is_array( $pParams ) && 0 < count( $pParams ) ) {
383
					$pWPRemoteArgs['headers']['Content-type'] = 'application/json';
384
					$pWPRemoteArgs['body']                    = json_encode( $pParams );
385
				}
386
			}
387
388
			$request_url = self::GetUrl( $pCanonizedPath, $pIsSandbox );
389
390
			$resource = explode( '?', $pCanonizedPath );
391
392
			// Disable the 'Expect: 100-continue' behaviour. This causes cURL to wait
393
			// for 2 seconds if the server does not support this header.
394
			$pWPRemoteArgs['headers']['Expect'] = '';
395
396
			if ( 'https' === substr( strtolower( $request_url ), 0, 5 ) ) {
397
				$pWPRemoteArgs['sslverify'] = false;
398
			}
399
400
			if ( false !== $pBeforeExecutionFunction &&
401
			     is_callable( $pBeforeExecutionFunction )
402
			) {
403
				$pWPRemoteArgs = call_user_func( $pBeforeExecutionFunction, $resource[0], $pWPRemoteArgs );
404
			}
405
406
			$result = self::ExecuteRequest( $request_url, $pWPRemoteArgs );
407
408
			if ( is_wp_error( $result ) ) {
409
				/**
410
				 * @var WP_Error $result
411
				 */
412
				if ( self::IsCurlError( $result ) ) {
413
					/**
414
					 * With dual stacked DNS responses, it's possible for a server to
415
					 * have IPv6 enabled but not have IPv6 connectivity.  If this is
416
					 * the case, cURL will try IPv4 first and if that fails, then it will
417
					 * fall back to IPv6 and the error EHOSTUNREACH is returned by the
418
					 * operating system.
419
					 */
420
					$matches = array();
421
					$regex   = '/Failed to connect to ([^:].*): Network is unreachable/';
422
					if ( preg_match( $regex, $result->get_error_message( 'http_request_failed' ), $matches ) ) {
423
						/**
424
						 * Validate IP before calling `inet_pton()` to avoid PHP un-catchable warning.
425
						 * @author Vova Feldman (@svovaf)
426
						 */
427
						if ( filter_var( $matches[1], FILTER_VALIDATE_IP ) ) {
428
							if ( strlen( inet_pton( $matches[1] ) ) === 16 ) {
429
//						    error_log('Invalid IPv6 configuration on server, Please disable or get native IPv6 on your server.');
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
430
								// Hook to an action triggered just before cURL is executed to resolve the IP version to v4.
431
								add_action( 'http_api_curl', 'Freemius_Api_WordPress::CurlResolveToIPv4', 10, 1 );
432
433
								// Re-run request.
434
								$result = self::ExecuteRequest( $request_url, $pWPRemoteArgs );
435
							}
436
						}
437
					}
438
				}
439
440
				if ( is_wp_error( $result ) ) {
441
					self::ThrowWPRemoteException( $result );
442
				}
443
			}
444
445
			$response_body = $result['body'];
446
447
			if ( empty( $response_body ) ) {
448
				return null;
449
			}
450
451
			$decoded = json_decode( $response_body );
452
453
			if ( is_null( $decoded ) ) {
454
				if ( preg_match( '/Please turn JavaScript on/i', $response_body ) &&
455
				     preg_match( '/text\/javascript/', $response_body )
456
				) {
457
					self::ThrowCloudFlareDDoSException( $response_body );
458
				} else if ( preg_match( '/Access control configuration prevents your request from being allowed at this time. Please contact your service provider if you feel this is incorrect./', $response_body ) &&
459
				            preg_match( '/squid/', $response_body )
460
				) {
461
					self::ThrowSquidAclException( $response_body );
462
				} else {
463
					$decoded = (object) array(
464
						'error' => (object) array(
465
							'type'    => 'Unknown',
466
							'message' => $response_body,
467
							'code'    => 'unknown',
468
							'http'    => 402
469
						)
470
					);
471
				}
472
			}
473
474
			return $decoded;
475
		}
476
477
478
		/**
479
		 * Makes an HTTP request. This method can be overridden by subclasses if
480
		 * developers want to do fancier things or use something other than wp_remote_request()
481
		 * to make the request.
482
		 *
483
		 * @param string     $pCanonizedPath The URL to make the request to
484
		 * @param string     $pMethod        HTTP method
485
		 * @param array      $pParams        The parameters to use for the POST body
486
		 * @param null|array $pWPRemoteArgs  wp_remote_request options.
487
		 *
488
		 * @return object[]|object|null
489
		 *
490
		 * @throws Freemius_Exception
491
		 */
492
		public function MakeRequest(
493
			$pCanonizedPath,
494
			$pMethod = 'GET',
495
			$pParams = array(),
496
			$pWPRemoteArgs = null
497
		) {
498
			$resource = explode( '?', $pCanonizedPath );
499
500
			// Only sign request if not ping.json connectivity test.
501
			$sign_request = ( '/v1/ping.json' !== strtolower( substr( $resource[0], - strlen( '/v1/ping.json' ) ) ) );
502
503
			return self::MakeStaticRequest(
504
				$pCanonizedPath,
505
				$pMethod,
506
				$pParams,
507
				$pWPRemoteArgs,
508
				$this->_isSandbox,
509
				$sign_request ? array( &$this, 'SignRequest' ) : null
510
			);
511
		}
512
513
		/**
514
		 * Sets CURLOPT_IPRESOLVE to CURL_IPRESOLVE_V4 for cURL-Handle provided as parameter
515
		 *
516
		 * @param resource $handle A cURL handle returned by curl_init()
517
		 *
518
		 * @return resource $handle A cURL handle returned by curl_init() with CURLOPT_IPRESOLVE set to
519
		 *                  CURL_IPRESOLVE_V4
520
		 *
521
		 * @link https://gist.github.com/golderweb/3a2aaec2d56125cc004e
522
		 */
523
		static function CurlResolveToIPv4( $handle ) {
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...
524
			curl_setopt( $handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
525
526
			return $handle;
527
		}
528
529
		#----------------------------------------------------------------------------------
530
		#region Connectivity Test
531
		#----------------------------------------------------------------------------------
532
533
		/**
534
		 * If successful connectivity to the API endpoint using ping.json endpoint.
535
		 *
536
		 *      - OR -
537
		 *
538
		 * Validate if ping result object is valid.
539
		 *
540
		 * @param mixed $pPong
541
		 *
542
		 * @return bool
543
		 */
544
		public static function Test( $pPong = null ) {
545
			$pong = is_null( $pPong ) ?
546
				self::Ping() :
547
				$pPong;
548
549
			return (
550
				is_object( $pong ) &&
551
				isset( $pong->api ) &&
552
				'pong' === $pong->api
553
			);
554
		}
555
556
		/**
557
		 * Ping API to test connectivity.
558
		 *
559
		 * @return object
560
		 */
561
		public static function Ping() {
562
			try {
563
				$result = self::MakeStaticRequest( '/v' . FS_API__VERSION . '/ping.json' );
564
			} catch ( Freemius_Exception $e ) {
565
				// Map to error object.
566
				$result = (object) $e->getResult();
567
			} catch ( Exception $e ) {
568
				// Map to error object.
569
				$result = (object) array(
570
					'error' => array(
571
						'type'    => 'Unknown',
572
						'message' => $e->getMessage() . ' (' . $e->getFile() . ': ' . $e->getLine() . ')',
573
						'code'    => 'unknown',
574
						'http'    => 402
575
					)
576
				);
577
			}
578
579
			return $result;
580
		}
581
582
		#endregion
583
584
		#----------------------------------------------------------------------------------
585
		#region Connectivity Exceptions
586
		#----------------------------------------------------------------------------------
587
588
		/**
589
		 * @param \WP_Error $pError
590
		 *
591
		 * @return bool
592
		 */
593
		private static function IsCurlError( WP_Error $pError ) {
594
			$message = $pError->get_error_message( 'http_request_failed' );
595
596
			return ( 0 === strpos( $message, 'cURL' ) );
597
		}
598
599
		/**
600
		 * @param WP_Error $pError
601
		 *
602
		 * @throws Freemius_Exception
603
		 */
604
		private static function ThrowWPRemoteException( WP_Error $pError ) {
605
			if ( self::IsCurlError( $pError ) ) {
606
				$message = $pError->get_error_message( 'http_request_failed' );
607
608
				#region Check if there are any missing cURL methods.
609
610
				$curl_required_methods = array(
611
					'curl_version',
612
					'curl_exec',
613
					'curl_init',
614
					'curl_close',
615
					'curl_setopt',
616
					'curl_setopt_array',
617
					'curl_error',
618
				);
619
620
				// Find all missing methods.
621
				$missing_methods = array();
622
				foreach ( $curl_required_methods as $m ) {
623
					if ( ! function_exists( $m ) ) {
624
						$missing_methods[] = $m;
625
					}
626
				}
627
628
				if ( ! empty( $missing_methods ) ) {
629
					throw new Freemius_Exception( array(
630
						'error'           => (object) array(
631
							'type'    => 'cUrlMissing',
632
							'message' => $message,
633
							'code'    => 'curl_missing',
634
							'http'    => 402
635
						),
636
						'missing_methods' => $missing_methods,
637
					) );
638
				}
639
640
				#endregion
641
642
				// cURL error - "cURL error {{errno}}: {{error}}".
643
				$parts = explode( ':', substr( $message, strlen( 'cURL error ' ) ), 2 );
644
645
				$code    = ( 0 < count( $parts ) ) ? $parts[0] : 'http_request_failed';
646
				$message = ( 1 < count( $parts ) ) ? $parts[1] : $message;
647
648
				$e = new Freemius_Exception( array(
649
					'error' => array(
650
						'code'    => $code,
651
						'message' => $message,
652
						'type'    => 'CurlException',
653
					),
654
				) );
655
			} else {
656
				$e = new Freemius_Exception( array(
657
					'error' => array(
658
						'code'    => $pError->get_error_code(),
659
						'message' => $pError->get_error_message(),
660
						'type'    => 'WPRemoteException',
661
					),
662
				) );
663
			}
664
665
			throw $e;
666
		}
667
668
		/**
669
		 * @param string $pResult
670
		 *
671
		 * @throws Freemius_Exception
672
		 */
673
		private static function ThrowCloudFlareDDoSException( $pResult = '' ) {
674
			throw new Freemius_Exception( array(
675
				'error' => (object) array(
676
					'type'    => 'CloudFlareDDoSProtection',
677
					'message' => $pResult,
678
					'code'    => 'cloudflare_ddos_protection',
679
					'http'    => 402
680
				)
681
			) );
682
		}
683
684
		/**
685
		 * @param string $pResult
686
		 *
687
		 * @throws Freemius_Exception
688
		 */
689
		private static function ThrowSquidAclException( $pResult = '' ) {
690
			throw new Freemius_Exception( array(
691
				'error' => (object) array(
692
					'type'    => 'SquidCacheBlock',
693
					'message' => $pResult,
694
					'code'    => 'squid_cache_block',
695
					'http'    => 402
696
				)
697
			) );
698
		}
699
700
		#endregion
701
	}