[5698] | 1 | <?php |
---|
| 2 | /*- |
---|
| 3 | * Copyright (c) 2011 Fredrik Lindberg - http://www.shapeshifter.se |
---|
| 4 | * All rights reserved. |
---|
| 5 | * |
---|
| 6 | * Redistribution and use in source and binary forms, with or without |
---|
| 7 | * modification, are permitted provided that the following conditions |
---|
| 8 | * are met: |
---|
| 9 | * 1. Redistributions of source code must retain the above copyright |
---|
| 10 | * notice, this list of conditions and the following disclaimer. |
---|
| 11 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
| 12 | * notice, this list of conditions and the following disclaimer in the |
---|
| 13 | * documentation and/or other materials provided with the distribution. |
---|
| 14 | * |
---|
| 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
---|
| 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
---|
| 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
---|
| 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
---|
| 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
---|
| 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
---|
| 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
---|
| 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
---|
| 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
---|
| 24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
---|
| 25 | * |
---|
| 26 | * Alternative this software might be licensed under the following license |
---|
| 27 | * |
---|
| 28 | * Copyright 2011 Fredrik Lindberg |
---|
| 29 | * |
---|
| 30 | * Licensed under the Apache License, Version 2.0 (the "License"); |
---|
| 31 | * you may not use this file except in compliance with the License. |
---|
| 32 | * You may obtain a copy of the License at |
---|
| 33 | * |
---|
| 34 | * http://www.apache.org/licenses/LICENSE-2.0 |
---|
| 35 | * |
---|
| 36 | * Unless required by applicable law or agreed to in writing, software |
---|
| 37 | * distributed under the License is distributed on an "AS IS" BASIS, |
---|
| 38 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
---|
| 39 | * See the License for the specific language governing permissions and |
---|
| 40 | * limitations under the License. |
---|
| 41 | * |
---|
| 42 | */ |
---|
| 43 | |
---|
| 44 | /* |
---|
| 45 | * UUID (RFC4122) Generator |
---|
| 46 | * http://tools.ietf.org/html/rfc4122 |
---|
| 47 | * |
---|
| 48 | * Implements version 1, 3, 4 and 5 |
---|
| 49 | */ |
---|
| 50 | class UUID { |
---|
| 51 | /* UUID versions */ |
---|
| 52 | const UUID_TIME = 1; /* Time based UUID */ |
---|
| 53 | const UUID_NAME_MD5 = 3; /* Name based (MD5) UUID */ |
---|
| 54 | const UUID_RANDOM = 4; /* Random UUID */ |
---|
| 55 | const UUID_NAME_SHA1 = 5; /* Name based (SHA1) UUID */ |
---|
| 56 | |
---|
| 57 | /* UUID formats */ |
---|
| 58 | const FMT_FIELD = 100; |
---|
| 59 | const FMT_STRING = 101; |
---|
| 60 | const FMT_BINARY = 102; |
---|
| 61 | const FMT_QWORD = 1; /* Quad-word, 128-bit (not impl.) */ |
---|
| 62 | const FMT_DWORD = 2; /* Double-word, 64-bit (not impl.) */ |
---|
| 63 | const FMT_WORD = 4; /* Word, 32-bit (not impl.) */ |
---|
| 64 | const FMT_SHORT = 8; /* Short (not impl.) */ |
---|
| 65 | const FMT_BYTE = 16; /* Byte */ |
---|
| 66 | const FMT_DEFAULT = 16; |
---|
| 67 | |
---|
| 68 | /* Field UUID representation */ |
---|
| 69 | static private $m_uuid_field = array( |
---|
| 70 | 'time_low' => 0, /* 32-bit */ |
---|
| 71 | 'time_mid' => 0, /* 16-bit */ |
---|
| 72 | 'time_hi' => 0, /* 16-bit */ |
---|
| 73 | 'clock_seq_hi' => 0, /* 8-bit */ |
---|
| 74 | 'clock_seq_low' => 0, /* 8-bit */ |
---|
| 75 | 'node' => array() /* 48-bit */ |
---|
| 76 | ); |
---|
| 77 | |
---|
| 78 | static private $m_generate = array( |
---|
| 79 | self::UUID_TIME => "generateTime", |
---|
| 80 | self::UUID_RANDOM => "generateRandom", |
---|
| 81 | self::UUID_NAME_MD5 => "generateNameMD5", |
---|
| 82 | self::UUID_NAME_SHA1 => "generateNameSHA1" |
---|
| 83 | ); |
---|
| 84 | |
---|
| 85 | static private $m_convert = array( |
---|
| 86 | self::FMT_FIELD => array( |
---|
| 87 | self::FMT_BYTE => "conv_field2byte", |
---|
| 88 | self::FMT_STRING => "conv_field2string", |
---|
| 89 | self::FMT_BINARY => "conv_field2binary" |
---|
| 90 | ), |
---|
| 91 | self::FMT_BYTE => array( |
---|
| 92 | self::FMT_FIELD => "conv_byte2field", |
---|
| 93 | self::FMT_STRING => "conv_byte2string", |
---|
| 94 | self::FMT_BINARY => "conv_byte2binary" |
---|
| 95 | ), |
---|
| 96 | self::FMT_STRING => array( |
---|
| 97 | self::FMT_BYTE => "conv_string2byte", |
---|
| 98 | self::FMT_FIELD => "conv_string2field", |
---|
| 99 | self::FMT_BINARY => "conv_string2binary" |
---|
| 100 | ), |
---|
| 101 | ); |
---|
| 102 | |
---|
| 103 | /* Swap byte order of a 32-bit number */ |
---|
| 104 | static private function swap32($x) { |
---|
| 105 | return (($x & 0x000000ff) << 24) | (($x & 0x0000ff00) << 8) | |
---|
| 106 | (($x & 0x00ff0000) >> 8) | (($x & 0xff000000) >> 24); |
---|
| 107 | } |
---|
| 108 | |
---|
| 109 | /* Swap byte order of a 16-bit number */ |
---|
| 110 | static private function swap16($x) { |
---|
| 111 | return (($x & 0x00ff) << 8) | (($x & 0xff00) >> 8); |
---|
| 112 | } |
---|
| 113 | |
---|
| 114 | /* Auto-detect UUID format */ |
---|
| 115 | static private function detectFormat($src) { |
---|
| 116 | if (is_string($src)) |
---|
| 117 | return self::FMT_STRING; |
---|
| 118 | else if (is_array($src)) { |
---|
| 119 | $len = count($src); |
---|
| 120 | if ($len == 1 || ($len % 2) == 0) |
---|
| 121 | return $len; |
---|
| 122 | else |
---|
| 123 | return (-1); |
---|
| 124 | } |
---|
| 125 | else |
---|
| 126 | return self::FMT_BINARY; |
---|
| 127 | } |
---|
| 128 | |
---|
| 129 | /* |
---|
| 130 | * Public API, generate a UUID of 'type' in format 'fmt' for |
---|
| 131 | * the given namespace 'ns' and node 'node' |
---|
| 132 | */ |
---|
| 133 | static public function generate($type, $fmt = self::FMT_BYTE, |
---|
| 134 | $node = "", $ns = "") { |
---|
| 135 | $func = self::$m_generate[$type]; |
---|
| 136 | if (!isset($func)) |
---|
| 137 | return null; |
---|
| 138 | $conv = self::$m_convert[self::FMT_FIELD][$fmt]; |
---|
| 139 | |
---|
| 140 | $uuid = self::$func($ns, $node); |
---|
| 141 | return self::$conv($uuid); |
---|
| 142 | } |
---|
| 143 | |
---|
| 144 | /* |
---|
| 145 | * Public API, convert a UUID from one format to another |
---|
| 146 | */ |
---|
| 147 | static public function convert($uuid, $from, $to) { |
---|
| 148 | $conv = self::$m_convert[$from][$to]; |
---|
| 149 | if (!isset($conv)) |
---|
| 150 | return ($uuid); |
---|
| 151 | |
---|
| 152 | return (self::$conv($uuid)); |
---|
| 153 | } |
---|
| 154 | |
---|
| 155 | /* |
---|
| 156 | * Generate an UUID version 4 (pseudo random) |
---|
| 157 | */ |
---|
| 158 | static private function generateRandom($ns, $node) { |
---|
| 159 | $uuid = self::$m_uuid_field; |
---|
| 160 | |
---|
| 161 | $uuid['time_hi'] = (4 << 12) | (mt_rand(0, 0x1000)); |
---|
| 162 | $uuid['clock_seq_hi'] = (1 << 7) | mt_rand(0, 128); |
---|
| 163 | $uuid['time_low'] = mt_rand(0, 0xffff) + (mt_rand(0, 0xffff) << 16); |
---|
| 164 | $uuid['time_mid'] = mt_rand(0, 0xffff); |
---|
| 165 | $uuid['clock_seq_low'] = mt_rand(0, 255); |
---|
| 166 | for ($i = 0; $i < 6; $i++) |
---|
| 167 | $uuid['node'][$i] = mt_rand(0, 255); |
---|
| 168 | return ($uuid); |
---|
| 169 | } |
---|
| 170 | |
---|
| 171 | /* |
---|
| 172 | * Generate UUID version 3 and 5 (name based) |
---|
| 173 | */ |
---|
| 174 | static private function generateName($ns, $node, $hash, $version) { |
---|
| 175 | $ns_fmt = self::detectFormat($ns); |
---|
| 176 | $field = self::convert($ns, $ns_fmt, self::FMT_FIELD); |
---|
| 177 | |
---|
| 178 | /* Swap byte order to keep it in big endian on all platforms */ |
---|
| 179 | $field['time_low'] = self::swap32($field['time_low']); |
---|
| 180 | $field['time_mid'] = self::swap16($field['time_mid']); |
---|
| 181 | $field['time_hi'] = self::swap16($field['time_hi']); |
---|
| 182 | |
---|
| 183 | /* Convert the namespace to binary and concatenate node */ |
---|
| 184 | $raw = self::convert($field, self::FMT_FIELD, self::FMT_BINARY); |
---|
| 185 | $raw .= $node; |
---|
| 186 | |
---|
| 187 | /* Hash the namespace and node and convert to a byte array */ |
---|
| 188 | $val = $hash($raw, true); |
---|
| 189 | $tmp = unpack('C16', $val); |
---|
| 190 | foreach (array_keys($tmp) as $key) |
---|
| 191 | $byte[$key - 1] = $tmp[$key]; |
---|
| 192 | |
---|
| 193 | /* Convert byte array to a field array */ |
---|
| 194 | $field = self::conv_byte2field($byte); |
---|
| 195 | |
---|
| 196 | $field['time_low'] = self::swap32($field['time_low']); |
---|
| 197 | $field['time_mid'] = self::swap16($field['time_mid']); |
---|
| 198 | $field['time_hi'] = self::swap16($field['time_hi']); |
---|
| 199 | |
---|
| 200 | /* Apply version and constants */ |
---|
| 201 | $field['clock_seq_hi'] &= 0x3f; |
---|
| 202 | $field['clock_seq_hi'] |= (1 << 7); |
---|
| 203 | $field['time_hi'] &= 0x0fff; |
---|
| 204 | $field['time_hi'] |= ($version << 12); |
---|
| 205 | |
---|
| 206 | return ($field); |
---|
| 207 | } |
---|
| 208 | static private function generateNameMD5($ns, $node) { |
---|
| 209 | return self::generateName($ns, $node, "md5", |
---|
| 210 | self::UUID_NAME_MD5); |
---|
| 211 | } |
---|
| 212 | static private function generateNameSHA1($ns, $node) { |
---|
| 213 | return self::generateName($ns, $node, "sha1", |
---|
| 214 | self::UUID_NAME_SHA1); |
---|
| 215 | } |
---|
| 216 | |
---|
| 217 | /* |
---|
| 218 | * Generate UUID version 1 (time based) |
---|
| 219 | */ |
---|
| 220 | static private function generateTime($ns, $node) { |
---|
| 221 | $uuid = self::$m_uuid_field; |
---|
| 222 | |
---|
| 223 | /* |
---|
| 224 | * Get current time in 100 ns intervals. The magic value |
---|
| 225 | * is the offset between UNIX epoch and the UUID UTC |
---|
| 226 | * time base October 15, 1582. |
---|
| 227 | */ |
---|
| 228 | $tp = gettimeofday(); |
---|
| 229 | $time = ($tp['sec'] * 10000000) + ($tp['usec'] * 10) + |
---|
| 230 | 0x01B21DD213814000; |
---|
| 231 | |
---|
| 232 | $uuid['time_low'] = $time & 0xffffffff; |
---|
| 233 | /* Work around PHP 32-bit bit-operation limits */ |
---|
| 234 | $high = intval($time / 0xffffffff); |
---|
| 235 | $uuid['time_mid'] = $high & 0xffff; |
---|
| 236 | $uuid['time_hi'] = (($high >> 16) & 0xfff) | (self::UUID_TIME << 12); |
---|
| 237 | |
---|
| 238 | /* |
---|
| 239 | * We don't support saved state information and generate |
---|
| 240 | * a random clock sequence each time. |
---|
| 241 | */ |
---|
| 242 | $uuid['clock_seq_hi'] = 0x80 | mt_rand(0, 64); |
---|
| 243 | $uuid['clock_seq_low'] = mt_rand(0, 255); |
---|
| 244 | |
---|
| 245 | /* |
---|
| 246 | * Node should be set to the 48-bit IEEE node identifier, but |
---|
| 247 | * we leave it for the user to supply the node. |
---|
| 248 | */ |
---|
| 249 | for ($i = 0; $i < 6; $i++) |
---|
| 250 | $uuid['node'][$i] = ord(substr($node, $i, 1)); |
---|
| 251 | |
---|
| 252 | return ($uuid); |
---|
| 253 | } |
---|
| 254 | |
---|
| 255 | /* Assumes correct byte order */ |
---|
| 256 | static private function conv_field2byte($src) { |
---|
| 257 | $uuid[0] = ($src['time_low'] & 0xff000000) >> 24; |
---|
| 258 | $uuid[1] = ($src['time_low'] & 0x00ff0000) >> 16; |
---|
| 259 | $uuid[2] = ($src['time_low'] & 0x0000ff00) >> 8; |
---|
| 260 | $uuid[3] = ($src['time_low'] & 0x000000ff); |
---|
| 261 | $uuid[4] = ($src['time_mid'] & 0xff00) >> 8; |
---|
| 262 | $uuid[5] = ($src['time_mid'] & 0x00ff); |
---|
| 263 | $uuid[6] = ($src['time_hi'] & 0xff00) >> 8; |
---|
| 264 | $uuid[7] = ($src['time_hi'] & 0x00ff); |
---|
| 265 | $uuid[8] = $src['clock_seq_hi']; |
---|
| 266 | $uuid[9] = $src['clock_seq_low']; |
---|
| 267 | |
---|
| 268 | for ($i = 0; $i < 6; $i++) |
---|
| 269 | $uuid[10+$i] = $src['node'][$i]; |
---|
| 270 | |
---|
| 271 | return ($uuid); |
---|
| 272 | } |
---|
| 273 | |
---|
| 274 | static private function conv_field2string($src) { |
---|
| 275 | $str = sprintf( |
---|
| 276 | '%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x', |
---|
| 277 | ($src['time_low']), ($src['time_mid']), ($src['time_hi']), |
---|
| 278 | $src['clock_seq_hi'], $src['clock_seq_low'], |
---|
| 279 | $src['node'][0], $src['node'][1], $src['node'][2], |
---|
| 280 | $src['node'][3], $src['node'][4], $src['node'][5]); |
---|
| 281 | return ($str); |
---|
| 282 | } |
---|
| 283 | |
---|
| 284 | static private function conv_field2binary($src) { |
---|
| 285 | $byte = self::conv_field2byte($src); |
---|
| 286 | return self::conv_byte2binary($byte); |
---|
| 287 | } |
---|
| 288 | |
---|
| 289 | static private function conv_byte2field($uuid) { |
---|
| 290 | $field = self::$m_uuid_field; |
---|
| 291 | $field['time_low'] = ($uuid[0] << 24) | ($uuid[1] << 16) | |
---|
| 292 | ($uuid[2] << 8) | $uuid[3]; |
---|
| 293 | $field['time_mid'] = ($uuid[4] << 8) | $uuid[5]; |
---|
| 294 | $field['time_hi'] = ($uuid[6] << 8) | $uuid[7]; |
---|
| 295 | $field['clock_seq_hi'] = $uuid[8]; |
---|
| 296 | $field['clock_seq_low'] = $uuid[9]; |
---|
| 297 | |
---|
| 298 | for ($i = 0; $i < 6; $i++) |
---|
| 299 | $field['node'][$i] = $uuid[10+$i]; |
---|
| 300 | return ($field); |
---|
| 301 | } |
---|
| 302 | |
---|
| 303 | static public function conv_byte2string($src) { |
---|
| 304 | $field = self::conv_byte2field($src); |
---|
| 305 | return self::conv_field2string($field); |
---|
| 306 | } |
---|
| 307 | |
---|
| 308 | static private function conv_byte2binary($src) { |
---|
| 309 | $raw = pack('C16', $src[0], $src[1], $src[2], $src[3], |
---|
| 310 | $src[4], $src[5], $src[6], $src[7], $src[8], $src[9], |
---|
| 311 | $src[10], $src[11], $src[12], $src[13], $src[14], $src[15]); |
---|
| 312 | return ($raw); |
---|
| 313 | } |
---|
| 314 | |
---|
| 315 | static private function conv_string2field($src) { |
---|
| 316 | $parts = sscanf($src, '%x-%x-%x-%x-%02x%02x%02x%02x%02x%02x'); |
---|
| 317 | $field = self::$m_uuid_field; |
---|
| 318 | $field['time_low'] = ($parts[0]); |
---|
| 319 | $field['time_mid'] = ($parts[1]); |
---|
| 320 | $field['time_hi'] = ($parts[2]); |
---|
| 321 | $field['clock_seq_hi'] = ($parts[3] & 0xff00) >> 8; |
---|
| 322 | $field['clock_seq_low'] = $parts[3] & 0x00ff; |
---|
| 323 | for ($i = 0; $i < 6; $i++) |
---|
| 324 | $field['node'][$i] = $parts[4+$i]; |
---|
| 325 | |
---|
| 326 | return ($field); |
---|
| 327 | } |
---|
| 328 | |
---|
| 329 | static private function conv_string2byte($src) { |
---|
| 330 | $field = self::conv_string2field($src); |
---|
| 331 | return self::conv_field2byte($field); |
---|
| 332 | } |
---|
| 333 | |
---|
| 334 | static private function conv_string2binary($src) { |
---|
| 335 | $byte = self::conv_string2byte($src); |
---|
| 336 | return self::conv_byte2binary($byte); |
---|
| 337 | } |
---|
| 338 | } |
---|
| 339 | |
---|
| 340 | ?> |
---|