| 1 |
<?php |
| 2 |
/** |
| 3 |
* Per-connection option resolution helpers (issue #275, spec #273/#264). |
| 4 |
* |
| 5 |
* WHY THIS FILE EXISTS |
| 6 |
* -------------------- |
| 7 |
* With multi-MLS support the LARGE per-MLS state (field selection, sync |
| 8 |
* settings, metadata blobs, the metadata-populated flag) lives in |
| 9 |
* "_{mls_id}"-suffixed options — one copy per connection — created by the |
| 10 |
* #274 migration and owned per connection from #275 on. Every read, write, |
| 11 |
* and delete of that state must resolve the option NAME the same single way: |
| 12 |
* |
| 13 |
* base name + current (or explicit) mls_id => "base_{mls_id}" |
| 14 |
* |
| 15 |
* These helpers are that single way. Call sites never build suffixed names |
| 16 |
* by hand and never need to know which connection is current. |
| 17 |
* |
| 18 |
* RESOLUTION RULE (one rule, no layered fallbacks): |
| 19 |
* - mls_id given (> 0) => that connection's suffixed option. |
| 20 |
* - mls_id omitted (0) => the CURRENT connection's suffixed option, |
| 21 |
* current = mlsimport_admin_options['mlsimport_mls_name'] |
| 22 |
* (task-level binding arrives with #265). |
| 23 |
* - no current connection (0) => the flat legacy name, so an unconfigured |
| 24 |
* install behaves exactly as before multi-MLS. |
| 25 |
* |
| 26 |
* The flat legacy options are NOT read as a fallback for a configured install: |
| 27 |
* the #274 load-time migration has already copied them into the current |
| 28 |
* connection's suffixed options before any of these helpers run (it hooks |
| 29 |
* init priority 6; admin screens, AJAX, and cron all run later). |
| 30 |
* |
| 31 |
* @since 7.2.0 |
| 32 |
* @package Mlsimport |
| 33 |
*/ |
| 34 |
|
| 35 |
if ( ! defined( 'ABSPATH' ) ) { |
| 36 |
exit; |
| 37 |
} |
| 38 |
|
| 39 |
/** |
| 40 |
* Return the mls_id of the CURRENT connection. |
| 41 |
* |
| 42 |
* Step by step: |
| 43 |
* 1. Read the flat admin options (still the single "which MLS is selected" |
| 44 |
* source for every settings screen — decision #266 keeps it that way |
| 45 |
* until task binding in #265). |
| 46 |
* 2. Cast the stored MLS name (a numeric id string) to int. |
| 47 |
* 3. 0 means "no MLS configured yet". |
| 48 |
* |
| 49 |
* @return int Current mls_id, or 0 when the install is unconfigured. |
| 50 |
*/ |
| 51 |
function mlsimport_current_mls_id(): int { |
| 52 |
// Step 1: the selected MLS lives in the flat admin options. |
| 53 |
$options = get_option( 'mlsimport_admin_options', array() ); |
| 54 |
if ( ! is_array( $options ) ) { |
| 55 |
return 0; |
| 56 |
} |
| 57 |
|
| 58 |
// Step 2+3: the MLS "name" field holds the numeric MLS identifier. |
| 59 |
return (int) trim( (string) ( $options['mlsimport_mls_name'] ?? '' ) ); |
| 60 |
} |
| 61 |
|
| 62 |
/** |
| 63 |
* Resolve the concrete option name for one piece of per-connection state. |
| 64 |
* |
| 65 |
* Step by step: |
| 66 |
* 1. An explicit mls_id (> 0) wins; 0 means "the current connection". |
| 67 |
* 2. A resolved id > 0 yields the suffixed per-connection name. |
| 68 |
* 3. No connection at all (unconfigured install) yields the flat legacy |
| 69 |
* name so pre-multi-MLS behavior is preserved unchanged. |
| 70 |
* |
| 71 |
* @param string $base Flat (legacy) option name, e.g. 'mlsimport_admin_mls_sync'. |
| 72 |
* @param int $mls_id Explicit connection id, or 0 for the current one. |
| 73 |
* @return string The option name to read/write. |
| 74 |
*/ |
| 75 |
function mlsimport_connection_option_name( string $base, int $mls_id = 0 ): string { |
| 76 |
// Step 1: fall back to the current connection when no id was given. |
| 77 |
if ( $mls_id <= 0 ) { |
| 78 |
$mls_id = mlsimport_current_mls_id(); |
| 79 |
} |
| 80 |
|
| 81 |
// Step 2+3: suffixed when a connection exists, flat legacy name otherwise. |
| 82 |
return $mls_id > 0 ? Mlsimport_Connections::option_key( $base, $mls_id ) : $base; |
| 83 |
} |
| 84 |
|
| 85 |
/** |
| 86 |
* Read one piece of per-connection state. |
| 87 |
* |
| 88 |
* @param string $base Flat (legacy) option name. |
| 89 |
* @param mixed $default Returned when the resolved option is absent. |
| 90 |
* @param int $mls_id Explicit connection id, or 0 for the current one. |
| 91 |
* @return mixed Stored value or $default. |
| 92 |
*/ |
| 93 |
function mlsimport_get_connection_option( string $base, $default = false, int $mls_id = 0 ) { |
| 94 |
return get_option( mlsimport_connection_option_name( $base, $mls_id ), $default ); |
| 95 |
} |
| 96 |
|
| 97 |
/** |
| 98 |
* Write one piece of per-connection state. |
| 99 |
* |
| 100 |
* Step by step: |
| 101 |
* 1. Resolve the concrete option name. |
| 102 |
* 2. First write creates the row NON-autoloaded — per-connection blobs are |
| 103 |
* large and only needed on admin/import paths (same rule as the #274 |
| 104 |
* migration copies), never on every front-end request. |
| 105 |
* 3. Later writes update in place; update_option keeps the stored autoload. |
| 106 |
* |
| 107 |
* @param string $base Flat (legacy) option name. |
| 108 |
* @param mixed $value Value to store. |
| 109 |
* @param int $mls_id Explicit connection id, or 0 for the current one. |
| 110 |
* @return bool True when the option now holds the value. |
| 111 |
*/ |
| 112 |
function mlsimport_update_connection_option( string $base, $value, int $mls_id = 0 ): bool { |
| 113 |
// Step 1: one resolution path for every writer. |
| 114 |
$name = mlsimport_connection_option_name( $base, $mls_id ); |
| 115 |
|
| 116 |
// Step 2: create non-autoloaded on first write. |
| 117 |
if ( false === get_option( $name, false ) ) { |
| 118 |
return (bool) add_option( $name, $value, '', 'no' ); |
| 119 |
} |
| 120 |
|
| 121 |
// Step 3: update; false for an identical value still means "stored". |
| 122 |
update_option( $name, $value, false ); |
| 123 |
return get_option( $name ) === $value; |
| 124 |
} |
| 125 |
|
| 126 |
/** |
| 127 |
* Delete one piece of per-connection state. |
| 128 |
* |
| 129 |
* Used by the "force a fresh metadata gather / reset field mapping" paths; |
| 130 |
* the delete is scoped to ONE connection so other connections' gathered |
| 131 |
* state and per-field customizations stay isolated (#264). |
| 132 |
* |
| 133 |
* @param string $base Flat (legacy) option name. |
| 134 |
* @param int $mls_id Explicit connection id, or 0 for the current one. |
| 135 |
* @return bool True when a stored option was deleted. |
| 136 |
*/ |
| 137 |
function mlsimport_delete_connection_option( string $base, int $mls_id = 0 ): bool { |
| 138 |
return delete_option( mlsimport_connection_option_name( $base, $mls_id ) ); |
| 139 |
} |
| 140 |
|