337 lines
9.5 KiB
PHP
337 lines
9.5 KiB
PHP
<?php
|
|
/**
|
|
* @copyright Copyright (c) 2010, The volkszaehler.org project
|
|
* @package util
|
|
* @license http://www.opensource.org/licenses/gpl-license.php GNU Public License
|
|
*/
|
|
/*
|
|
* This file is part of volkzaehler.org
|
|
*
|
|
* volkzaehler.org is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* any later version.
|
|
*
|
|
* volkzaehler.org is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
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 unknown_type $uuid
|
|
*/
|
|
public static function import($uuid) {
|
|
return new self(self::makeBin($uuid, 16));
|
|
}
|
|
|
|
/**
|
|
* Validation of UUID's
|
|
*
|
|
* @param string $uuid
|
|
*/
|
|
public static function validate($uuid) {
|
|
return (boolean) preg_match('/^[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}$/', $uuid);
|
|
}
|
|
|
|
/**
|
|
* 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));
|
|
}
|
|
|
|
/**
|
|
* Generates a Version 1 UUID.
|
|
* These are derived from the time at which they were generated.
|
|
*
|
|
* @param $node
|
|
*/
|
|
protected static function mintTime($node = NULL) {
|
|
// 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(TRUE) * 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);
|
|
}
|
|
else {
|
|
// 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;
|
|
}
|
|
|
|
/**
|
|
* Generate a Version 4 UUID.
|
|
* These are derived soly from random numbers.
|
|
*/
|
|
protected static function mintRand() {
|
|
// 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;
|
|
}
|
|
|
|
/**
|
|
* Generates a Version 3 or Version 5 UUID.
|
|
* These are derived from a hash of a name and its namespace, in binary form.
|
|
*
|
|
* @param integer $ver the version (MD5 or SHA1)
|
|
* @param string $node the name string
|
|
* $param string $ns the namespace
|
|
*/
|
|
protected static function mintName($ver, $node, $ns) {
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Insure that an input string is either binary or hexadecimal.
|
|
* Returns binary representation, or FALSE on failure.
|
|
*
|
|
* @param unkown_type $str
|
|
* @param integer $len
|
|
*/
|
|
protected static function makeBin($str, $len) {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
?>
|
|
|