Skip to content
Rémi Lanvin edited this page Jul 22, 2021 · 14 revisions

IPv4/IPv6 manipulation library for PHP. This library is inspired by Python ipaddress.

Feel free to read the source code for more info.

This documentation applies to version >= 3.0.

IP Addresses

The IPv4 and IPv6 objects provide a way to create and manipulate IP Addresses. They both inherit from IP.

      +------+
      |  IP  |
      +------+
        |  |
    +---+  +---+
    |          |
+------+    +------+
| IPv4 |    | IPv6 |
+------+    +------+

Creation

IP instances can be created from many different base types and representations. Each method is available for IPv4 and IPv6 class.

From string

The most common use case is to create an instance from a human-readable string representation of an IP address.

$ip = IPv4::createFromString('192.168.0.1');
$ip = IPv6::createFromString('2001:db8::');

A InvalidArgumentException will be thrown in the value provided cannot be converted into an IP address.

$ip = IPv4::createFromString('2001:db8::');
// InvalidArgumentException: The string "2001:db8::" is not a valid IPv4 address.

From a numeric representation

It is/was relatively common to represent IP addresses as integer, for example using ip2long, especially for storage of IPv4 in a database. However is isn't as practical with IPv6, because there is a risk to go over the max int value even on 64-bits systems (PHP_INT_MAX). When this happens PHP will implicitely convert the integer to a float or double, but this leads to a loss of precision. Read more about Floating point precision on the official doc.

Therefore when using numeric representation, it highly recommended to use numeric strings instead of integers or floats.

  • createFromNumericString creates an instance from a numeric string containing an positive decimal integer value (a string containing a negative integer will not be accepted).
  • createFromInt creates an instance from an signed decimal integer (such as the result of ip2long). This method is safe to use on a 64-bit systems with IPv4, but is not recommended for IPv6 as most IPv6 can't fit into an integer. On 32-bit systems, IPv4 will overflow into a negative integer. Negative integers will be converted to unsigned positive.
  • createFromFloat creates an instance from a float/double containing an positive decimal integer value (a negative float/double will not be considered valid). This method exists for backwards compatibility with 32-bit systems and some fringe use cases, but is dangerous for most use cases, especially with IPv6, due to the loss of precision of big float/double numbers.
$ip = IPv4::createFromNumericString('2130706433'); // 127.0.0.1
$ip = IPv4::createFromInt(2130706433); // 127.0.0.1

$ip = IPv6::createFromNumericString('55835404833073476206743540170770874368'); // 2a01:8200::
$ip = IPv6::createFromInt(55835404833073476206743540170770874368); // will not work because it'll be converted to a float
// TypeError: Argument 1 passed to PhpIP\IPv6::createFromInt() must be of the type int, float given
$ip = IPv6::createFromFloat(55835404833073476206743540170770874368); // might or might not work depending on your system - use at your own risk

From other types

  • createFromBinaryString creates an instance from a binary string, such as the result of inet_pton. Binary strings must be exactly 4 chars (32 bits) for IPv4 and 16 chars (128 bits) for IPv6.
  • createFromGmp creates from a GMP object

Type auto-detection

IP addresses can be created auto-magically either by calling directly the constructor of IPv4 or IPv6, or using the convenience factory method IP::create($ip). In this case the library will attempt to guess the type of the input parameter and generate the appropriate IP instance.

$ip = new IPv4('192.168.0.1');
$ip = new IPv6('2001:db8::');
$ip = new IPv6('::192.168.0.0'); // IPV4-mapped IPv6
$ip = new IPv6('::ffff:192.168.0.0'); // IPV4-mapped IPv6

$ip = IP::create('192.168.0.1'); // return IPv4
$ip = IP::create('2001:db8::'); // return IPv6

The implicit creation with create and constructor methods are safe and convenient to use as long as you work with a string representing a human readable IP address. This is equivalent to createFromString.

However with every other types, there are some caveats with makes the auto-detection not recommended unless you are sure what you are doing.

  • IP::create() will return IPv4 by default for integers less than 2^32. If you work with IPv6 or both versions, you might want to use IPv6 constructors or an explicit create method.
  • Binary strings (such as the result of inet_pton) will only work with IPv4. Auto-detection of binary string are not supported with IPv6 due to the risk of confusion with regular string representation of an abbreviated IPv6. See bug #57 for more in-depth info on this topic.
$ip = IP::create(294967295); // 17.148.215.255
$ip = IP::create("\000\000\000*"): // 0.0.0.42

Additionally, the same warning as for the explicit create methods apply:

  • With an integer there is a risk of overflow, particularly with IPv6. (same warning as createFromInt)
  • With a float/double containing there is a risk of loss of precision and unpredictable results, particularly with IPv6. (same warning as createFromFloat).

A InvalidArgumentException will be thrown in the value provided cannot be converted into an IP address.

Converting to string

IPv4 and IPv6 objects are convertible to string by default. You can also explicitly generate a string representation with humanReadable($short_form) method. The parameter $short_form (default true) indicates whether or not to display a short version of the address.

$ip = new IPv4(294967295);
echo $ip; // 17.148.215.255
echo $ip->humanReadable(false); // 017.148.215.255

$ip = new IPv6('2a01:8200::');
echo $ip; // 2a01:8200::
echo $ip->humanReadable(false); // 2a01:8200:0000:0000:0000:0000:0000:0000

Note: IPv4-mapped IPv6 will be displayed as plain IPv6 in uncompressed mode.

$ip = new IPv6('::ffff:192.168.0.0');
echo $ip; // ::ffff:192.168.0.0
echo $ip->humanReadable(false),"\n"; // but 0000:0000:0000:0000:0000:ffff:c0a8:0000

Converting to integer

Conversion to integer is done with the numeric() method. Due the limits of the integer type in PHP, this method will return a numeric string, not an actual integer.

This method accepts an optional parameter $base. Default is to return in base 10 (decimal).

$ip = new IPv4('127.0.0.0');
echo $ip->numeric(); // '2130706432'

echo $ip->numeric(2); // '1111111000000000000000000000000'
echo $ip->numeric(16); // '7f000000'

$ip = new IPv6('2a01:8200::');
echo $ip->numeric(); // '55835404833073476206743540170770874368'

echo $ip->numeric(2); // '101010000000011000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
echo $ip->numeric(16); // '2a018200000000000000000000000000'

Converting to binary

The binary() method returns the IP address as a binary string, identical to what inet_pton would return. This could be used to insert into a MySQL VARBINARY field for example.

$ip = new IPv6('2a01:8200::');
$ip->binary(); // this is a binary string

Operations

PHP doesn't support overloading of operators, so you have to call the methods directly. All methods accepts any data type that can be converted into a IP address.

Arithmetic operators

$ip = new IPv4('127.0.0.0');
echo $ip->plus(1); // 127.0.0.1
echo $ip->minus(1); // 126.255.255.255

Operators will throw a OutOfBoundException if you reach the boundaries of the given IP address (for example, trying to add 1 to 255.255.255.255).

Bitwise operators

$ip = new IPv4('127.0.0.0');
echo $ip->bit_or('255.255.0.0'); // 255.255.0.0
echo $ip->bit_and('255.255.0.0'); // 127.0.0.0
echo $ip->bit_xor('255.255.0.0'); // 128.255.0.0
echo $ip->bit_negate(); // 128.255.255.255

Other methods

Name Description
getVersion() Return the IP version (4 or 6)
isReserved() Return true if the address is reserved in the IANA special-purpose address registry
isPrivate() Return true if the address is specifically reserved for private (forwardable, non globally routable) networks
isPublic() Return true if the address is allocated for public networks
reversePointer() The name of the reverse DNS PTR record for the IP address. Example IP::create('212.212.204.85')->reversePointer(); // '85.204.212.212.in-addr.arpa.
isLinkLocal() Return true if the address is a link-local addresses
isLoopback() Return true if the address is a loopback addresses
matches($ip, $mask) Perform wildcard mask matching common in network Access Control Lists and OSPF dynamic routing.

Blocks (networks)

The IPv4Block and IPv6Block objects provide a way to create and manipulate IP networks. They use the CIDR block notation (e.g. 192.168.0.0/24). They both inherit from IPBlock.

         +-----------+
         |  IPBlock  |
         +-----------+
             |  |
      +------+  +------+
      |                |
+-----------+    +-----------+
| IPv4Block |    | IPv6Block |
+-----------+    +-----------+

Creation

Like IP addresses, blocks can be created either by instantiating directly IPv4Block or IPv6Block, or by using the convenience factory method IPBlock::create().

$block = new IPv4Block('128.0.0.0/16');
$block = new IPv4Block('128.0.0.0', '16'); // alternate way of calling the constructor

$block = new IPv6Block('2001:0db8::/32');
$block = new IPv6Block('2001:0db8::', '32'); // alternate way of calling the constructor

$block = IPBlock::create('128.0.0.0/16'); // IPv4Block
  • The IP parameter can be anything that can be converted into an IP address (string, integer, etc.)
  • The prefix must be an integer between 0 and 32 (IPv4) or 0 and 128 (IPv6)

Class methods

These methods are available statically on IPv4Block and IPv6Block classes.

Name Description
getReservedBlocks() Return an array containing all the reserved blocks defined in this version of the IP protocol as per the IANA special-purpose address registry
getPrivateBlocks() Return an array containing all only the private (forwardable, non globally routable) blocks defined in this version of the IP protocol
getLoopbackBlock() Return the loopback block of this version of the IP protocol
getLinkLocalBlock() Return the link-local block of this version of the IP protocol.

Example:

echo IPv6Block::getLinkLocalBlock(); // fe80::/10

Basic methods

Name Description
getFirstIp(), getNetworkAddress() Return the first IP of the block, also known as the network address
getLastIp(), getBroadcastAddress() Return the last IP of the block, also known as the broadcast address
getNetmask() Return the netmask as an IP object
getDelta() Return the delta to last IP of the block, also known as "host mask" as an IP object
getNbAddresses(), count() Return the number of IP addresses in the block. Because this number can be huge, this returns a numeric string, not an actual integer.
getGivenIp() Return the IP that was used to create the block
getPrefixLength() Return the prefix length of the bloc
getMaxPrefixLength() Return the maximum allowed prefix length for this type of block based on the IP version (constant)
getVersion() Return the IP version of the block (4 or 6)

Example:

$block = new IPv4Block('128.0.0.0/16');
echo $block->getNetmask(); // 255.255.0.0
echo $block->getDelta(); // 0.0.255.255
echo $block->getFirstIp(); // 128.0.0.0
echo $block->getLastIp(); // 128.0.255.255
echo $block->getNbAddresses(); // '65536'

$block = new IPv6Block('2001:0db8::/32');
echo $block->getNetmask(); // ffff:ffff::
echo $block->getDelta(); // ::ffff:ffff:ffff:ffff:ffff:ffff
echo $block->getFirstIp(); // 2001:db8::
echo $block->getLastIp(); // 2001:db8:ffff:ffff:ffff:ffff:ffff:ffff
echo $block->getNbAddresses(); // '79228162514264337593543950336'

Arithmetic operators

Like IP addresses, you can make addition and subtractions with blocks. You cannot however do bitwise operations.

$block = new IPv4Block('192.168.0.0/24');
echo $block->plus(42); // 192.168.42.0/24
echo $block->minus(42); // 192.167.214.0/24

Operators will throw a OutOfBoundException if you reach the boundaries of the given IP version (for example, if you try to add 256 to 255.255.255.0/24).

Blocks as containers

IP blocks are containers for IP addresses or other IP blocks. You can test whether a block contains another block or IP, or is contained by another block using contains (or containsIP()/containsBlock()) and isIn. The method isIn is also available for IP objects, to test if a given IP is in a given block.

The method overlaps returns true if the block overlaps another block, whether is is bigger or smaller.

Example:

$block = IPBlock::create('192.168.0.0/24');
$block->contains('192.168.0.42'); // true
$block->contains('192.168.1.0/24'); // false
$block->isIn('192.168.0.0/16'); // true
$block->overlaps('192.168.0.0/16'); // true

$ip = IP::create('192.168.0.42');
$ip->isIn($block); // true

Iterator and array access

IP addresses contained in a block can be iterated with foreach directly using the block object.

$block = IPBlock::create('192.168.0.0/24');
foreach ( $block as $ip ) {
    echo $ip,"\n";
}
// outputs:
// 192.168.0.0
// 192.168.0.1
// 192.168.0.2
// ...
// 192.168.0.255

It's also possible to access an IP address directly using array notation.

$block = IPBlock::create('192.168.0.0/24');
echo $block[0]; // 192.168.0.0
echo $block[42]; // 192.168.0.42

$block = IPBlock::create('2001:db8::/32');
echo $block['79228162514264337543950335']; // 2001:db8:41:8937:4bc6:a7ef:9abd:6fff

Trying to access an offset outside of the block will throw a OutOfBoundsException.

Note: It's not possible to use array notation to set values inside the block.

Sublocks

Blocks can be split into smaller blocks with the getSubblocks($prefix) method. This method returns a IPBlockIterator object, so you can loop with foreach. The value of $prefix must be greater than the current block prefix. For example, you can split a /16 into /24, but not the other way around. Note that $prefix accepts an optional leading '/', e.g. you can pass "/24" instead of "24" if you find it clearer.

$blocks = IPBlock::create('192.168.0.0/16')->getSubBlocks('/24');
echo $blocks->count(),"\n"; // 256

foreach ( $blocks as $block ) {
	echo $block,"\n";
}
// outputs:
// 192.168.0.0/24
// 192.168.1.0/24
// 192.168.2.0/24
// ...
// 192.168.255.0/24

Superblock

The block containing the current block can be obtained with getSuperBlock($prefix).

$block = IPBlock::create('192.168.42.0/24');
echo $block->getSuperBlock('/16'); // 192.168.0.0