user_email ) ) { return; } $donor = Donors::get_by_email( $user->user_email ); if ( ! $donor || empty( $donor['id'] ) ) { return; } $donor_id = is_numeric( $donor['id'] ) ? (int) $donor['id'] : 0; if ( $donor_id <= 0 ) { return; } // Already linked to this user. $existing_user_id = isset( $donor['user_id'] ) && is_numeric( $donor['user_id'] ) ? (int) $donor['user_id'] : 0; if ( $existing_user_id === $user->ID ) { return; } // Link the donor to this WP user (only if not linked to a different user). // Direct $wpdb->update so the WHERE clause can carry the // "still unlinked" condition atomically. A read-then-write via // Donors::update() would race against another concurrent login // that shares this email (email-change flows, shared-mailbox // households) and could silently overwrite a link the other // request just established. if ( $existing_user_id <= 0 ) { global $wpdb; $donors_table = $wpdb->prefix . 'suredonation_donors'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Atomic conditional update; the abstraction layer doesn't support multi-column WHERE. $wpdb->update( $donors_table, [ 'user_id' => $user->ID, 'updated_at' => current_time( 'mysql' ), ], [ 'id' => $donor_id, 'user_id' => 0, ], [ '%d', '%s' ], [ '%d', '%d' ] ); } } }