user_id < 1 ? -1 : $this->user_id; } public function getUser(): ?User { if(!$this->userLookedUp && ($userId = $this->getUserId()) > 0) { $this->userLookedUp = true; try { $this->user = User::byId($userId); } catch(RuntimeException $ex) {} } return $this->user; } public function isSuccess(): bool { return boolval($this->attempt_success); } public function getRemoteAddress(): string { return $this->attempt_ip; } public function getCountry(): string { return $this->attempt_country; } public function getCountryName(): string { return get_country_name($this->getCountry()); } public function getCreatedTime(): int { return $this->attempt_created === null ? -1 : $this->attempt_created; } public function getUserAgent(): string { return $this->attempt_user_agent; } public function getClientInfo(): ClientInfo { return ClientInfo::decode($this->attempt_client_info); } public static function remaining(string $remoteAddr): int { return (int)DB::prepare( 'SELECT 5 - COUNT(*)' . ' FROM `' . DB::PREFIX . self::TABLE . '`' . ' WHERE `attempt_success` = 0' . ' AND `attempt_created` > NOW() - INTERVAL 1 HOUR' . ' AND `attempt_ip` = INET6_ATON(:remote_ip)' ) ->bind('remote_ip', $remoteAddr) ->fetchColumn(); } public static function create( string $remoteAddr, string $countryCode, bool $success, ?User $user = null, string $userAgent = null, ?ClientInfo $clientInfo = null ): void { $userAgent = $userAgent ?? filter_input(INPUT_SERVER, 'HTTP_USER_AGENT') ?? ''; $clientInfo ??= ClientInfo::parse($_SERVER); $createLog = DB::prepare( 'INSERT INTO `' . DB::PREFIX . self::TABLE . '` (`user_id`, `attempt_success`, `attempt_ip`, `attempt_country`, `attempt_user_agent`, `attempt_client_info`)' . ' VALUES (:user, :success, INET6_ATON(:ip), :country, :user_agent, :client_info)' ) ->bind('user', $user === null ? null : $user->getId()) // this null situation should never ever happen but better safe than sorry ! ->bind('success', $success ? 1 : 0) ->bind('ip', $remoteAddr) ->bind('country', $countryCode) ->bind('user_agent', $userAgent) ->bind('client_info', $clientInfo->encode()) ->execute(); } private static function countQueryBase(): string { return sprintf(self::QUERY_SELECT, 'COUNT(*)'); } public static function countAll(?User $user = null): int { $getCount = DB::prepare( self::countQueryBase() . ($user === null ? '' : ' WHERE `user_id` = :user') ); if($user !== null) $getCount->bind('user', $user->getId()); return (int)$getCount->fetchColumn(); } private static function byQueryBase(): string { return sprintf(self::QUERY_SELECT, sprintf(self::SELECT, self::TABLE)); } public static function all(?Pagination $pagination = null, ?User $user = null): array { $attemptsQuery = self::byQueryBase() . ($user === null ? '' : ' WHERE `user_id` = :user') . ' ORDER BY `attempt_created` DESC'; if($pagination !== null) $attemptsQuery .= ' LIMIT :range OFFSET :offset'; $getAttempts = DB::prepare($attemptsQuery); if($user !== null) $getAttempts->bind('user', $user->getId()); if($pagination !== null) $getAttempts->bind('range', $pagination->getRange()) ->bind('offset', $pagination->getOffset()); return $getAttempts->fetchObjects(self::class); } }