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 <[email protected]>
    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'];
                }
            }
        }
    }