|null */ public function readForProduct( int $productId ): ?array { if ( $productId <= 0 ) { return null; } $meta = $this->metaRepository->findByProductAndKey( $productId, self::META_KEY_LINK_HEALTH_STATUS ); if ( $meta === null || $meta->metaValue() === null ) { return null; } $data = json_decode( $meta->metaValue(), true ); return is_array( $data ) ? $data : null; } /** * Persist one Cloud result for a product. * * @param int $productId Product id. * @param CloudLinkHealthResult $result Cloud result. * @param DateTimeImmutable $recordedAt Local recorded-at instant. */ public function persistResult( int $productId, CloudLinkHealthResult $result, DateTimeImmutable $recordedAt ): bool { if ( $productId <= 0 ) { return false; } if ( $result->status() === 'unchecked' || self::timestampFromString( $result->checkedAtIso() ) === null ) { return false; } $payload = array( 'url' => $result->url(), 'status' => $result->status(), 'http_status' => $result->httpStatus(), 'message' => $result->message(), 'checked_at' => $result->checkedAtIso(), 'recorded_at' => $recordedAt->format( DATE_ATOM ), ); $value = self::jsonEncode( $payload ); if ( $value === null ) { return false; } $existing = $this->metaRepository->findByProductAndKey( $productId, self::META_KEY_LINK_HEALTH_STATUS ); if ( $existing === null ) { $this->metaRepository->save( ProductMetaRecord::createNew( $productId, self::META_KEY_LINK_HEALTH_STATUS, $recordedAt, array( 'metaValue' => $value ) ) ); return true; } if ( self::isOlderThanExisting( $result, $existing ) ) { return false; } $this->metaRepository->save( $existing->withMetaValue( $value )->withUpdatedAt( $recordedAt ) ); return true; } /** * Persist all result rows that can be attributed to product ids. * * @param list $results Cloud results. * @param array> $contexts URL contexts. * @param DateTimeImmutable $recordedAt Local recorded-at instant. */ public function persistResults( array $results, array $contexts, DateTimeImmutable $recordedAt ): int { $persisted = 0; foreach ( $results as $result ) { $context = $contexts[ $result->url() ] ?? array(); $productId = $context['product_id'] ?? null; if ( is_string( $productId ) && ctype_digit( $productId ) ) { $productId = (int) $productId; } if ( ! is_int( $productId ) || $productId <= 0 ) { continue; } if ( $this->persistResult( $productId, $result, $recordedAt ) ) { ++$persisted; } } return $persisted; } /** * Whether a product has a status payload fresher than the TTL. * * @param int $productId Product id. * @param DateTimeImmutable $now Current instant. * @param int $ttlSeconds Freshness TTL in seconds. */ public function isFresh( int $productId, DateTimeImmutable $now, int $ttlSeconds ): bool { if ( $ttlSeconds <= 0 ) { return false; } $data = $this->readForProduct( $productId ); if ( $data === null ) { return false; } $stamp = is_scalar( $data['recorded_at'] ?? null ) ? (string) $data['recorded_at'] : ''; if ( $stamp === '' ) { $stamp = is_scalar( $data['checked_at'] ?? null ) ? (string) $data['checked_at'] : ''; } if ( $stamp === '' ) { return false; } try { $recordedAt = new DateTimeImmutable( $stamp ); } catch ( Exception $unused ) { unset( $unused ); return false; } return $recordedAt->getTimestamp() >= ( $now->getTimestamp() - $ttlSeconds ); } /** * JSON-encode a product-meta payload. * * @param array $payload Payload. */ private static function jsonEncode( array $payload ): ?string { if ( function_exists( 'wp_json_encode' ) ) { $value = wp_json_encode( $payload ); } else { // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode -- Pure-unit fallback when WordPress is unavailable. $value = json_encode( $payload ); } return is_string( $value ) ? $value : null; } /** * Whether an incoming result is older than an existing persisted status. * * @param CloudLinkHealthResult $result Incoming result. * @param ProductMetaRecord $existing Existing product meta. */ private static function isOlderThanExisting( CloudLinkHealthResult $result, ProductMetaRecord $existing ): bool { $incoming = self::timestampFromString( $result->checkedAtIso() ); if ( $incoming === null || $existing->metaValue() === null ) { return false; } $payload = json_decode( $existing->metaValue(), true ); if ( ! is_array( $payload ) ) { return false; } $existingCheckedAt = is_scalar( $payload['checked_at'] ?? null ) ? (string) $payload['checked_at'] : ''; $existingStamp = self::timestampFromString( $existingCheckedAt ); return $existingStamp !== null && $incoming < $existingStamp; } /** * Parse an ISO-ish timestamp. * * @param string $value Timestamp string. */ private static function timestampFromString( string $value ): ?int { $value = trim( $value ); if ( $value === '' ) { return null; } try { return ( new DateTimeImmutable( $value ) )->getTimestamp(); } catch ( Exception $unused ) { unset( $unused ); return null; } } }