isValidToken($cookieToken)) { $parts['c'] = $cookieToken; } // IP адрес — всегда $parts['i'] = $this->getClientIp(); // User Agent hash — всегда (для дополнительной уникальности) $parts['ua'] = $this->getUserAgentHash(); // Если куки нет — добавляем признак "без куки" для отладки if (empty($cookieToken)) { $parts['nc'] = '1'; } // Комбинация всех частей → хеш return md5('rl:' . $action . ':' . implode('|', $parts)); } /** * Генерирует и устанавливает токен, если его нет * * @return string|null Токен или null если уже есть */ public function ensureToken(): ?string { if (empty($_COOKIE[self::COOKIE_NAME])) { $token = $this->generateToken(); // Устанавливаем куку на год setcookie( self::COOKIE_NAME, $token, [ 'expires' => time() + self::COOKIE_TTL, 'path' => '/', 'secure' => true, 'samesite' => 'Lax', 'httponly' => true, ] ); return $token; } return null; } /** * Проверяет, установлен ли токен * * @return bool */ public function hasToken(): bool { return !empty($_COOKIE[self::COOKIE_NAME]); } /** * Получает JS код для установки токена при первом визите * * @return string JavaScript код */ public function getJsScript(): string { return << 500) { $ua = substr($ua, 0, 500); } return md5($ua); } }