|null */ private static ?array $shadow_cache = null; /** * Enable shadow_read_only for a module (only meaningful in the verify state). * * @param string $module Module identifier. * @return bool True on success. */ public static function enable_shadow_read( string $module ): bool { $flags = self::all_shadow(); $flags[ $module ] = true; update_option( self::SHADOW_OPTION_KEY, $flags, false ); self::$shadow_cache = null; return true; } /** * Disable shadow_read_only for a module. * * @param string $module Module identifier. * @return bool */ public static function disable_shadow_read( string $module ): bool { $flags = self::all_shadow(); unset( $flags[ $module ] ); update_option( self::SHADOW_OPTION_KEY, $flags, false ); self::$shadow_cache = null; return true; } /** * Whether shadow_read_only is currently active for a module. * * Returns true only when: * - Module is in the `verify` state, AND * - Shadow flag has been explicitly enabled. * * @param string $module Module identifier. * @return bool */ public static function is_shadow_read_active( string $module ): bool { if ( 'verify' !== self::get( $module ) ) { return false; } $flags = self::all_shadow(); return ! empty( $flags[ $module ] ); } /** * Return all shadow flags. * * @return array */ public static function all_shadow(): array { if ( null !== self::$shadow_cache ) { return self::$shadow_cache; } $saved = get_option( self::SHADOW_OPTION_KEY, array() ); if ( ! is_array( $saved ) ) { $saved = array(); } self::$shadow_cache = $saved; return $saved; } /** * Check if a module is fully complete (reads and writes on custom table only). * * @param string $module Module identifier. * @return bool True if module state is 'complete'. */ public static function is_complete( string $module ): bool { return 'complete' === self::get( $module ); } /** * Check if dual-write is active for a module. * * @param string $module Module identifier. * @return bool True if module is in a write-active state. */ public static function is_write_active( string $module ): bool { return in_array( self::get( $module ), self::WRITE_ACTIVE_STATES, true ); } /** * Check if reads should come from the custom/zone table. * * @param string $module Module identifier. * @return bool True if module is in a read-custom state. */ public static function is_read_custom( string $module ): bool { return in_array( self::get( $module ), self::READ_CUSTOM_STATES, true ); } /** * Check if query interceptors should be active. * * @param string $module Module identifier. * @return bool True if module is in a query-active state. */ public static function is_query_active( string $module ): bool { return in_array( self::get( $module ), self::QUERY_ACTIVE_STATES, true ); } /** * Reset a module to idle (rollback). * * @param string $module Module identifier. * @return void */ public static function reset( string $module ): void { self::set( $module, 'idle' ); self::$cache = null; // Invalidate request-level cache. } /** * Request-level cache for all() to avoid repeated get_option() calls. * * @var array|null */ private static ?array $cache = null; /** * Return all module states. * * @return array */ public static function all(): array { if ( null !== self::$cache ) { return self::$cache; } $saved = get_option( self::OPTION_KEY, array() ); if ( ! is_array( $saved ) ) { $saved = array(); } $all_modules = array_merge( self::HPCT_MODULES, self::ZONE_MODULES ); $defaults = array_fill_keys( $all_modules, 'idle' ); self::$cache = array_merge( $defaults, $saved ); return self::$cache; } /** * Return only HPCT-inherited module states. * * @return array */ public static function hpct_modules(): array { $all = self::all(); return array_intersect_key( $all, array_flip( self::HPCT_MODULES ) ); } /** * Return only zone module states. * * @return array */ public static function zone_modules(): array { $all = self::all(); return array_intersect_key( $all, array_flip( self::ZONE_MODULES ) ); } /** * Map an HPCT feature flag status to a WPDO state. * * Used during HPCT import to translate HPCT's 4-state model * to WPDO's 7-state model. * * @param string $hpct_status HPCT status (disabled/migrating/verified/enabled). * @return string WPDO state. */ public static function map_hpct_status( string $hpct_status ): string { return match ( $hpct_status ) { 'disabled' => 'idle', 'migrating' => 'backfill', 'verified' => 'cutover', 'enabled' => 'complete', default => 'idle', }; } }