IP Address Range PHP Class
I’m not sure how useful this piece of code really is, but it gave me the chance to write some funky PHP code (have a look at the next() method).
Example usage
<?php
// Single host:
foreach(new IpRange('10.10.10.10') as $ip)
{
echo $ip . "\n"
}
// >>> 10.10.10.10
// All hosts on a private network:
foreach(new IpRange('192.168.0.1-255') as $ip)
{
echo $ip . "\n";
}
// >>> 192.168.0.1
// >>> 192.168.0.2
// >>> ...
// >>> 192.168.0.254
// >>> 192.168.0.255
// All normal (not broadcast, or multicast) IP addresses:
foreach(new IpRange('1-232.0-255.0-255.0-255') as $k => $v)
{
echo "$k => $v\n";
}
// >>> 0 => 1.0.0.0
// >>> 1 => 1.0.0.1
// >>> 2 => 1.0.0.2
// >>> ...
// >>> 7315795 => 1.111.161.83
// >>> 7315796 => 1.111.161.84
// >>> etc
Source
<?php
/*
Copyright (c) 2011, Matthew Davey <matthewd@project-2501.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
IpRange Class
Given an IP address where each octet can either be a number between 0 and
255, or a range i.e. 100-200. Return an iterable object that returns all
IP addresses in between.
Examples
--------
Single host:
foreach(new IpRange('10.10.10.10') as $ip)
{
echo $ip . "\n"
}
>>> 10.10.10.10
All hosts on a private network:
foreach(new IpRange('192.168.0.1-255') as $ip)
{
echo $ip . "\n";
}
>>> 192.168.0.1
>>> 192.168.0.2
>>> ...
>>> 192.168.0.254
>>> 192.168.0.255
All normal (not broadcast, or multicast) IP addresses:
foreach(new IpRange('1-232.0-255.0-255.0-255') as $key => $value)
{
echo "$key => $value\n";
}
>>> 0 => 1.0.0.0
>>> 1 => 1.0.0.1
>>> 2 => 1.0.0.2
>>> ...
>>> 7315795 => 1.111.161.83
>>> 7315796 => 1.111.161.84
>>> etc
*/
class IpRange implements Iterator
{
protected $count; // Current position, used as the key
protected $a; // ranges for each octet, l => low, h => high
protected $b; //
protected $c; // Most significant to least is aaa.bbb.ccc.ddd
protected $d; //
protected $ca; // current value of a
protected $cb; // current value of b
protected $cc; // current value of c
protected $cd; // current value of d
protected $isValid; // flag set when next() is call at end of ranges
public function __construct($string)
{
if(preg_match('#^(\d+|\d+-\d+)\.(\d+|\d+-\d+)\.(\d+|\d+-\d+)\.(\d+|\d+-\d+)$#', $string, $matches) !== 1)
{
throw new InvalidArgumentException('Invalid format. Each octet should either be a number between 0 and 255, or a range "40-120"');
}
// Parse each octet and find the low/high values (high === low if there is no range specified)
foreach(array(1 => 'a', 2 => 'b', 3 => 'c', 4 => 'd') as $i => $position)
{
$range = $matches[$i];
if(strpos($range, '-') !== false)
{
list($low, $high) = explode('-', $range);
}
else
{
list($low, $high) = array($range, $range);
}
// Check the IP address is at least mostly valid. We don't need to check for <0 as our regex will reject it first.
if($high > 255) throw new InvalidArgumentException("Invalid IP address. The octet '$high' cannot be greater than 255");
// If given a range like 255-0, flip the high/low value over
if($low > $high) list($high, $low) = array($low, $high);
$this->{$position} = array('l' => $low, 'h' => $high);
}
$this->rewind();
}
public function current()
{
return "{$this->ca}.{$this->cb}.{$this->cc}.{$this->cd}";
}
public function key()
{
return $this->count;
}
public function rewind()
{
$this->ca = $this->a['l'];
$this->cb = $this->b['l'];
$this->cc = $this->c['l'];
$this->cd = $this->d['l'];
$this->count = 0;
$this->isValid = true;
}
public function valid()
{
return $this->isValid;
}
public function next()
{
$this->count++;
// Least significant to most. Null is our guard.
foreach(array('d', 'c', 'b', 'a', null) as $position)
{
// Check if unable to generate the next IP address
if($position === null)
{
$this->isValid = false;
break;
}
// Check if the value is less than the maximum for this
// position, if so increment the value and stop. Otherwise set
// this position to its lowest value, and continue onto the next
// highest position.
if($this->{"c$position"} < $this->{$position}['h'])
{
$this->{"c$position"}++;
break;
}
else
{
$this->{"c$position"} = $this->{$position}['l'];
}
}
}
}