← All changes
|
includes/integrations/class-google-pagespeed-client.php
+46
-1
2.0.1
→
2.7.0
View file →
| @@ -111,8 +111,34 @@ | ||
| 111 | 111 | return new self('', $timeout ?? self::DEFAULT_TIMEOUT, $access_token !== '' ? $access_token : null); |
| 112 | 112 | } |
| 113 | 113 | |
| 114 | 114 | /** |
| 115 | + * Whether the site holds a credential the PageSpeed API will accept. | |
| 116 | + * | |
| 117 | + * The counterpart to for_site(): either of the first two rungs of its auth | |
| 118 | + * order is enough, and a caller that wants to refuse the keyless third rung | |
| 119 | + * asks this rather than testing one credential itself. Callers that did the | |
| 120 | + * latter locked out every site configured with only an API key — the | |
| 121 | + * credential for_site() actually *prefers*, since a dedicated key bills its | |
| 122 | + * own project quota (#519). | |
| 123 | + * | |
| 124 | + * @since 2.1.1 | |
| 125 | + * | |
| 126 | + * @return bool True when an API key or an OAuth token is configured. | |
| 127 | + */ | |
| 128 | + public static function site_has_credentials(): bool { | |
| 129 | + if (!class_exists('\\ThinkRank\\Core\\Settings')) { | |
| 130 | + return false; | |
| 131 | + } | |
| 132 | + | |
| 133 | + $settings = new \ThinkRank\Core\Settings(); | |
| 134 | + | |
| 135 | + // OAuth tokens are encrypted at rest; Settings::get() decrypts them. | |
| 136 | + return '' !== trim((string) $settings->get('google_pagespeed_api_key', '')) | |
| 137 | + || '' !== trim((string) $settings->get('google_access_token', '')); | |
| 138 | + } | |
| 139 | + | |
| 140 | + /** | |
| 115 | 141 | * Run PageSpeed test for a URL |
| 116 | 142 | * |
| 117 | 143 | * @param string $url URL to test |
| 118 | 144 | * @param string $strategy Device strategy ('mobile' or 'desktop') |
| @@ -185,13 +211,32 @@ | ||
| 185 | 211 | set_transient('thinkrank_psi_failure_' . $hash, $e->getMessage(), self::FAILURE_TTL); |
| 186 | 212 | throw $e; |
| 187 | 213 | } |
| 188 | 214 | |
| 215 | + // Lighthouse answers 200 with a populated lighthouseResult even when the | |
| 216 | + // audit itself failed (NO_FCP, ERRORED_DOCUMENT_REQUEST, …); the score | |
| 217 | + // then comes back null. Coercing that to 0 stored a failed run as a | |
| 218 | + // genuine "this site scores 0" measurement, which every consumer — | |
| 219 | + // the SEO score's mobile factor most visibly — has no way to tell from | |
| 220 | + // a real result. Treat it as the failure it is so the caller's existing | |
| 221 | + // failure handling applies. | |
| 222 | + $runtime_error = $result['lighthouseResult']['runtimeError']['code'] ?? ''; | |
| 223 | + $raw_score = $result['lighthouseResult']['categories']['performance']['score'] ?? null; | |
| 224 | + | |
| 225 | + if (('' !== $runtime_error && 'NO_ERROR' !== $runtime_error) || null === $raw_score) { | |
| 226 | + $message = $result['lighthouseResult']['runtimeError']['message'] | |
| 227 | + ?? __('PageSpeed Insights returned no performance score for this URL.', 'thinkrank'); | |
| 228 | + | |
| 229 | + set_transient('thinkrank_psi_failure_' . $hash, $message, self::FAILURE_TTL); | |
| 230 | + | |
| 231 | + throw new \Exception($message); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped | |
| 232 | + } | |
| 233 | + | |
| 189 | 234 | $snapshot = [ |
| 190 | 235 | 'core_web_vitals' => $this->parse_core_web_vitals($result), |
| 191 | 236 | 'opportunities' => $this->parse_opportunities($result), |
| 192 | 237 | 'diagnostics' => $this->parse_diagnostics($result), |
| 193 | - 'performance_score' => (float) (($result['lighthouseResult']['categories']['performance']['score'] ?? 0) * 100), | |
| 238 | + 'performance_score' => (float) ($raw_score * 100), | |
| 194 | 239 | 'loading_experience' => $result['loadingExperience'] ?? [], |
| 195 | 240 | 'fetched_at' => time(), |
| 196 | 241 | ]; |
| 197 | 242 | |