Files
2026-09-19 01:03:23 +08:00

49 lines
1.2 KiB
PHP

<?php
namespace App\Support;
use App\Models\PageVisit;
use Illuminate\Http\Request;
/**
* Visitor IP behind Cloudflare.
*
* Prefer CF-Connecting-IP (Cloudflare overwrites it). Fall back to
* $request->ip() when the request did not come through CDN.
* Do not trust JSON/query `ip` or a client-prefixed X-Forwarded-For hop.
*/
final class VisitorIp
{
public static function fromRequest(Request $request): string
{
foreach (['CF-Connecting-IP', 'True-Client-IP'] as $header) {
$ip = self::normalize($request->headers->get($header));
if ($ip !== '') {
return $ip;
}
}
$fallback = self::normalize((string) $request->ip());
return $fallback !== '' ? $fallback : substr((string) $request->ip(), 0, 64);
}
public static function normalize(?string $raw): string
{
$raw = trim((string) $raw);
if ($raw === '') {
return '';
}
if (str_contains($raw, ',')) {
$raw = trim(explode(',', $raw, 2)[0]);
}
if (filter_var($raw, FILTER_VALIDATE_IP) === false) {
return '';
}
$norm = PageVisit::normalizeIp($raw);
return $norm !== '' ? substr($norm, 0, 64) : '';
}
}