cache = new DbStatementCache($dbConn); } public function countAttempts( ?bool $success = null, UserInfo|string|null $userInfo = null, IPAddress|string|null $remoteAddr = null, TimeSpan|int|null $timeRange = null ): int { if($userInfo instanceof UserInfo) $userInfo = $userInfo->getId(); if($remoteAddr instanceof IPAddress) $remoteAddr = (string)$remoteAddr; if($timeRange instanceof TimeSpan) $timeRange = (int)$timeRange->totalSeconds(); $hasSuccess = $success !== null; $hasUserInfo = $userInfo !== null; $hasRemoteAddr = $remoteAddr !== null; $hasTimeRange = $timeRange !== null; $args = 0; $query = 'SELECT COUNT(*) FROM msz_login_attempts'; if($hasSuccess) { ++$args; $query .= sprintf(' WHERE attempt_success %s 0', $success ? '<>' : '='); } if($hasUserInfo) $query .= sprintf(' %s user_id = ?', ++$args > 1 ? 'AND' : 'WHERE'); if($hasRemoteAddr) $query .= sprintf(' %s attempt_ip = INET6_ATON(?)', ++$args > 1 ? 'AND' : 'WHERE'); if($hasTimeRange) $query .= sprintf(' %s attempt_created > NOW() - INTERVAL ? SECOND', ++$args > 1 ? 'AND' : 'WHERE'); $args = 0; $stmt = $this->cache->get($query); if($hasUserInfo) $stmt->addParameter(++$args, $userInfo); if($hasRemoteAddr) $stmt->addParameter(++$args, $remoteAddr); if($hasTimeRange) $stmt->addParameter(++$args, $timeRange); $stmt->execute(); $result = $stmt->getResult(); $count = 0; if($result->next()) $count = $result->getInteger(0); return $count; } public function countRemainingAttempts(IPAddress|string $remoteAddr): int { return self::REMAINING_MAX - $this->countAttempts( success: false, timeRange: self::REMAINING_WINDOW, remoteAddr: $remoteAddr ); } public function getAttempts( ?bool $success = null, UserInfo|string|null $userInfo = null, IPAddress|string|null $remoteAddr = null, TimeSpan|int|null $timeRange = null, ?Pagination $pagination = null ): iterable { if($userInfo instanceof UserInfo) $userInfo = $userInfo->getId(); if($remoteAddr instanceof IPAddress) $remoteAddr = (string)$remoteAddr; if($timeRange instanceof TimeSpan) $timeRange = (int)$timeRange->totalSeconds(); $hasSuccess = $success !== null; $hasUserInfo = $userInfo !== null; $hasRemoteAddr = $remoteAddr !== null; $hasTimeRange = $timeRange !== null; $hasPagination = $pagination !== null; $args = 0; $query = 'SELECT user_id, attempt_success, INET6_NTOA(attempt_ip), attempt_country, UNIX_TIMESTAMP(attempt_created), attempt_user_agent, attempt_client_info FROM msz_login_attempts'; if($hasSuccess) { ++$args; $query .= sprintf(' WHERE attempt_success %s 0', $success ? '<>' : '='); } if($hasUserInfo) $query .= sprintf(' %s user_id = ?', ++$args > 1 ? 'AND' : 'WHERE'); if($hasRemoteAddr) $query .= sprintf(' %s attempt_ip = INET6_ATON(?)', ++$args > 1 ? 'AND' : 'WHERE'); if($hasTimeRange) $query .= sprintf(' %s attempt_created > NOW() - INTERVAL ? SECOND', ++$args > 1 ? 'AND' : 'WHERE'); $query .= ' ORDER BY attempt_created DESC'; if($hasPagination) $query .= ' LIMIT ? OFFSET ?'; $args = 0; $stmt = $this->cache->get($query); if($hasUserInfo) $stmt->addParameter(++$args, $userInfo); if($hasRemoteAddr) $stmt->addParameter(++$args, $remoteAddr); if($hasTimeRange) $stmt->addParameter(++$args, $timeRange); if($hasPagination) { $stmt->addParameter(++$args, $pagination->getRange()); $stmt->addParameter(++$args, $pagination->getOffset()); } $stmt->execute(); return $stmt->getResult()->getIterator(LoginAttemptInfo::fromResult(...)); } public function recordAttempt( bool $success, IPAddress|string $remoteAddr, string $countryCode, string $userAgentString, ?ClientInfo $clientInfo = null, UserInfo|string|null $userInfo = null ): void { if($remoteAddr instanceof IPAddress) $remoteAddr = (string)$remoteAddr; if($userInfo instanceof UserInfo) $userInfo = $userInfo->getId(); $hasUserInfo = $userInfo !== null; $clientInfo = json_encode($clientInfo ?? ClientInfo::parse($userAgentString)); $stmt = $this->cache->get('INSERT INTO msz_login_attempts (user_id, attempt_success, attempt_ip, attempt_country, attempt_user_agent, attempt_client_info) VALUES (?, ?, INET6_ATON(?), ?, ?, ?)'); $stmt->addParameter(1, $userInfo); $stmt->addParameter(2, $success ? 1 : 0); $stmt->addParameter(3, $remoteAddr); $stmt->addParameter(4, $countryCode); $stmt->addParameter(5, $userAgentString); $stmt->addParameter(6, $clientInfo); $stmt->execute(); } }