'not_generated', 'pending' => 'pending', 'generated' => 'generated', 'error' => 'error', ); const PROVIDER_STATES = array( 'pending' => 'pending', 'success' => 'success', 'error' => 'error', ); public $state; public function __construct() { $this->state = jetpack_boost_ds_get( 'critical_css_state' ); } public function clear() { jetpack_boost_ds_delete( 'critical_css_state' ); } public function save() { $this->state['updated'] = microtime( true ); jetpack_boost_ds_set( 'critical_css_state', $this->state ); if ( $this->is_generated() ) { /** * Fires when critical CSS has successfully been generated. */ do_action( 'jetpack_boost_critical_css_generated' ); } } public function set_error( $message ) { if ( empty( $message ) ) { error_log( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log 'Critical CSS: set_error() called with empty message' ); return $this; } $this->state['status_error'] = $message; $this->state['status'] = self::GENERATION_STATES['error']; return $this; } public function set_provider_error_dismissed( $provider_key, $error_type, $dismissed ) { if ( empty( $this->state['providers'] ) ) { return new WP_Error( 'invalid_provider_key', 'No providers exist' ); } $provider_index = array_search( $provider_key, array_column( $this->state['providers'], 'key' ), true ); if ( $provider_index === false ) { return new WP_Error( 'invalid_provider_key', 'Invalid provider key' ); } if ( ! isset( $this->state['providers'][ $provider_index ]['dismissed_errors'] ) ) { $this->state['providers'][ $provider_index ]['dismissed_errors'] = array(); } if ( $dismissed ) { if ( ! in_array( $error_type, $this->state['providers'][ $provider_index ]['dismissed_errors'], true ) ) { $this->state['providers'][ $provider_index ]['dismissed_errors'][] = $error_type; } } else { $key = array_search( $error_type, $this->state['providers'][ $provider_index ]['dismissed_errors'], true ); if ( $key !== false ) { unset( $this->state['providers'][ $provider_index ]['dismissed_errors'][ $key ] ); $this->state['providers'][ $provider_index ]['dismissed_errors'] = array_values( $this->state['providers'][ $provider_index ]['dismissed_errors'] ); } } return true; } /** * Update a provider's state. The provider must already exist in the state to be updated. * * @param string $provider_key The provider key. * @param array $state An array to overlay over the current state. * @return bool|WP_Error True on success, WP_Error on failure. */ private function update_provider_state( $provider_key, $state ) { if ( empty( $this->state['providers'] ) ) { return new WP_Error( 'invalid_provider_key', 'No providers exist' ); } $provider_index = array_search( $provider_key, array_column( $this->state['providers'], 'key' ), true ); if ( $provider_index === false ) { return new WP_Error( 'invalid_provider_key', 'Invalid provider key' ); } $this->state['providers'][ $provider_index ] = array_merge( $this->state['providers'][ $provider_index ], $state ); $this->maybe_set_generated(); return true; } /** * Set a provider's state to error. * * @param string $provider_key The provider key. * @param array $errors A list of errors to store with this provider. * @return bool|WP_Error True on success, WP_Error on failure. */ public function set_provider_errors( $provider_key, $errors ) { return $this->update_provider_state( $provider_key, array( 'status' => self::PROVIDER_STATES['error'], 'errors' => $errors, ) ); } /** * Set a provider's state to success. * * @param string $provider_key The provider key. * @return bool|WP_Error True on success, WP_Error on failure. */ public function set_provider_success( $provider_key ) { return $this->update_provider_state( $provider_key, array( 'status' => self::PROVIDER_STATES['success'], ) ); } /** * Set the state to generated if all providers are done. Should be called wherever * a provider's state is updated. */ private function maybe_set_generated() { if ( empty( $this->state['providers'] ) ) { return; } $provider_states = array_column( $this->state['providers'], 'status' ); $is_done = ! in_array( self::GENERATION_STATES['pending'], $provider_states, true ); if ( $is_done ) { $this->state['status'] = self::GENERATION_STATES['generated']; } } public function has_errors() { // Check if any of the providers have errors as well. $any_provider_has_error = in_array( 'error', array_unique( wp_list_pluck( $this->state['providers'], 'status' ) ), true ); return self::GENERATION_STATES['error'] === $this->state['status'] || $any_provider_has_error; } public function get_error_message() { return isset( $this->state['status_error'] ) ? $this->state['status_error'] : null; } public function is_generated() { return self::GENERATION_STATES['generated'] === $this->state['status']; } public function is_requesting() { return self::GENERATION_STATES['pending'] === $this->state['status']; } public function prepare_request() { $this->state = array( 'status' => self::GENERATION_STATES['pending'], 'providers' => array(), 'created' => microtime( true ), 'updated' => microtime( true ), ); return $this; } public function set_pending_providers( $providers ) { foreach ( $providers as $key => $provider ) { $providers[ $key ]['status'] = self::PROVIDER_STATES['pending']; } $this->state['providers'] = $providers; return $this; } /** * Add providers to the state, sets their status to pending * and sets the generation status to pending. * * @param array $providers The providers to include in the state and set as pending. * @return $this */ public function prepare_for_generation( $providers ) { $this->set_pending_providers( $providers ); $this->state['status'] = self::GENERATION_STATES['pending']; return $this; } /** * Get fresh state */ public function get() { $this->state = jetpack_boost_ds_get( 'critical_css_state' ); return $this->state; } public function has_pending_provider( $needles = array() ) { if ( empty( $this->state['providers'] ) ) { return false; } $providers = $this->state['providers']; foreach ( $providers as $provider ) { if ( ! empty( $provider['key'] ) && ! empty( $provider['status'] ) && self::PROVIDER_STATES['pending'] === $provider['status'] && in_array( $provider['key'], $needles, true ) ) { return true; } } return false; } }