This library can handle IPv4, IPv6 addresses, as well as IP ranges, in CIDR formats (like ::1/128
or 127.0.0.1/32
) and in pattern format (like ::*:*
or 127.0.*.*
).
The only requirement is PHP 5.3.3. No external dependencies and no special PHP configuration are needed (yes, it will always work even if PHP has not been built with IPv6 support!).
Download the latest version, unzip it and add these lines in our PHP files:
require_once 'path/to/iplib/ip-lib.php';
Simply run composer require mlocati/ip-lib
, or add these lines to your composer.json
file:
"require": {
"mlocati/ip-lib": "1.*"
}
To parse an IPv4 address:
$address = \IPLib\Address\IPv4::fromString('127.0.0.1');
To parse an IPv6 address:
$address = \IPLib\Address\IPv6::fromString('::1');
To parse an address in any format (IPv4 or IPv6):
$address = \IPLib\Factory::addressFromString('::1');
$address = \IPLib\Factory::addressFromString('127.0.0.1');
$address = \IPLib\Factory::addressFromString('::1');
echo (string) $address->getPreviousAddress();
// prints ::
echo (string) $address->getNextAddress();
// prints ::2
To parse a subnet (CIDR) range:
$range = \IPLib\Range\Subnet::fromString('127.0.0.1/24');
$range = \IPLib\Range\Subnet::fromString('::1/128');
To parse a pattern (asterisk notation) range:
$range = \IPLib\Range\Pattern::fromString('127.0.0.*');
$range = \IPLib\Range\Pattern::fromString('::*');
To parse an andress as a range:
$range = \IPLib\Range\Single::fromString('127.0.0.1');
$range = \IPLib\Range\Single::fromString('::1');
To parse a range in any format:
$range = \IPLib\Factory::rangeFromString('127.0.0.*');
$range = \IPLib\Factory::rangeFromString('::1/128');
$range = \IPLib\Factory::rangeFromString('::');
$range = \IPLib\Factory::rangeFromBoundaries('192.168.0.1', '192.168.255.255');
echo (string) $range;
// prints 192.168.0.0/16
$range = \IPLib\Factory::rangeFromString('127.0.0.*');
echo (string) $range->getStartAddress();
// prints 127.0.0.0
echo (string) $range->getEndAddress();
// prints 127.0.0.255
Both IP addresses and ranges have a toString
method that you can use to retrieve a textual representation:
echo \IPLib\Factory::addressFromString('127.0.0.1')->toString();
// prints 127.0.0.1
echo \IPLib\Factory::addressFromString('127.000.000.001')->toString();
// prints 127.0.0.1
echo \IPLib\Factory::addressFromString('::1')->toString();
// prints ::1
echo \IPLib\Factory::addressFromString('0:0::1')->toString();
// prints ::1
echo \IPLib\Factory::rangeFromString('0:0::1/64')->toString();
// prints ::1/64
When working with IPv6, you may want the full (expanded) representation of the addresses. In this case, simply use a true
parameter for the toString
method:
echo \IPLib\Factory::addressFromString('::')->toString(true);
// prints 0000:0000:0000:0000:0000:0000:0000:0000
echo \IPLib\Factory::addressFromString('::1')->toString(true);
// prints 0000:0000:0000:0000:0000:0000:0000:0001
echo \IPLib\Factory::addressFromString('fff::')->toString(true);
// prints 0fff:0000:0000:0000:0000:0000:0000:0000
echo \IPLib\Factory::addressFromString('::0:0')->toString(true);
// prints 0000:0000:0000:0000:0000:0000:0000:0000
echo \IPLib\Factory::addressFromString('1:2:3:4:5:6:7:8')->toString(true);
// prints 0001:0002:0003:0004:0005:0006:0007:0008
echo \IPLib\Factory::rangeFromString('0:0::1/64')->toString();
// prints 0000:0000:0000:0000:0000:0000:0000:0001/64
All the range types offer a contains
method, and all the IP address types offer a matches
method: you can call them to check if an address is contained in a range:
$address = \IPLib\Factory::addressFromString('1:2:3:4:5:6:7:8');
$range = \IPLib\Factory::rangeFromString('0:0::1/64');
$contained = $address->matches($range);
// that's equivalent to
$contained = $range->contains($address);
Please remark that if the address is IPv4 and the range is IPv6 (or vice-versa), the result will always be false
.
All the range types offer a containsRange
method: you can call them to check if an address range fully contains another range:
$range1 = \IPLib\Factory::rangeFromString('0:0::1/64');
$range2 = \IPLib\Factory::rangeFromString('0:0::1/65');
$contained = $range1->containsRange($range2);
If you want to know if an address is within a private network, or if it's a public IP, or whatever you want, you can use the getRangeType
method:
$address = \IPLib\Factory::addressFromString('::');
$typeID = $address->getRangeType();
$typeName = \IPLib\Range\Type::getName();
The most notable values of the range type ID are:
\IPLib\Range\Type::T_UNSPECIFIED
if the address is all zeros (0.0.0.0
or::
)\IPLib\Range\Type::T_LOOPBACK
if the address is the localhost (usually127.0.0.1
or::1
)\IPLib\Range\Type::T_PRIVATENETWORK
if the address is in the local network (for instance192.168.0.1
orfc00::1
)\IPLib\Range\Type::T_PUBLIC
if the address is for public usage (for instance104.25.25.33
or2001:503:ba3e::2:30
)
If you want to know the type of an address range, you can use the getRangeType
method:
$range = \IPLib\Factory::rangeFromString('2000:0::1/64');
$type = $range->getRangeType();
// $type is \IPLib\Range\Type::T_PUBLIC
echo \IPLib\Range\Type::getName($type);
// 'Public address'
Please remark that if a range spans across multiple range types, you'll get NULL as the range type:
$range = \IPLib\Factory::rangeFromString('::/127');
$type = $range->getRangeType();
// $type is null
echo \IPLib\Range\Type::getName($type);
// 'Unknown type'
This library supports IPv4/IPv6 ranges in pattern format (eg. 192.168.*.*
) and in CIDR/subnet format (eg. 192.168.0.0/16
), and it offers a way to convert between the two formats:
// This will print ::*:*:*:*
echo \IPLib\Factory::rangeFromString('::/64')->asPattern()->toString();
// This will print 1:2::/96
echo \IPLib\Factory::rangeFromString('1:2::*:*')->asSubnet()->toString();
// This will print 192.168.0.0/24
echo \IPLib\Factory::rangeFromString('192.168.0.*')->asSubnet()->toString();
// This will print 10.*.*.*
echo \IPLib\Factory::rangeFromString('10.0.0.0/8')->asPattern()->toString();
You can use the getSubnetMask()
to get the subnet mask for IPv4 ranges:
// This will print 255.255.255.0
echo \IPLib\Factory::rangeFromString('192.168.0.*')->getSubnetMask()->toString();
// This will print 255.255.255.252
echo \IPLib\Factory::rangeFromString('192.168.0.12/30')->getSubnetMask()->toString();
This package offers a great feature: you can store address ranges in a database table, and check if an address is contained in one of the saved ranges with a simple query.
To save a range, you need to store the address type (for IPv4 it's 4
, for IPv6 it's 6
), as well as two values representing the start and the end of the range.
These methods are:
$range->getAddressType();
$range->getComparableStartString();
$range->getComparableEndString();
Let's assume that you saved the type in a field called addressType
, and the range boundaries in two fields called rangeFrom
and rangeTo
.
When you want to check if an address is within a stored range, simply use the getComparableString
method of the address and check if it's between the fields rangeFrom
and rangeTo
, and check if the stored addressType
is the same as the one of the address instance you want to check.
Here's a sample code:
/*
* Let's assume that:
* - $pdo is a PDO instance
* - $range is a range object
* - $address is an address object
*/
// Save the $range object
$insertQuery = $pdo->prepare('
insert into ranges (addressType, rangeFrom, rangeTo)
values (:addressType, :rangeFrom, :rangeTo)
');
$insertQuery->execute(array(
':addressType' => $range->getAddressType(),
':rangeFrom' => $range->getComparableStartString(),
':rangeTo' => $range->getComparableEndString(),
));
// Retrieve the saved ranges where an address $address falls:
$searchQuery = $pdo->prepare('
select * from ranges
where addressType = :addressType
and :address between rangeFrom and rangeTo
');
$searchQuery->execute(array(
':addressType' => $address->getAddressType(),
':address' => $address->getComparableString(),
));
$rows = $searchQuery->fetchAll();
$searchQuery->closeCursor();