1[![TravisCI Build Status](https://api.travis-ci.org/mlocati/ip-lib.svg?branch=master)](https://travis-ci.org/mlocati/ip-lib) 2[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/mlocati/ip-lib?branch=master&svg=true)](https://ci.appveyor.com/project/mlocati/ip-lib) 3[![StyleCI Status](https://styleci.io/repos/54139375/shield)](https://styleci.io/repos/54139375) 4[![Coverage Status](https://coveralls.io/repos/github/mlocati/ip-lib/badge.svg?branch=master)](https://coveralls.io/github/mlocati/ip-lib?branch=master) 5[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/mlocati/ip-lib/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/mlocati/ip-lib/?branch=master) 6 7# IPLib - Handle IPv4, IPv6 and IP ranges 8 9 10## Introduction 11 12This 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.*.*`). 13 14 15## Requirements 16 17The only requirement is PHP 5.3.3. 18__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!). 19 20 21## Manual installation 22 23[Download](https://github.com/mlocati/ip-lib/releases) the latest version, unzip it and add these lines in our PHP files: 24 25```php 26require_once 'path/to/iplib/ip-lib.php'; 27``` 28 29 30## Installation with Composer 31 32Simply run `composer require mlocati/ip-lib`, or add these lines to your `composer.json` file: 33 34```json 35"require": { 36 "mlocati/ip-lib": "1.*" 37} 38``` 39 40 41## Sample usage 42 43 44### Parse an address 45 46To parse an IPv4 address: 47 48```php 49$address = \IPLib\Address\IPv4::fromString('127.0.0.1'); 50``` 51 52To parse an IPv6 address: 53 54```php 55$address = \IPLib\Address\IPv6::fromString('::1'); 56``` 57 58To parse an address in any format (IPv4 or IPv6): 59 60```php 61$address = \IPLib\Factory::addressFromString('::1'); 62$address = \IPLib\Factory::addressFromString('127.0.0.1'); 63``` 64 65 66### Get the next/previous addresses 67 68```php 69$address = \IPLib\Factory::addressFromString('::1'); 70echo (string) $address->getPreviousAddress(); 71// prints :: 72echo (string) $address->getNextAddress(); 73// prints ::2 74``` 75 76 77### Parse an IP address range 78 79To parse a subnet (CIDR) range: 80 81```php 82$range = \IPLib\Range\Subnet::fromString('127.0.0.1/24'); 83$range = \IPLib\Range\Subnet::fromString('::1/128'); 84``` 85 86To parse a pattern (asterisk notation) range: 87 88```php 89$range = \IPLib\Range\Pattern::fromString('127.0.0.*'); 90$range = \IPLib\Range\Pattern::fromString('::*'); 91``` 92 93To parse an andress as a range: 94 95```php 96$range = \IPLib\Range\Single::fromString('127.0.0.1'); 97$range = \IPLib\Range\Single::fromString('::1'); 98``` 99 100To parse a range in any format: 101 102```php 103$range = \IPLib\Factory::rangeFromString('127.0.0.*'); 104$range = \IPLib\Factory::rangeFromString('::1/128'); 105$range = \IPLib\Factory::rangeFromString('::'); 106``` 107 108 109### Retrive a range from its boundaries 110 111```php 112$range = \IPLib\Factory::rangeFromBoundaries('192.168.0.1', '192.168.255.255'); 113echo (string) $range; 114// prints 192.168.0.0/16 115``` 116 117 118### Retrive the boundaries of a range 119 120```php 121$range = \IPLib\Factory::rangeFromString('127.0.0.*'); 122echo (string) $range->getStartAddress(); 123// prints 127.0.0.0 124echo (string) $range->getEndAddress(); 125// prints 127.0.0.255 126``` 127 128 129### Format addresses and ranges 130 131Both IP addresses and ranges have a `toString` method that you can use to retrieve a textual representation: 132 133```php 134echo \IPLib\Factory::addressFromString('127.0.0.1')->toString(); 135// prints 127.0.0.1 136echo \IPLib\Factory::addressFromString('127.000.000.001')->toString(); 137// prints 127.0.0.1 138echo \IPLib\Factory::addressFromString('::1')->toString(); 139// prints ::1 140echo \IPLib\Factory::addressFromString('0:0::1')->toString(); 141// prints ::1 142echo \IPLib\Factory::rangeFromString('0:0::1/64')->toString(); 143// prints ::1/64 144``` 145 146When 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: 147 148```php 149echo \IPLib\Factory::addressFromString('::')->toString(true); 150// prints 0000:0000:0000:0000:0000:0000:0000:0000 151echo \IPLib\Factory::addressFromString('::1')->toString(true); 152// prints 0000:0000:0000:0000:0000:0000:0000:0001 153echo \IPLib\Factory::addressFromString('fff::')->toString(true); 154// prints 0fff:0000:0000:0000:0000:0000:0000:0000 155echo \IPLib\Factory::addressFromString('::0:0')->toString(true); 156// prints 0000:0000:0000:0000:0000:0000:0000:0000 157echo \IPLib\Factory::addressFromString('1:2:3:4:5:6:7:8')->toString(true); 158// prints 0001:0002:0003:0004:0005:0006:0007:0008 159echo \IPLib\Factory::rangeFromString('0:0::1/64')->toString(); 160// prints 0000:0000:0000:0000:0000:0000:0000:0001/64 161``` 162 163 164### Check if an address is contained in a range 165 166All 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: 167 168```php 169$address = \IPLib\Factory::addressFromString('1:2:3:4:5:6:7:8'); 170$range = \IPLib\Factory::rangeFromString('0:0::1/64'); 171 172$contained = $address->matches($range); 173// that's equivalent to 174$contained = $range->contains($address); 175``` 176 177Please remark that if the address is IPv4 and the range is IPv6 (or vice-versa), the result will always be `false`. 178 179 180### Check if a range contains another range 181 182All the range types offer a `containsRange` method: you can call them to check if an address range fully contains another range: 183 184```php 185$range1 = \IPLib\Factory::rangeFromString('0:0::1/64'); 186$range2 = \IPLib\Factory::rangeFromString('0:0::1/65'); 187$contained = $range1->containsRange($range2); 188``` 189 190 191### Getting the type of an IP address 192 193If 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: 194 195```php 196$address = \IPLib\Factory::addressFromString('::'); 197 198$typeID = $address->getRangeType(); 199 200$typeName = \IPLib\Range\Type::getName(); 201``` 202 203The most notable values of the range type ID are: 204- `\IPLib\Range\Type::T_UNSPECIFIED` if the address is all zeros (`0.0.0.0` or `::`) 205- `\IPLib\Range\Type::T_LOOPBACK` if the address is the localhost (usually `127.0.0.1` or `::1`) 206- `\IPLib\Range\Type::T_PRIVATENETWORK` if the address is in the local network (for instance `192.168.0.1` or `fc00::1`) 207- `\IPLib\Range\Type::T_PUBLIC` if the address is for public usage (for instance `104.25.25.33` or `2001:503:ba3e::2:30`) 208 209 210### Getting the type of an IP address range 211 212If you want to know the type of an address range, you can use the `getRangeType` method: 213 214```php 215$range = \IPLib\Factory::rangeFromString('2000:0::1/64'); 216$type = $range->getRangeType(); 217// $type is \IPLib\Range\Type::T_PUBLIC 218echo \IPLib\Range\Type::getName($type); 219// 'Public address' 220``` 221 222Please remark that if a range spans across multiple range types, you'll get NULL as the range type: 223 224```php 225$range = \IPLib\Factory::rangeFromString('::/127'); 226$type = $range->getRangeType(); 227// $type is null 228echo \IPLib\Range\Type::getName($type); 229// 'Unknown type' 230``` 231 232### Converting IP ranges 233 234This 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: 235 236```php 237// This will print ::*:*:*:* 238echo \IPLib\Factory::rangeFromString('::/64')->asPattern()->toString(); 239 240// This will print 1:2::/96 241echo \IPLib\Factory::rangeFromString('1:2::*:*')->asSubnet()->toString(); 242 243// This will print 192.168.0.0/24 244echo \IPLib\Factory::rangeFromString('192.168.0.*')->asSubnet()->toString(); 245 246// This will print 10.*.*.* 247echo \IPLib\Factory::rangeFromString('10.0.0.0/8')->asPattern()->toString(); 248``` 249 250### Getting the subnet mask for IPv4 ranges 251 252You can use the `getSubnetMask()` to get the subnet mask for IPv4 ranges: 253 254```php 255// This will print 255.255.255.0 256echo \IPLib\Factory::rangeFromString('192.168.0.*')->getSubnetMask()->toString(); 257 258// This will print 255.255.255.252 259echo \IPLib\Factory::rangeFromString('192.168.0.12/30')->getSubnetMask()->toString(); 260``` 261 262### Using a database 263 264This 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. 265 266To 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. 267These methods are: 268```php 269$range->getAddressType(); 270$range->getComparableStartString(); 271$range->getComparableEndString(); 272``` 273 274Let's assume that you saved the type in a field called `addressType`, and the range boundaries in two fields called `rangeFrom` and `rangeTo`. 275 276When 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. 277 278Here's a sample code: 279 280```php 281/* 282 * Let's assume that: 283 * - $pdo is a PDO instance 284 * - $range is a range object 285 * - $address is an address object 286 */ 287 288// Save the $range object 289$insertQuery = $pdo->prepare(' 290 insert into ranges (addressType, rangeFrom, rangeTo) 291 values (:addressType, :rangeFrom, :rangeTo) 292'); 293$insertQuery->execute(array( 294 ':addressType' => $range->getAddressType(), 295 ':rangeFrom' => $range->getComparableStartString(), 296 ':rangeTo' => $range->getComparableEndString(), 297)); 298 299// Retrieve the saved ranges where an address $address falls: 300$searchQuery = $pdo->prepare(' 301 select * from ranges 302 where addressType = :addressType 303 and :address between rangeFrom and rangeTo 304'); 305$searchQuery->execute(array( 306 ':addressType' => $address->getAddressType(), 307 ':address' => $address->getComparableString(), 308)); 309$rows = $searchQuery->fetchAll(); 310$searchQuery->closeCursor(); 311``` 312