Rev 635 | Rev 686 | 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 | |
31 | // Padding zeros don't count to the check digit (December 2021) |
||
32 | $ary = explode('-', $str); |
||
33 | foreach ($ary as &$a) $a = ltrim($a, '0'); |
||
34 | $str = implode('-', $ary); |
||
35 | |||
635 | daniel-mar | 36 | $wrkstr = str_replace('-', '', $str); // remove separators |
37 | for ($i=0; $i<36; $i++) { |
||
38 | $wrkstr = str_ireplace(chr(ord('a')+$i), (string)($i+10), $wrkstr); |
||
39 | } |
||
40 | $nbdigits = strlen($wrkstr); |
||
41 | $parity = $nbdigits & 1; |
||
42 | $sum = 0; |
||
43 | for ($n=$nbdigits-1; $n>=0; $n--) { |
||
44 | $digit = $wrkstr[$n]; |
||
45 | if (($n & 1) != $parity) $digit *= 2; |
||
46 | if ($digit > 9) $digit -= 9; |
||
47 | $sum += $digit; |
||
48 | } |
||
49 | return ($sum%10) == 0 ? 0 : 10-($sum%10); |
||
50 | } |
||
51 | |||
683 | daniel-mar | 52 | // Translates a weid to an oid |
53 | // "weid:EXAMPLE-3" becomes "1.3.6.1.4.1.37553.8.32488192274" |
||
54 | // If it failed (e.g. wrong namespace, wrong checksum, etc.) then false is returned. |
||
55 | // If the weid ends with '?', then it will be replaced with the checksum, |
||
56 | // e.g. weid:EXAMPLE-? becomes weid:EXAMPLE-3 |
||
57 | public static function weid2oid(&$weid) { |
||
635 | daniel-mar | 58 | |
683 | daniel-mar | 59 | $p = strrpos($weid,':'); |
60 | $namespace = substr($weid, 0, $p+1); |
||
61 | $rest = substr($weid, $p+1); |
||
62 | |||
63 | $namespace = strtolower($namespace); // namespace is case insensitive |
||
64 | if ($namespace == 'weid:') { |
||
65 | // Class C |
||
66 | $base = '1-3-6-1-4-1-SZ5-8'; |
||
67 | } else if ($namespace == 'weid:pen:') { |
||
68 | // Class B |
||
69 | $base = '1-3-6-1-4-1'; |
||
70 | } else if ($namespace == 'weid:root:') { |
||
71 | // Class A |
||
72 | $base = ''; |
||
73 | } else { |
||
74 | // Wrong namespace |
||
75 | return false; |
||
76 | } |
||
77 | |||
78 | $weid = $rest; |
||
79 | |||
80 | $elements = array_merge(($base != '') ? explode('-', $base) : array(), explode('-', $weid)); |
||
635 | daniel-mar | 81 | $actual_checksum = array_pop($elements); |
82 | $expected_checksum = self::weLuhnGetCheckDigit(implode('-',$elements)); |
||
83 | if ($actual_checksum != '?') { |
||
84 | if ($actual_checksum != $expected_checksum) return false; // wrong checksum |
||
85 | } else { |
||
86 | // If checksum is '?', it will be replaced by the actual checksum, |
||
87 | // e.g. weid:EXAMPLE-? becomes weid:EXAMPLE-3 |
||
88 | $weid = str_replace('?', $expected_checksum, $weid); |
||
89 | } |
||
90 | foreach ($elements as &$arc) { |
||
91 | //$arc = strtoupper(base_convert($arc, 36, 10)); |
||
92 | $arc = strtoupper(self::base_convert_bigint($arc, 36, 10)); |
||
93 | } |
||
94 | $oidstr = implode('.', $elements); |
||
95 | |||
683 | daniel-mar | 96 | $weid = $namespace . $weid; // add namespace again |
97 | |||
635 | daniel-mar | 98 | return $oidstr; |
99 | } |
||
100 | |||
683 | daniel-mar | 101 | // Converts an OID to WEID |
102 | // "1.3.6.1.4.1.37553.8.32488192274" becomes "weid:EXAMPLE-3" |
||
103 | public static function oid2weid($oid) { |
||
104 | if (substr($oid,0,1) === '.') $oid = substr($oid,1); // remove leading dot |
||
105 | |||
106 | if ($oid !== '') { |
||
107 | $elements = explode('.', $oid); |
||
108 | foreach ($elements as &$arc) { |
||
109 | //$arc = strtoupper(base_convert($arc, 10, 36)); |
||
110 | $arc = strtoupper(self::base_convert_bigint($arc, 10, 36)); |
||
111 | } |
||
112 | $weidstr = implode('-', $elements); |
||
113 | } else { |
||
114 | $weidstr = ''; |
||
635 | daniel-mar | 115 | } |
116 | |||
683 | daniel-mar | 117 | $is_class_c = (strpos($weidstr, '1-3-6-1-4-1-SZ5-8-') === 0) || |
118 | ($weidstr === '1-3-6-1-4-1-SZ5-8'); |
||
119 | $is_class_b = ((strpos($weidstr, '1-3-6-1-4-1-') === 0) || |
||
120 | ($weidstr === '1-3-6-1-4-1')) |
||
121 | && !$is_class_c; |
||
122 | $is_class_a = !$is_class_b && !$is_class_c; |
||
635 | daniel-mar | 123 | |
683 | daniel-mar | 124 | $checksum = self::weLuhnGetCheckDigit($weidstr); |
125 | |||
126 | if ($is_class_c) { |
||
127 | $weidstr = substr($weidstr, strlen('1-3-6-1-4-1-SZ5-8-')); |
||
128 | $namespace = 'weid:'; |
||
129 | } else if ($is_class_b) { |
||
130 | $weidstr = substr($weidstr, strlen('1-3-6-1-4-1-')); |
||
131 | $namespace = 'weid:pen:'; |
||
132 | } else if ($is_class_a) { |
||
133 | // $weidstr stays |
||
134 | $namespace = 'weid:root:'; |
||
135 | } |
||
136 | |||
137 | return $namespace . ($weidstr == '' ? $checksum : $weidstr . '-' . $checksum); |
||
635 | daniel-mar | 138 | } |
139 | |||
140 | protected static function base_convert_bigint($numstring, $frombase, $tobase) { |
||
141 | $frombase_str = ''; |
||
142 | for ($i=0; $i<$frombase; $i++) { |
||
143 | $frombase_str .= strtoupper(base_convert((string)$i, 10, 36)); |
||
144 | } |
||
145 | |||
146 | $tobase_str = ''; |
||
147 | for ($i=0; $i<$tobase; $i++) { |
||
148 | $tobase_str .= strtoupper(base_convert((string)$i, 10, 36)); |
||
149 | } |
||
150 | |||
151 | $length = strlen($numstring); |
||
152 | $result = ''; |
||
153 | $number = array(); |
||
154 | for ($i = 0; $i < $length; $i++) { |
||
155 | $number[$i] = stripos($frombase_str, $numstring[$i]); |
||
156 | } |
||
157 | do { // Loop until whole number is converted |
||
158 | $divide = 0; |
||
159 | $newlen = 0; |
||
160 | for ($i = 0; $i < $length; $i++) { // Perform division manually (which is why this works with big numbers) |
||
161 | $divide = $divide * $frombase + $number[$i]; |
||
162 | if ($divide >= $tobase) { |
||
163 | $number[$newlen++] = (int)($divide / $tobase); |
||
164 | $divide = $divide % $tobase; |
||
165 | } else if ($newlen > 0) { |
||
166 | $number[$newlen++] = 0; |
||
167 | } |
||
168 | } |
||
169 | $length = $newlen; |
||
170 | $result = $tobase_str[$divide] . $result; // Divide is basically $numstring % $tobase (i.e. the new character) |
||
171 | } |
||
172 | while ($newlen != 0); |
||
173 | |||
174 | return $result; |
||
175 | } |
||
176 | } |
||
177 | |||
178 | |||
683 | daniel-mar | 179 | # --- Usage Example --- |
180 | |||
635 | daniel-mar | 181 | /* |
683 | daniel-mar | 182 | echo "Class C tests:\n\n"; |
635 | daniel-mar | 183 | |
683 | daniel-mar | 184 | var_dump($oid = '1.3.6.1.4.1.37553.8')."\n"; |
185 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
186 | $weid = 'weid:?'; |
||
187 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
188 | var_dump($weid)."\n"; |
||
189 | echo "\n"; |
||
635 | daniel-mar | 190 | |
683 | daniel-mar | 191 | var_dump($oid = '1.3.6.1.4.1.37553.8.32488192274')."\n"; |
192 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
193 | $weid = 'weid:EXAMPLE-?'; |
||
194 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
195 | var_dump($weid)."\n"; |
||
196 | $weid = 'weid:00000example-?'; |
||
197 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
198 | var_dump($weid)."\n"; |
||
199 | echo "\n"; |
||
635 | daniel-mar | 200 | |
683 | daniel-mar | 201 | echo "Class B tests:\n\n"; |
635 | daniel-mar | 202 | |
683 | daniel-mar | 203 | var_dump($oid = '1.3.6.1.4.1')."\n"; |
204 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
205 | $weid = 'weid:pen:?'; |
||
206 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
207 | var_dump($weid)."\n"; |
||
208 | echo "\n"; |
||
635 | daniel-mar | 209 | |
683 | daniel-mar | 210 | var_dump($oid = '1.3.6.1.4.1.37553.7.99.99.99')."\n"; |
211 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
212 | $weid = 'weid:pen:SZ5-7-2R-2R-2R-?'; |
||
213 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
214 | var_dump($weid)."\n"; |
||
215 | $weid = 'weid:pen:000SZ5-7-02R-00002R-002r-?'; |
||
216 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
217 | var_dump($weid)."\n"; |
||
218 | echo "\n"; |
||
219 | |||
220 | var_dump($oid = '1.3.6.1.4.1.37476.9999')."\n"; |
||
221 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
222 | $weid = 'weid:pen:SX0-7PR-?'; |
||
223 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
224 | var_dump($weid)."\n"; |
||
225 | echo "\n"; |
||
226 | |||
227 | echo "Class A tests:\n\n"; |
||
228 | |||
229 | var_dump($oid = '')."\n"; |
||
230 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
231 | $weid = 'weid:root:?'; |
||
232 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
233 | var_dump($weid)."\n"; |
||
234 | echo "\n"; |
||
235 | |||
236 | var_dump($oid = '.2.999')."\n"; |
||
237 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
238 | $weid = 'weid:root:2-RR-?'; |
||
239 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
240 | var_dump($weid)."\n"; |
||
241 | echo "\n"; |
||
242 | |||
243 | var_dump($oid = '2.999')."\n"; |
||
244 | var_dump(WeidOidConverter::oid2weid($oid))."\n"; |
||
245 | $weid = 'weid:root:2-RR-?'; |
||
246 | var_dump(WeidOidConverter::weid2oid($weid))."\n"; |
||
247 | var_dump($weid)."\n"; |
||
248 | echo "\n"; |
||
635 | daniel-mar | 249 | */ |