diff --git a/.travis.yml b/.travis.yml index a1fcdd8..c3fd631 100755 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,8 @@ language: php php: - - 5.4 - - 5.5 - - 5.6 - 7.0 - 7.1 - 7.2 - 7.3 install: - - composer install -matrix: - include: - - php: 5.3 - dist: precise \ No newline at end of file + - composer install \ No newline at end of file diff --git a/composer.json b/composer.json index c92b0c6..eee269c 100755 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "homepage": "https://github.com/rlanvin/php-ip", "license": "MIT", "require": { - "php": ">=5.3.0", + "php": "~7.0", "ext-gmp": "*" }, "require-dev": { diff --git a/src/IP.php b/src/IP.php index 1924cdc..92d43d6 100644 --- a/src/IP.php +++ b/src/IP.php @@ -50,6 +50,11 @@ function gmp_shiftr($x, $n) */ abstract class IP { + const IP_VERSION = null; + const MAX_INT = null; + const NB_BITS = null; + const NB_BYTES = null; + /** * Internal representation of the IP as a numeric format. * For IPv4, this will be an SIGNED int (32 bits). @@ -69,6 +74,126 @@ abstract class IP */ protected $class; + /** + * Constructor tries to guess what is the $ip. + * + * @param $ip mixed String, binary string, int or float + */ + public function __construct($ip) + { + if (is_int($ip)) { + $this->ip = self::fromInt($ip); + + return; + } + + // float (or double) with an integer value + if (is_float($ip) && $ip == floor($ip)) { + $this->ip = self::fromFloat($ip); + + return; + } + + if (is_string($ip)) { + $this->ip = self::fromString($ip); + + return; + } + + if ((is_resource($ip) && get_resource_type($ip) === 'GMP integer') || $ip instanceof \GMP) { + $this->ip = self::fromGMP($ip); + + return; + } + + throw new \InvalidArgumentException(sprintf('Unsupported argument type: "%s".', gettype($ip))); + } + + /** + * @param int $ip + * + * @return \GMP + */ + private static function fromInt(int $ip): \GMP + { + $ip = gmp_init(sprintf('%u', $ip), 10); + + if (gmp_cmp($ip, static::MAX_INT) > 0) { + throw new \InvalidArgumentException(sprintf('The integer "%s" is not a valid IPv%d address.', gmp_strval($ip), static::IP_VERSION)); + } + + return $ip; + } + + /** + * @param float $ip + * + * @return \GMP + */ + private static function fromFloat(float $ip): \GMP + { + $ip = gmp_init(sprintf('%s', $ip), 10); + + if (gmp_cmp($ip, 0) < 0 || gmp_cmp($ip, static::MAX_INT) > 0) { + throw new \InvalidArgumentException(sprintf('The double "%s" is not a valid IPv%d address.', gmp_strval($ip), static::IP_VERSION)); + } + + return $ip; + } + + /** + * @param string $ip + * + * @return \GMP + */ + private static function fromString(string $ip): \GMP + { + // binary, packed string + if (@inet_ntop($ip) !== false) { + if (strlen($ip) != static::NB_BYTES) { + throw new \InvalidArgumentException(sprintf('The binary string "%s" is not a valid IPv%d address.', $ip, static::IP_VERSION)); + } + + $hex = unpack('H*', $ip); + + return gmp_init($hex[1], 16); + } + + $filterFlag = constant('FILTER_FLAG_IPV'.static::IP_VERSION); + if (filter_var($ip, FILTER_VALIDATE_IP, $filterFlag)) { + $ip = inet_pton($ip); + $hex = unpack('H*', $ip); + + return gmp_init($hex[1], 16); + } + + // numeric string (decimal) + if (ctype_digit($ip)) { + $ip = gmp_init($ip, 10); + if (gmp_cmp($ip, static::MAX_INT) > 0) { + throw new \InvalidArgumentException(sprintf('"%s" is not a valid decimal IPv%d address.', gmp_strval($ip), static::IP_VERSION)); + } + + return $ip; + } + + throw new \InvalidArgumentException(sprintf('The string "%s" is not a valid IPv%d address.', $ip, static::IP_VERSION)); + } + + /** + * @param \GMP $ip + * + * @return \GMP + */ + private function fromGMP(\GMP $ip): \GMP + { + if (gmp_cmp($ip, 0) < 0 || gmp_cmp($ip, static::MAX_INT) > 0) { + throw new \InvalidArgumentException(sprintf('"%s" is not a valid decimal IPv%d address.', gmp_strval($ip), static::IP_VERSION)); + } + + return $ip; + } + /** * Take an IP string/int and return an object of the correct type. * @@ -137,7 +262,7 @@ public function numeric($base = 10) */ public function binary() { - $hex = str_pad($this->numeric(16), static::NB_BITS/4, '0', STR_PAD_LEFT); + $hex = str_pad($this->numeric(16), static::NB_BITS / 4, '0', STR_PAD_LEFT); return pack('H*', $hex); } diff --git a/src/IPv4.php b/src/IPv4.php index 18912fa..2a8197b 100644 --- a/src/IPv4.php +++ b/src/IPv4.php @@ -20,6 +20,7 @@ class IPv4 extends IP const IP_VERSION = 4; const MAX_INT = '4294967295'; const NB_BITS = 32; + const NB_BYTES = 4; /** * Workaround for lack of late static binding in PHP 5.2 @@ -27,66 +28,12 @@ class IPv4 extends IP */ protected $class = __CLASS__; - public function getVersion() - { - return self::IP_VERSION; - } - /** - * Constructor tries to guess what is the $ip. - * - * @param $ip mixed String, binary string, int or float + * {@inheritdoc} */ - public function __construct($ip) + public function getVersion() { - if (is_int($ip)) { - // if an integer is provided, we have to be careful of the architecture - // on 32 bits plateform, it's always a valid IP - // on 64 bits plateform, we have to test the value - $ip = gmp_init(sprintf('%u', $ip), 10); - if (gmp_cmp($ip, self::MAX_INT) > 0) { - throw new \InvalidArgumentException(sprintf('The integer %s is not a valid IPv4 address', gmp_strval($ip))); - } - $this->ip = $ip; - } elseif (is_float($ip) && $ip == floor($ip)) { - // float (or double) with an integer value - $ip = gmp_init(sprintf('%s', $ip), 10); - if (gmp_cmp($ip, 0) < 0 || gmp_cmp($ip, self::MAX_INT) > 0) { - throw new \InvalidArgumentException(sprintf('The double %s is not a valid IPv4 address', gmp_strval($ip))); - } - $this->ip = $ip; - } elseif (is_string($ip)) { - // binary string - if (!ctype_print($ip)) { - if (strlen($ip) != 4) { - throw new \InvalidArgumentException('The binary string is not a valid IPv4 address'); - } - $hex = unpack('H*', $ip); - $this->ip = gmp_init($hex[1], 16); - } - // human readable IPv4 - elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { - $this->ip = gmp_init(sprintf('%u', ip2long($ip))); - } - // numeric string (decimal) - elseif (ctype_digit($ip)) { - $ip = gmp_init($ip); - if (gmp_cmp($ip, self::MAX_INT) > 0) { - throw new \InvalidArgumentException(sprintf('%s is not a valid decimal IPv4 address', gmp_strval($ip))); - } - - $this->ip = $ip; - } else { - throw new \InvalidArgumentException("$ip is not a valid IPv4 address"); - } - } elseif ((is_resource($ip) && get_resource_type($ip) == 'GMP integer') || $ip instanceof \GMP) { - if (gmp_cmp($ip, 0) < 0 || gmp_cmp($ip, self::MAX_INT) > 0) { - throw new \InvalidArgumentException(sprintf('%s is not a valid decimal IPv4 address', gmp_strval($ip))); - } - $this->ip = $ip; - } else { - throw new \InvalidArgumentException('Unsupported argument type: '.gettype($ip)); - } + return self::IP_VERSION; } /** diff --git a/src/IPv6.php b/src/IPv6.php index aa28aa3..bb583d0 100644 --- a/src/IPv6.php +++ b/src/IPv6.php @@ -22,6 +22,7 @@ class IPv6 extends IP const IP_VERSION = 6; const MAX_INT = '340282366920938463463374607431768211455'; const NB_BITS = 128; + const NB_BYTES = 16; /** * Workaround for lack of late static binding in PHP 5.2 @@ -29,63 +30,12 @@ class IPv6 extends IP */ protected $class = __CLASS__; - public function getVersion() - { - return self::IP_VERSION; - } - /** - * Constuctor tries to guess what is $ip. - * - * @param mixed $ip + * {@inheritdoc} */ - public function __construct($ip) + public function getVersion() { - if (is_int($ip)) { - // always a valid IP, since even in 64bits plateform, it's less than max value - $this->ip = gmp_init(sprintf('%u', $ip), 10); - } elseif (is_float($ip) && $ip == floor($ip)) { - // float (or double) with an integer value - $ip = gmp_init(sprintf('%s', $ip), 10); - if (gmp_cmp($ip, 0) < 0 || gmp_cmp($ip, self::MAX_INT) > 0) { - throw new \InvalidArgumentException(sprintf('The double %s is not a valid IPv6 address', gmp_strval($ip))); - } - $this->ip = $ip; - } elseif (is_string($ip)) { - // binary string - if (!ctype_print($ip)) { - // probably the result of inet_pton - // must be 16 bytes exactly to be valid - if (strlen($ip) != 16) { - throw new \InvalidArgumentException('The binary string is not a valid IPv6 address'); - } - $hex = unpack('H*', $ip); - $this->ip = gmp_init($hex[1], 16); - } - // valid human readable representation - elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { - $ip = inet_pton($ip); - $hex = unpack('H*', $ip); - $this->ip = gmp_init($hex[1], 16); - } - // numeric string (decimal) - elseif (ctype_digit($ip)) { - $ip = gmp_init($ip, 10); - if (gmp_cmp($ip, '340282366920938463463374607431768211455') > 0) { - throw new \InvalidArgumentException(sprintf('%s is not a valid decimal IPv6 address', gmp_strval($ip))); - } - $this->ip = $ip; - } else { - throw new \InvalidArgumentException("$ip is not a valid IPv6 address"); - } - } elseif ((is_resource($ip) && get_resource_type($ip) == 'GMP integer') || $ip instanceof \GMP) { - if (gmp_cmp($ip, 0) < 0 || gmp_cmp($ip, self::MAX_INT) > 0) { - throw new \InvalidArgumentException(sprintf('%s is not a valid decimal IPv6 address', gmp_strval($ip))); - } - $this->ip = $ip; - } else { - throw new \InvalidArgumentException('Unsupported argument type: '.gettype($ip)); - } + return self::IP_VERSION; } /**