is_css_transient_caching_disabled() ) { return; } $date = isset( $args[0] ) && $args[0] instanceof DateTimeInterface ? $args[0] : new DateTimeImmutable(); $transient_count = isset( $args[1] ) ? (int) $args[1] : $this->query_css_transient_count(); $date_string = $date->format( 'Ymd' ); $time_series = $this->get_time_series(); $time_series[ $date_string ] = $transient_count; ksort( $time_series ); $sampling_range = $this->get_sampling_range(); $time_series = array_slice( $time_series, - $sampling_range, null, true ); $this->persist_time_series( $time_series ); $moving_average = $this->calculate_average( $time_series ); if ( $moving_average > 0.0 && $moving_average > (float) $this->get_threshold() ) { $this->disable_css_transient_caching(); } } /** * Check whether transient caching of stylesheets is disabled. * * @return bool Whether transient caching of stylesheets is disabled. */ private function is_css_transient_caching_disabled() { return AMP_Options_Manager::get_option( Option::DISABLE_CSS_TRANSIENT_CACHING, false ); } /** * Disable transient caching of stylesheets. */ private function disable_css_transient_caching() { AMP_Options_Manager::update_option( Option::DISABLE_CSS_TRANSIENT_CACHING, true ); } /** * Query the number of transients containing cache stylesheets. * * @return int Count of transients caching stylesheets. */ public function query_css_transient_count() { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE '_transient_amp-parsed-stylesheet%'" ); } /** * Handle update to plugin. * * @param string $old_version Old version. */ public function handle_plugin_update( $old_version ) { // Reset the disabling of the CSS caching subsystem when updating from versions 1.5.0 or 1.5.1. if ( version_compare( $old_version, '1.5.0', '>=' ) && version_compare( $old_version, '1.5.2', '<' ) ) { AMP_Options_Manager::update_option( Option::DISABLE_CSS_TRANSIENT_CACHING, false ); } } /** * Get the time series stored in the WordPress options table. * * @return int[] Time series with the count of transients per day. */ public function get_time_series() { return (array) get_option( self::TIME_SERIES_OPTION_KEY, [] ); } /** * Get the default threshold to use. * * @return float Default threshold to use. */ public function get_default_threshold() { return self::DEFAULT_THRESHOLD; } /** * Get the default sampling range to use. * * @return int Default sampling range to use. */ public function get_default_sampling_range() { return self::DEFAULT_SAMPLING_RANGE; } /** * Persist the time series in the database. * * @param int[] $time_series Associative array of integers with the key being a date string and the value the count * of transients. */ private function persist_time_series( $time_series ) { update_option( self::TIME_SERIES_OPTION_KEY, $time_series, false ); } /** * Calculate the average for the provided time series. * * Note: The single highest value is discarded to calculate the average, so as to avoid a single outlier causing the * threshold to be reached. * * @param int[] $time_series Associative array of integers with the key being a date string and the value the count * of transients. * @return float Average value for the provided time series. */ private function calculate_average( $time_series ) { $sum = array_sum( $time_series ); $sum_without_outlier = $sum - max( $time_series ); $count_without_outlier = count( $time_series ) - 1; if ( $count_without_outlier <= 0 ) { return 0.0; } return $sum_without_outlier / $count_without_outlier; } /** * Get the threshold to check the moving average against. * * This can be filtered via the 'amp_css_transient_monitoring_threshold' filter. * * @return float Threshold to use. */ private function get_threshold() { /** * Filters the threshold to use for disabling transient caching of stylesheets. * * @since 1.5.0 * * @param int $threshold Maximum average number of transients per day. */ $threshold = (float) apply_filters( 'amp_css_transient_monitoring_threshold', self::DEFAULT_THRESHOLD ); return $threshold > 0.0 ? $threshold : self::DEFAULT_THRESHOLD; } /** * Get the sampling range to limit the time series to for calculating the moving average. * * This can be filtered via the 'amp_css_transient_monitoring_sampling_range' filter. * * @return int Sampling range to use. */ private function get_sampling_range() { /** * Filters the sampling range to use for monitoring the transient caching of stylesheets. * * @since 1.5.0 * * @param int $sampling_rage Sampling range in number of days. */ $sampling_range = (int) apply_filters( 'amp_css_transient_monitoring_sampling_range', self::DEFAULT_SAMPLING_RANGE ); return $sampling_range > 0 ? $sampling_range : self::DEFAULT_SAMPLING_RANGE; } }