Passed
Push — master ( 2371a9...c5c5fb )
by Ondřej
01:50
created

MacAddr8::from6ByteMacAddrForIp6()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 1
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
namespace Ivory\Value;
4
5
use Ivory\Exception\ParseException;
6
7
/**
8
 * Representation of an 8-byte MAC address.
9
 *
10
 * The objects are immutable.
11
 */
12
class MacAddr8
13
{
14
    private $canonAddr;
15
16
    /**
17
     * Creates an 8-byte MAC address from its string representation.
18
     *
19
     * The same input formats as by PostgreSQL are accepted. Consistently with PostgreSQL, both 6 and 8 byte MAC
20
     * addresses are accepted, 6 bytes converted to 8 bytes by 4th and 5th bytes set to FF and FE, respectively.
21
     *
22
     * Any input which is comprised of pairs of hex digits (on byte boundaries), optionally separated consistently by
23
     * one of `':'`, `'-'` or `'.'`, is accepted. The number of hex digits must be either 16 (8 bytes) or 12 (6 bytes).
24
     * Leading and trailing whitespace is ignored. The following are examples of input formats that are accepted:
25
     * `'XX:XX:XX:XX:XX:XX:XX:XX'`
26
     * `'XX-XX-XX-XX-XX-XX-XX-XX'`
27
     * `'XXXXXX:XXXXXXXXXX'`
28
     * `'XXXXXX-XXXXXXXXXX'`
29
     * `'XXXX.XXXX.XXXX.XXXX'`
30
     * `'XXXX-XXXX-XXXX-XXXX'`
31
     * `'XXXXXXXX:XXXXXXXX'`
32
     * `'XXXXXXXXXXXXXXXX'`
33
     *
34
     * @param string $addr the string representation of the 8-byte MAC address in one of the formats mentioned above;
35
     *                     other formats are rejected by a {@link ParseException}
36
     * @return MacAddr8
37
     */
38
    public static function fromString(string $addr): MacAddr8
39
    {
40
        $canon = '';
41
        $len = strlen($addr);
42
        $sep = null;
43
        $digits = 0;
44
        for ($i = 0; $i < $len; $i++) {
45
            $c = $addr[$i];
46
            if (ctype_xdigit($c)) {
47
                if (($digits % 2) == 0 && $digits > 0) {
48
                    $canon .= ':';
49
                }
50
                $canon .= $c;
51
                $digits++;
52
            } elseif ($sep === null && ($c == ':' || $c == '-' || $c == '.')) {
53
                $sep = $c;
54
            } elseif ($c !== $sep && !ctype_space($c)) {
55
                throw new ParseException('Invalid macaddr8 string', $i);
56
            }
57
        }
58
59
        if ($digits == 12) {
0 ignored issues
show
introduced by
The condition $digits == 12 is always false.
Loading history...
60
            $canon = substr($canon, 0, 8) . ':ff:fe' . substr($canon, 8);
61
        } elseif ($digits != 16) {
0 ignored issues
show
introduced by
The condition $digits != 16 is always true.
Loading history...
62
            throw new ParseException('Invalid macaddr8 string: only 6 or 8 bytes may be provided');
63
        }
64
65
        return new MacAddr8(strtolower($canon));
66
    }
67
68
    /**
69
     * Creates an 8-byte MAC address from a 6-byte MAC address using the standard conversion.
70
     *
71
     * 8 bytes are made from 6 bytes in such a way that bytes `FFFE` are inserted in the middle of the 6 bytes.
72
     *
73
     * @see from6ByteMacAddrForIp6() for a modified conversion used by IPv6
74
     * @param MacAddr $macAddr
75
     * @return MacAddr8
76
     */
77
    public static function from6ByteMacAddr(MacAddr $macAddr): MacAddr8
78
    {
79
        $inStr = $macAddr->toString();
80
        $canon = substr($inStr, 0, 8) . ':ff:fe' . substr($inStr, 8);
81
        return new MacAddr8($canon);
82
    }
83
84
    /**
85
     * Creates an 8-byte MAC address from a 6-byte MAC address using the IPv6 conversion.
86
     *
87
     * 8 bytes are made from 6 bytes in such a way that `FFFE` are inserted in the middle of the 6 bytes, and the 7th
88
     * bit is set. This is the conversion used by IPv6.
89
     *
90
     * @see from6ByteMacAddr() for the standard conversion, i.e., without setting the 7th bit
91
     * @param MacAddr $macAddr
92
     * @return MacAddr8
93
     */
94
    public static function from6ByteMacAddrForIp6(MacAddr $macAddr): MacAddr8
95
    {
96
        $inStr = $macAddr->toString();
97
        $scanned = sscanf(substr($inStr, 0, 2), '%x', $firstByte);
98
        assert($scanned == 1);
99
        $firstByte |= 0b000000010;
100
        $canon = sprintf('%02x', $firstByte) . substr($inStr, 2, 6) . ':ff:fe' . substr($inStr, 8);
101
102
        return new MacAddr8($canon);
103
    }
104
105
    private function __construct(string $canonAddr)
106
    {
107
        $this->canonAddr = $canonAddr;
108
    }
109
110
    /**
111
     * @return string the conventional canonical form of the 8-byte MAC address, i.e., in the
112
     *                  <tt>'XX:XX:XX:XX:XX:XX:XX:XX'</tt> format, all the digits in lowercase
113
     */
114
    final public function toString(): string
115
    {
116
        return $this->canonAddr;
117
    }
118
119
    public function __toString()
120
    {
121
        return $this->toString();
122
    }
123
}
124