laipower/wp-content/plugins/jetpack-protect/jetpack_vendor/automattic/jetpack-boost-speed-score/src/class-speed-score-request.php

336 lines
7.6 KiB
PHP

<?php
/**
* Represents a request to generate a pair of speed scores.
*
* @package automattic/jetpack-boost-speed-score
*/
namespace Automattic\Jetpack\Boost_Speed_Score;
use Automattic\Jetpack\Boost_Core\Lib\Boost_API;
use Automattic\Jetpack\Boost_Core\Lib\Cacheable;
use Automattic\Jetpack\Boost_Core\Lib\Url;
/**
* Class Speed_Score_Request
*/
class Speed_Score_Request extends Cacheable {
/**
* Algorithm to use when defining a hash for the cache.
*/
const CACHE_KEY_HASH_ALGO = 'md5';
/**
* The URL to get the Speed Scores for.
*
* @var string $url Url to get the Speed Scores for.
*/
private $url;
/**
* Active Jetpack Boost modules.
*
* @var array $active_modules Active modules.
*/
private $active_modules;
/**
* When the Speed Scores request was created, in seconds since epoch.
*
* @var float $created Speed Scores request creation timestamp.
*/
private $created;
/**
* Current status of the Speed Score request.
*
* @var string $status Speed Scores request status.
*/
private $status;
/**
* Number of retries attempted.
*
* @var int $retry_count Number of times this Speed Score request has been retried.
*/
private $retry_count;
/**
* The error returned
*
* @var array $error Speed Scores error.
*/
private $error;
/**
* Where the Speed Scores request was made from.
*
* @var string $client A string passed to Speed_Score to identify where the request was made from.
*/
private $client;
/**
* Constructor.
*
* @param string $url The URL to get the Speed Scores for.
* @param array $active_modules Active modules.
* @param null $created When the Speed Scores request was created, in seconds since epoch.
* @param string $status Status of the Speed Scores request.
* @param null $error The Speed Scores error.
* @param string $client A string identifying where the request was made from.
*/
public function __construct( $url, $active_modules = array(), $created = null, $status = 'pending', $error = null, $client = null ) {
$this->set_cache_id( self::generate_cache_id_from_url( $url ) );
$this->url = $url;
$this->active_modules = $active_modules;
$this->created = $created === null ? microtime( true ) : $created;
$this->status = $status;
$this->error = $error;
$this->client = $client;
$this->retry_count = 0;
}
/**
* Generate the cache ID from the URL.
*
* @param string $url The URL to get the Speed Scores for.
*
* @return string
*/
public static function generate_cache_id_from_url( $url ) {
return hash( self::CACHE_KEY_HASH_ALGO, $url );
}
/**
* Get the list of active performance modules while this request was created.
*
* @return string
*/
public function get_active_performance_modules() {
// List of modules that affect the speed score.
$performance_modules = array(
'cloud_css',
'critical_css',
'image_cdn',
'minify_css',
'minify_js',
'render_blocking_js',
);
return array_intersect( $this->active_modules, $performance_modules );
}
/**
* Convert this object to a plain array for JSON serialization.
*/
#[\ReturnTypeWillChange]
public function jsonSerialize() {
return array(
'id' => $this->get_cache_id(),
'url' => $this->url,
'active_modules' => $this->get_active_performance_modules(),
'created' => $this->created,
'status' => $this->status,
'error' => $this->error,
'client' => $this->client,
'retry_count' => $this->retry_count,
);
}
/**
* This is intended to be the reverse of JsonSerializable->jsonSerialize.
*
* @param mixed $data The data to turn into an object.
*
* @return Speed_Score_Request
*/
public static function jsonUnserialize( $data ) {
$object = new Speed_Score_Request(
$data['url'],
$data['active_modules'],
$data['created'],
$data['status'],
$data['error'],
$data['client']
);
if ( ! empty( $data['id'] ) ) {
$object->set_cache_id( $data['id'] );
}
if ( ! empty( $data['retry_count'] ) ) {
$object->retry_count = intval( $data['retry_count'] );
}
return $object;
}
/**
* Return the cache prefix.
*
* @return string
*/
protected static function cache_prefix() {
return 'jetpack_boost_speed_scores_';
}
/**
* Send a Speed Scores request to the API.
*
* @return true|\WP_Error True on success, WP_Error on failure.
*/
public function execute() {
$response = Boost_API::post(
'speed-scores',
array(
'request_id' => $this->get_cache_id(),
'url' => Url::normalize( $this->url ),
'active_modules' => $this->active_modules,
'client' => $this->client,
)
);
if ( is_wp_error( $response ) ) {
$this->status = 'error';
$this->error = $response->get_error_message();
$this->store();
return $response;
}
return true;
}
/**
* Is this request pending?
*/
public function is_pending() {
return 'pending' === $this->status;
}
/**
* Did the request fail?
*/
public function is_error() {
return 'error' === $this->status;
}
/**
* Did the request succeed?
*/
public function is_success() {
return 'success' === $this->status;
}
/**
* Poll for updates to this Speed Scores request.
*
* @return true|\WP_Error True on success, WP_Error on failure.
*/
public function poll_update() {
$response = Boost_API::get(
sprintf(
'speed-scores/%s',
$this->get_cache_id()
)
);
if ( is_wp_error( $response ) ) {
// Special case: If the request is not found, restart it.
if ( 'not_found' === $response->get_error_code() ) {
return $this->restart();
}
return $response;
}
switch ( $response['status'] ) {
case 'pending':
// The initial job probably failed, dispatch again if so.
if ( $this->created <= strtotime( '-15 mins' ) ) {
return $this->restart();
}
break;
case 'error':
$this->status = 'error';
$this->error = $response['error'];
$this->store();
break;
case 'success':
$this->status = 'success';
$this->store();
$this->record_history( $response );
break;
default:
return new \WP_Error(
'invalid_response',
__(
'Invalid response from WPCOM API while polling for speed scores',
'jetpack-boost-speed-score'
),
$response
);
}
return true;
}
/**
* Restart this request; useful when WPCOM doesn't recognize the request or it times out.
*/
private function restart() {
// Enforce a maximum number of restarts.
if ( $this->retry_count > 2 ) {
$this->status = 'error';
$this->error = 'Maximum number of retries exceeded';
$this->store();
return new \WP_Error(
'error',
$this->error
);
}
$result = $this->execute();
if ( is_wp_error( $result ) ) {
return $result;
}
$this->created = time();
++$this->retry_count;
$this->store();
return true;
}
/**
* Save the speed score record to history.
*
* @param array $response Response from api.
*/
private function record_history( $response ) {
$history = new Speed_Score_History( $this->url );
$last_history = $history->latest();
$last_scores = $last_history ? $last_history['scores'] : null;
$last_theme = $last_history ? $last_history['theme'] : null;
$current_theme = wp_get_theme()->get( 'Name' );
// Only change if there is a difference from last score or the theme changed.
if ( $last_scores !== $response['scores'] || $current_theme !== $last_theme ) {
$history->push(
array(
'timestamp' => time(),
'scores' => $response['scores'],
'theme' => $current_theme,
)
);
}
}
}