1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace LeKoala\CommonExtensions\FieldType; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use SilverStripe\ORM\DB; |
7
|
|
|
use SilverStripe\ORM\FieldType\DBField; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* A field to store ip address in binary formats |
11
|
|
|
* |
12
|
|
|
* @link https://stackoverflow.com/questions/22636912/store-both-ipv4-and-ipv6-address-in-a-single-column |
13
|
|
|
* @link https://www.php.net/manual/en/function.inet-pton.php |
14
|
|
|
* @link https://www.php.net/manual/en/function.inet-ntop.php |
15
|
|
|
* @link https://github.com/S1lentium/IPTools/blob/master/src/IP.php |
16
|
|
|
*/ |
17
|
|
|
class DBBinaryIP extends DBField |
18
|
|
|
{ |
19
|
|
|
const IP_V4 = 'IPv4'; |
20
|
|
|
const IP_V6 = 'IPv6'; |
21
|
|
|
|
22
|
|
|
const IP_V4_MAX_PREFIX_LENGTH = 32; |
23
|
|
|
const IP_V6_MAX_PREFIX_LENGTH = 128; |
24
|
|
|
|
25
|
|
|
const IP_V4_OCTETS = 4; |
26
|
|
|
const IP_V6_OCTETS = 16; |
27
|
|
|
|
28
|
|
|
public function requireField() |
29
|
|
|
{ |
30
|
|
|
// Use direct sql statement here |
31
|
|
|
$sql = "binary(16)"; |
32
|
|
|
DB::require_field($this->tableName, $this->name, $sql); |
33
|
|
|
} |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @return string A readable ip address like 127.0.0.1 |
37
|
|
|
*/ |
38
|
|
|
public function Nice() |
39
|
|
|
{ |
40
|
|
|
if (!$this->value) { |
41
|
|
|
return $this->nullValue(); |
|
|
|
|
42
|
|
|
} |
43
|
|
|
return inet_ntop($this->value); |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @return string |
48
|
|
|
*/ |
49
|
|
|
public function BinValue() |
50
|
|
|
{ |
51
|
|
|
$binary = array(); |
52
|
|
|
foreach (unpack('C*', $this->value) as $char) { |
53
|
|
|
$binary[] = str_pad(decbin($char), 8, '0', STR_PAD_LEFT); |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
return implode($binary); |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @return string |
61
|
|
|
*/ |
62
|
|
|
public function HexValue() |
63
|
|
|
{ |
64
|
|
|
return bin2hex($this->value); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* @return string |
69
|
|
|
*/ |
70
|
|
|
public function LongValue() |
71
|
|
|
{ |
72
|
|
|
$long = 0; |
73
|
|
|
if ($this->getVersion() === self::IP_V4) { |
74
|
|
|
$long = sprintf('%u', ip2long(inet_ntop($this->value))); |
75
|
|
|
} else { |
76
|
|
|
$octet = self::IP_V6_OCTETS - 1; |
77
|
|
|
foreach ($chars = unpack('C*', $this->value) as $char) { |
|
|
|
|
78
|
|
|
$long = bcadd($long, bcmul($char, bcpow(256, $octet--))); |
79
|
|
|
} |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
return $long; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
public function scaffoldFormField($title = null, $params = null) |
86
|
|
|
{ |
87
|
|
|
return false; |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
public function nullValue() |
91
|
|
|
{ |
92
|
|
|
return null; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* @return string |
97
|
|
|
*/ |
98
|
|
|
public function getVersion() |
99
|
|
|
{ |
100
|
|
|
$version = ''; |
101
|
|
|
|
102
|
|
|
if (filter_var(inet_ntop($this->value), FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { |
103
|
|
|
$version = self::IP_V4; |
104
|
|
|
} elseif (filter_var(inet_ntop($this->value), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { |
105
|
|
|
$version = self::IP_V6; |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
return $version; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* @return int |
113
|
|
|
*/ |
114
|
|
|
public function getMaxPrefixLength() |
115
|
|
|
{ |
116
|
|
|
return $this->getVersion() === self::IP_V4 |
117
|
|
|
? self::IP_V4_MAX_PREFIX_LENGTH |
118
|
|
|
: self::IP_V6_MAX_PREFIX_LENGTH; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* @return int |
123
|
|
|
*/ |
124
|
|
|
public function getOctetsCount() |
125
|
|
|
{ |
126
|
|
|
return $this->getVersion() === self::IP_V4 |
127
|
|
|
? self::IP_V4_OCTETS |
128
|
|
|
: self::IP_V6_OCTETS; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* @return string |
133
|
|
|
*/ |
134
|
|
|
public function getReversePointer() |
135
|
|
|
{ |
136
|
|
|
if ($this->getVersion() === self::IP_V4) { |
137
|
|
|
$reverseOctets = array_reverse(explode('.', $this->Nice())); |
138
|
|
|
$reversePointer = implode('.', $reverseOctets) . '.in-addr.arpa'; |
139
|
|
|
} else { |
140
|
|
|
$unpacked = unpack('H*hex', $this->value); |
141
|
|
|
$reverseOctets = array_reverse(str_split($unpacked['hex'])); |
|
|
|
|
142
|
|
|
$reversePointer = implode('.', $reverseOctets) . '.ip6.arpa'; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
return $reversePointer; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
public function prepValueForDB($value) |
149
|
|
|
{ |
150
|
|
|
if (!$value) { |
151
|
|
|
return $this->nullValue(); |
|
|
|
|
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
// String ip contains dots |
155
|
|
|
if (strpos($value, '.') !== false) { |
156
|
|
|
return inet_pton($value); |
157
|
|
|
} |
158
|
|
|
// Strlen 16 = already binary |
159
|
|
|
if (strlen($value) === 16) { |
160
|
|
|
return $value; |
161
|
|
|
} |
162
|
|
|
throw new Exception("$value seems an invalid ip value"); |
163
|
|
|
} |
164
|
|
|
} |
165
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.