null, 'error' => 'no_page', 'fetched_at' => null ); } $cached = get_option( 'yopingme_status_cache', array() ); // A different slug's payload is not this page's news. if ( is_array( $cached ) && isset( $cached['slug'] ) && $cached['slug'] !== $slug ) { self::flush(); $cached = array(); } $lock = get_transient( 'yopingme_status_fresh' ); if ( false !== $lock ) { // Handle old-format string locks defensively (treat as absent). if ( is_string( $lock ) ) { $lock = false; } elseif ( is_array( $lock ) && isset( $lock['slug'] ) && $lock['slug'] === $slug ) { // Slug matches; lock is valid for this slug. An 'inflight' lock // (see below) is handled the same as any other non-'ok' state: // last-good data keeps rendering while a concurrent request is // out fetching. return self::result( $cached, 'ok' === $lock['state'] ? null : (string) $lock['state'] ); } else { // Slug mismatch or invalid format; treat lock as absent and proceed to fetch. $lock = false; } } // Stampede guard: this runs on public page render, so a page that // suddenly gets a burst of concurrent visitors after the lock expires // must not send a burst of concurrent requests to the same upstream // URL. Claim the slot with a short 'inflight' lock BEFORE the request // goes out; any render that lands while this one is in flight sees // the lock above and serves last-good instead of piling on. The // fetch's real outcome overwrites this lock below, whatever it is. set_transient( 'yopingme_status_fresh', array( 'state' => 'inflight', 'slug' => $slug ), 15 ); $response = wp_remote_get( YOPINGME_APP_URL . 'public/status-pages/' . rawurlencode( $slug ), array( 'timeout' => 4, 'headers' => array( 'Accept' => 'application/json' ), ) ); if ( is_wp_error( $response ) ) { set_transient( 'yopingme_status_fresh', array( 'state' => 'unreachable', 'slug' => $slug ), self::ERROR_TTL ); return self::result( $cached, 'unreachable' ); } $code = wp_remote_retrieve_response_code( $response ); if ( 404 === $code ) { // The page is gone or disabled: the mandatory-link contract says // the shortcode renders nothing, so the stale payload must not // keep the strip alive. Drop it. self::flush(); set_transient( 'yopingme_status_fresh', array( 'state' => 'not_found', 'slug' => $slug ), self::NOT_FOUND_TTL ); return array( 'data' => null, 'error' => 'not_found', 'fetched_at' => null ); } if ( 200 !== $code ) { set_transient( 'yopingme_status_fresh', array( 'state' => 'http_' . $code, 'slug' => $slug ), self::ERROR_TTL ); return self::result( $cached, 'http_' . $code ); } $doc = json_decode( wp_remote_retrieve_body( $response ), true ); if ( ! is_array( $doc ) || ! isset( $doc['page'] ) || ! isset( $doc['services'] ) ) { set_transient( 'yopingme_status_fresh', array( 'state' => 'bad_response', 'slug' => $slug ), self::ERROR_TTL ); return self::result( $cached, 'bad_response' ); } $fresh = array( 'slug' => $slug, 'payload' => $doc, 'fetched_at' => time(), ); update_option( 'yopingme_status_cache', $fresh, false ); set_transient( 'yopingme_status_fresh', array( 'state' => 'ok', 'slug' => $slug ), self::TTL ); return self::result( $fresh, null ); } /** * Drop the cached document and its lock (slug change, key change, upgrade). */ public static function flush() { delete_option( 'yopingme_status_cache' ); delete_transient( 'yopingme_status_fresh' ); } /** * @param array|mixed $cached Cached payload wrapper. * @param string|null $error Current error code, if any. * @return array */ private static function result( $cached, $error ) { $data = ( is_array( $cached ) && isset( $cached['payload'] ) && is_array( $cached['payload'] ) ) ? $cached['payload'] : null; return array( 'data' => $data, 'error' => $error, 'fetched_at' => ( $data && ! empty( $cached['fetched_at'] ) ) ? (int) $cached['fetched_at'] : null, ); } }