| @@ -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 ); |
| @@ -428,9 +437,15 @@ | ||
| 428 | 437 | * |
| 429 | 438 | * @param array<string,mixed> $row A parsed run. |
| 430 | 439 | */ |
| 431 | 440 | public static function record( array $row ): void { |
| 432 | - $history = self::history(); | |
| 441 | + // The table is the store. It is created on activation and on | |
| 442 | + // admin_init, but record() can run from CLI on a site that has done | |
| 443 | + // neither yet, so make sure it exists before writing. | |
| 444 | + Score_Store::maybe_install(); | |
| 445 | + Score_Store::insert( $row, isset( $row['source'] ) ? (string) $row['source'] : 'local' ); | |
| 446 | + | |
| 447 | + $history = self::history_option(); | |
| 433 | 448 | array_unshift( $history, $row ); |
| 434 | 449 | if ( count( $history ) > self::MAX_HISTORY ) { |
| 435 | 450 | $history = array_slice( $history, 0, self::MAX_HISTORY ); |
| 436 | 451 | } |
| @@ -447,8 +462,27 @@ | ||
| 447 | 462 | * |
| 448 | 463 | * @return array<int,array<string,mixed>> |
| 449 | 464 | */ |
| 450 | 465 | public static function history(): array { |
| 466 | + Score_Store::maybe_install(); | |
| 467 | + $rows = Score_Store::history( self::MAX_HISTORY ); | |
| 468 | + if ( ! empty( $rows ) ) { | |
| 469 | + return $rows; | |
| 470 | + } | |
| 471 | + // Empty table on a site whose migration has not run yet — fall back | |
| 472 | + // so the panel never looks like it lost the user's history. | |
| 473 | + return self::history_option(); | |
| 474 | + } | |
| 475 | + | |
| 476 | + /** | |
| 477 | + * The legacy option-based history. | |
| 478 | + * | |
| 479 | + * Retained for the one-time migration in Score_Store and as a fallback, | |
| 480 | + * NOT as a second source of truth. Nothing else should call it. | |
| 481 | + * | |
| 482 | + * @return array<int,array<string,mixed>> | |
| 483 | + */ | |
| 484 | + private static function history_option(): array { | |
| 451 | 485 | $raw = get_option( self::HISTORY_OPTION, array() ); |
| 452 | 486 | if ( ! is_array( $raw ) ) { |
| 453 | 487 | return array(); |
| 454 | 488 | } |
| @@ -460,11 +494,30 @@ | ||
| 460 | 494 | } |
| 461 | 495 | return $out; |
| 462 | 496 | } |
| 463 | 497 | |
| 464 | - /** 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 | + */ | |
| 465 | 512 | public static function latest(): ?array { |
| 466 | - 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 ) { | |
| 467 | 520 | if ( ! empty( $row['ok'] ) ) { |
| 468 | 521 | return $row; |
| 469 | 522 | } |
| 470 | 523 | } |