From 3cf30a3dc0f3d57fcfea2a4a674f5ea84006ba0e Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Tue, 8 Jun 2010 02:29:51 +0200 Subject: [PATCH] added DrUUID RFC4122 library for PHP5 by J. King (http://jkingweb.ca/) --- backend/init.php | 1 + backend/lib/util/uuid.php | 296 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 297 insertions(+) create mode 100644 backend/lib/util/uuid.php diff --git a/backend/init.php b/backend/init.php index c0723fc..5ab30e2 100644 --- a/backend/init.php +++ b/backend/init.php @@ -31,6 +31,7 @@ function __autoload($className) { $mapping = array( // util classes '/^Registry$/' => 'util/registry', + '/^Uuid$/' => 'util/uuid', // model classes '/^(Channel|User|Group|Database(Object)?)$/'=> 'model/$1', diff --git a/backend/lib/util/uuid.php b/backend/lib/util/uuid.php new file mode 100644 index 0000000..07024cc --- /dev/null +++ b/backend/lib/util/uuid.php @@ -0,0 +1,296 @@ +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 UUIDException("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 .= self::randomBytes(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 = self::randomBytes(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 = self::randomBytes(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 UUIDException("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 UUIDException("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); + } + + public static function initRandom() { + /* Look for a system-provided source of randomness, which is usually crytographically secure. + /dev/urandom is tried first simply out of bias for Linux systems. */ + if (is_readable('/dev/urandom')) { + self::$randomSource = fopen('/dev/urandom', 'rb'); + self::$randomFunc = 'randomFRead'; + } + else if (class_exists('COM', 0)) { + try { + self::$randomSource = new COM('CAPICOM.Utilities.1'); // See http://msdn.microsoft.com/en-us/library/aa388182(VS.85).aspx + self::$randomFunc = 'randomCOM'; + } + catch(Exception $e) {} + } + return self::$randomFunc; + } + + public static function randomBytes($bytes) { + return call_user_func(array('self', self::$randomFunc), $bytes); + } + + protected static function randomTwister($bytes) { + /* Get the specified number of random bytes, using mt_rand(). + Randomness is returned as a string of bytes. */ + $rand = ""; + for ($a = 0; $a < $bytes; $a++) { + $rand .= chr(mt_rand(0, 255)); + } + return $rand; + } + + protected static function randomFRead($bytes) { + /* Get the specified number of random bytes using a file handle + previously opened with UUID::initRandom(). + Randomness is returned as a string of bytes. */ + return fread(self::$randomSource, $bytes); + } + + protected static function randomCOM($bytes) { + /* Get the specified number of random bytes using Windows' + randomness source via a COM object previously created by UUID::initRandom(). + Randomness is returned as a string of bytes. */ + return base64_decode(self::$randomSource->GetRandom($bytes,0)); // straight binary mysteriously doesn't work, hence the base64 + } +} + +class UUIDException extends Exception { +} \ No newline at end of file