176 lines
4.3 KiB
PHP
176 lines
4.3 KiB
PHP
|
|
<?php
|
||
|
|
/**
|
||
|
|
* 单次访问(Visit)生命周期:一次独立打开 = 一条日志;同 visit 内续步仅 UPDATE。
|
||
|
|
*/
|
||
|
|
class VisitorVisitContext
|
||
|
|
{
|
||
|
|
const KEY_LOG_ID = 'cloak_visit_log_id';
|
||
|
|
const KEY_FINALIZED = 'cloak_visit_finalized';
|
||
|
|
const KEY_ACTIVE = 'cloak_visit_active';
|
||
|
|
|
||
|
|
public static function clear()
|
||
|
|
{
|
||
|
|
unset(
|
||
|
|
$_SESSION[self::KEY_LOG_ID],
|
||
|
|
$_SESSION[self::KEY_FINALIZED],
|
||
|
|
$_SESSION[self::KEY_ACTIVE]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @return int
|
||
|
|
*/
|
||
|
|
public static function currentLogId()
|
||
|
|
{
|
||
|
|
return (int) ($_SESSION[self::KEY_LOG_ID] ?? 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function isFinalized()
|
||
|
|
{
|
||
|
|
return !empty($_SESSION[self::KEY_FINALIZED]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ip_check 入口:识别刷新导航并开启新 visit。
|
||
|
|
*/
|
||
|
|
public static function evaluateAtEntry()
|
||
|
|
{
|
||
|
|
if (self::isFcheckRequest()) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (self::isRefreshNavigation()) {
|
||
|
|
self::clear();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function markVisitActive()
|
||
|
|
{
|
||
|
|
$_SESSION[self::KEY_ACTIVE] = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function isFcheckRequest()
|
||
|
|
{
|
||
|
|
$script = $_SERVER['SCRIPT_NAME'] ?? $_SERVER['PHP_SELF'] ?? '';
|
||
|
|
return basename($script) === 'f_check.php';
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function hasContinuationMarker()
|
||
|
|
{
|
||
|
|
if (isset($_GET['time']) && (string) $_GET['time'] !== '') {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return self::isFcheckRequest();
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function isRefreshNavigation()
|
||
|
|
{
|
||
|
|
if (self::hasContinuationMarker()) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (self::currentLogId() <= 0 || self::isFinalized()) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
$mode = strtolower((string) ($_SERVER['HTTP_SEC_FETCH_MODE'] ?? ''));
|
||
|
|
return $mode === 'navigate';
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function isNewVisit()
|
||
|
|
{
|
||
|
|
if (self::isRefreshNavigation()) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
if (self::currentLogId() <= 0) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
if (self::isFinalized()) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function isContinuation()
|
||
|
|
{
|
||
|
|
if (self::isRefreshNavigation()) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (self::hasContinuationMarker()) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
if (self::currentLogId() > 0 && !self::isFinalized()) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
if (!empty($_SESSION[self::KEY_ACTIVE]) && self::currentLogId() <= 0) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* INSERT 前:已 finalize 或刷新后的新 visit 清除旧绑定。
|
||
|
|
*/
|
||
|
|
public static function prepareForNewInsert()
|
||
|
|
{
|
||
|
|
if (!self::isNewVisit()) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
$hadFinalized = self::isFinalized();
|
||
|
|
unset($_SESSION[self::KEY_FINALIZED]);
|
||
|
|
if ($hadFinalized || self::isRefreshNavigation()) {
|
||
|
|
unset($_SESSION[self::KEY_LOG_ID]);
|
||
|
|
}
|
||
|
|
if (empty($_SESSION[self::KEY_ACTIVE])) {
|
||
|
|
self::markVisitActive();
|
||
|
|
}
|
||
|
|
if (class_exists('VisitorApiAudit', false)) {
|
||
|
|
VisitorApiAudit::clearByApi();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function shouldPersistAsUpdate()
|
||
|
|
{
|
||
|
|
return self::currentLogId() > 0 && !self::isFinalized() && !self::isNewVisit();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param int $logId
|
||
|
|
*/
|
||
|
|
public static function bindLogId($logId)
|
||
|
|
{
|
||
|
|
$logId = (int) $logId;
|
||
|
|
if ($logId > 0) {
|
||
|
|
$_SESSION[self::KEY_LOG_ID] = $logId;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param string|null $result
|
||
|
|
*/
|
||
|
|
public static function finalizeVisit($result = null)
|
||
|
|
{
|
||
|
|
$_SESSION[self::KEY_FINALIZED] = 1;
|
||
|
|
unset($_SESSION[self::KEY_ACTIVE]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param string $result
|
||
|
|
*/
|
||
|
|
public static function shouldFinalizeOnResult($result)
|
||
|
|
{
|
||
|
|
$r = (string) $result;
|
||
|
|
return $r === 'true' || $r === 'false';
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param array $logs
|
||
|
|
* @return array
|
||
|
|
*/
|
||
|
|
public static function normalizeLogsForDb(array $logs)
|
||
|
|
{
|
||
|
|
$result = (string) ($logs['result'] ?? '');
|
||
|
|
if (function_exists('cloak_normalize_log_reason')) {
|
||
|
|
$logs['reason'] = cloak_normalize_log_reason($result, $logs['reason'] ?? '');
|
||
|
|
}
|
||
|
|
return $logs;
|
||
|
|
}
|
||
|
|
}
|