query( $wpdb->prepare( "INSERT IGNORE INTO %i (option_name, option_value, autoload) VALUES (%s, %s, 'off')", $wpdb->options, $name, $token ) ); if ( ! $taken ) { // A request that died leaves its lock: take it over once it expired. $taken = (int) $wpdb->query( $wpdb->prepare( "UPDATE %i SET option_value = %s WHERE option_name = %s AND CAST( SUBSTRING_INDEX( option_value, ':', 1 ) AS UNSIGNED ) < %d", $wpdb->options, $token, $name, $now ) ); } // phpcs:enable self::forget_cache( $name ); return $taken ? $token : ''; } /** * Release a lock, only when it is still ours (its value equals the token). * * @since 1.0.0 * @param string $name Option name of the lock row. * @param string $token Token returned by acquire(). * @return void */ public static function release( string $name, string $token ): void { global $wpdb; if ( '' === $name || '' === $token ) { return; } $wpdb->query( $wpdb->prepare( 'DELETE FROM %i WHERE option_name = %s AND option_value = %s', $wpdb->options, $name, $token ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- atomic lock row; must not be cached. self::forget_cache( $name ); } /** * Remove a lock whoever holds it (deactivate, Data section). * * @since 1.0.0 * @param string $name Option name of the lock row. * @return void */ public static function clear( string $name ): void { global $wpdb; if ( '' === $name ) { return; } $wpdb->query( $wpdb->prepare( 'DELETE FROM %i WHERE option_name = %s', $wpdb->options, $name ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- atomic lock row; must not be cached. self::forget_cache( $name ); } /** * Drop cached copies of the lock option (it's written with raw queries). * * @since 1.0.0 * @param string $name Option name. * @return void */ private static function forget_cache( string $name ): void { wp_cache_delete( $name, 'options' ); $missing = wp_cache_get( 'notoptions', 'options' ); if ( is_array( $missing ) && isset( $missing[ $name ] ) ) { unset( $missing[ $name ] ); wp_cache_set( 'notoptions', $missing, 'options' ); } } }