Completed
Push — master ( 918974...334de5 )
by Michele
15s queued 10s
created

Pattern::getReverseDNSLookupName()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 6
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 83
    public function __construct(AddressInterface $fromAddress, AddressInterface $toAddress, $asterisksCount)
54
    {
55 83
        $this->fromAddress = $fromAddress;
56 83
        $this->toAddress = $toAddress;
57 83
        $this->asterisksCount = $asterisksCount;
58 83
    }
59
60
    /**
61
     * {@inheritdoc}
62
     *
63
     * @see \IPLib\Range\RangeInterface::__toString()
64
     */
65 24
    public function __toString()
66
    {
67 24
        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 114
    public static function fromString($range, $supportNonDecimalIPv4 = false)
79
    {
80 114
        if (!is_string($range) || strpos($range, '*') === false) {
81 42
            return null;
82
        }
83 74
        if ($range === '*.*.*.*') {
84 3
            return new static(IPv4::fromString('0.0.0.0'), IPv4::fromString('255.255.255.255'), 4);
85
        }
86 71
        if ($range === '*:*:*:*:*:*:*:*') {
87 1
            return new static(IPv6::fromString('::'), IPv6::fromString('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 8);
88
        }
89 70
        $matches = null;
90 70
        if (strpos($range, '.') !== false && preg_match('/^[^*]+((?:\.\*)+)$/', $range, $matches)) {
91 45
            $asterisksCount = strlen($matches[1]) >> 1;
92 45
            if ($asterisksCount > 0) {
93 45
                $missingDots = 3 - substr_count($range, '.');
94 45
                if ($missingDots > 0) {
95 2
                    $range .= str_repeat('.*', $missingDots);
96 2
                    $asterisksCount += $missingDots;
97
                }
98
            }
99 45
            $fromAddress = IPv4::fromString(str_replace('*', '0', $range), true, $supportNonDecimalIPv4);
100 45
            if ($fromAddress === null) {
101 1
                return null;
102
            }
103 44
            $fixedBytes = array_slice($fromAddress->getBytes(), 0, -$asterisksCount);
104 44
            $otherBytes = array_fill(0, $asterisksCount, 255);
105 44
            $toAddress = IPv4::fromBytes(array_merge($fixedBytes, $otherBytes));
106
107 44
            return new static($fromAddress, $toAddress, $asterisksCount);
108
        }
109 25
        if (strpos($range, ':') !== false && preg_match('/^[^*]+((?::\*)+)$/', $range, $matches)) {
110 21
            $asterisksCount = strlen($matches[1]) >> 1;
111 21
            $fromAddress = IPv6::fromString(str_replace('*', '0', $range));
112 21
            if ($fromAddress === null) {
113
                return null;
114
            }
115 21
            $fixedWords = array_slice($fromAddress->getWords(), 0, -$asterisksCount);
116 21
            $otherWords = array_fill(0, $asterisksCount, 0xffff);
117 21
            $toAddress = IPv6::fromWords(array_merge($fixedWords, $otherWords));
118
119 21
            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 70
    public function toString($long = false)
131
    {
132 70
        if ($this->asterisksCount === 0) {
133 2
            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 54
    public function getAddressType()
172
    {
173 54
        return $this->fromAddress->getAddressType();
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     *
179
     * @see \IPLib\Range\RangeInterface::getStartAddress()
180
     */
181 18
    public function getStartAddress()
182
    {
183 18
        return $this->fromAddress;
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     *
189
     * @see \IPLib\Range\RangeInterface::getEndAddress()
190
     */
191 18
    public function getEndAddress()
192
    {
193 18
        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 14
    public function asSubnet()
222
    {
223 14
        switch ($this->getAddressType()) {
224 14 View Code Duplication
            case AddressType::T_IPv4:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
225 5
                return new Subnet($this->getStartAddress(), $this->getEndAddress(), 8 * (4 - $this->asterisksCount));
226 9 View Code Duplication
            case AddressType::T_IPv6:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
227 9
                return new Subnet($this->getStartAddress(), $this->getEndAddress(), 16 * (8 - $this->asterisksCount));
228
        }
229
    }
230
231
    /**
232
     * {@inheritdoc}
233
     *
234
     * @see \IPLib\Range\RangeInterface::asPattern()
235
     */
236 5
    public function asPattern()
237
    {
238 5
        return $this;
239 1
    }
240
241 4
    /**
242 4
     * {@inheritdoc}
243
     *
244
     * @see \IPLib\Range\RangeInterface::getSubnetMask()
245 4
     */
246 1
    public function getSubnetMask()
247 1
    {
248
        if ($this->getAddressType() !== AddressType::T_IPv4) {
249 3
            return null;
250 3
        }
251
        switch ($this->asterisksCount) {
252
            case 0:
253 4
                $bytes = array(255, 255, 255, 255);
254
                break;
255
            case 4:
256
                $bytes = array(0, 0, 0, 0);
257
                break;
258
            default:
259
                $bytes = array_pad(array_fill(0, 4 - $this->asterisksCount, 255), 4, 0);
260
                break;
261
        }
262
263
        return IPv4::fromBytes($bytes);
264
    }
265
266
    /**
267
     * {@inheritdoc}
268
     *
269
     * @see \IPLib\Range\RangeInterface::getReverseDNSLookupName()
270
     */
271
    public function getReverseDNSLookupName()
272
    {
273
        return $this->asterisksCount === 0 ? array($this->getStartAddress()->getReverseDNSLookupName()) : $this->asSubnet()->getReverseDNSLookupName();
274
    }
275
}
276