Passed
Push — master ( 33405b...4f3302 )
by Matthieu
01:55
created

Subnet::fromString()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 3
nop 1
dl 0
loc 13
ccs 7
cts 7
cp 1
crap 4
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 RangeException;
13
use BadMethodCallException;
14
use InvalidArgumentException;
15
16
use mracine\IPTools\Iterators\SubnetIterator;
17
use mracine\IPTools\IPv4\Address;
18
use mracine\IPTools\IPv4\Netmask;
19
use mracine\IPTools\IPVersion;
20
/**
21
 * Class Subnet represents an IPv4 IP address with a netmask to specify a subnet
22
 *
23
 * Subnet implements :
24
 *  * Countable : number of addresses
25
 *  * ArrayAccess : Address can be acceesed by offset (ex : $range[10])
26
 *  * IteratorAggregate :   range can be itrated (foreach)
27
28
 * @package IPTools
29
 */
30
class Subnet extends Range
31
{
32
    /**
33
     * @var Netmask $netmask 
34
     */
35
    protected $netmask;
36
37
     /**
38
     * Creates an IP Subnet from a an IP network addresses and a netamsk
39
     *
40
     * Lower and upper bound are automaticly choosen, no need to choose wich one to pass first.
41
     * Only 
42
     *
43
     * @param Address $baseAddress the first address of the range
44
     * @param int $count the number  other bound
45
     */
46 4
    public function __construct(Address $network, Address $netmask)
47
    {
48 4
        if (!($netmask instanceof Netmask))
49
        {
50
            /**
51
             * @todo implement
52
             */   
53 1
            throw new InvalidArgumentException("Not implemented");
54
        }
55
        // Verify parameters validity
56 3
        if( ($network->int() & $netmask->int()) != $network->int())
57
        {
58 1
            throw new RangeException(sprintf("Invalid network adress %s, this address is not usable with the netmask %s", (string)$network, (string)$netmask));
59
        }
60
61
62
63 2
        parent::__construct($network, $network->shift(count($netmask)-1));
64 2
        $this->netmask = $netmask;
65 2
    }
66
67
   /**
68
     * Construct an IPv4 Subnet from a CIDR notation (#.#.#.#/#)
69
     *
70
     * @param Address $network the network base address  of the subnet
71
     * @param int $cidr the CIDR notation of the netmask
72
     * @throws OutOfBoundsException when $cidr is negatve or greater than 32
73
     * @throws RangeException when network address and netmask does not form a valid subnet 
74
     * @return Subnet
75
     */
76 2
    public static function fromCidr(Address $network, int $cidr)
77
    {
78
        // Ensure CIDR is valid, use Address::fromCidr to have validation logic in only one place 
79 2
        $netmask = Netmask::fromCidr($cidr);
80 2
        return new static($network, $netmask);
81
    }
82
83
    /**
84
     * Returns an IPv4 Subnet from network address and netmask given as an address 
85
     *
86
     * @param Address $network the network base address of the subnet
87
     * @param Address $netmask the netmask address
88
     * @throws DomainException when the netmask address is not a valid netmask
89
     * @throws RangeException when network and netmask addresses does not form a valid subnet 
90
     * @return Subnet
91
     */
92
    // public static function fromAddresses(Address $network, Address $netmask)
93
    // {
94
    //     return new static($network, $netmask->asCidr());
95
    // }
96
97
    /**
98
     * Return a subnet from an addresss and a CIDR form netmask, the addresscan be within the subnet, not only the network address
99
     *
100
     * @param Address $address an address within the desired subnet
101
     * @param int $cidr the CIDR notation of the netmask
102
     * @return Subnet
103
     */
104 5
    public static function fromContainedAddressCidr(Address $address, int $cidr)
105
    {
106 5
        $netmask = Netmask::fromCidr($cidr)->int();
107 5
        $network = Address::fromInteger($address->int() & $netmask);
108 5
        return static::fromCidr($network, $cidr);
109
    }
110
111
112
    /**
113
     * Return a subnet from string
114
     *
115
     * 
116
     * 
117
     * @param string $subnet a CIDR formated or netmask representtation of the subnet : x.x.x.x/24 or x.x.x.x/255.255.255.0
118
     * @throws InvaidArgumentException when provided sting cannot be converted to a valid subnet
119
     * @return Subnet
120
     */
121 24
    public static function fromString(string $subnet)
122
    {
123 24
        @list($address, $netmask, $trash) = explode('/', $subnet);
124 24
        if (!is_null($trash) || is_null($netmask))
0 ignored issues
show
introduced by
The condition is_null($trash) is always false.
Loading history...
125
        {
126 10
            throw new InvalidArgumentException(sprintf("%s cannot be converted to subnet. Valid formats are : x.x.x.x/cidr or x.x.x.x/y.y.y.y", $subnet));
127
        }
128
129 14
        if(ctype_digit($netmask))
130
        {
131 6
            return static::fromCidr(Address::fromString($address), (int)$netmask);
132
        }
133 8
        return new static(Address::fromString($address), Netmask::fromString($netmask));
134
    }
135
136
    /**
137
     * Get the network address
138
     *
139
     * NOICE : return a fresh instance of address
140
     *
141
     * @return Address
142
     */
143 4
    public function getNetworkAddress()
144
    {
145 4
        return $this->getLowerBound();
146
    }
147
148
    /**
149
     * Get the netmask address 
150
     *
151
     * @return Netmask
152
     */
153 4
    public function getNetmaskAddress()
154
    {
155 4
        return $this->netmask;
156
    }
157
    /**
158
     * Get the broadcast address 
159
     *
160
     * @return Address
161
     */
162 6
    public function getBroadcastAddress()
163
    {
164 6
        return $this->getUpperBound();
165
    }
166
167
    /**
168
     * Returns a Subnet shifted by an amount of units (count)
169
     *
170
     * Need explain...
171
     *
172
     * @param int $offset
173
     * @throws OutOfBounsException when the resulting subet is invalid
174
     * @return Subnet
175
     */  
176 2
    public function shift(int $offset)
177
    {
178 2
        return static::fromCidr(
179 2
            $this->getNetworkAddress()->shift($offset*count($this)),
180 2
            $this->getNetmaskAddress()->asCidr()
181
         );
182
    }
183
184
    /*
185
     * interface IteratorAggregate
186
     */
187
188
    /**
189
     * Obtain an iterator to traverse the Subnet
190
     *
191
     * Allows to iterate in the subnet with foreach ($subnet as $address) {...} )
192
     *
193
     * @return SubnetIterator
194
     */
195 1
    public function getIterator()
196
    {
197 1
        return new SubnetIterator($this);
198
    }
199
}
200