Passed
Push — master ( 4f3302...913bce )
by Matthieu
01:55
created

Range::isIn()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 1
dl 0
loc 3
ccs 2
cts 2
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
     * @deprecated Use Address::isIn
132
     * @see Address::isIn
133
     * @param Address $ip
134
     * @return bool
135
     */
136 12
    public function contains(Address $ip)
137
    {
138 12
        return $ip->isIn($this);
139
    }
140
141
    /**
142
     * Tells if the range is contained in an other range
143
     *
144
     * Return true if the provided address is between the boundaries of the range.  
145
     *
146
     * @param Address $ip 
147
     * @return bool
148
     */
149 8
    public function isIn(Range $container)
150
    {
151 8
        return ($container->getLowerBound()->int()<=$this->getLowerBound()->int()) && ($this->getUpperBound()->int()<=$container->getUpperBound()->int());
152
    }
153
154
155
156
    /**
157
     * Tells if two ranges are equals (sames boundaries)
158
     *
159
     * Return true if the provided range is the same as this one.  
160
     *
161
     * @param Range $range 
162
     * @return bool
163
     */
164 4
    public function match(Range $range)
165
    {
166 4
        return ( ($this->getLowerBound()->int()==$range->getLowerBound()->int()) && ($this->getUpperBound()->int()==$range->getUpperBound()->int()));
167
    }
168
169
    /**
170
     * Return the number of Addresses in the range (including boundaries)
171
     *
172
     * @return int
173
     */
174 4
    public function count()
175
    {
176 4
        return $this->count; 
177
    }
178
179
    /**
180
     * Returns a range shifted by an amount of units (count)
181
     *
182
     * Need explain...
183
     *
184
     * @param int $offset
185
     * @throws OutOfBounsException when the resulting subet is invalid
186
     * @return Subnet
187
     */  
188 1
    public function shift(int $offset)
189
    {
190 1
        return static::fromCount($this->getLowerBound()->shift($offset*count($this)), count($this));
191
    }
192
193
    /**
194
     * Returns a subnet with the same netmask having network addres one more than broadcast 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 next()
203
    {
204 1
        return $this->shift(1);
205
    }
206
207
    /**
208
     * Returns a subnet with the same netmask having broacast addres one less than network address of the original subnet ($this)
209
     *
210
     * Need explain...
211
     *
212
     * @param int $offset
213
     * @throws OutOfBounsException when the resulting subet is invalid (when braodcast address of self = 255.255.255.255)
214
     * @return Subnet
215
     */  
216 1
    public function previous()
217
    {
218 1
        return $this->shift(-1);
219
    }
220
221
    /*
222
     * interface ArrayAccess
223
     */
224
225
    /**
226
     * Checks if there is an Address at the given index within the Range.
227
     *
228
     * @param int|string $offset address index
229
     * @throws InvalidArgumentException when $offset is not an integer
230
     * @return boolean
231
     */
232 12
    public function offsetExists($offset)
233
    {
234 12
         if (is_string($offset))
235
        {
236 2
            if (!preg_match('/^-?[0-9]+$/', $offset))
237
            {
238 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)));
239
            }           
240 1
            $offset = (int)$offset;
241
        }
242 11
       if (!is_int($offset))
243
        {
244 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)));
245
        }
246 8
        if ($offset<0 || $offset>=count($this))
247
        {
248 4
            return false;
249
        }
250 4
        return true;
251
    }
252
253
    /**
254
     * Return the Address at the given index within the Range.
255
     *
256
     * NOTICE : a fresh instance of Address is returned each time the method is called
257
     *
258
     * @param int $offset address index
259
     * @throws InvalidArgumentException when $offset is not an integer
260
     * @throws OutOfBoundsException when $offset is negative or is greater than range size
261
     * @return Address a fresh instance that represents the address at $offset  
262
     */
263 5
    public function offsetGet($offset)
264
    {
265 5
        if (!$this->offsetExists($offset))
266
        {
267 1
            throw new OutOfBoundsException();
268
        }
269
270 5
        return $this->getLowerBound()->shift($offset);
271
    }
272
273
    /**
274
     * Set the value of the address at a particular offset in a Range
275
     *
276
     * Unsupported : non-sense
277
     *
278
     * @param int $offset
279
     * @param Address $value
280
     * @throws BadMethodCallException always
281
     */ 
282 1
    public function offsetSet($offset,  $value)
283
    {
284 1
        throw new BadMethodCallException(sprintf("class %s is immutable, cannot modify an address value in a Range", self::class));
285
    }
286
    
287
    /**
288
     * Remove an address at a particular offset in a range
289
     *
290
     * Unsupported : non-sense
291
     *
292
     * @param int $offset
293
     * @throws BadMethodCallException always
294
     */ 
295 1
    public function offsetUnset($offset)
296
    {
297 1
        throw new BadMethodCallException(sprintf("class %s is immutable, cannot unset an address value in a Range", self::class));
298
    }
299
300
    /*
301
     * interface IteratorAggregate
302
     */
303
304
    /**
305
     * Obtain an iterator to traverse the range
306
     *
307
     * Allows to iterate in the range (foreach ($subnet as $address) {...} )
308
     *
309
     * @return RangeIterator
310
     */
311
312 1
    public function getIterator()
313
    {
314 1
        return new RangeIterator($this);
315
    }
316
}
317