Subversion Repositories oidplus

Rev

Rev 686 | Rev 771 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /**
  4.  * WEID<=>OID Converter
  5.  * (c) Webfan.de, ViaThinkSoft
  6.  * Revision 2021-12-08
  7.  **/
  8.  
  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.  
  27. class WeidOidConverter {
  28.  
  29.         protected static function weLuhnGetCheckDigit($str) {
  30.                 // Padding zeros don't count to the check digit (December 2021)
  31.                 $ary = explode('-', $str);
  32.                 foreach ($ary as &$a) {
  33.                         $a = ltrim($a, '0');
  34.                         if ($a === '') $a = '0';
  35.                 }
  36.                 $str = implode('-', $ary);
  37.  
  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++) {
  43.                         $wrkstr = str_ireplace(chr(ord('a')+$i), (string)($i+10), $wrkstr);
  44.                 }
  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
  52.                 $nbdigits = strlen($wrkstr);
  53.                 $parity = $nbdigits & 1; // mod 2
  54.                 $sum = 0;
  55.                 for ($n=$nbdigits-1; $n>=0; $n--) {
  56.                         $digit = (int)$wrkstr[$n];
  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.  
  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) {
  70.  
  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));
  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.  
  108.                 $weid = strtolower($namespace) . strtoupper($weid); // add namespace again
  109.  
  110.                 return $oidstr;
  111.         }
  112.  
  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 = '';
  127.                 }
  128.  
  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;
  135.  
  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:';
  147.                 } else {
  148.                         // should not happen
  149.                         return false;
  150.                 }
  151.  
  152.                 return $namespace . ($weidstr == '' ? $checksum : $weidstr . '-' . $checksum);
  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.  
  194. # --- Usage Example ---
  195.  
  196. /*
  197. echo "Class C tests:\n\n";
  198.  
  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";
  205.  
  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";
  215.  
  216. echo "Class B tests:\n\n";
  217.  
  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";
  224.  
  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";
  264. */
  265.