prefix ); } public static function acquire(): bool { global $wpdb; if ( self::$held ) { return true; } self::$held = '1' === (string) $wpdb->get_var( $wpdb->prepare( 'SELECT GET_LOCK(%s, 5)', self::name() ) ); if ( self::$held ) { // wp_send_json/wp_die/redirect exits do not execute PHP finally blocks. register_shutdown_function( array( self::class, 'release' ) ); // Bootstrap may have cached options before another request finished saving. // Refresh only when entering the critical section, never on re-entry. wp_cache_delete( 'alloptions', 'options' ); wp_cache_delete( 'notoptions', 'options' ); wp_cache_delete( 'abientot_settings', 'options' ); wp_cache_delete( 'abientot_location_weekly_schedule', 'options' ); } return self::$held; } /** Hold through the complete configuration operation; shutdown releases on exits. */ public static function require_configuration_lock(): void { if ( self::acquire() ) { return; } wp_die( esc_html__( 'Another booking is being saved. Please try again.', 'abientot' ), '', array( 'response' => 409, 'back_link' => true ) ); } public static function release(): void { global $wpdb; if ( self::$held ) { $suppressed = $wpdb->suppress_errors( true ); try { $released = $wpdb->get_var( $wpdb->prepare( 'SELECT RELEASE_LOCK(%s)', self::name() ) ); } catch ( \Throwable $error ) { return; } finally { $wpdb->suppress_errors( $suppressed ); } // Retain ownership and queued callbacks when release is unconfirmed. // Shutdown retries; a persisted booking is never reported as a rollback. if ( '1' !== (string) $released ) { return; } self::$held = false; try { do_action( 'abientot_booking_lock_released' ); } catch ( \Throwable $error ) { } } } }