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 ( 74a577...62d274 )
by Brad
02:30
created

FS_Api::is_temporary_down()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
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 22 and the first side effect is on line 10.

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
	 * @package     Freemius
4
	 * @copyright   Copyright (c) 2015, Freemius, Inc.
5
	 * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
6
	 * @since       1.0.4
7
	 */
8
9
	if ( ! defined( 'ABSPATH' ) ) {
10
		exit;
11
	}
12
13
	/**
14
	 * Class FS_Api
15
	 *
16
	 * Wraps Freemius API SDK to handle:
17
	 *      1. Clock sync.
18
	 *      2. Fallback to HTTP when HTTPS fails.
19
	 *      3. Adds caching layer to GET requests.
20
	 *      4. Adds consistency for failed requests by using last cached version.
21
	 */
22
	class FS_Api {
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
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...
23
		/**
24
		 * @var FS_Api[]
25
		 */
26
		private static $_instances = array();
27
28
		/**
29
		 * @var FS_Option_Manager Freemius options, options-manager.
30
		 */
31
		private static $_options;
32
33
		/**
34
		 * @var FS_Cache_Manager API Caching layer
35
		 */
36
		private static $_cache;
37
38
		/**
39
		 * @var int Clock diff in seconds between current server to API server.
40
		 */
41
		private static $_clock_diff;
42
43
		/**
44
		 * @var Freemius_Api
45
		 */
46
		private $_api;
47
48
		/**
49
		 * @var string
50
		 */
51
		private $_slug;
52
53
		/**
54
		 * @var FS_Logger
55
		 * @since 1.0.4
56
		 */
57
		private $_logger;
58
59
		/**
60
		 * @param string      $slug
61
		 * @param string      $scope      'app', 'developer', 'user' or 'install'.
62
		 * @param number      $id         Element's id.
63
		 * @param string      $public_key Public key.
64
		 * @param bool        $is_sandbox
65
		 * @param bool|string $secret_key Element's secret key.
66
		 *
67
		 * @return FS_Api
68
		 */
69
		static function instance( $slug, $scope, $id, $public_key, $is_sandbox, $secret_key = false ) {
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...
70
			$identifier = md5( $slug . $scope . $id . $public_key . ( is_string( $secret_key ) ? $secret_key : '' ) . json_encode( $is_sandbox ) );
71
72
			if ( ! isset( self::$_instances[ $identifier ] ) ) {
73
				self::_init();
74
75
				self::$_instances[ $identifier ] = new FS_Api( $slug, $scope, $id, $public_key, $secret_key, $is_sandbox );
76
			}
77
78
			return self::$_instances[ $identifier ];
79
		}
80
81
		private static function _init() {
82
			if ( isset( self::$_options ) ) {
83
				return;
84
			}
85
86
			if ( ! class_exists( 'Freemius_Api' ) ) {
87
				require_once WP_FS__DIR_SDK . '/Freemius.php';
88
			}
89
90
			self::$_options = FS_Option_Manager::get_manager( WP_FS__OPTIONS_OPTION_NAME, true );
91
			self::$_cache   = FS_Cache_Manager::get_manager( WP_FS__API_CACHE_OPTION_NAME );
92
93
			self::$_clock_diff = self::$_options->get_option( 'api_clock_diff', 0 );
94
			Freemius_Api::SetClockDiff( self::$_clock_diff );
95
96
			if ( self::$_options->get_option( 'api_force_http', false ) ) {
97
				Freemius_Api::SetHttp();
98
			}
99
		}
100
101
		/**
102
		 * @param string      $slug
103
		 * @param string      $scope      'app', 'developer', 'user' or 'install'.
104
		 * @param number      $id         Element's id.
105
		 * @param string      $public_key Public key.
106
		 * @param bool|string $secret_key Element's secret key.
107
		 * @param bool        $is_sandbox
108
		 */
109
		private function __construct( $slug, $scope, $id, $public_key, $secret_key, $is_sandbox ) {
110
			$this->_api = new Freemius_Api( $scope, $id, $public_key, $secret_key, $is_sandbox );
111
112
			$this->_slug   = $slug;
113
			$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $slug . '_api', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
114
		}
115
116
		/**
117
		 * Find clock diff between server and API server, and store the diff locally.
118
		 *
119
		 * @param bool|int $diff
120
		 *
121
		 * @return bool|int False if clock diff didn't change, otherwise returns the clock diff in seconds.
122
		 */
123
		private function _sync_clock_diff( $diff = false ) {
124
			$this->_logger->entrance();
125
126
			// Sync clock and store.
127
			$new_clock_diff = ( false === $diff ) ?
128
				Freemius_Api::FindClockDiff() :
129
				$diff;
130
131
			if ( $new_clock_diff === self::$_clock_diff ) {
132
				return false;
133
			}
134
135
			self::$_clock_diff = $new_clock_diff;
0 ignored issues
show
Documentation Bug introduced by
It seems like $new_clock_diff can also be of type boolean. However, the property $_clock_diff is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
136
137
			// Update API clock's diff.
138
			Freemius_Api::SetClockDiff( self::$_clock_diff );
139
140
			// Store new clock diff in storage.
141
			self::$_options->set_option( 'api_clock_diff', self::$_clock_diff, true );
142
143
			return $new_clock_diff;
144
		}
145
146
		/**
147
		 * Override API call to enable retry with servers' clock auto sync method.
148
		 *
149
		 * @param string $path
150
		 * @param string $method
151
		 * @param array  $params
152
		 * @param bool   $retry Is in retry or first call attempt.
153
		 *
154
		 * @return array|mixed|string|void
155
		 */
156
		private function _call( $path, $method = 'GET', $params = array(), $retry = false ) {
157
			$this->_logger->entrance( $method . ':' . $path );
158
159
			if ( self::is_temporary_down() ) {
160
				$result = $this->get_temporary_unavailable_error();
161
			} else {
162
				$result = $this->_api->Api( $path, $method, $params );
163
164
				if ( null !== $result &&
165
				     isset( $result->error ) &&
166
				     isset( $result->error->code ) &&
167
				     'request_expired' === $result->error->code
168
				) {
169
					if ( ! $retry ) {
170
						$diff = isset( $result->error->timestamp ) ?
171
							( time() - strtotime( $result->error->timestamp ) ) :
172
							false;
173
174
						// Try to sync clock diff.
175
						if ( false !== $this->_sync_clock_diff( $diff ) ) {
176
							// Retry call with new synced clock.
177
							return $this->_call( $path, $method, $params, true );
178
						}
179
					}
180
				}
181
			}
182
183
			if ( $this->_logger->is_on() && self::is_api_error( $result ) ) {
184
				// Log API errors.
185
				$this->_logger->api_error( $result );
186
			}
187
188
			return $result;
189
		}
190
191
		/**
192
		 * Override API call to wrap it in servers' clock sync method.
193
		 *
194
		 * @param string $path
195
		 * @param string $method
196
		 * @param array  $params
197
		 *
198
		 * @return array|mixed|string|void
199
		 * @throws Freemius_Exception
200
		 */
201
		function call( $path, $method = 'GET', $params = array() ) {
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...
202
			return $this->_call( $path, $method, $params );
203
		}
204
205
		/**
206
		 * Get API request URL signed via query string.
207
		 *
208
		 * @param string $path
209
		 *
210
		 * @return string
211
		 */
212
		function get_signed_url( $path ) {
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...
213
			return $this->_api->GetSignedUrl( $path );
214
		}
215
216
		/**
217
		 * @param string $path
218
		 * @param bool   $flush
219
		 * @param int    $expiration (optional) Time until expiration in seconds from now, defaults to 24 hours
220
		 *
221
		 * @return stdClass|mixed
222
		 */
223
		function get( $path = '/', $flush = false, $expiration = WP_FS__TIME_24_HOURS_IN_SEC ) {
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...
224
			$this->_logger->entrance( $path );
225
226
			$cache_key = $this->get_cache_key( $path );
227
228
			// Always flush during development.
229
			if ( WP_FS__DEV_MODE || $this->_api->IsSandbox() ) {
230
				$flush = true;
231
			}
232
233
			$cached_result = self::$_cache->get( $cache_key );
234
235
			if ( $flush || ! self::$_cache->has_valid( $cache_key ) ) {
236
				$result = $this->call( $path );
237
238
				if ( ! is_object( $result ) || isset( $result->error ) ) {
239
					// Api returned an error.
240
					if ( is_object( $cached_result ) &&
241
					     ! isset( $cached_result )
242
					) {
243
						// If there was an error during a newer data fetch,
244
						// fallback to older data version.
245
						$result = $cached_result;
246
247
						if ( $this->_logger->is_on() ) {
248
							$this->_logger->warn( 'Fallback to cached API result: ' . var_export( $cached_result, true ) );
249
						}
250
					} else {
251
						// If no older data version, return result without
252
						// caching the error.
253
						return $result;
254
					}
255
				}
256
257
				self::$_cache->set( $cache_key, $result, $expiration );
258
259
				$cached_result = $result;
260
			} else {
261
				$this->_logger->log( 'Using cached API result.' );
262
			}
263
264
			return $cached_result;
265
		}
266
267
		/**
268
		 * Check if there's a cached version of the API request.
269
		 *
270
		 * @author Vova Feldman (@svovaf)
271
		 * @since  1.2.1
272
		 *
273
		 * @param string $path
274
		 * @param string $method
275
		 * @param array  $params
276
		 *
277
		 * @return bool
278
		 */
279
		function is_cached( $path, $method = 'GET', $params = array() ) {
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...
280
			$cache_key = $this->get_cache_key( $path, $method, $params );
281
282
			return self::$_cache->has_valid( $cache_key );
283
		}
284
285
		/**
286
		 * Invalidate a cached version of the API request.
287
		 *
288
		 * @author Vova Feldman (@svovaf)
289
		 * @since  1.2.1.5
290
		 *
291
		 * @param string $path
292
		 * @param string $method
293
		 * @param array  $params
294
		 */
295
		function purge_cache( $path, $method = 'GET', $params = array() ) {
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...
296
			$this->_logger->entrance( "{$method}:{$path}" );
297
298
			$cache_key = $this->get_cache_key( $path, $method, $params );
299
300
			self::$_cache->purge( $cache_key );
301
		}
302
303
		/**
304
		 * @param string $path
305
		 * @param string $method
306
		 * @param array  $params
307
		 *
308
		 * @return string
309
		 * @throws \Freemius_Exception
310
		 */
311
		private function get_cache_key( $path, $method = 'GET', $params = array() ) {
312
			$canonized = $this->_api->CanonizePath( $path );
313
//			$exploded = explode('/', $canonized);
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% 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...
314
//			return $method . '_' . array_pop($exploded) . '_' . md5($canonized . json_encode($params));
315
			return strtolower( $method . ':' . $canonized ) . ( ! empty( $params ) ? '#' . md5( json_encode( $params ) ) : '' );
316
		}
317
318
		/**
319
		 * Test API connectivity.
320
		 *
321
		 * @author Vova Feldman (@svovaf)
322
		 * @since  1.0.9 If fails, try to fallback to HTTP.
323
		 * @since  1.1.6 Added a 5-min caching mechanism, to prevent from overloading the server if the API if
324
		 *         temporary down.
325
		 *
326
		 * @return bool True if successful connectivity to the API.
327
		 */
328
		static function test() {
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...
329
			self::_init();
330
331
			$cache_key = 'ping_test';
332
333
			$test = self::$_cache->get_valid( $cache_key, null );
334
335
			if ( is_null( $test ) ) {
336
				$test = Freemius_Api::Test();
337
338
				if ( false === $test && Freemius_Api::IsHttps() ) {
339
					// Fallback to HTTP, since HTTPS fails.
340
					Freemius_Api::SetHttp();
341
342
					self::$_options->set_option( 'api_force_http', true, true );
343
344
					$test = Freemius_Api::Test();
345
346
					if ( false === $test ) {
347
						/**
348
						 * API connectivity test fail also in HTTP request, therefore,
349
						 * fallback to HTTPS to keep connection secure.
350
						 *
351
						 * @since 1.1.6
352
						 */
353
						self::$_options->set_option( 'api_force_http', false, true );
354
					}
355
				}
356
357
				self::$_cache->set( $cache_key, $test, WP_FS__TIME_5_MIN_IN_SEC );
358
			}
359
360
			return $test;
361
		}
362
363
		/**
364
		 * Check if API is temporary down.
365
		 *
366
		 * @author Vova Feldman (@svovaf)
367
		 * @since  1.1.6
368
		 *
369
		 * @return bool
370
		 */
371
		static function is_temporary_down() {
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...
372
			self::_init();
373
374
			$test = self::$_cache->get_valid( 'ping_test', null );
375
376
			return ( false === $test );
377
		}
378
379
		/**
380
		 * @author Vova Feldman (@svovaf)
381
		 * @since  1.1.6
382
		 *
383
		 * @return object
384
		 */
385
		private function get_temporary_unavailable_error() {
386
			return (object) array(
387
				'error' => (object) array(
388
					'type'    => 'TemporaryUnavailable',
389
					'message' => 'API is temporary unavailable, please retry in ' . ( self::$_cache->get_record_expiration( 'ping_test' ) - WP_FS__SCRIPT_START_TIME ) . ' sec.',
390
					'code'    => 'temporary_unavailable',
391
					'http'    => 503
392
				)
393
			);
394
		}
395
396
		/**
397
		 * Ping API for connectivity test, and return result object.
398
		 *
399
		 * @author   Vova Feldman (@svovaf)
400
		 * @since    1.0.9
401
		 *
402
		 * @param null|string $unique_anonymous_id
403
		 * @param array       $params
404
		 *
405
		 * @return object
406
		 */
407
		function ping( $unique_anonymous_id = null, $params = array() ) {
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...
408
			$this->_logger->entrance();
409
410
			if ( self::is_temporary_down() ) {
411
				return $this->get_temporary_unavailable_error();
412
			}
413
414
			$pong = is_null( $unique_anonymous_id ) ?
415
				Freemius_Api::Ping() :
416
				$this->_call( 'ping.json?' . http_build_query( array_merge(
417
						array( 'uid' => $unique_anonymous_id ),
418
						$params
419
					) ) );
420
421
			if ( $this->is_valid_ping( $pong ) ) {
422
				return $pong;
423
			}
424
425
			if ( self::should_try_with_http( $pong ) ) {
426
				// Fallback to HTTP, since HTTPS fails.
427
				Freemius_Api::SetHttp();
428
429
				self::$_options->set_option( 'api_force_http', true, true );
430
431
				$pong = is_null( $unique_anonymous_id ) ?
432
					Freemius_Api::Ping() :
433
					$this->_call( 'ping.json?' . http_build_query( array_merge(
434
							array( 'uid' => $unique_anonymous_id ),
435
							$params
436
						) ) );
437
438
				if ( ! $this->is_valid_ping( $pong ) ) {
439
					self::$_options->set_option( 'api_force_http', false, true );
440
				}
441
			}
442
443
			return $pong;
444
		}
445
446
		/**
447
		 * Check if based on the API result we should try
448
		 * to re-run the same request with HTTP instead of HTTPS.
449
		 *
450
		 * @author Vova Feldman (@svovaf)
451
		 * @since  1.1.6
452
		 *
453
		 * @param $result
454
		 *
455
		 * @return bool
456
		 */
457
		private static function should_try_with_http( $result ) {
458
			if ( ! Freemius_Api::IsHttps() ) {
459
				return false;
460
			}
461
462
			return ( ! is_object( $result ) ||
463
			         ! isset( $result->error ) ||
464
			         ! isset( $result->error->code ) ||
465
			         ! in_array( $result->error->code, array(
466
				         'curl_missing',
467
				         'cloudflare_ddos_protection',
468
				         'maintenance_mode',
469
				         'squid_cache_block',
470
				         'too_many_requests',
471
			         ) ) );
472
473
		}
474
475
		/**
476
		 * Check if valid ping request result.
477
		 *
478
		 * @author Vova Feldman (@svovaf)
479
		 * @since  1.1.1
480
		 *
481
		 * @param mixed $pong
482
		 *
483
		 * @return bool
484
		 */
485
		function is_valid_ping( $pong ) {
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...
486
			return Freemius_Api::Test( $pong );
487
		}
488
489
		function get_url( $path = '' ) {
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...
490
			return Freemius_Api::GetUrl( $path, $this->_api->IsSandbox() );
491
		}
492
493
		/**
494
		 * Clear API cache.
495
		 *
496
		 * @author Vova Feldman (@svovaf)
497
		 * @since  1.0.9
498
		 */
499
		static function clear_cache() {
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...
500
			self::_init();
501
502
			self::$_cache = FS_Cache_Manager::get_manager( WP_FS__API_CACHE_OPTION_NAME );
503
			self::$_cache->clear();
504
		}
505
506
		#----------------------------------------------------------------------------------
507
		#region Error Handling
508
		#----------------------------------------------------------------------------------
509
510
		/**
511
		 * @author Vova Feldman (@svovaf)
512
		 * @since  1.2.1.5
513
		 *
514
		 * @param mixed $result
515
		 *
516
		 * @return bool Is API result contains an error.
517
		 */
518
		static function is_api_error( $result ) {
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...
519
			return ( is_object( $result ) && isset( $result->error ) ) ||
520
			       is_string( $result );
521
		}
522
523
		/**
524
		 * Checks if given API result is a non-empty and not an error object.
525
		 *
526
		 * @author Vova Feldman (@svovaf)
527
		 * @since  1.2.1.5
528
		 *
529
		 * @param mixed       $result
530
		 * @param string|null $required_property Optional property we want to verify that is set.
531
		 *
532
		 * @return bool
533
		 */
534
		static function is_api_result_object( $result, $required_property = 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...
535
			return (
536
				is_object( $result ) &&
537
				! isset( $result->error ) &&
538
				( empty( $required_property ) || isset( $result->{$required_property} ) )
539
			);
540
		}
541
542
		/**
543
		 * Checks if given API result is a non-empty entity object with non-empty ID.
544
		 *
545
		 * @author Vova Feldman (@svovaf)
546
		 * @since  1.2.1.5
547
		 *
548
		 * @param mixed $result
549
		 *
550
		 * @return bool
551
		 */
552
		static function is_api_result_entity( $result ) {
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...
553
			return self::is_api_result_object( $result, 'id' ) &&
554
			       FS_Entity::is_valid_id( $result->id );
555
		}
556
557
		#endregion
558
	}