get_var( $wpdb->prepare( "SELECT 1 FROM $sessions WHERE day = %s AND visitor_hash = %s LIMIT 1", // phpcs:ignore WordPress.DB.PreparedSQL $today, $visitor_hash ) ); return (bool) $exists; } /** * Current day's salt, creating/rotating it if the day has turned. * * @return string */ private static function current_salt() { $store = get_option( self::SALT_OPTION ); $today = sxanl_site_date(); if ( is_array( $store ) && isset( $store['day'], $store['salt'] ) && $store['day'] === $today ) { return (string) $store['salt']; } return self::rotate_salt(); } /** * Generate a new salt for today, keeping yesterday's only long enough for * an in-flight request boundary, then persist. * * @return string the new salt */ public static function rotate_salt() { $today = sxanl_site_date(); $salt = bin2hex( self::random_bytes( 32 ) ); $prev = get_option( self::SALT_OPTION ); $store = array( 'day' => $today, 'salt' => $salt, 'prev_day' => is_array( $prev ) && isset( $prev['day'] ) ? $prev['day'] : '', 'prev_salt' => is_array( $prev ) && isset( $prev['salt'] ) ? $prev['salt'] : '', ); update_option( self::SALT_OPTION, $store, false ); SXANL_Settings::update( array( 'hash_salt_rotated' => $today ) ); return $salt; } /** * Random bytes with graceful fallback. * * @param int $len Bytes. * @return string */ private static function random_bytes( $len ) { try { return random_bytes( $len ); } catch ( \Exception $e ) { return wp_generate_password( $len, true, true ); } } }