PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 7.1.2
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v7.1.2
7.2.1 7.2 7.1.2 7.1.1 7.1 7.0.4 7.0.6 7.0.7 6.3.8 6.3.7 6.3.6 6.3.5 6.3.4 6.3.3 6.3.1 trunk 5.7.3 5.7.5 5.8.1 5.8.2 5.8.3 5.8.4 5.8.6 6.0.4 6.0.5 All 36 releases
mlsimport / includes / live / live-cache.php

live-cache.php in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 7.1.2, at includes/live/live-cache.php

146 lines 5.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Live mode: transient cache for direct MLS reads.
4 *
5 * One idiom: mlsimport_live_remember( $key, $fetch ). Each entry stores its
6 * payload plus a freshness deadline inside a transient that lives a full day,
7 * so an MLS outage serves warm-stale data instead of an empty page. Keys hash
8 * the normalized request (sorted params, floats rounded) so map pans and
9 * equivalent filter orders reuse entries. Cleanup rides WP's daily
10 * expired-transient purge; "Clear live cache" bumps a generation salt so
11 * every keyed entry is retired at once — no options scan, no object-cache
12 * flush (which would evict unrelated site cache on persistent backends).
13 *
14 * @package Mlsimport
15 */
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit;
19 }
20
21 /**
22 * Build a cache key from a normalized request description.
23 *
24 * @param string $group Short group tag (search, get, …).
25 * @param array $parts Request-defining values (params + config essentials).
26 * @return string
27 */
28 function mlsimport_live_cache_key( string $group, array $parts ): string {
29 // Group tag + md5 of (generation salt | normalized request). The salt makes
30 // a cache-clear retire every key; normalization makes equivalent requests
31 // collapse to one key.
32 return 'mlsimport_live_' . $group . '_' . md5(
33 mlsimport_live_cache_version() . '|' . (string) wp_json_encode( mlsimport_live_cache_normalize( $parts ) )
34 );
35 }
36
37 /**
38 * The cache generation salt baked into every keyed entry. Bumping it retires
39 * all keyed entries at once; the orphaned rows expire with their day TTL.
40 *
41 * @return int
42 */
43 function mlsimport_live_cache_version(): int {
44 // Read the salt option; floor at 1 so a missing/zeroed option is still valid.
45 return max( 1, (int) get_option( 'mlsimport_live_cache_version', 1 ) );
46 }
47
48 /**
49 * Normalize request values for stable cache keys: recursive key sort and
50 * floats rounded to 3 decimals (~110m) so tiny map-pan deltas share entries.
51 *
52 * @param array $parts Request values.
53 * @return array
54 */
55 function mlsimport_live_cache_normalize( array $parts ): array {
56 $out = array();
57 // Walk every value, normalizing in place.
58 foreach ( $parts as $key => $value ) {
59 if ( is_array( $value ) ) {
60 // Nested arrays (e.g. multi-value filters): recurse.
61 $out[ $key ] = mlsimport_live_cache_normalize( $value );
62 } elseif ( is_float( $value ) || ( is_string( $value ) && is_numeric( $value ) && false !== strpos( $value, '.' ) ) ) {
63 // A real float, or a numeric string carrying a decimal point (map
64 // coords arrive as strings): round to 3dp so tiny pan deltas match.
65 $out[ $key ] = round( (float) $value, 3 );
66 } else {
67 // Ints, bools, plain strings: keep verbatim.
68 $out[ $key ] = $value;
69 }
70 }
71 // Sort by key so param order never changes the resulting key.
72 ksort( $out );
73 return $out;
74 }
75
76 /**
77 * Return the cached value for $key, refreshing it via $fetch when stale.
78 *
79 * $fetch returning null means "the source failed" — the stale payload (when
80 * one exists) is served instead and retried on the next request.
81 *
82 * @param string $key Cache key from mlsimport_live_cache_key().
83 * @param callable $fetch Returns the fresh value, or null on failure.
84 * @return mixed Null only when there is no fresh value and no stale copy.
85 */
86 function mlsimport_live_remember( string $key, callable $fetch ) {
87 // Load any existing entry and pin "now" for the freshness comparison.
88 $entry = get_transient( $key );
89 $now = time();
90
91 // Fresh hit: a well-formed entry still inside its freshness window — return
92 // the payload without touching the source.
93 if ( is_array( $entry ) && array_key_exists( 'data', $entry ) && isset( $entry['fresh_until'] ) && $entry['fresh_until'] >= $now ) {
94 return $entry['data'];
95 }
96
97 // Miss or stale: go to the source.
98 $fresh = $fetch();
99 if ( null !== $fresh ) {
100 // Success: store payload + a fresh-until deadline, but let the whole
101 // row live a full day so it can still be served warm-stale after.
102 set_transient(
103 $key,
104 array(
105 'data' => $fresh,
106 'fresh_until' => $now + mlsimport_live_cache_ttl(),
107 ),
108 DAY_IN_SECONDS
109 );
110 return $fresh;
111 }
112
113 // Source failed: serve warm-stale when we have it.
114 if ( is_array( $entry ) && array_key_exists( 'data', $entry ) ) {
115 return $entry['data'];
116 }
117
118 // No fresh value and no stale copy: the caller gets nothing.
119 return null;
120 }
121
122 /**
123 * Retire every live-mode cache entry (the settings screen button).
124 *
125 * Keyed entries are invalidated by bumping the generation salt. The handful
126 * of fixed-name transients (the entitlement flag, price ceiling, provider
127 * tokens) are deleted directly — the API also evicts them from persistent
128 * object caches.
129 *
130 * @return void
131 */
132 function mlsimport_live_cache_clear(): void {
133 // Bump the generation salt: every keyed entry now hashes to a new key and
134 // is effectively retired (the orphans expire with their day TTL).
135 update_option( 'mlsimport_live_cache_version', mlsimport_live_cache_version() + 1 );
136
137 // Fixed-name shared transients aren't keyed by the salt, so delete them by
138 // hand. The Provider Family module owns the provider-token list.
139 $fixed = array( 'mlsimport_live_entitlement_checked', 'mlsimport_live_price_ceiling' );
140 // Delete each — this also evicts it from a persistent object cache.
141 foreach ( $fixed as $name ) {
142 delete_transient( $name );
143 }
144 Mlsimport_Provider_Family::clear_direct_access_tokens();
145 }
146