getIP_util.php 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. <?php
  2. /**
  3. * Normalize and validate an IP address candidate from a request header.
  4. *
  5. * Trims whitespace, takes the first comma-separated token (for XFF-like
  6. * headers that may contain a chain of addresses), and validates the result
  7. * with filter_var().
  8. *
  9. * @param string $raw Raw header value.
  10. * @param int $extraFlags Additional FILTER_FLAG_* flags (e.g. FILTER_FLAG_IPV6).
  11. *
  12. * @return string|false The validated IP string, or false on failure.
  13. */
  14. function normalizeCandidateIp($raw, $extraFlags = 0)
  15. {
  16. $ip = trim($raw);
  17. // For XFF-like values, take the first address before a comma.
  18. if (($pos = strpos($ip, ',')) !== false) {
  19. $ip = trim(substr($ip, 0, $pos));
  20. }
  21. if ($ip === '') {
  22. return false;
  23. }
  24. return filter_var($ip, FILTER_VALIDATE_IP, $extraFlags);
  25. }
  26. /**
  27. * @return string
  28. */
  29. function getClientIp()
  30. {
  31. // Cloudflare IPv6 header — must be a valid IPv6 address.
  32. if (!empty($_SERVER['HTTP_CF_CONNECTING_IPV6'])) {
  33. $ip = normalizeCandidateIp($_SERVER['HTTP_CF_CONNECTING_IPV6'], FILTER_FLAG_IPV6);
  34. if ($ip !== false) {
  35. return preg_replace('/^::ffff:/', '', $ip);
  36. }
  37. }
  38. // Other forwarding / proxy headers — accept any valid IP.
  39. foreach (['HTTP_CLIENT_IP', 'HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR'] as $header) {
  40. if (!empty($_SERVER[$header])) {
  41. $ip = normalizeCandidateIp($_SERVER[$header]);
  42. if ($ip !== false) {
  43. return preg_replace('/^::ffff:/', '', $ip);
  44. }
  45. }
  46. }
  47. // Fallback: REMOTE_ADDR is set by the web server and is always a single IP.
  48. $ip = normalizeCandidateIp($_SERVER['REMOTE_ADDR'] ?? '');
  49. if ($ip !== false) {
  50. return preg_replace('/^::ffff:/', '', $ip);
  51. }
  52. return $_SERVER['REMOTE_ADDR'] ?? '';
  53. }