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 — feature/gallery-template-clien... ( 80cd5b...be3a46 )
by Brad
02:36
created

Freemius_Api_WordPress   C

Complexity

Total Complexity 70

Size/Duplication

Total Lines 611
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 0
loc 611
rs 5.5561
c 0
b 0
f 0
wmc 70
lcom 1
cbo 2

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 2
A GetUrl() 0 9 3
A SetClockDiff() 0 3 1
A FindClockDiff() 0 6 1
A SetHttp() 0 3 1
A IsHttps() 0 3 1
A SignRequest() 0 16 3
B GenerateAuthorizationParams() 0 45 5
A GetSignedUrl() 0 14 3
B ExecuteRequest() 0 29 6
A GetLogger() 0 3 1
F MakeStaticRequest() 0 125 23
A MakeRequest() 0 20 2
A CurlResolveToIPv4() 0 5 1
A Test() 0 11 4
A Ping() 0 20 3
A IsCurlError() 0 5 1
B ThrowWPRemoteException() 0 63 7
A ThrowCloudFlareDDoSException() 0 10 1
A ThrowSquidAclException() 0 10 1

How to fix   Complexity   

Complex Class

Complex classes like Freemius_Api_WordPress often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Freemius_Api_WordPress, and based on these observations, apply Extract Interface, too.

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