Passed
Push — master ( dbb22f...476838 )
by Matthieu
04:08
created

Range::contains()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 1
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Copyright (c) 2018 Mattheu Racine
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace mracine\IPTools\IPv4;
10
11
use OutOfBoundsException;
12
use InvalidArgumentException;
13
use BadMethodCallException;
14
15
use mracine\IPTools\Iterators\RangeIterator;
16
use mracine\IPTools\IPv4\Address;
17
use mracine\IPTools\IPVersion;
18
19
/**
20
 * Represents a range of IPv4 addresses
21
 *
22
 * A range of IPv4 address are consecutives addresses
23
 *
24
 * A range implements :
25
 *  * Countable : number of addresses
26
 *  * ArrayAccess : Address can be acceesed by offset (ex : $range[10])
27
 *  * IteratorAggregate :   range can be itrated (foreach)
28
 * 
29
 * @package IPTools
30
 */
31
32
class Range implements IPVersion, \Countable, \ArrayAccess, \IteratorAggregate
33
{
34
35
    /**
36
     * @var Address $baseAddress The first address of the range
37
     */
38
    protected $baseAddress;
39
40
    /**
41
     * @var Address upperBound The last address of the range
42
     */
43
    // private $upperBound;
44
    /**
45
     * @var int $count the address count in the range
46
     */
47
    protected $count;
48
49
50
    // Implemnents version(); 
51
    use IPv4;
52
53
    /**
54
     * Creates an IP range from a couple of IP addresses
55
     *
56
     * Lower and upper bound are automaticly choosen, no need to choose wich one to pas first.
57
     *
58
     * @param Address $baseAddress the first address of the range
59
     * @param int $count the number  other bound
60
     */
61 7
    public function __construct(Address $lowerBound, Address $upperBound)
62
    {
63 7
        $lower = $lowerBound->int();
64 7
        $upper = $upperBound->int();
65 7
        if ($lower > $upper)
66
        {
67 2
            $temp = $lower;
68 2
            $lower = $upper;
69 2
            $upper = $temp;
70
        }
71 7
        $this->baseAddress = new Address($lower);
72 7
        $this->count = $upper - $lower + 1;
73 7
    } 
74
75
    /**
76
     * Returns an Range object from a base Address and a count of Addresses in the range 
77
     *
78
     * Negative count values rearange the lower bound Address
79
     * @param Address $baseAddress the lower bound
80
     * @param int $count the number of addresses in the Range
81
     * @return self
82
     */
83 9
    public static function fromCount(Address $baseAddress, int $count)
84
    {
85 9
        if ($count == 0)
86
        {
87 1
            throw new InvalidArgumentException("Cannot assign a range of 0 addresses");
88
        }
89 8
        if ($count<0)
90
        {
91 4
            $delta = $count+1;
92
        }
93
        else
94
        {
95 4
            $delta = $count-1;
96
        }
97 8
        $upperBound = $baseAddress->shift($delta);
98 8
        return new static($baseAddress, $upperBound);
99
    }
100
101
102
    /**
103
     * Get the first address of the range
104
     *
105
     * NOTICE : The returned address is a newly created address instance.
106
     * 
107
     * @return Address
108
     */
109 3
    public function getLowerBound()
110
    {
111 3
        return clone($this->baseAddress);
112
    }
113
114
    /**
115
     * Returns the last address of the range
116
     *
117
     * NOTICE : The returned address is a newly created address instance.   
118
     *
119
     * @return Address
120
     */
121 3
    public function getUpperBound()
122
    {
123 3
        return new Address($this->getLowerBound()->int()+$this->count-1);
124
    }
125
126
    /**
127
     * Tells if an address is contained in the range
128
     *
129
     * Return true if the provided address is between the boundaries of the range.  
130
     *
131
     * @param Address $ip 
132
     * @return bool
133
     */
134 12
    public function contains(Address $ip)
135
    {
136 12
        $ipVal = $ip->int();
137
138 12
        return ($ipVal>=$this->getLowerBound()->int()) && ($ipVal<=$this->getUpperBound()->int());
139
    }
140
141
142
    /**
143
     * Tells if two ranges are equals (sames boundaries)
144
     *
145
     * Return true if the provided range is the same as this one.  
146
     *
147
     * @param Range $range 
148
     * @return bool
149
     */
150 4
    public function match(Range $range)
151
    {
152 4
        return ( ($this->getLowerBound()->int()==$range->getLowerBound()->int()) && ($this->getUpperBound()->int()==$range->getUpperBound()->int()));
153
    }
154
155
    /**
156
     * Return the number of Addresses in the range (including boundaries)
157
     *
158
     * @return int
159
     */
160 4
    public function count()
161
    {
162 4
        return $this->count; 
163
    }
164
165
    /**
166
     * Returns a range shifted by an amount of units (count)
167
     *
168
     * Need explain...
169
     *
170
     * @param int $offset
171
     * @throws OutOfBounsException when the resulting subet is invalid
172
     * @return Subnet
173
     */  
174 1
    public function shift(int $offset)
175
    {
176 1
        return static::fromCount($this->getLowerBound()->shift($offset*count($this)), count($this));
177
    }
178
179
    /**
180
     * Returns a subnet with the same netmask having network addres one more than broadcast address of the original subnet ($this)
181
     *
182
     * Need explain...
183
     *
184
     * @param int $offset
185
     * @throws OutOfBounsException when the resulting subet is invalid (when braodcast address of self = 255.255.255.255)
186
     * @return Subnet
187
     */  
188 1
    public function next()
189
    {
190 1
        return $this->shift(1);
191
    }
192
193
    /**
194
     * Returns a subnet with the same netmask having broacast addres one less than network address of the original subnet ($this)
195
     *
196
     * Need explain...
197
     *
198
     * @param int $offset
199
     * @throws OutOfBounsException when the resulting subet is invalid (when braodcast address of self = 255.255.255.255)
200
     * @return Subnet
201
     */  
202 1
    public function previous()
203
    {
204 1
        return $this->shift(-1);
205
    }
206
207
    /*
208
     * interface ArrayAccess
209
     */
210
211
    /**
212
     * Checks if there is an Address at the given index within the Range.
213
     *
214
     * @param int|string $offset address index
215
     * @throws InvalidArgumentException when $offset is not an integer
216
     * @return boolean
217
     */
218 12
    public function offsetExists($offset)
219
    {
220 12
         if (is_string($offset))
221
        {
222 2
            if (!preg_match('/^-?[0-9]+$/', $offset))
223
            {
224 1
                throw new InvalidArgumentException(sprintf('Invalid key type (%s), only integers or strings representing integers can be used to acces address in a Range', gettype($offset)));
225
            }           
226 1
            $offset = (int)$offset;
227
        }
228 11
       if (!is_int($offset))
229
        {
230 3
            throw new InvalidArgumentException(sprintf('Invalid key type (%s), only integers or strings representing integers can be used to acces address in a Range', gettype($offset)));
231
        }
232 8
        if ($offset<0 || $offset>=count($this))
233
        {
234 4
            return false;
235
        }
236 4
        return true;
237
    }
238
239
    /**
240
     * Return the Address at the given index within the Range.
241
     *
242
     * NOTICE : a fresh instance of Address is returned each time the method is called
243
     *
244
     * @param int $offset address index
245
     * @throws InvalidArgumentException when $offset is not an integer
246
     * @throws OutOfBoundsException when $offset is negative or is greater than range size
247
     * @return Address a fresh instance that represents the address at $offset  
248
     */
249 5
    public function offsetGet($offset)
250
    {
251 5
        if (!$this->offsetExists($offset))
252
        {
253 1
            throw new OutOfBoundsException();
254
        }
255
256 5
        return $this->getLowerBound()->shift($offset);
257
    }
258
259
    /**
260
     * Set the value of the address at a particular offset in a Range
261
     *
262
     * Unsupported : non-sense
263
     *
264
     * @param int $offset
265
     * @param Address $value
266
     * @throws BadMethodCallException always
267
     */ 
268 1
    public function offsetSet($offset,  $value)
269
    {
270 1
        throw new BadMethodCallException(sprintf("class %s is immutable, cannot modify an address value in a Range", self::class));
271
    }
272
    
273
    /**
274
     * Remove an address at a particular offset in a range
275
     *
276
     * Unsupported : non-sense
277
     *
278
     * @param int $offset
279
     * @throws BadMethodCallException always
280
     */ 
281 1
    public function offsetUnset($offset)
282
    {
283 1
        throw new BadMethodCallException(sprintf("class %s is immutable, cannot unset an address value in a Range", self::class));
284
    }
285
286
    /*
287
     * interface IteratorAggregate
288
     */
289
290
    /**
291
     * Obtain an iterator to traverse the range
292
     *
293
     * Allows to iterate in the range (foreach ($subnet as $address) {...} )
294
     *
295
     * @return RangeIterator
296
     */
297
298 1
    public function getIterator()
299
    {
300 1
        return new RangeIterator($this);
301
    }
302
}
303