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