Rev 686 | Rev 771 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
635 | daniel-mar | 1 | <?php |
2 | |||
3 | /** |
||
4 | * WEID<=>OID Converter |
||
5 | * (c) Webfan.de, ViaThinkSoft |
||
683 | daniel-mar | 6 | * Revision 2021-12-08 |
635 | daniel-mar | 7 | **/ |
8 | |||
683 | daniel-mar | 9 | // What is a WEID? |
10 | // A WEID (WEhowski IDentifier) is an alternative representation of an |
||
11 | // OID (Object IDentifier) defined by Till Wehowski. |
||
12 | // In OIDs, arcs are in decimal base 10. In WEIDs, the arcs are in base 36. |
||
13 | // Also, each WEID has a check digit at the end (called WeLohn Check Digit). |
||
14 | // |
||
15 | // Changes in the December 2021 definition by Daniel Marschall: |
||
16 | // - There are several classes of WEIDs which have different OID bases: |
||
17 | // "Class C" WEID: weid:EXAMPLE-3 (base .1.3.6.1.4.1.37553.8.) |
||
18 | // oid:1.3.6.1.4.1.37553.8.32488192274 |
||
19 | // "Class B" WEID: weid:pen:SX0-7PR-6 (base .1.3.6.1.4.1.) |
||
20 | // oid:1.3.6.1.4.1.37476.9999 |
||
21 | // "Class A" WEID: weid:root:2-RR-2 (base .) |
||
22 | // oid:2.999 |
||
23 | // - The namespace (weid:, weid:pen:, weid:root:) is now case insensitive. |
||
24 | // - Padding with '0' characters is valid (e.g. weid:000EXAMPLE-3) |
||
25 | // The paddings do not count into the WeLuhn check-digit. |
||
26 | |||
635 | daniel-mar | 27 | class WeidOidConverter { |
28 | |||
29 | protected static function weLuhnGetCheckDigit($str) { |
||
683 | daniel-mar | 30 | // Padding zeros don't count to the check digit (December 2021) |
31 | $ary = explode('-', $str); |
||
686 | daniel-mar | 32 | foreach ($ary as &$a) { |
33 | $a = ltrim($a, '0'); |
||
34 | if ($a === '') $a = '0'; |
||
35 | } |
||
683 | daniel-mar | 36 | $str = implode('-', $ary); |
37 | |||
686 | daniel-mar | 38 | // remove separators of the WEID string |
39 | $wrkstr = str_replace('-', '', $str); |
||
40 | |||
41 | // Replace 'a' with '10', 'b' with '11', etc. |
||
42 | for ($i=0; $i<26; $i++) { |
||
635 | daniel-mar | 43 | $wrkstr = str_ireplace(chr(ord('a')+$i), (string)($i+10), $wrkstr); |
44 | } |
||
686 | daniel-mar | 45 | |
46 | // At the end, $wrkstr should only contain digits! Verify it! |
||
47 | for ($i=0; $i<strlen($wrkstr); $i++) { |
||
48 | if (($wrkstr[$i]<'0') || ($wrkstr[$i]>'9')) return false; |
||
49 | } |
||
50 | |||
51 | // Now do the standard Luhn algorithm |
||
635 | daniel-mar | 52 | $nbdigits = strlen($wrkstr); |
686 | daniel-mar | 53 | $parity = $nbdigits & 1; // mod 2 |
635 | daniel-mar | 54 | $sum = 0; |
55 | for ($n=$nbdigits-1; $n>=0; $n--) { |
||
686 | daniel-mar | 56 | $digit = (int)$wrkstr[$n]; |
635 | daniel-mar | 57 | if (($n & 1) != $parity) $digit *= 2; |
58 | if ($digit > 9) $digit -= 9; |
||
59 | $sum += $digit; |
||
60 | } |
||
61 | return ($sum%10) == 0 ? 0 : 10-($sum%10); |
||
62 | } |
||
63 | |||
683 | daniel-mar | 64 | // Translates a weid to an oid |
65 | // "weid:EXAMPLE-3" becomes "1.3.6.1.4.1.37553.8.32488192274" |
||
66 | // If it failed (e.g. wrong namespace, wrong checksum, etc.) then false is returned. |
||
67 | // If the weid ends with '?', then it will be replaced with the checksum, |
||
68 | // e.g. weid:EXAMPLE-? becomes weid:EXAMPLE-3 |
||
69 | public static function weid2oid(&$weid) { |
||
635 | daniel-mar | 70 | |
683 | daniel-mar | 71 | $p = strrpos($weid,':'); |
72 | $namespace = substr($weid, 0, $p+1); |
||
73 | $rest = substr($weid, $p+1); |
||
74 | |||
75 | $namespace = strtolower($namespace); // namespace is case insensitive |
||
76 | if ($namespace == 'weid:') { |
||
77 | // Class C |
||
78 | $base = '1-3-6-1-4-1-SZ5-8'; |
||
79 | } else if ($namespace == 'weid:pen:') { |
||
80 | // Class B |
||
81 | $base = '1-3-6-1-4-1'; |
||
82 | } else if ($namespace == 'weid:root:') { |
||
83 | // Class A |
||
84 | $base = ''; |
||
85 | } else { |
||
86 | // Wrong namespace |
||
87 | return false; |
||
88 | } |
||
89 | |||
90 | $weid = $rest; |
||
91 | |||
92 | $elements = array_merge(($base != '') ? explode('-', $base) : array(), explode('-', $weid)); |
||
635 | daniel-mar | 93 | $actual_checksum = array_pop($elements); |
94 | $expected_checksum = self::weLuhnGetCheckDigit(implode('-',$elements)); |
||
95 | if ($actual_checksum != '?') { |
||
96 | if ($actual_checksum != $expected_checksum) return false; // wrong checksum |
||
97 | } else { |
||
98 | // If checksum is '?', it will be replaced by the actual checksum, |
||
99 | // e.g. weid:EXAMPLE-? becomes weid:EXAMPLE-3 |
||
100 | $weid = str_replace('?', $expected_checksum, $weid); |
||
101 | } |
||
102 | foreach ($elements as &$arc) { |
||
103 | //$arc = strtoupper(base_convert($arc, 36, 10)); |
||
104 | $arc = strtoupper(self::base_convert_bigint($arc, 36, 10)); |
||
105 | } |
||
106 | $oidstr = implode('.', $elements); |
||
107 | |||
750 | daniel-mar | 108 | $weid = strtolower($namespace) . strtoupper($weid); // add namespace again |
683 | daniel-mar | 109 | |
635 | daniel-mar | 110 | return $oidstr; |
111 | } |
||
112 | |||
683 | daniel-mar | 113 | // Converts an OID to WEID |
114 | // "1.3.6.1.4.1.37553.8.32488192274" becomes "weid:EXAMPLE-3" |
||
115 | public static function oid2weid($oid) { |
||
116 | if (substr($oid,0,1) === '.') $oid = substr($oid,1); // remove leading dot |
||
117 | |||
118 | if ($oid !== '') { |
||
119 | $elements = explode('.', $oid); |
||
120 | foreach ($elements as &$arc) { |
||
121 | //$arc = strtoupper(base_convert($arc, 10, 36)); |
||
122 | $arc = strtoupper(self::base_convert_bigint($arc, 10, 36)); |
||
123 | } |
||
124 | $weidstr = implode('-', $elements); |
||
125 | } else { |
||
126 | $weidstr = ''; |
||
635 | daniel-mar | 127 | } |
128 | |||
683 | daniel-mar | 129 | $is_class_c = (strpos($weidstr, '1-3-6-1-4-1-SZ5-8-') === 0) || |
130 | ($weidstr === '1-3-6-1-4-1-SZ5-8'); |
||
131 | $is_class_b = ((strpos($weidstr, '1-3-6-1-4-1-') === 0) || |
||
132 | ($weidstr === '1-3-6-1-4-1')) |
||
133 | && !$is_class_c; |
||
134 | $is_class_a = !$is_class_b && !$is_class_c; |
||
635 | daniel-mar | 135 | |
683 | daniel-mar | 136 | $checksum = self::weLuhnGetCheckDigit($weidstr); |
137 | |||
138 | if ($is_class_c) { |
||
139 | $weidstr = substr($weidstr, strlen('1-3-6-1-4-1-SZ5-8-')); |
||
140 | $namespace = 'weid:'; |
||
141 | } else if ($is_class_b) { |
||
142 | $weidstr = substr($weidstr, strlen('1-3-6-1-4-1-')); |
||
143 | $namespace = 'weid:pen:'; |
||
144 | } else if ($is_class_a) { |
||
145 | // $weidstr stays |
||
146 | $namespace = 'weid:root:'; |
||
686 | daniel-mar | 147 | } else { |
148 | // should not happen |
||
149 | return false; |
||
683 | daniel-mar | 150 | } |
151 | |||
152 | return $namespace . ($weidstr == '' ? $checksum : $weidstr . '-' . $checksum); |
||
635 | daniel-mar | 153 | } |
154 | |||
155 | protected static function base_convert_bigint($numstring, $frombase, $tobase) { |
||
156 | $frombase_str = ''; |
||
157 | for ($i=0; $i<$frombase; $i++) { |
||
158 | $frombase_str .= strtoupper(base_convert((string)$i, 10, 36)); |
||
159 | } |
||
160 | |||
161 | $tobase_str = ''; |
||
162 | for ($i=0; $i<$tobase; $i++) { |
||
163 | $tobase_str .= strtoupper(base_convert((string)$i, 10, 36)); |
||
164 | } |
||
165 | |||
166 | $length = strlen($numstring); |
||
167 | $result = ''; |
||
168 | $number = array(); |
||
169 | for ($i = 0; $i < $length; $i++) { |
||
170 | $number[$i] = stripos($frombase_str, $numstring[$i]); |
||
171 | } |
||
172 | do { // Loop until whole number is converted |
||
173 | $divide = 0; |
||
174 | $newlen = 0; |
||
175 | for ($i = 0; $i < $length; $i++) { // Perform division manually (which is why this works with big numbers) |
||
176 | $divide = $divide * $frombase + $number[$i]; |
||
177 | if ($divide >= $tobase) { |
||
178 | $number[$newlen++] = (int)($divide / $tobase); |
||
179 | $divide = $divide % $tobase; |
||
180 | } else if ($newlen > 0) { |
||
181 | $number[$newlen++] = 0; |
||
182 | } |
||
183 | } |
||
184 | $length = $newlen; |
||
185 | $result = $tobase_str[$divide] . $result; // Divide is basically $numstring % $tobase (i.e. the new character) |
||
186 | } |
||
187 | while ($newlen != 0); |
||
188 | |||
189 | return $result; |
||
190 | } |
||
191 | } |
||
192 | |||
193 | |||
683 | daniel-mar | 194 | # --- Usage Example --- |
195 | |||
635 | daniel-mar | 196 | /* |
683 | daniel-mar | 197 | echo "Class C tests:\n\n"; |
635 | daniel-mar | 198 | |
683 | daniel-mar | 199 | var_dump($oid = '1.3.6.1.4.1.37553.8')."\n"; |
200 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
201 | $weid = 'weid:?'; |
||
202 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
203 | var_dump($weid)."\n"; |
||
204 | echo "\n"; |
||
635 | daniel-mar | 205 | |
683 | daniel-mar | 206 | var_dump($oid = '1.3.6.1.4.1.37553.8.32488192274')."\n"; |
207 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
208 | $weid = 'weid:EXAMPLE-?'; |
||
209 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
210 | var_dump($weid)."\n"; |
||
211 | $weid = 'weid:00000example-?'; |
||
212 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
213 | var_dump($weid)."\n"; |
||
214 | echo "\n"; |
||
635 | daniel-mar | 215 | |
683 | daniel-mar | 216 | echo "Class B tests:\n\n"; |
635 | daniel-mar | 217 | |
683 | daniel-mar | 218 | var_dump($oid = '1.3.6.1.4.1')."\n"; |
219 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
220 | $weid = 'weid:pen:?'; |
||
221 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
222 | var_dump($weid)."\n"; |
||
223 | echo "\n"; |
||
635 | daniel-mar | 224 | |
683 | daniel-mar | 225 | var_dump($oid = '1.3.6.1.4.1.37553.7.99.99.99')."\n"; |
226 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
227 | $weid = 'weid:pen:SZ5-7-2R-2R-2R-?'; |
||
228 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
229 | var_dump($weid)."\n"; |
||
230 | $weid = 'weid:pen:000SZ5-7-02R-00002R-002r-?'; |
||
231 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
232 | var_dump($weid)."\n"; |
||
233 | echo "\n"; |
||
234 | |||
235 | var_dump($oid = '1.3.6.1.4.1.37476.9999')."\n"; |
||
236 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
237 | $weid = 'weid:pen:SX0-7PR-?'; |
||
238 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
239 | var_dump($weid)."\n"; |
||
240 | echo "\n"; |
||
241 | |||
242 | echo "Class A tests:\n\n"; |
||
243 | |||
244 | var_dump($oid = '')."\n"; |
||
245 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
246 | $weid = 'weid:root:?'; |
||
247 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
248 | var_dump($weid)."\n"; |
||
249 | echo "\n"; |
||
250 | |||
251 | var_dump($oid = '.2.999')."\n"; |
||
252 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
253 | $weid = 'weid:root:2-RR-?'; |
||
254 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
255 | var_dump($weid)."\n"; |
||
256 | echo "\n"; |
||
257 | |||
258 | var_dump($oid = '2.999')."\n"; |
||
259 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
260 | $weid = 'weid:root:2-RR-?'; |
||
261 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
262 | var_dump($weid)."\n"; |
||
263 | echo "\n"; |
||
635 | daniel-mar | 264 | */ |