UTC_TIMESTAMP() order by id desc limit 1', (string) $userId ); if (!$row || !isset($row['password_resets'])) { return null; } return $row['password_resets']; } public static function findById(int $id): ?array { $row = DB::selectOne( 'select id, user_id, code_hash, expires_at, attempts, used_at from password_resets where id = ? limit 1', (string) $id ); if (!$row || !isset($row['password_resets'])) { return null; } return $row['password_resets']; } public static function incrementAttempts(int $id): bool { $result = DB::update( 'update password_resets set attempts = attempts + 1 where id = ?', (string) $id ); return $result !== false; } public static function markUsed(int $id): bool { $result = DB::update( 'update password_resets set used_at = NOW() where id = ?', (string) $id ); return $result !== false; } public static function listByUserId(int $userId, int $limit = 20): array { if ($userId <= 0) { return []; } if ($limit < 1) { $limit = 20; } elseif ($limit > 100) { $limit = 100; } $rows = DB::select( 'select id, user_id, expires_at, attempts, used_at, created from password_resets where user_id = ? order by id desc limit ?', (string) $userId, (string) $limit ); return self::unwrapList($rows); } }