PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.3.3
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.3.3
1.3.3 1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 All 29 releases
← All changes | includes/class-score.php +30 -2 1.1.71.3.3 View file →
@@ -193,8 +193,17 @@
193 193 /* translators: %d: HTTP status code. */
194 194 __( 'PageSpeed Insights returned HTTP %d.', 'xspeed' ),
195 195 $code
196 196 );
197 +
198 + // A keyless refusal is Google's shared anonymous pool running dry,
199 + // not a fault on this site — and Google's own sentence (project
200 + // numbers, quota metric names) reads like a broken plugin. Name
201 + // the two remedies instead. (#426)
202 + if ( '' === trim( $api_key ) && ( 429 === $code || preg_match( '/quota|rate limit/i', $message ) ) ) {
203 + $message = __( 'Google\'s shared anonymous PageSpeed quota is exhausted right now — this is not a problem with your site. Add a free PageSpeed API key in the Speed Test settings, or connect this site to xSpeed Hub to run tests through it.', 'xspeed' );
204 + }
205 +
197 206 return self::failure( 'psi', $url, $strategy, $message );
198 207 }
199 208
200 209 $row = self::parse_psi( $url, $strategy, $json );
@@ -485,11 +494,30 @@
485 494 }
486 495 return $out;
487 496 }
488 497
489 - /** Most recent successful run, or null. */
498 + /**
499 + * Most recent successful run, or null.
500 + *
501 + * Asks the store for the newest `ok` row rather than scanning the capped
502 + * history window. Failed runs are recorded too, so a site whose audits
503 + * keep failing — the unauthenticated PSI quota refuses often, and the key
504 + * is optional — would push its last real score out of the window after
505 + * MAX_HISTORY failures and then report no score at all (#306 review,
506 + * issue 1). Reproduced: one genuine audit of 91, then 30 failures, and
507 + * latest() returned null.
508 + *
509 + * The option fallback still scans, because that path has no query to make
510 + * and is only reached when the table is unavailable.
511 + */
490 512 public static function latest(): ?array {
491 - foreach ( self::history() as $row ) {
513 + Score_Store::maybe_install();
514 + $row = Score_Store::latest_ok();
515 + if ( is_array( $row ) ) {
516 + return $row;
517 + }
518 +
519 + foreach ( self::history_option() as $row ) {
492 520 if ( ! empty( $row['ok'] ) ) {
493 521 return $row;
494 522 }
495 523 }