added DrUUID RFC4122 library for PHP5 by J. King (http://jkingweb.ca/)

This commit is contained in:
Steffen Vogel 2010-06-08 02:29:51 +02:00
parent b9b639b6a0
commit 3cf30a3dc0
2 changed files with 297 additions and 0 deletions

View file

@ -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',

296
backend/lib/util/uuid.php Normal file
View file

@ -0,0 +1,296 @@
<?php
/*
* Copyright (c) 2009 J. King
*
* 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.
*/
/*
* DrUUID RFC4122 library for PHP5 by J. King (http://jkingweb.ca/)
* Licensed under MIT license
* See http://jkingweb.ca/code/php/lib.uuid/ for documentation
* Last revised 2010-02-15
*/
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';
protected static $randomFunc = 'randomTwister';
protected static $randomSource = NULL;
//instance properties
protected $bytes;
protected $hex;
protected $string;
protected $urn;
protected $version;
protected $variant;
protected $node;
protected $time;
public static function mint($ver = 1, $node = NULL, $ns = NULL) {
/* Create a new UUID based on provided data. */
switch((int) $ver) {
case 1:
return new self(self::mintTime($node));
case 2:
// Version 2 is not supported
throw new UUIDException("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 UUIDException("Selected version is invalid or unsupported.");
}
}
public static function import($uuid) {
/* Import an existing UUID. */
return new self(self::makeBin($uuid, 16));
}
public static function compare($a, $b) {
/* Compares the binary representations of two UUIDs.
The comparison will return true if they are bit-exact,
or if neither is valid. */
if (self::makeBin($a, 16)==self::makeBin($b, 16))
return TRUE;
else
return FALSE;
}
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 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 {
}