Subversion Repositories uuid_mac_utils

Compare Revisions

Regard whitespace Rev 81 → Rev 82

/trunk/includes/uuid_utils.inc.php
1548,6 → 1548,75
substr($hash, 20, 12);
}
 
/**
* The sorting of SQL Server is rather confusing and incompatible with UUIDv6 and UUIDv7.
* Therefore this method generates UUID which are sortable by SQL Server.
* Version 1: Resolution of 1 milliseconds, random part of 16 bits, local timezone, NOT UUIDv8 conform.
* Version 2: Resolution of 1 milliseconds, random part of 18 bits, UTC time, UUIDv8 conform.
* C# implementation: https://gist.github.com/danielmarschall/7fafd270a3bc107d38e8449ce7420c25
* PHP implementation: https://github.com/danielmarschall/uuid_mac_utils/blob/master/includes/uuid_utils.inc.php
*
* @param int $hickelUuidVersion (optional)
* @param DateTime $dt (optional)
* @return string The UUID
*/
function gen_uuid_v8_sqlserver_sortable(int $hickelUuidVersion = 2, DateTime $dt = null): string {
// The sorting in SQL Server is like this:
 
if ($dt == null) $dt = new DateTime();
 
// First Sort block 5, nibbles from left to right (i.e. 000000000001 < 000000000010 < ... < 010000000000 < 100000000000)
if ($hickelUuidVersion == 1) {
$block5 = "000000000000";
} else if ($hickelUuidVersion == 2) {
$block5 = "4849434B454C"/*Hex:"HICKEL"*/;
} else {
throw new Exception("Invalid version");
}
 
// Then: Sort block 4, nibbles from left to right
if ($hickelUuidVersion == 1) {
$year = $dt->format('Y');
$block4 = substr($year, 2, 2).substr($year, 0, 2); // Example: 0x2420 = 2024
} else {
$variant = 0x8; // First nibble needs to be 0b10_ (0x8-0xB) for "RFC 4122bis". We use it to store 2 more random bits.
$rnd2bits = rand(0x0, 0x3);
$year = $dt->format('Y');
$block4 = sprintf('%01x%03x', $variant + ($rnd2bits & 0x3), $year);
}
 
// Then: Sort block 3, bytes from right to left (i.e. 0100 < 1000 < 0001 < 0010)
if ($hickelUuidVersion == 1) {
$block3 = $dt->format('dm');
} else {
$uuidVersion = 8; // First nibble needs to be "8" for "UUIDv8 = Custom UUID"
$dayOfYear = intval($dt->format('z')) + 1; /* 1..366 */
$block3 = sprintf('%01x%03x', $uuidVersion, $dayOfYear);
}
 
// Then: Sort block 2, bytes from right to left
if ($hickelUuidVersion == 1) {
$block2 = $dt->format('ih');
} else {
$minuteOfDay = (intval($dt->format('i')) + intval($dt->format('h')) * 60) + 1; // 1..1440
$block2 = sprintf('%04x', $minuteOfDay);
}
 
// Then: Sort block 1, bytes from right to left
$millisecond8bits = ceil(($dt->format('v') / 999) * 255);
if ($hickelUuidVersion == 1) {
$rnd16bits = rand(0x0000, 0xFFFF-1);
$block1 = sprintf('%04x%02x', $rnd16bits, $millisecond8bits).$dt->format('s');
} else {
$rnd16bits = rand(0x0000, 0xFFFF);
$block1 = sprintf('%04x%02x%02x', $rnd16bits, $millisecond8bits, $dt->format('s'));
}
 
// Now build and parse UUID
usleep((int)ceil(999 / 255)); // Make sure that "millisecond" is not repeated on this system
return "$block1-$block2-$block3-$block4-$block5";
}
 
# --------------------------------------
 
// http://php.net/manual/de/function.hex2bin.php#113057
/trunk/index.php
2,8 → 2,8
 
/*
* UUID & MAC Utils
* Copyright 2017 - 2023 Daniel Marschall, ViaThinkSoft
* Version 2023-11-10
* Copyright 2017 - 2024 Daniel Marschall, ViaThinkSoft
* Version 2024-03-09
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
46,6 → 46,7
<li><a href="#gen_uuidv6"><font color="green">New:</font> Generate reordered Gregorian time-based (version 6) UUID</a></li>
<li><a href="#gen_uuidv4">Generate random (version 4) UUID</a></li>
<li><a href="#gen_uuidv1">Generate Gregorian time-based (version 1) UUID</a></li>
<li><a href="#gen_uuidv8_sqlserver"><font color="green">New:</font> Generate SQL server sortable time-based (version 8) UUID</a></li>
</ul></li>
<li><a href="#gen_other_uuid">Generate other UUID types</a><ul>
<li><a href="#gen_uuid_ncs">NCS (variant 0) UUID</a></li>
241,6 → 242,73
<input type="hidden" name="uuid" value="CREATE"> <input type="submit" value="Create and display another UUID">
</form>
 
<h3 id="gen_uuidv8_sqlserver">Generate SQL server sortable time-based (version 8) UUID</h3>
 
<p><i>The sorting of SQL Server is rather confusing and incompatible with UUIDv6 and UUIDv7.<br>
Therefore this method developed by <a href="https://www.hickelsoft.de/">HickelSOFT</a>
generates UUID which are sortable by SQL Server.<br>
Version 1: Resolution of 1 milliseconds, random part of 16 bits, local timezone, NOT UUIDv8 conform.<br>
Version 2: Resolution of 1 milliseconds, random part of 18 bits, UTC time, UUIDv8 conform.</i><br>
<a href="https://gist.github.com/danielmarschall/7fafd270a3bc107d38e8449ce7420c25">C# implementation</a> |
<a href="https://github.com/danielmarschall/uuid_mac_utils/blob/master/includes/uuid_utils.inc.php">PHP implementation</a>
</p>
 
<script>
function show_uuidv8_sqlserver_info() {
document.getElementById("uuidv8_sqlserver_info_button").style.display = "none";
document.getElementById("uuidv8_sqlserver_info").style.display = "block";
}
</script>
<p><a id="uuidv8_sqlserver_info_button" href="javascript:show_uuidv8_sqlserver_info()">Show format</a>
<pre id="uuidv8_sqlserver_info" style="display:none">Version 2: Resolution of 1 milliseconds, random part of 18 bits, UTC time, UUIDv8 conform:
- 16 bit Random data
- 8 bit UTC Milliseconds transformed from 1000ms to 0..255 (hex encoded)
- 8 bit UTC Seconds (hex encoded)
- 16 bit UTC Minute of the day (1..1440, hex encoded)
- 4 bit UUID version 8
- 12 bit UTC Day of the year (1..366, hex encoded)
- 2 bit UUID Variant (0b10)
- 2 bit Random data
- 12 bit UTC Year (hex encoded)
- 48 bit Signature "HICKEL" = 0x4849434B454C
 
Version 1: Resolution of 1 milliseconds, random part of 16 bits, local timezone, NOT UUIDv8 conform.
- 16 bit Random data
- 8 bit Generator's local timezone Milliseconds transformed from 1000ms to 0..255 (hex encoded)
- 8 bit Generator's local timezone Seconds (BCD encoded)
- 8 bit Generator's local timezone Minute (BCD encoded)
- 8 bit Generator's local timezone Hour (BCD encoded)
- 8 bit Generator's local timezone Day (BCD encoded)
- 8 bit Generator's local timezone Month (BCD encoded)
- 8 bit Generator's local timezone 2-digit year (BCD encoded)
- 8 bit Generator's local timezone 2-digit century (BCD encoded)
- 48 bit Signature 0x000000000000</pre></p>
 
<?php
if (AUTO_NEW_UUIDS > 0) { /** @phpstan-ignore-line */
echo '<p>Here are '.AUTO_NEW_UUIDS.' UUIDs that were created just for you! (Reload the page to get more)</p>';
 
echo '<pre>';
for ($i=0; $i<AUTO_NEW_UUIDS; $i++) {
$uuid = gen_uuid_v8_sqlserver_sortable();
echo '<a href="interprete_uuid.php?uuid='.$uuid.'">'.$uuid.'</a><br>';
}
echo '</pre>';
}
?>
 
<form method="GET" action="interprete_uuid.php">
<input type="hidden" name="version" value="8_sqlserver_v2">
<input type="hidden" name="uuid" value="CREATE"> <input type="submit" value="Create and display another UUID (new version)">
</form><br>
 
<form method="GET" action="interprete_uuid.php">
<input type="hidden" name="version" value="8_sqlserver_v1">
<input type="hidden" name="uuid" value="CREATE"> <input type="submit" value="Create and display another UUID (old version)">
</form>
 
 
 
<h2 id="gen_other_uuid">Generate other UUID types</h2>
 
<p><i>The following types of UUIDs are less common and/or require special knowledge. Please only use the following
397,8 → 465,7
else $bits = strlen(hash($algo, '', true)) * 8;
if ($bits < 128) $friendlyName .= " (Small hash size! $bits bits)"; // <-- this is not described in Appendix C.2
 
$space = $algo;
$tmp[$friendlyName] = '<option value="8_namebased_'.$space.'">'.htmlentities($friendlyName).'</option>';
$tmp[$friendlyName] = '<option value="8_namebased_'.$algo.'">'.htmlentities($friendlyName).'</option>';
}
natsort($tmp);
foreach ($tmp as $html) {
/trunk/interprete_uuid.php
2,8 → 2,8
 
/*
* UUID interpreter for PHP
* Copyright 2017 - 2023 Daniel Marschall, ViaThinkSoft
* Version 2023-08-02
* Copyright 2017 - 2024 Daniel Marschall, ViaThinkSoft
* Version 2024-03-09
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
21,15 → 21,16
$uuid = isset($_GET['uuid']) ? trim($_GET['uuid']) : 'CREATE';
 
$version = $_REQUEST['version'] ?? null;
$hash_sqlserver_version = null;
$hash_algo = null;
if (!is_null($version)) {
if (preg_match('@^(8)_namebased_(.+)$@', $version, $m)) {
if (preg_match('@^(8)_sqlserver_v(.+)$@', $version, $m)) {
$version = $m[1];
$hash_uuid = $m[2];
} else {
$hash_uuid = null;
$hash_sqlserver_version = $m[2];
} else if (preg_match('@^(8)_namebased_(.+)$@', $version, $m)) {
$version = $m[1];
$hash_algo = $m[2];
}
} else {
$hash_uuid = null;
}
if (!is_numeric($version) || (strlen($version)!=1)) $version = 1; // default: Version 1 / time based
 
85,8 → 86,10
} else if ($version == '7') {
$uuid = gen_uuid_unix_epoch();
} else if ($version == '8') {
if ($hash_uuid != null) {
$uuid = gen_uuid_v8_namebased($hash_uuid, trim($_REQUEST['nb_ns']??''), trim($_REQUEST['nb_val']??''));
if ($hash_sqlserver_version != null) {
$uuid = gen_uuid_v8_sqlserver_sortable(intval($hash_sqlserver_version));
} else if ($hash_algo != null) {
$uuid = gen_uuid_v8_namebased($hash_algo, trim($_REQUEST['nb_ns']??''), trim($_REQUEST['nb_val']??''));
} else {
$uuid = gen_uuid_custom(trim($_REQUEST['block1']??'0'), trim($_REQUEST['block2']??'0'), trim($_REQUEST['block3']??'0'), trim($_REQUEST['block4']??'0'), trim($_REQUEST['block5']??'0'));
}