Rev 36 | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 36 | Rev 43 | ||
---|---|---|---|
Line 1... | Line 1... | ||
1 | <?php |
1 | <?php |
2 | 2 | ||
3 | /* |
3 | /* |
4 | * UUID utils for PHP |
4 | * UUID utils for PHP |
5 | * Copyright 2011 - 2023 Daniel Marschall, ViaThinkSoft |
5 | * Copyright 2011 - 2024 Daniel Marschall, ViaThinkSoft |
6 | * Version 2023-11-11 |
6 | * Version 2024-03-09 |
7 | * |
7 | * |
8 | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | * Licensed under the Apache License, Version 2.0 (the "License"); |
9 | * you may not use this file except in compliance with the License. |
9 | * you may not use this file except in compliance with the License. |
10 | * You may obtain a copy of the License at |
10 | * You may obtain a copy of the License at |
11 | * |
11 | * |
Line 831... | Line 831... | ||
831 | case 3: |
831 | case 3: |
832 | echo sprintf("%-32s %s\n", "Variant:", "[0b111] Reserved for future use"); |
832 | echo sprintf("%-32s %s\n", "Variant:", "[0b111] Reserved for future use"); |
833 | break; |
833 | break; |
834 | } |
834 | } |
835 | 835 | ||
- | 836 | // START: HickelSOFT UUID |
|
- | 837 | ||
- | 838 | // Block 5 |
|
- | 839 | $signature = substr($uuid,20,12); |
|
- | 840 | if (strtolower($signature) == '5ce32bd83b96') { |
|
- | 841 | // HickelSOFT "SQL Server sortable UUID in C#" |
|
- | 842 | // Version 2: Resolution of 1 milliseconds, random part of 18 bits, UTC time, UUIDv8 conform. |
|
- | 843 | // Example: 2088dc33-000d-8045-87e8-5ce32bd83b96 |
|
- | 844 | // Block 4 |
|
- | 845 | $unused2bits = hexdec(substr($uuid,16,1)) & 0x3; |
|
- | 846 | $year = hexdec(substr($uuid,17,3)); |
|
- | 847 | // Block 3 |
|
- | 848 | $dayOfYear = hexdec(substr($uuid,13,3)); // 1..366 |
|
- | 849 | $day = (($dayOfYear < 1) || ($dayOfYear > 366)) ? "XX" : intval(getDateFromDay($year, $dayOfYear)->format('d')); |
|
- | 850 | $month = (($dayOfYear < 1) || ($dayOfYear > 366)) ? "XX" : intval(getDateFromDay($year, $dayOfYear)->format('m')); |
|
- | 851 | // Block 2 |
|
- | 852 | $minuteOfDay = hexdec(substr($uuid,8,4)); // 1..1440 |
|
- | 853 | $minutes = (($minuteOfDay < 1) || ($minuteOfDay > 1440)) ? "XX" : ($minuteOfDay-1) % 60; |
|
- | 854 | $hours = (($minuteOfDay < 1) || ($minuteOfDay > 1440)) ? "XX" : (int)floor(($minuteOfDay-1) / 60); |
|
- | 855 | // Block 1 |
|
- | 856 | $rnd16bits = substr($uuid,0,4); |
|
- | 857 | $millisecond8bits = hexdec(substr($uuid,4,2)); |
|
- | 858 | $milliseconds = round($millisecond8bits / 255 * 999); |
|
- | 859 | $seconds = hexdec(substr($uuid,6,2)); |
|
- | 860 | // Verbose info |
|
- | 861 | $utc_time = |
|
- | 862 | str_pad("$year",4,'0',STR_PAD_LEFT).'-'. |
|
- | 863 | str_pad("$month",2,'0',STR_PAD_LEFT).'-'. |
|
- | 864 | str_pad("$day",2,'0',STR_PAD_LEFT).' '. |
|
- | 865 | str_pad("$hours",2,'0',STR_PAD_LEFT).':'. |
|
- | 866 | str_pad("$minutes",2,'0',STR_PAD_LEFT).':'. |
|
- | 867 | str_pad("$seconds",2,'0',STR_PAD_LEFT)."'". |
|
- | 868 | str_pad("$milliseconds",2,'0',STR_PAD_LEFT); |
|
- | 869 | if (strpos($utc_time,'X') === false) { |
|
- | 870 | $deviation = "(deviation -2ms..2ms)"; |
|
- | 871 | echo "\n<u>Interpretation of <a href=\"https://gist.github.com/danielmarschall/7fafd270a3bc107d38e8449ce7420c25\">HickelSOFT \"SQL Server Sortable Custom UUID\", Version 2</a></u>\n\n"; |
|
- | 872 | echo sprintf("%-32s %s\n", "Random 16 bits:", "[0x$rnd16bits] 0b".str_pad("".base_convert($rnd16bits, 16, 2), 16, '0', STR_PAD_LEFT)); |
|
- | 873 | echo sprintf("%-32s %s\n", "Milliseconds:", "[0x".substr($uuid,4,2)."] $milliseconds $deviation"); |
|
- | 874 | echo sprintf("%-32s %s\n", "Seconds:", "[0x".substr($uuid,6,2)."] $seconds"); |
|
- | 875 | echo sprintf("%-32s %s\n", "Minute of day:", "[0x".substr($uuid,8,4)."] $minuteOfDay (".str_pad("$hours",2,'0',STR_PAD_LEFT).":".str_pad("$minutes",2,'0',STR_PAD_LEFT).")"); |
|
- | 876 | echo sprintf("%-32s %s\n", "Day of year:", "[0x".substr($uuid,13,3)."] $dayOfYear (Day=$day, Month=$month)"); |
|
- | 877 | echo sprintf("%-32s %s\n", "Unused 2 bits:", "[$unused2bits] 0b".str_pad("".base_convert("$unused2bits", 16, 2), 2, '0', STR_PAD_LEFT)); |
|
- | 878 | echo sprintf("%-32s %s\n", "Year:", "[0x".substr($uuid,17,3)."] $year"); |
|
- | 879 | echo sprintf("%-32s %s\n", "Signature:", "[0x".substr($uuid,20,12)."] HickelSOFT \"SQL Server Sortable Custom UUID\", Version 2 (very likely)"); |
|
- | 880 | echo sprintf("%-32s %s\n", "UTC Date Time:", "$utc_time $deviation"); |
|
- | 881 | } |
|
- | 882 | } else if (strtolower($signature) == '000000000000') { |
|
- | 883 | // HickelSOFT "SQL Server sortable UUID in C#" |
|
- | 884 | // Version 1: Resolution of 1 milliseconds, random part of 16 bits, local timezone, NOT UUIDv8 conform. |
|
- | 885 | // Example: ff38da51-1301-0903-2420-000000000000 |
|
- | 886 | // Block 4 |
|
- | 887 | $year = substr($uuid,18,2) . substr($uuid,16,2); |
|
- | 888 | $year = (!is_numeric($year) || ($year < 2000) || ($year > 2999)) ? "XXXX" : $year = intval($year); |
|
- | 889 | // Block 3 |
|
- | 890 | $day = substr($uuid,12,2); |
|
- | 891 | $day = (!is_numeric($day) || ($day < 0) || ($day >= 60)) ? "XX" : intval($day); |
|
- | 892 | $month = substr($uuid,14,2); |
|
- | 893 | $month = (!is_numeric($month) || ($month < 0) || ($month >= 60)) ? "XX" : intval($month); |
|
- | 894 | // Block 2 |
|
- | 895 | $minutes = substr($uuid,8,2); |
|
- | 896 | $minutes = (!is_numeric($minutes) || ($minutes < 0) || ($minutes >= 60)) ? "XX" : intval($minutes); |
|
- | 897 | $hours = substr($uuid,10,2); |
|
- | 898 | $hours = (!is_numeric($hours) || ($hours < 0) || ($hours >= 60)) ? "XX" : intval($hours); |
|
- | 899 | // Block 1 |
|
- | 900 | $rnd16bits = substr($uuid,0,4); |
|
- | 901 | $millisecond8bits = hexdec(substr($uuid,4,2)); |
|
- | 902 | $milliseconds = round($millisecond8bits / 255 * 999); |
|
- | 903 | $seconds = substr($uuid,6,2); |
|
- | 904 | $seconds = (!is_numeric($seconds) || ($seconds < 0) || ($seconds >= 60)) ? "XX" : intval($seconds); |
|
- | 905 | // Verbose info |
|
- | 906 | $local_time = |
|
- | 907 | str_pad("$year",4,'0',STR_PAD_LEFT).'-'. |
|
- | 908 | str_pad("$month",2,'0',STR_PAD_LEFT).'-'. |
|
- | 909 | str_pad("$day",2,'0',STR_PAD_LEFT).' '. |
|
- | 910 | str_pad("$hours",2,'0',STR_PAD_LEFT).':'. |
|
- | 911 | str_pad("$minutes",2,'0',STR_PAD_LEFT).':'. |
|
- | 912 | str_pad("$seconds",2,'0',STR_PAD_LEFT)."'". |
|
- | 913 | str_pad("$milliseconds",2,'0',STR_PAD_LEFT); |
|
- | 914 | if (strpos($local_time,'X') === false) { |
|
- | 915 | $deviation = "(deviation -4ms..0ms)"; |
|
- | 916 | echo "\n<u>Interpretation of <a href=\"https://gist.github.com/danielmarschall/7fafd270a3bc107d38e8449ce7420c25\">HickelSOFT \"SQL Server Sortable Custom UUID\", Version 1</a></u>\n\n"; |
|
- | 917 | echo sprintf("%-32s %s\n", "Random 16 bits:", "[0x$rnd16bits] 0b".str_pad(base_convert($rnd16bits, 16, 2), 16, '0', STR_PAD_LEFT)); |
|
- | 918 | echo sprintf("%-32s %s\n", "Milliseconds:", "[0x".substr($uuid,4,2)."] $milliseconds $deviation"); |
|
- | 919 | echo sprintf("%-32s %s\n", "Seconds:", "[0x".substr($uuid,6,2)."] $seconds"); |
|
- | 920 | echo sprintf("%-32s %s\n", "Minutes:", "[0x".substr($uuid,8,2)."] $minutes"); |
|
- | 921 | echo sprintf("%-32s %s\n", "Hours:", "[0x".substr($uuid,10,2)."] $hours"); |
|
- | 922 | echo sprintf("%-32s %s\n", "Day:", "[0x".substr($uuid,12,2)."] $day"); |
|
- | 923 | echo sprintf("%-32s %s\n", "Month:", "[0x".substr($uuid,14,2)."] $month"); |
|
- | 924 | echo sprintf("%-32s %s\n", "Year:", "[0x".substr($uuid,16,4)."] $year"); |
|
- | 925 | echo sprintf("%-32s %s\n", "Signature:", "[0x".substr($uuid,20,12)."] HickelSOFT \"SQL Server Sortable Custom UUID\", Version 1 (maybe)"); |
|
- | 926 | echo sprintf("%-32s %s\n", "Generator's Local Date Time:", "$local_time $deviation"); |
|
- | 927 | } |
|
- | 928 | } |
|
- | 929 | ||
- | 930 | // END: HickelSOFT UUID |
|
- | 931 | ||
836 | if (!$echo) { |
932 | if (!$echo) { |
837 | $out = ob_get_contents(); |
933 | $out = ob_get_contents(); |
838 | ob_end_clean(); |
934 | ob_end_clean(); |
839 | return $out; |
935 | return $out; |
840 | } else { |
936 | } else { |
Line 845... | Line 941... | ||
845 | function uuid_canonize($uuid) { |
941 | function uuid_canonize($uuid) { |
846 | if (!uuid_valid($uuid)) return false; |
942 | if (!uuid_valid($uuid)) return false; |
847 | return oid_to_uuid(uuid_to_oid($uuid)); |
943 | return oid_to_uuid(uuid_to_oid($uuid)); |
848 | } |
944 | } |
849 | 945 | ||
- | 946 | /* |
|
- | 947 | assert(oid_to_uuid('2.25.111325678376819997685911819737516232943')=='53c08bb6-b2eb-5038-bf28-ad41a08c50ef'); |
|
- | 948 | assert(oid_to_uuid('1.2.840.113556.1.8000.2554.21440.35766.45803.20536.48936.11354528.9195759')=='53c08bb6-b2eb-5038-bf28-ad41a08c50ef'); |
|
- | 949 | assert(oid_to_uuid('1.3.6.1.4.1.54392.1.6034977117478539320.13774449957690691823')=='53c08bb6-b2eb-5038-bf28-ad41a08c50ef'); |
|
- | 950 | assert(oid_to_uuid('1.3.6.1.4.1.54392.2.1405127606.3001765944.3207114049.2693550319')=='53c08bb6-b2eb-5038-bf28-ad41a08c50ef'); |
|
- | 951 | assert(oid_to_uuid('1.3.6.1.4.1.54392.3.21440.35766.45803.20536.48936.44353.41100.20719')=='53c08bb6-b2eb-5038-bf28-ad41a08c50ef'); |
|
- | 952 | */ |
|
850 | function oid_to_uuid($oid) { |
953 | function oid_to_uuid($oid) { |
851 | // TODO: Also support Non-2.25 base UUID-to-OID |
- | |
852 | if (!is_uuid_oid($oid,true)) return false; |
- | |
853 | - | ||
854 | if (substr($oid,0,1) == '.') { |
954 | if (substr($oid,0,1) == '.') $oid = substr($oid, 1); // remove leading dot |
855 | $oid = substr($oid, 1); |
- | |
856 | } |
- | |
857 | $ary = explode('.', $oid); |
- | |
858 | 955 | ||
859 | if (!isset($ary[2])) return false; |
956 | // Information about Microsoft and Waterjuice UUID-OID: https://waterjuiceweb.wordpress.com/2019/09/24/guids-to-oids/ |
860 | 957 | ||
- | 958 | $ary = explode('.', $oid); |
|
- | 959 | if ((count($ary) == 3) && (strpos($oid, '2.25.') === 0)) { |
|
- | 960 | // ISO/ITU-T UUID-to-OID |
|
- | 961 | // Example: {53c08bb6-b2eb-5038-bf28-ad41a08c50ef} = 2.25.111325678376819997685911819737516232943 |
|
861 | $val = $ary[2]; |
962 | $val = $ary[2]; |
862 | - | ||
863 | $x = gmp_init($val, 10); |
963 | $dec = gmp_init($val, 10); |
864 | $y = gmp_strval($x, 16); |
964 | $hex = gmp_strval($dec, 16); |
865 | $y = str_pad($y, 32, "0", STR_PAD_LEFT); |
965 | $hex = str_pad($hex, 32, "0", STR_PAD_LEFT); |
- | 966 | return substr($hex,0,8).'-'.substr($hex,8,4).'-'.substr($hex,12,4).'-'.substr($hex,16,4).'-'.substr($hex,20,12); |
|
- | 967 | } else if ((count($ary) == 14) && (strpos($oid, '1.2.840.113556.1.8000.2554.') === 0)) { |
|
866 | return substr($y, 0, 8).'-'. |
968 | // Microsoft UUID-to-OID |
- | 969 | // Example: {53c08bb6-b2eb-5038-bf28-ad41a08c50ef} = 1.2.840.113556.1.8000.2554.21440.35766.45803.20536.48936.11354528.9195759 |
|
- | 970 | $a = intval($ary[7]); |
|
867 | substr($y, 8, 4).'-'. |
971 | $b = intval($ary[8]); |
- | 972 | $c = intval($ary[9]); |
|
868 | substr($y, 12, 4).'-'. |
973 | $d = intval($ary[10]); |
869 | substr($y, 16, 4).'-'. |
974 | $e = intval($ary[11]); |
870 | substr($y, 20, 12); |
975 | $f = intval($ary[12]); |
- | 976 | $g = intval($ary[13]); |
|
- | 977 | return dechex($a).dechex($b).'-'.dechex($c).'-'.dechex($d).'-'.dechex($e).'-'.dechex($f).dechex($g); |
|
- | 978 | } else if ((count($ary) == 10) && (strpos($oid, '1.3.6.1.4.1.54392.1.') === 0)) { |
|
- | 979 | // Waterjuice UUID-to-OID 2x64 Bits |
|
- | 980 | // Example: {53c08bb6-b2eb-5038-bf28-ad41a08c50ef} = 1.3.6.1.4.1.54392.1.6034977117478539320.13774449957690691823 |
|
- | 981 | $a1 = gmp_strval(gmp_init($ary[8],10),16); if (strlen($a1)>16) return false; |
|
- | 982 | $a2 = gmp_strval(gmp_init($ary[9],10),16); if (strlen($a2)>16) return false; |
|
- | 983 | $hex = |
|
- | 984 | str_pad($a1, 16, "0", STR_PAD_LEFT). |
|
- | 985 | str_pad($a2, 16, "0", STR_PAD_LEFT); |
|
- | 986 | return substr($hex,0,8).'-'.substr($hex,8,4).'-'.substr($hex,12,4).'-'.substr($hex,16,4).'-'.substr($hex,20,12); |
|
- | 987 | } else if ((count($ary) == 12) && (strpos($oid, '1.3.6.1.4.1.54392.2.') === 0)) { |
|
- | 988 | // Waterjuice UUID-to-OID 4x32 Bits |
|
- | 989 | // Example: {53c08bb6-b2eb-5038-bf28-ad41a08c50ef} = 1.3.6.1.4.1.54392.2.1405127606.3001765944.3207114049.2693550319 |
|
- | 990 | $a1 = gmp_strval(gmp_init($ary[8],10),16); if (strlen($a1)>8) return false; |
|
- | 991 | $a2 = gmp_strval(gmp_init($ary[9],10),16); if (strlen($a2)>8) return false; |
|
- | 992 | $a3 = gmp_strval(gmp_init($ary[10],10),16); if (strlen($a3)>8) return false; |
|
- | 993 | $a4 = gmp_strval(gmp_init($ary[11],10),16); if (strlen($a4)>8) return false; |
|
- | 994 | $hex = |
|
- | 995 | str_pad($a1, 8, "0", STR_PAD_LEFT). |
|
- | 996 | str_pad($a2, 8, "0", STR_PAD_LEFT). |
|
- | 997 | str_pad($a3, 8, "0", STR_PAD_LEFT). |
|
- | 998 | str_pad($a4, 8, "0", STR_PAD_LEFT); |
|
- | 999 | return substr($hex,0,8).'-'.substr($hex,8,4).'-'.substr($hex,12,4).'-'.substr($hex,16,4).'-'.substr($hex,20,12); |
|
- | 1000 | } else if ((count($ary) == 16) && (strpos($oid, '1.3.6.1.4.1.54392.3.') === 0)) { |
|
- | 1001 | // Waterjuice UUID-to-OID 8x16 Bits |
|
- | 1002 | // Example: {53c08bb6-b2eb-5038-bf28-ad41a08c50ef} = 1.3.6.1.4.1.54392.3.21440.35766.45803.20536.48936.44353.41100.20719 |
|
- | 1003 | $a1 = gmp_strval(gmp_init($ary[8],10),16); if (strlen($a1)>4) return false; |
|
- | 1004 | $a2 = gmp_strval(gmp_init($ary[9],10),16); if (strlen($a2)>4) return false; |
|
- | 1005 | $a3 = gmp_strval(gmp_init($ary[10],10),16); if (strlen($a3)>4) return false; |
|
- | 1006 | $a4 = gmp_strval(gmp_init($ary[11],10),16); if (strlen($a4)>4) return false; |
|
- | 1007 | $a5 = gmp_strval(gmp_init($ary[12],10),16); if (strlen($a5)>4) return false; |
|
- | 1008 | $a6 = gmp_strval(gmp_init($ary[13],10),16); if (strlen($a6)>4) return false; |
|
- | 1009 | $a7 = gmp_strval(gmp_init($ary[14],10),16); if (strlen($a7)>4) return false; |
|
- | 1010 | $a8 = gmp_strval(gmp_init($ary[15],10),16); if (strlen($a8)>4) return false; |
|
- | 1011 | $hex = |
|
- | 1012 | str_pad($a1, 4, "0", STR_PAD_LEFT). |
|
- | 1013 | str_pad($a2, 4, "0", STR_PAD_LEFT). |
|
- | 1014 | str_pad($a3, 4, "0", STR_PAD_LEFT). |
|
- | 1015 | str_pad($a4, 4, "0", STR_PAD_LEFT). |
|
- | 1016 | str_pad($a5, 4, "0", STR_PAD_LEFT). |
|
- | 1017 | str_pad($a6, 4, "0", STR_PAD_LEFT). |
|
- | 1018 | str_pad($a7, 4, "0", STR_PAD_LEFT). |
|
- | 1019 | str_pad($a8, 4, "0", STR_PAD_LEFT); |
|
- | 1020 | return substr($hex,0,8).'-'.substr($hex,8,4).'-'.substr($hex,12,4).'-'.substr($hex,16,4).'-'.substr($hex,20,12); |
|
- | 1021 | } else { |
|
- | 1022 | return false; |
|
- | 1023 | } |
|
871 | } |
1024 | } |
872 | 1025 | ||
873 | function is_uuid_oid($oid, $only_allow_root=false) { |
1026 | function is_uuid_oid($oid, $only_allow_root=false) { |
874 | // TODO: Also support Non-2.25 base UUID-to-OID |
- | |
875 | if (substr($oid,0,1) == '.') $oid = substr($oid, 1); // remove leading dot |
1027 | if (substr($oid,0,1) == '.') $oid = substr($oid, 1); // remove leading dot |
876 | 1028 | ||
877 | $ary = explode('.', $oid); |
- | |
878 | - | ||
879 | if ($only_allow_root) { |
1029 | if ($only_allow_root) { |
880 | if (count($ary) != 3) return false; |
1030 | return oid_to_uuid($oid) !== false; |
881 | } else { |
1031 | } else { |
882 | if (count($ary) < 3) return false; |
1032 | // TODO: Check range of the components (e.g. max 128 bits for 2.25) |
883 | } |
- | |
884 | - | ||
885 | if ($ary[0] != '2') return false; |
1033 | if (strpos($oid,'2.25.') === 0) return true; |
886 | if ($ary[1] != '25') return false; |
1034 | if (strpos($oid,'1.2.840.113556.1.8000.2554.') === 0) return true; |
887 | for ($i=2; $i<count($ary); $i++) { |
- | |
888 | $v = $ary[$i]; |
- | |
889 | if (!is_numeric($v)) return false; |
1035 | if (strpos($oid,'1.3.6.1.4.1.54392.1.') === 0) return true; |
890 | if ($i == 2) { |
- | |
891 | // Must be in the range of 128 bit UUID |
1036 | if (strpos($oid,'1.3.6.1.4.1.54392.2.') === 0) return true; |
892 | $test = gmp_init($v, 10); |
- | |
893 | if (strlen(gmp_strval($test, 16)) > 32) return false; |
1037 | if (strpos($oid,'1.3.6.1.4.1.54392.3.') === 0) return true; |
894 | } |
- | |
895 | if ($v < 0) return false; |
1038 | return false; |
896 | } |
1039 | } |
897 | - | ||
898 | return true; |
- | |
899 | } |
1040 | } |
900 | 1041 | ||
901 | function uuid_to_oid($uuid, $base='2.25') { |
1042 | function uuid_to_oid($uuid, $base='2.25') { |
902 | if (!uuid_valid($uuid)) return false; |
1043 | if (!uuid_valid($uuid)) return false; |
903 | #$base = oid_sanitize($base); |
1044 | #$base = oid_sanitize($base); |
Line 1407... | Line 1548... | ||
1407 | substr($hash, 12, 4).'-'. |
1548 | substr($hash, 12, 4).'-'. |
1408 | substr($hash, 16, 4).'-'. |
1549 | substr($hash, 16, 4).'-'. |
1409 | substr($hash, 20, 12); |
1550 | substr($hash, 20, 12); |
1410 | } |
1551 | } |
1411 | 1552 | ||
- | 1553 | /** |
|
- | 1554 | * The sorting of SQL Server is rather confusing and incompatible with UUIDv6 and UUIDv7. |
|
- | 1555 | * Therefore this method generates UUID which are sortable by SQL Server. |
|
- | 1556 | * Version 1: Resolution of 1 milliseconds, random part of 16 bits, local timezone, 48 zero bits "signature", NOT UUIDv8 conform. |
|
- | 1557 | * Version 2: Resolution of 1 milliseconds, random part of 16 bits, UTC time, 48 bit random "signature", UUIDv8 conform. |
|
- | 1558 | * C# implementation: https://gist.github.com/danielmarschall/7fafd270a3bc107d38e8449ce7420c25 |
|
- | 1559 | * PHP implementation: https://github.com/danielmarschall/uuid_mac_utils/blob/master/includes/uuid_utils.inc.php |
|
- | 1560 | * |
|
- | 1561 | * @param int $hickelUuidVersion (optional) |
|
- | 1562 | * @param DateTime $dt (optional) |
|
- | 1563 | * @return string The UUID |
|
- | 1564 | */ |
|
- | 1565 | function gen_uuid_v8_sqlserver_sortable(int $hickelUuidVersion = 2, DateTime $dt = null): string { |
|
- | 1566 | // The sorting in SQL Server is like this: |
|
- | 1567 | ||
- | 1568 | if ($dt == null) $dt = new DateTime(); |
|
- | 1569 | ||
- | 1570 | // First Sort block 5, nibbles from left to right (i.e. 000000000001 < 000000000010 < ... < 010000000000 < 100000000000) |
|
- | 1571 | if ($hickelUuidVersion == 1) { |
|
- | 1572 | $block5 = "000000000000"; |
|
- | 1573 | } else if ($hickelUuidVersion == 2) { |
|
- | 1574 | $block5 = "5ce32bd83b96"; |
|
- | 1575 | } else { |
|
- | 1576 | throw new Exception("Invalid version"); |
|
- | 1577 | } |
|
- | 1578 | ||
- | 1579 | // Then: Sort block 4, nibbles from left to right |
|
- | 1580 | if ($hickelUuidVersion == 1) { |
|
- | 1581 | $year = $dt->format('Y'); |
|
- | 1582 | $block4 = substr($year, 2, 2).substr($year, 0, 2); // Example: 0x2420 = 2024 |
|
- | 1583 | } else { |
|
- | 1584 | $variant = 0x8; // First nibble needs to be 0b10_ (0x8-0xB) for "RFC 4122bis". We use it to store 2 more random bits. |
|
- | 1585 | $unused2bits = 0; // Cannot be used for random, because it would affect the sorting |
|
- | 1586 | $year = $dt->format('Y'); |
|
- | 1587 | $block4 = sprintf('%01x%03x', $variant + ($unused2bits & 0x3), $year); |
|
- | 1588 | } |
|
- | 1589 | ||
- | 1590 | // Then: Sort block 3, bytes from right to left (i.e. 0100 < 1000 < 0001 < 0010) |
|
- | 1591 | if ($hickelUuidVersion == 1) { |
|
- | 1592 | $block3 = $dt->format('dm'); |
|
- | 1593 | } else { |
|
- | 1594 | $uuidVersion = 8; // First nibble needs to be "8" for "UUIDv8 = Custom UUID" |
|
- | 1595 | $dayOfYear = intval($dt->format('z')) + 1; /* 1..366 */ |
|
- | 1596 | $block3 = sprintf('%01x%03x', $uuidVersion, $dayOfYear); |
|
- | 1597 | } |
|
- | 1598 | ||
- | 1599 | // Then: Sort block 2, bytes from right to left |
|
- | 1600 | if ($hickelUuidVersion == 1) { |
|
- | 1601 | $block2 = $dt->format('ih'); |
|
- | 1602 | } else { |
|
- | 1603 | $minuteOfDay = (intval($dt->format('i')) + intval($dt->format('h')) * 60) + 1; // 1..1440 |
|
- | 1604 | $block2 = sprintf('%04x', $minuteOfDay); |
|
- | 1605 | } |
|
- | 1606 | ||
- | 1607 | // Then: Sort block 1, bytes from right to left |
|
- | 1608 | if ($hickelUuidVersion == 1) { |
|
- | 1609 | $millisecond8bits = ceil(($dt->format('v') / 999) * 255); // deviation -4ms..0ms |
|
- | 1610 | $rnd16bits = _random_int(0x0000, 0xFFFF-1); |
|
- | 1611 | $block1 = sprintf('%04x%02x', $rnd16bits, $millisecond8bits).$dt->format('s'); |
|
- | 1612 | } else { |
|
- | 1613 | $millisecond8bits = round(($dt->format('v') / 999) * 255); // deviation -2ms..2ms |
|
- | 1614 | $rnd16bits = _random_int(0x0000, 0xFFFF); |
|
- | 1615 | $block1 = sprintf('%04x%02x%02x', $rnd16bits, $millisecond8bits, $dt->format('s')); |
|
- | 1616 | } |
|
- | 1617 | ||
- | 1618 | $sleep_ms = (int)ceil(999 / 255); // Make sure that "millisecond" is not repeated on this system |
|
- | 1619 | if (time_nanosleep(0,$sleep_ms*1000*1000) !== true) usleep($sleep_ms*1000); |
|
- | 1620 | ||
- | 1621 | return strtolower("$block1-$block2-$block3-$block4-$block5"); |
|
- | 1622 | } |
|
- | 1623 | ||
1412 | # -------------------------------------- |
1624 | # -------------------------------------- |
1413 | 1625 | ||
1414 | // http://php.net/manual/de/function.hex2bin.php#113057 |
1626 | // http://php.net/manual/de/function.hex2bin.php#113057 |
1415 | if (!function_exists('hex2bin')) { |
1627 | if (!function_exists('hex2bin')) { |
1416 | function hex2bin($str) { |
1628 | function hex2bin($str) { |
Line 1449... | Line 1661... | ||
1449 | $sponge = SHA3::init(SHA3::SHAKE256); |
1661 | $sponge = SHA3::init(SHA3::SHAKE256); |
1450 | $sponge->absorb($msg); |
1662 | $sponge->absorb($msg); |
1451 | $bin = $sponge->squeeze($outputLength); |
1663 | $bin = $sponge->squeeze($outputLength); |
1452 | return $binary ? $bin : bin2hex($bin); |
1664 | return $binary ? $bin : bin2hex($bin); |
1453 | } |
1665 | } |
- | 1666 | ||
- | 1667 | /** |
|
- | 1668 | * Converts the day of year of a year into a DateTime object |
|
- | 1669 | * @param int $year The year |
|
- | 1670 | * @param int $dayOfYear The day of year (value 1 till 365 or 1 till 366 for leap years) |
|
- | 1671 | * @return \DateTime The resulting date |
|
- | 1672 | */ |
|
- | 1673 | function getDateFromDay(int $year, int $dayOfYear): \DateTime { |
|
- | 1674 | // Note: "Y z" and "z Y" make a difference for leap years (last tested with PHP 8.0.3) |
|
- | 1675 | $date = \DateTime::createFromFormat('Y z', strval($year) . ' ' . strval($dayOfYear-1)); |
|
- | 1676 | return $date; |
|
- | 1677 | } |
|
- | 1678 |