Subversion Repositories oidplus

Rev

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
*/