PluginProbe
Tiered Pricing Table for WooCommerce / 2.1.2
Tiered Pricing Table for WooCommerce v2.1.2
8.0.2 7.1.7 7.1.5 7.1.4 7.1.1 6.5.0 6.4.0 6.1.0 trunk 1.0 1.1 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.2.0 2.2.1 2.2.2 2.2.3 2.3.0 2.3.1 2.3.2 2.3.3 All 91 releases
← All changes | freemius/includes/class-fs-api.php +149 -248 8.0.2 → 2.1.2 View file →
@@ -55,25 +55,9 @@
55 55 * @since 1.0.4
56 56 */
57 57 private $_logger;
58 58
59 - /**
60 - * @author Leo Fajardo (@leorw)
61 - * @since 2.3.0
62 - *
63 - * @var string
64 - */
65 - private $_sdk_version;
66 -
67 - /**
68 - * @author Leo Fajardo (@leorw)
69 - * @since 2.5.0
70 - *
71 - * @var string
72 - */
73 - private $_url;
74 -
75 - /**
59 + /**
76 60 * @param string $slug
77 61 * @param string $scope 'app', 'developer', 'user' or 'install'.
78 62 * @param number $id Element's id.
79 63 * @param string $public_key Public key.
@@ -78,29 +62,18 @@
78 62 * @param number $id Element's id.
79 63 * @param string $public_key Public key.
80 64 * @param bool $is_sandbox
81 65 * @param bool|string $secret_key Element's secret key.
82 - * @param null|string $sdk_version
83 - * @param null|string $url
84 66 *
85 67 * @return FS_Api
86 68 */
87 - static function instance(
88 - $slug,
89 - $scope,
90 - $id,
91 - $public_key,
92 - $is_sandbox,
93 - $secret_key = false,
94 - $sdk_version = null,
95 - $url = null
96 - ) {
69 + static function instance( $slug, $scope, $id, $public_key, $is_sandbox, $secret_key = false ) {
97 70 $identifier = md5( $slug . $scope . $id . $public_key . ( is_string( $secret_key ) ? $secret_key : '' ) . json_encode( $is_sandbox ) );
98 71
99 72 if ( ! isset( self::$_instances[ $identifier ] ) ) {
100 73 self::_init();
101 74
102 - self::$_instances[ $identifier ] = new FS_Api( $slug, $scope, $id, $public_key, $secret_key, $is_sandbox, $sdk_version, $url );
75 + self::$_instances[ $identifier ] = new FS_Api( $slug, $scope, $id, $public_key, $secret_key, $is_sandbox );
103 76 }
104 77
105 78 return self::$_instances[ $identifier ];
106 79 }
@@ -131,27 +104,14 @@
131 104 * @param number $id Element's id.
132 105 * @param string $public_key Public key.
133 106 * @param bool|string $secret_key Element's secret key.
134 107 * @param bool $is_sandbox
135 - * @param null|string $sdk_version
136 - * @param null|string $url
137 108 */
138 - private function __construct(
139 - $slug,
140 - $scope,
141 - $id,
142 - $public_key,
143 - $secret_key,
144 - $is_sandbox,
145 - $sdk_version,
146 - $url
147 - ) {
109 + private function __construct( $slug, $scope, $id, $public_key, $secret_key, $is_sandbox ) {
148 110 $this->_api = new Freemius_Api_WordPress( $scope, $id, $public_key, $secret_key, $is_sandbox );
149 111
150 - $this->_slug = $slug;
151 - $this->_sdk_version = $sdk_version;
152 - $this->_url = $url;
153 - $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $slug . '_api', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
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 );
154 114 }
155 115
156 116 /**
157 117 * Find clock diff between server and API server, and store the diff locally.
@@ -188,95 +148,47 @@
188 148 *
189 149 * @param string $path
190 150 * @param string $method
191 151 * @param array $params
192 - * @param bool $in_retry Is in retry or first call attempt.
152 + * @param bool $retry Is in retry or first call attempt.
193 153 *
194 154 * @return array|mixed|string|void
195 155 */
196 - private function _call( $path, $method = 'GET', $params = array(), $in_retry = false ) {
197 - $this->_logger->entrance( $method . ':' . $path );
156 + private function _call( $path, $method = 'GET', $params = array(), $retry = false ) {
157 + $this->_logger->entrance( $method . ':' . $path );
198 158
199 - $force_http = ( ! $in_retry && self::$_options->get_option( 'api_force_http', false ) );
159 + if ( self::is_temporary_down() ) {
160 + $result = $this->get_temporary_unavailable_error();
161 + } else {
162 + $result = $this->_api->Api( $path, $method, $params );
200 163
201 - if ( self::is_temporary_down() ) {
202 - $result = $this->get_temporary_unavailable_error();
203 - } else {
204 - /**
205 - * @since 2.3.0 Include the SDK version with all API requests that going through the API manager. IMPORTANT: Only pass the SDK version if the caller didn't include it yet.
206 - */
207 - if ( ! empty( $this->_sdk_version ) ) {
208 - if ( false === strpos( $path, 'sdk_version=' ) &&
209 - ! isset( $params['sdk_version'] )
210 - ) {
211 - // Always add the sdk_version param in the querystring. DO NOT INCLUDE IT IN THE BODY PARAMS, OTHERWISE, IT MAY LEAD TO AN UNEXPECTED PARAMS PARSING IN CASES WHERE THE $params IS A REGULAR NON-ASSOCIATIVE ARRAY.
212 - $path = add_query_arg( 'sdk_version', $this->_sdk_version, $path );
213 - }
214 - }
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;
215 173
216 - /**
217 - * @since 2.5.0 Include the site's URL, if available, in all API requests that are going through the API manager.
218 - */
219 - if ( ! empty( $this->_url ) ) {
220 - if ( false === strpos( $path, 'url=' ) &&
221 - ! isset( $params['url'] )
222 - ) {
223 - $path = add_query_arg( 'url', $this->_url, $path );
224 - }
225 - }
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 + }
226 182
227 - $result = $this->_api->Api( $path, $method, $params );
183 + if ( $this->_logger->is_on() && self::is_api_error( $result ) ) {
184 + // Log API errors.
185 + $this->_logger->api_error( $result );
186 + }
228 187
229 - if (
230 - ! $in_retry &&
231 - null !== $result &&
232 - isset( $result->error ) &&
233 - isset( $result->error->code )
234 - ) {
235 - $retry = false;
188 + return $result;
189 + }
236 190
237 - if ( 'request_expired' === $result->error->code ) {
238 - $diff = isset( $result->error->timestamp ) ?
239 - ( time() - strtotime( $result->error->timestamp ) ) :
240 - false;
241 -
242 - // Try to sync clock diff.
243 - if ( false !== $this->_sync_clock_diff( $diff ) ) {
244 - // Retry call with new synced clock.
245 - $retry = true;
246 - }
247 - } else if (
248 - Freemius_Api_WordPress::IsHttps() &&
249 - FS_Api::is_ssl_error_response( $result )
250 - ) {
251 - $force_http = true;
252 - $retry = true;
253 - }
254 -
255 - if ( $retry ) {
256 - if ( $force_http ) {
257 - $this->toggle_force_http( true );
258 - }
259 -
260 - $result = $this->_call( $path, $method, $params, true );
261 - }
262 - }
263 - }
264 -
265 - if ( self::is_api_error( $result ) ) {
266 - if ( $this->_logger->is_on() ) {
267 - // Log API errors.
268 - $this->_logger->api_error( $result );
269 - }
270 -
271 - if ( $force_http ) {
272 - $this->toggle_force_http( false );
273 - }
274 - }
275 -
276 - return $result;
277 - }
278 -
279 191 /**
280 192 * Override API call to wrap it in servers' clock sync method.
281 193 *
282 194 * @param string $path
@@ -317,14 +229,11 @@
317 229 if ( WP_FS__DEV_MODE || $this->_api->IsSandbox() ) {
318 230 $flush = true;
319 231 }
320 232
321 - $has_valid_cache = self::$_cache->has_valid( $cache_key, $expiration );
322 - $cached_result = $has_valid_cache ?
323 - self::$_cache->get( $cache_key ) :
324 - null;
233 + $cached_result = self::$_cache->get( $cache_key );
325 234
326 - if ( $flush || is_null( $cached_result ) ) {
235 + if ( $flush || ! self::$_cache->has_valid( $cache_key, $expiration ) ) {
327 236 $result = $this->call( $path );
328 237
329 238 if ( ! is_object( $result ) || isset( $result->error ) ) {
330 239 // Api returned an error.
@@ -338,14 +247,14 @@
338 247 if ( $this->_logger->is_on() ) {
339 248 $this->_logger->warn( 'Fallback to cached API result: ' . var_export( $cached_result, true ) );
340 249 }
341 250 } else {
342 - if ( is_object( $result ) && isset( $result->error->http ) && 404 == $result->error->http ) {
251 + if ( is_object( $result ) && 404 == $result->error->http ) {
343 252 /**
344 253 * If the response code is 404, cache the result for half of the `$expiration`.
345 254 *
346 255 * @author Leo Fajardo (@leorw)
347 - * @since 2.2.4
256 + * @since 2.2.3.1
348 257 */
349 258 $expiration /= 2;
350 259 } else {
351 260 // If no older data version and the response code is not 404, return result without
@@ -354,11 +263,9 @@
354 263 }
355 264 }
356 265 }
357 266
358 - if ( is_numeric( $expiration ) ) {
359 - self::$_cache->set( $cache_key, $result, $expiration );
360 - }
267 + self::$_cache->set( $cache_key, $result, $expiration );
361 268
362 269 $cached_result = $result;
363 270 } else {
364 271 $this->_logger->log( 'Using cached API result.' );
@@ -366,45 +273,8 @@
366 273
367 274 return $cached_result;
368 275 }
369 276
370 - /**
371 - * @todo Remove this method after migrating Freemius::safe_remote_post() to FS_Api::call().
372 - *
373 - * @author Leo Fajardo (@leorw)
374 - * @since 2.5.4
375 - *
376 - * @param string $url
377 - * @param array $remote_args
378 - *
379 - * @return array|WP_Error The response array or a WP_Error on failure.
380 - */
381 - static function remote_request( $url, $remote_args ) {
382 - if ( ! class_exists( 'Freemius_Api_WordPress' ) ) {
383 - require_once WP_FS__DIR_SDK . '/FreemiusWordPress.php';
384 - }
385 -
386 - if ( method_exists( 'Freemius_Api_WordPress', 'RemoteRequest' ) ) {
387 - return Freemius_Api_WordPress::RemoteRequest( $url, $remote_args );
388 - }
389 -
390 - // The following is for backward compatibility when a modified PHP SDK version is in use and the `Freemius_Api_WordPress:RemoteRequest()` method doesn't exist.
391 - $response = wp_remote_request( $url, $remote_args );
392 -
393 - if (
394 - is_array( $response ) &&
395 - (
396 - empty( $response['headers'] ) ||
397 - empty( $response['headers']['x-api-server'] )
398 - )
399 - ) {
400 - // API is considered blocked if the response doesn't include the `x-api-server` header. When there's no error but this header doesn't exist, the response is usually not in the expected form (e.g., cannot be JSON-decoded).
401 - $response = new WP_Error( 'api_blocked', htmlentities( $response['body'] ) );
402 - }
403 -
404 - return $response;
405 - }
406 -
407 277 /**
408 278 * Check if there's a cached version of the API request.
409 279 *
410 280 * @author Vova Feldman (@svovaf)
@@ -473,40 +343,53 @@
473 343 // return $method . '_' . array_pop($exploded) . '_' . md5($canonized . json_encode($params));
474 344 return strtolower( $method . ':' . $canonized ) . ( ! empty( $params ) ? '#' . md5( json_encode( $params ) ) : '' );
475 345 }
476 346
477 - /**
478 - * @author Leo Fajardo (@leorw)
479 - * @since 2.5.4
480 - *
481 - * @param bool $is_http
482 - */
483 - private function toggle_force_http( $is_http ) {
484 - self::$_options->set_option( 'api_force_http', $is_http, true );
347 + /**
348 + * Test API connectivity.
349 + *
350 + * @author Vova Feldman (@svovaf)
351 + * @since 1.0.9 If fails, try to fallback to HTTP.
352 + * @since 1.1.6 Added a 5-min caching mechanism, to prevent from overloading the server if the API if
353 + * temporary down.
354 + *
355 + * @return bool True if successful connectivity to the API.
356 + */
357 + static function test() {
358 + self::_init();
485 359
486 - if ( $is_http ) {
487 - Freemius_Api_WordPress::SetHttp();
488 - } else if ( method_exists( 'Freemius_Api_WordPress', 'SetHttps' ) ) {
489 - Freemius_Api_WordPress::SetHttps();
490 - }
491 - }
360 + $cache_key = 'ping_test';
492 361
493 - /**
494 - * @author Leo Fajardo (@leorw)
495 - * @since 2.5.4
496 - *
497 - * @param mixed $response
498 - *
499 - * @return bool
500 - */
501 - static function is_blocked( $response ) {
502 - return (
503 - self::is_api_error_object( $response, true ) &&
504 - isset( $response->error->code ) &&
505 - 'api_blocked' === $response->error->code
506 - );
507 - }
362 + $test = self::$_cache->get_valid( $cache_key, null );
508 363
364 + if ( is_null( $test ) ) {
365 + $test = Freemius_Api_WordPress::Test();
366 +
367 + if ( false === $test && Freemius_Api_WordPress::IsHttps() ) {
368 + // Fallback to HTTP, since HTTPS fails.
369 + Freemius_Api_WordPress::SetHttp();
370 +
371 + self::$_options->set_option( 'api_force_http', true, true );
372 +
373 + $test = Freemius_Api_WordPress::Test();
374 +
375 + if ( false === $test ) {
376 + /**
377 + * API connectivity test fail also in HTTP request, therefore,
378 + * fallback to HTTPS to keep connection secure.
379 + *
380 + * @since 1.1.6
381 + */
382 + self::$_options->set_option( 'api_force_http', false, true );
383 + }
384 + }
385 +
386 + self::$_cache->set( $cache_key, $test, WP_FS__TIME_5_MIN_IN_SEC );
387 + }
388 +
389 + return $test;
390 + }
391 +
509 392 /**
510 393 * Check if API is temporary down.
511 394 *
512 395 * @author Vova Feldman (@svovaf)
@@ -539,8 +422,58 @@
539 422 );
540 423 }
541 424
542 425 /**
426 + * Ping API for connectivity test, and return result object.
427 + *
428 + * @author Vova Feldman (@svovaf)
429 + * @since 1.0.9
430 + *
431 + * @param null|string $unique_anonymous_id
432 + * @param array $params
433 + *
434 + * @return object
435 + */
436 + function ping( $unique_anonymous_id = null, $params = array() ) {
437 + $this->_logger->entrance();
438 +
439 + if ( self::is_temporary_down() ) {
440 + return $this->get_temporary_unavailable_error();
441 + }
442 +
443 + $pong = is_null( $unique_anonymous_id ) ?
444 + Freemius_Api_WordPress::Ping() :
445 + $this->_call( 'ping.json?' . http_build_query( array_merge(
446 + array( 'uid' => $unique_anonymous_id ),
447 + $params
448 + ) ) );
449 +
450 + if ( $this->is_valid_ping( $pong ) ) {
451 + return $pong;
452 + }
453 +
454 + if ( self::should_try_with_http( $pong ) ) {
455 + // Fallback to HTTP, since HTTPS fails.
456 + Freemius_Api_WordPress::SetHttp();
457 +
458 + self::$_options->set_option( 'api_force_http', true, true );
459 +
460 + $pong = is_null( $unique_anonymous_id ) ?
461 + Freemius_Api_WordPress::Ping() :
462 + $this->_call( 'ping.json?' . http_build_query( array_merge(
463 + array( 'uid' => $unique_anonymous_id ),
464 + $params
465 + ) ) );
466 +
467 + if ( ! $this->is_valid_ping( $pong ) ) {
468 + self::$_options->set_option( 'api_force_http', false, true );
469 + }
470 + }
471 +
472 + return $pong;
473 + }
474 +
475 + /**
543 476 * Check if based on the API result we should try
544 477 * to re-run the same request with HTTP instead of HTTPS.
545 478 *
546 479 * @author Vova Feldman (@svovaf)
@@ -567,8 +500,22 @@
567 500 ) ) );
568 501
569 502 }
570 503
504 + /**
505 + * Check if valid ping request result.
506 + *
507 + * @author Vova Feldman (@svovaf)
508 + * @since 1.1.1
509 + *
510 + * @param mixed $pong
511 + *
512 + * @return bool
513 + */
514 + function is_valid_ping( $pong ) {
515 + return Freemius_Api_WordPress::Test( $pong );
516 + }
517 +
571 518 function get_url( $path = '' ) {
572 519 return Freemius_Api_WordPress::GetUrl( $path, $this->_api->IsSandbox() );
573 520 }
574 521
@@ -584,16 +531,8 @@
584 531 self::$_cache = FS_Cache_Manager::get_manager( WP_FS__API_CACHE_OPTION_NAME );
585 532 self::$_cache->clear();
586 533 }
587 534
588 - /**
589 - * @author Leo Fajardo (@leorw)
590 - * @since 2.5.4
591 - */
592 - static function clear_force_http_flag() {
593 - self::$_options->unset_option( 'api_force_http' );
594 - }
595 -
596 535 #----------------------------------------------------------------------------------
597 536 #region Error Handling
598 537 #----------------------------------------------------------------------------------
599 538
@@ -614,54 +553,16 @@
614 553 * @author Vova Feldman (@svovaf)
615 554 * @since 2.0.0
616 555 *
617 556 * @param mixed $result
618 - * @param bool $ignore_message
619 557 *
620 558 * @return bool Is API result contains an error.
621 559 */
622 - static function is_api_error_object( $result, $ignore_message = false ) {
560 + static function is_api_error_object( $result ) {
623 561 return (
624 562 is_object( $result ) &&
625 563 isset( $result->error ) &&
626 - ( $ignore_message || isset( $result->error->message ) )
627 - );
628 - }
629 -
630 - /**
631 - * @author Leo Fajardo (@leorw)
632 - * @since 2.5.4
633 - *
634 - * @param WP_Error|object|string $response
635 - *
636 - * @return bool
637 - */
638 - static function is_ssl_error_response( $response ) {
639 - $http_error = null;
640 -
641 - if ( $response instanceof WP_Error ) {
642 - if (
643 - isset( $response->errors ) &&
644 - isset( $response->errors['http_request_failed'] )
645 - ) {
646 - $http_error = strtolower( $response->errors['http_request_failed'][0] );
647 - }
648 - } else if (
649 - self::is_api_error_object( $response ) &&
650 - ! empty( $response->error->message )
651 - ) {
652 - $http_error = $response->error->message;
653 - }
654 -
655 - return (
656 - ! empty( $http_error ) &&
657 - (
658 - false !== strpos( $http_error, 'curl error 35' ) ||
659 - (
660 - false === strpos( $http_error, '</html>' ) &&
661 - false !== strpos( $http_error, 'ssl' )
662 - )
663 - )
564 + isset( $result->error->message )
664 565 );
665 566 }
666 567
667 568 /**