Completed
Pull Request — master (#69)
by Michele
06:19
created

Pattern::getComparableEndString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace IPLib\Range;
4
5
use IPLib\Address\AddressInterface;
6
use IPLib\Address\IPv4;
7
use IPLib\Address\IPv6;
8
use IPLib\Address\Type as AddressType;
9
10
/**
11
 * Represents an address range in pattern format (only ending asterisks are supported).
12
 *
13
 * @example 127.0.*.*
14
 * @example ::/8
15
 */
16
class Pattern extends AbstractRange
17
{
18
    /**
19
     * Starting address of the range.
20
     *
21
     * @var \IPLib\Address\AddressInterface
22
     */
23
    protected $fromAddress;
24
25
    /**
26
     * Final address of the range.
27
     *
28
     * @var \IPLib\Address\AddressInterface
29
     */
30
    protected $toAddress;
31
32
    /**
33
     * Number of ending asterisks.
34
     *
35
     * @var int
36
     */
37
    protected $asterisksCount;
38
39
    /**
40
     * The type of the range of this IP range.
41
     *
42
     * @var int|false|null false if this range crosses multiple range types, null if yet to be determined
43
     */
44
    protected $rangeType;
45
46
    /**
47
     * Initializes the instance.
48
     *
49
     * @param \IPLib\Address\AddressInterface $fromAddress
50
     * @param \IPLib\Address\AddressInterface $toAddress
51
     * @param int $asterisksCount
52
     */
53 137
    public function __construct(AddressInterface $fromAddress, AddressInterface $toAddress, $asterisksCount)
54
    {
55 137
        $this->fromAddress = $fromAddress;
56 137
        $this->toAddress = $toAddress;
57 137
        $this->asterisksCount = $asterisksCount;
58 137
    }
59
60
    /**
61
     * {@inheritdoc}
62
     *
63
     * @see \IPLib\Range\RangeInterface::__toString()
64
     */
65 66
    public function __toString()
66
    {
67 66
        return $this->toString();
68
    }
69
70
    /**
71
     * Try get the range instance starting from its string representation.
72
     *
73
     * @param string|mixed $range
74
     * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
75
     *
76
     * @return static|null
77
     */
78 129
    public static function fromString($range, $supportNonDecimalIPv4 = false)
79
    {
80 129
        if (!is_string($range) || strpos($range, '*') === false) {
81 45
            return null;
82
        }
83 86
        if ($range === '*.*.*.*') {
84 4
            return new static(IPv4::fromString('0.0.0.0'), IPv4::fromString('255.255.255.255'), 4);
85
        }
86 82
        if ($range === '*:*:*:*:*:*:*:*') {
87 2
            return new static(IPv6::fromString('::'), IPv6::fromString('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 8);
88
        }
89 80
        $matches = null;
90 80
        if (strpos($range, '.') !== false && preg_match('/^[^*]+((?:\.\*)+)$/', $range, $matches)) {
91 48
            $asterisksCount = strlen($matches[1]) >> 1;
92 48
            if ($asterisksCount > 0) {
93 48
                $missingDots = 3 - substr_count($range, '.');
94 48
                if ($missingDots > 0) {
95 2
                    $range .= str_repeat('.*', $missingDots);
96 2
                    $asterisksCount += $missingDots;
97
                }
98
            }
99 48
            $fromAddress = IPv4::fromString(str_replace('*', '0', $range), true, $supportNonDecimalIPv4);
100 48
            if ($fromAddress === null) {
101 1
                return null;
102
            }
103 47
            $fixedBytes = array_slice($fromAddress->getBytes(), 0, -$asterisksCount);
104 47
            $otherBytes = array_fill(0, $asterisksCount, 255);
105 47
            $toAddress = IPv4::fromBytes(array_merge($fixedBytes, $otherBytes));
106
107 47
            return new static($fromAddress, $toAddress, $asterisksCount);
108
        }
109 32
        if (strpos($range, ':') !== false && preg_match('/^[^*]+((?::\*)+)$/', $range, $matches)) {
110 28
            $asterisksCount = strlen($matches[1]) >> 1;
111 28
            $fromAddress = IPv6::fromString(str_replace('*', '0', $range));
112 28
            if ($fromAddress === null) {
113
                return null;
114
            }
115 28
            $fixedWords = array_slice($fromAddress->getWords(), 0, -$asterisksCount);
116 28
            $otherWords = array_fill(0, $asterisksCount, 0xffff);
117 28
            $toAddress = IPv6::fromWords(array_merge($fixedWords, $otherWords));
118
119 28
            return new static($fromAddress, $toAddress, $asterisksCount);
120
        }
121
122 4
        return null;
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     *
128
     * @see \IPLib\Range\RangeInterface::toString()
129
     */
130 112
    public function toString($long = false)
131
    {
132 112
        if ($this->asterisksCount === 0) {
133 56
            return $this->fromAddress->toString($long);
134
        }
135
        switch (true) {
136 68
            case $this->fromAddress instanceof \IPLib\Address\IPv4:
137 42
                $chunks = explode('.', $this->fromAddress->toString());
138 42
                $chunks = array_slice($chunks, 0, -$this->asterisksCount);
139 42
                $chunks = array_pad($chunks, 4, '*');
140 42
                $result = implode('.', $chunks);
141 42
                break;
142 26
            case $this->fromAddress instanceof \IPLib\Address\IPv6:
143 26
                if ($long) {
144 6
                    $chunks = explode(':', $this->fromAddress->toString(true));
145 6
                    $chunks = array_slice($chunks, 0, -$this->asterisksCount);
146 6
                    $chunks = array_pad($chunks, 8, '*');
147 6
                    $result = implode(':', $chunks);
148 26
                } elseif ($this->asterisksCount === 8) {
149 2
                    $result = '*:*:*:*:*:*:*:*';
150
                } else {
151 24
                    $bytes = $this->toAddress->getBytes();
152 24
                    $bytes = array_slice($bytes, 0, -$this->asterisksCount * 2);
153 24
                    $bytes = array_pad($bytes, 16, 1);
154 24
                    $address = IPv6::fromBytes($bytes);
155 24
                    $before = substr($address->toString(false), 0, -strlen(':101') * $this->asterisksCount);
156 24
                    $result = $before . str_repeat(':*', $this->asterisksCount);
157
                }
158 26
                break;
159
            default:
160
                throw new \Exception('@todo'); // @codeCoverageIgnore
161
        }
162
163 68
        return $result;
164
    }
165
166
    /**
167
     * {@inheritdoc}
168
     *
169
     * @see \IPLib\Range\RangeInterface::getAddressType()
170
     */
171 66
    public function getAddressType()
172
    {
173 66
        return $this->fromAddress->getAddressType();
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     *
179
     * @see \IPLib\Range\RangeInterface::getStartAddress()
180
     */
181 30
    public function getStartAddress()
182
    {
183 30
        return $this->fromAddress;
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     *
189
     * @see \IPLib\Range\RangeInterface::getEndAddress()
190
     */
191 30
    public function getEndAddress()
192
    {
193 30
        return $this->toAddress;
194
    }
195
196
    /**
197
     * {@inheritdoc}
198
     *
199
     * @see \IPLib\Range\RangeInterface::getComparableStartString()
200
     */
201 34
    public function getComparableStartString()
202
    {
203 34
        return $this->fromAddress->getComparableString();
204
    }
205
206
    /**
207
     * {@inheritdoc}
208
     *
209
     * @see \IPLib\Range\RangeInterface::getComparableEndString()
210
     */
211 34
    public function getComparableEndString()
212
    {
213 34
        return $this->toAddress->getComparableString();
214
    }
215
216
    /**
217
     * {@inheritdoc}
218
     *
219
     * @see \IPLib\Range\RangeInterface::asSubnet()
220
     */
221 26
    public function asSubnet()
222
    {
223 26
        return new Subnet($this->getStartAddress(), $this->getEndAddress(), $this->getNetworkPrefix());
224
    }
225 9
226
    /**
227 17
     * {@inheritdoc}
228
     *
229
     * @see \IPLib\Range\RangeInterface::asPattern()
230
     */
231
    public function asPattern()
232
    {
233
        return $this;
234
    }
235
236 14
    /**
237
     * {@inheritdoc}
238 14
     *
239
     * @see \IPLib\Range\RangeInterface::getSubnetMask()
240
     */
241
    public function getSubnetMask()
242
    {
243
        if ($this->getAddressType() !== AddressType::T_IPv4) {
244
            return null;
245
        }
246 5
        switch ($this->asterisksCount) {
247
            case 0:
248 5
                $bytes = array(255, 255, 255, 255);
249 1
                break;
250
            case 4:
251 4
                $bytes = array(0, 0, 0, 0);
252 4
                break;
253
            default:
254
                $bytes = array_pad(array_fill(0, 4 - $this->asterisksCount, 255), 4, 0);
255 4
                break;
256 1
        }
257 1
258
        return IPv4::fromBytes($bytes);
259 3
    }
260 3
261
    /**
262
     * {@inheritdoc}
263 4
     *
264
     * @see \IPLib\Range\RangeInterface::getReverseDNSLookupName()
265
     */
266
    public function getReverseDNSLookupName()
267
    {
268
        return $this->asterisksCount === 0 ? array($this->getStartAddress()->getReverseDNSLookupName()) : $this->asSubnet()->getReverseDNSLookupName();
269
    }
270
271 12
    /**
272
     * {@inheritdoc}
273 12
     *
274
     * @see \IPLib\Range\RangeInterface::getSize()
275
     */
276 View Code Duplication
    public function getSize()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
277
    {
278
        $fromAddress = $this->fromAddress;
279
        $maxPrefix = $fromAddress::getNumberOfBits();
280
        $prefix = $this->getNetworkPrefix();
281
282
        return pow(2, ($maxPrefix - $prefix));
283
    }
284
285
    /**
286
     * @return float|int
287
     */
288
    private function getNetworkPrefix()
289
    {
290
        switch ($this->getAddressType()) {
291
            case AddressType::T_IPv4:
292
                return 8 * (4 - $this->asterisksCount);
293
            case AddressType::T_IPv6:
294
                return 16 * (8 - $this->asterisksCount);
295
        }
296
    }
297
}
298