Files
coruna-lab/app/Models/PageVisit.php
T
2026-08-14 13:15:52 +08:00

105 lines
2.6 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PageVisit extends Model
{
public $timestamps = false;
protected $fillable = [
'channel_id',
'client_uid',
'session_id',
'user_agent',
'os',
'os_version',
'browser',
'browser_version',
'ip',
'domain',
'referer',
'created_at',
];
protected function casts(): array
{
return [
'created_at' => 'datetime',
];
}
public static function normalizeDomain(?string $value): ?string
{
$value = trim((string) $value);
if ($value === '') {
return null;
}
$value = preg_replace('#^https?://#i', '', $value) ?? $value;
$value = explode('/', $value, 2)[0];
$value = strtolower(rtrim($value));
if ($value === '' || ! str_contains($value, '.')) {
return null;
}
return substr($value, 0, 255);
}
public const VISITOR_UID_LENGTH = 16;
/**
* Stable visitor id: same domain+ip always yields the same 16-hex value.
*/
public static function visitorUid(?string $domain, ?string $ip): string
{
$domainKey = self::normalizeDomain($domain) ?? strtolower(trim((string) $domain));
$ipKey = self::normalizeIp($ip);
return substr(hash('sha256', $domainKey."\0".$ipKey), 0, self::VISITOR_UID_LENGTH);
}
/**
* Unwrap IPv4-mapped IPv6 (`::ffff:1.2.3.4` → `1.2.3.4`) and canonicalize.
*/
public static function normalizeIp(?string $ip): string
{
$ip = strtolower(trim((string) $ip));
if ($ip === '') {
return '';
}
if (str_starts_with($ip, '::ffff:')) {
$mapped = substr($ip, 7);
if (filter_var($mapped, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
return $mapped;
}
}
$packed = filter_var($ip, FILTER_VALIDATE_IP) ? inet_pton($ip) : false;
if ($packed !== false) {
$canon = inet_ntop($packed);
if (is_string($canon) && $canon !== '') {
return strtolower($canon);
}
}
return $ip;
}
/**
* @return list<string>
*/
public static function ipLookupKeys(?string $ip): array
{
$norm = self::normalizeIp($ip);
if ($norm === '') {
return [];
}
$keys = [$norm];
if (filter_var($norm, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
$keys[] = '::ffff:'.$norm;
}
return array_values(array_unique($keys));
}
}