. */ namespace Volkszaehler\Util; /** * DrUUID RFC4122 library for PHP5 * * @author J. King * @package util * @link http://jkingweb.ca/code/php/lib.uuid/ * @license Licensed under MIT license * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ class UUID { const MD5 = 3; const SHA1 = 5; const clearVer = 15; // 00001111 Clears all bits of version byte with AND const clearVar = 63; // 00111111 Clears all relevant bits of variant byte with AND const varRes = 224; // 11100000 Variant reserved for future use const varMS = 192; // 11000000 Microsft GUID variant const varRFC = 128; // 10000000 The RFC 4122 variant (this variant) const varNCS = 0; // 00000000 The NCS compatibility variant const version1 = 16; // 00010000 const version3 = 48; // 00110000 const version4 = 64; // 01000000 const version5 = 80; // 01010000 const interval = 0x01b21dd213814000; // Time (in 100ns steps) between the start of the UTC and Unix epochs const nsDNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; const nsURL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8'; const nsOID = '6ba7b812-9dad-11d1-80b4-00c04fd430c8'; const nsX500 = '6ba7b814-9dad-11d1-80b4-00c04fd430c8'; //instance properties protected $bytes; protected $hex; protected $string; protected $urn; protected $version; protected $variant; protected $node; protected $time; /** * Create a new UUID based on provided data * * @param integer $ver * @param string $node * @param string $ns */ public static function mint($ver = 1, $node = NULL, $ns = NULL) { switch((int) $ver) { case 1: return new self(self::mintTime($node)); case 2: // Version 2 is not supported throw new \Exception("Version 2 is unsupported."); case 3: return new self(self::mintName(self::MD5, $node, $ns)); case 4: return new self(self::mintRand()); case 5: return new self(self::mintName(self::SHA1, $node, $ns)); default: throw new \Exception("Selected version is invalid or unsupported."); } } /** * Import an existing UUID * * @param unkown_type $uuid */ public static function import($uuid) { return new self(self::makeBin($uuid, 16)); } /** * Compares the binary representations of two UUIDs. * The comparison will return TRUE if they are bit-exact, * or if neither is valid * * @param unknown_type $a * @param unknown_type $b */ public static function compare($a, $b) { return (self::makeBin($a, 16) == self::makeBin($b, 16)); } public function __toString() { return $this->string; } public function __get($var) { switch($var) { case "bytes": return $this->bytes; case "hex": return bin2hex($this->bytes); case "string": return $this->__toString(); case "urn": return "urn:uuid:".$this->__toString(); case "version": return ord($this->bytes[6]) >> 4; case "variant": $byte = ord($this->bytes[8]); if ($byte >= self::varRes) return 3; if ($byte >= self::varMS) return 2; if ($byte >= self::varRFC) return 1; else return 0; case "node": if (ord($this->bytes[6])>>4==1) return bin2hex(substr($this->bytes,10)); else return NULL; case "time": if (ord($this->bytes[6])>>4==1) { // Restore contiguous big-endian byte order $time = bin2hex($this->bytes[6].$this->bytes[7].$this->bytes[4].$this->bytes[5].$this->bytes[0].$this->bytes[1].$this->bytes[2].$this->bytes[3]); // Clear version flag $time[0] = "0"; // Do some reverse arithmetic to get a Unix timestamp $time = (hexdec($time) - self::interval) / 10000000; return $time; } else return NULL; default: return NULL; } } protected function __construct($uuid) { if (strlen($uuid) != 16) { throw new \Exception("Input must be a 128-bit integer."); } $this->bytes = $uuid; // Optimize the most common use $this->string = bin2hex(substr($uuid,0,4))."-". bin2hex(substr($uuid,4,2))."-". bin2hex(substr($uuid,6,2))."-". bin2hex(substr($uuid,8,2))."-". bin2hex(substr($uuid,10,6)); } protected static function mintTime($node = NULL) { /* Generates a Version 1 UUID. These are derived from the time at which they were generated. */ // Get time since Gregorian calendar reform in 100ns intervals // This is exceedingly difficult because of PHP's (and pack()'s) // integer size limits. // Note that this will never be more accurate than to the microsecond. $time = microtime(1) * 10000000 + self::interval; // Convert to a string representation $time = sprintf("%F", $time); preg_match("/^\d+/", $time, $time); //strip decimal point // And now to a 64-bit binary representation $time = base_convert($time[0], 10, 16); $time = pack("H*", str_pad($time, 16, "0", STR_PAD_LEFT)); // Reorder bytes to their proper locations in the UUID $uuid = $time[4].$time[5].$time[6].$time[7].$time[2].$time[3].$time[0].$time[1]; // Generate a random clock sequence $uuid .= Random::getBytes(2); // set variant $uuid[8] = chr(ord($uuid[8]) & self::clearVar | self::varRFC); // set version $uuid[6] = chr(ord($uuid[6]) & self::clearVer | self::version1); // Set the final 'node' parameter, a MAC address if ($node) $node = self::makeBin($node, 6); if (!$node) { // If no node was provided or if the node was invalid, // generate a random MAC address and set the multicast bit $node = Random::getBytes(6); $node[0] = pack("C", ord($node[0]) | 1); } $uuid .= $node; return $uuid; } protected static function mintRand() { /* Generate a Version 4 UUID. These are derived soly from random numbers. */ // generate random fields $uuid = Random::getBytes(16); // set variant $uuid[8] = chr(ord($uuid[8]) & self::clearVar | self::varRFC); // set version $uuid[6] = chr(ord($uuid[6]) & self::clearVer | self::version4); return $uuid; } protected static function mintName($ver, $node, $ns) { /* Generates a Version 3 or Version 5 UUID. These are derived from a hash of a name and its namespace, in binary form. */ if (!$node) throw new \Exception("A name-string is required for Version 3 or 5 UUIDs."); // if the namespace UUID isn't binary, make it so $ns = self::makeBin($ns, 16); if (!$ns) throw new \Exception("A binary namespace is required for Version 3 or 5 UUIDs."); switch($ver) { case self::MD5: $version = self::version3; $uuid = md5($ns.$node,1); break; case self::SHA1: $version = self::version5; $uuid = substr(sha1($ns.$node,1),0, 16); break; } // set variant $uuid[8] = chr(ord($uuid[8]) & self::clearVar | self::varRFC); // set version $uuid[6] = chr(ord($uuid[6]) & self::clearVer | $version); return ($uuid); } protected static function makeBin($str, $len) { /* Insure that an input string is either binary or hexadecimal. Returns binary representation, or FALSE on failure. */ if ($str instanceof self) return $str->bytes; if (strlen($str)==$len) return $str; else $str = preg_replace("/^urn:uuid:/is", "", $str); // strip URN scheme and namespace $str = preg_replace("/[^a-f0-9]/is", "", $str); // strip non-hex characters if (strlen($str) != ($len * 2)) return FALSE; else return pack("H*", $str); } } ?>