print - php obtener ip visitante
Trabajando con direcciones IPv6 en PHP (5)
¿Qué tal inet_ntop()
? Entonces, en lugar de cortar cosas en números enteros, simplemente usa un varbinary(16)
para almacenarlo.
Después de buscar algo a fondo, noté una ligera falta de funciones en PHP para manejar IPv6 . Para mi propia satisfacción personal, creé algunas funciones para ayudar a la transición.
La función IPv6ToLong()
es una solución temporal a la que aparece aquí: Cómo almacenar una dirección compatible con IPv6 en una base de datos relacional . Dividirá la IP en dos enteros y los devolverá en una matriz.
/**
* Convert an IPv4 address to IPv6
*
* @param string IP Address in dot notation (192.168.1.100)
* @return string IPv6 formatted address or false if invalid input
*/
function IPv4To6($Ip) {
static $Mask = ''::ffff:''; // This tells IPv6 it has an IPv4 address
$IPv6 = (strpos($Ip, ''::'') === 0);
$IPv4 = (strpos($Ip, ''.'') > 0);
if (!$IPv4 && !$IPv6) return false;
if ($IPv6 && $IPv4) $Ip = substr($Ip, strrpos($Ip, '':'')+1); // Strip IPv4 Compatibility notation
elseif (!$IPv4) return $Ip; // Seems to be IPv6 already?
$Ip = array_pad(explode(''.'', $Ip), 4, 0);
if (count($Ip) > 4) return false;
for ($i = 0; $i < 4; $i++) if ($Ip[$i] > 255) return false;
$Part7 = base_convert(($Ip[0] * 256) + $Ip[1], 10, 16);
$Part8 = base_convert(($Ip[2] * 256) + $Ip[3], 10, 16);
return $Mask.$Part7.'':''.$Part8;
}
/**
* Replace ''::'' with appropriate number of '':0''
*/
function ExpandIPv6Notation($Ip) {
if (strpos($Ip, ''::'') !== false)
$Ip = str_replace(''::'', str_repeat('':0'', 8 - substr_count($Ip, '':'')).'':'', $Ip);
if (strpos($Ip, '':'') === 0) $Ip = ''0''.$Ip;
return $Ip;
}
/**
* Convert IPv6 address to an integer
*
* Optionally split in to two parts.
*
* @see https://stackoverflow.com/questions/420680/
*/
function IPv6ToLong($Ip, $DatabaseParts= 2) {
$Ip = ExpandIPv6Notation($Ip);
$Parts = explode('':'', $Ip);
$Ip = array('''', '''');
for ($i = 0; $i < 4; $i++) $Ip[0] .= str_pad(base_convert($Parts[$i], 16, 2), 16, 0, STR_PAD_LEFT);
for ($i = 4; $i < 8; $i++) $Ip[1] .= str_pad(base_convert($Parts[$i], 16, 2), 16, 0, STR_PAD_LEFT);
if ($DatabaseParts == 2)
return array(base_convert($Ip[0], 2, 10), base_convert($Ip[1], 2, 10));
else return base_convert($Ip[0], 2, 10) + base_convert($Ip[1], 2, 10);
}
Para estas funciones, normalmente las implemento llamando a esta función primero:
/**
* Attempt to find the client''s IP Address
*
* @param bool Should the IP be converted using ip2long?
* @return string|long The IP Address
*/
function GetRealRemoteIp($ForDatabase= false, $DatabaseParts= 2) {
$Ip = ''0.0.0.0'';
if (isset($_SERVER[''HTTP_CLIENT_IP'']) && $_SERVER[''HTTP_CLIENT_IP''] != '''')
$Ip = $_SERVER[''HTTP_CLIENT_IP''];
elseif (isset($_SERVER[''HTTP_X_FORWARDED_FOR'']) && $_SERVER[''HTTP_X_FORWARDED_FOR''] != '''')
$Ip = $_SERVER[''HTTP_X_FORWARDED_FOR''];
elseif (isset($_SERVER[''REMOTE_ADDR'']) && $_SERVER[''REMOTE_ADDR''] != '''')
$Ip = $_SERVER[''REMOTE_ADDR''];
if (($CommaPos = strpos($Ip, '','')) > 0)
$Ip = substr($Ip, 0, ($CommaPos - 1));
$Ip = IPv4To6($Ip);
return ($ForDatabase ? IPv6ToLong($Ip, $DatabaseParts) : $Ip);
}
Alguien por favor dime si estoy reinventando la rueda aquí o si he hecho algo mal.
Esta implementación convierte IPv4 a IPv6. Cualquier dirección IPv6 que no toque.
Aquí hay una función alternativa usando filter_var (PHP> = 5.2)
function IPv4To6($ip) {
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === true) {
if (strpos($ip, ''.'') > 0) {
$ip = substr($ip, strrpos($ip, '':'')+1);
} else { //native ipv6
return $ip;
}
}
$is_v4 = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
if (!$is_v4) { return false; }
$iparr = array_pad(explode(''.'', $ip), 4, 0);
$Part7 = base_convert(($iparr[0] * 256) + $iparr[1], 10, 16);
$Part8 = base_convert(($iparr[2] * 256) + $iparr[3], 10, 16);
return ''::ffff:''.$Part7.'':''.$Part8;
}
La extensión de filtro de PHP.net contiene algunas constantes para hacer coincidir direcciones IPv4 e IPv6, lo que puede ser útil para verificar la dirección. Aunque no he visto ninguna herramienta de conversión.
También puede almacenar la dirección en un binario (16) en mysql, por lo que debe tener la opción de enviarlo en binario desde IPv6ToLong ().
Esto es realmente algo que debe agregarse de forma nativa en PHP, especialmente cuando muchos servidores web habilitados para IPv6 informan :: FFFF: 1.2.3.4 como IP del cliente y es incompatible con ip2long, y romperá muchas cosas.
Volviendo atrás, escribí dos funciones, dtr_pton
y dtr_ntop
que funcionan tanto con IPv4 como con IPv6. Los convertirá de ida y vuelta entre imprimible y binario.
La primera función, dtr_pton
comprobará si el argumento proporcionado es válido IPv4 o IPv6 válido. Dependiendo del resultado, se podría lanzar una excepción, o se podría devolver la representación binaria de la IP. Al usar esta función, puede realizar AND''ing u OR''ing contra el resultado (para subredes / whathaveyou). Sugeriría que los almacene en su base de datos como VARBINARY(39)
o VARCHAR(39)
.
/**
* dtr_pton
*
* Converts a printable IP into an unpacked binary string
*
* @author Mike Mackintosh - [email protected]
* @param string $ip
* @return string $bin
*/
function dtr_pton( $ip ){
if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)){
return current( unpack( "A4", inet_pton( $ip ) ) );
}
elseif(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)){
return current( unpack( "A16", inet_pton( $ip ) ) );
}
throw new /Exception("Please supply a valid IPv4 or IPv6 address");
return false;
}
La segunda función, dtr_ntop
, convertirá la representación binaria de la IP a una dirección IP imprimible.
/**
* dtr_ntop
*
* Converts an unpacked binary string into a printable IP
*
* @author Mike Mackintosh - [email protected]
* @param string $str
* @return string $ip
*/
function dtr_ntop( $str ){
if( strlen( $str ) == 16 OR strlen( $str ) == 4 ){
return inet_ntop( pack( "A".strlen( $str ) , $str ) );
}
throw new /Exception( "Please provide a 4 or 16 byte string" );
return false;
}
Además, esta es una forma rápida de expandir las direcciones IPv6 encontradas en
function expand($ip){
$hex = unpack("H*hex", inet_pton($ip));
$ip = substr(preg_replace("/([A-f0-9]{4})/", "$1:", $hex[''hex'']), 0, -1);
return $ip;
}
Además, una buena lectura sobre el tema se puede encontrar en HighOnPHP: 5 consejos para trabajar con IPv6 en PHP . Este artículo utiliza algunos de los métodos descritos anteriormente en una clase orientada a objetos que se puede encontrar en GitHub: mikemackintosh / dTR-IP