├── composer.json └── src ├── UUIDParseException.php └── UUID.php /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fleshgrinder/uuid", 3 | "description": "UUID value object for PHP", 4 | "license": "Unlicense", 5 | "authors": [ 6 | { 7 | "name": "Richard Fussenegger", 8 | "email": "fleshgrinder@users.noreply.github.com" 9 | } 10 | ], 11 | "require": { 12 | "php": "^5.4 || ^7.0", 13 | "paragonie/random_compat": "^2.0" 14 | }, 15 | "require-dev": { 16 | "phpunit/phpunit": "^5.0" 17 | }, 18 | "autoload": { 19 | "classmap": [ 20 | "src" 21 | ] 22 | }, 23 | "autoload-dev": { 24 | "classmap": [ 25 | "src", 26 | "tests" 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/UUIDParseException.php: -------------------------------------------------------------------------------- 1 | getMessage(); // Expected at least 32 characters, but got 3 characters 16 | * echo $e->getInput(); // php 17 | * echo $e->getPosition(); // 0 18 | * } 19 | * 20 | * try { 21 | * UUID::parse('12345678-1234-1234-1234-123456789php'); 22 | * } 23 | * catch (UUIDParseException $e) { 24 | * echo $e->getMessage(); // Expected hexadecimal digit, but found 'p' (0x70) 25 | * echo $e->getInput(); // 12345678-1234-1234-1234-123456789php 26 | * echo $e->getPosition(); // 33 27 | * } 28 | * 29 | * try { 30 | * UUID::parse('12345678-1234-1234-1234-123456789abcdef'); 31 | * } 32 | * catch (UUIDParseException $e) { 33 | * echo $e->getMessage(); // Expected no more than 32 hexadecimal digits 34 | * echo $e->getInput(); // 12345678-1234-1234-1234-123456789abcdef 35 | * echo $e->getPosition(); // 37 36 | * } 37 | * 38 | * ?> 39 | * ``` 40 | * 41 | * @see \UUIDimpl::parse() 42 | */ 43 | final class UUIDParseException extends Exception { 44 | /** @var string */ 45 | private $input; 46 | 47 | /** @var int */ 48 | private $position; 49 | 50 | /** 51 | * Construct new UUID parse exception instance. 52 | * 53 | * @param string $reason why parsing the UUID string failed. 54 | * @param string $input that should be parsed. 55 | * @param int $position at which parsing failed. 56 | * @param Exception|null $previous exception that lead to this failure, 57 | * if any. 58 | */ 59 | public function __construct($reason, $input, $position = 0, Exception $previous = null) { 60 | parent::__construct($reason, 0, $previous); 61 | 62 | $this->input = $input; 63 | $this->position = $position; 64 | } 65 | 66 | /** 67 | * Get the original input string that should have been parsed as a UUID. 68 | * 69 | * @return string 70 | */ 71 | public function getInput() { 72 | return $this->input; 73 | } 74 | 75 | /** 76 | * Get the position in the input string where the parsing failure occurred. 77 | * 78 | * @return int 79 | */ 80 | public function getPosition() { 81 | return $this->position; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/UUID.php: -------------------------------------------------------------------------------- 1 | isNil()); 51 | * assert($uuid->getVariant() === 0); 52 | * assert($uuid->getVersion() === 0); 53 | * 54 | * $uuid = UUID::v3(UUID::NamespaceDNS(), 'php.net'); 55 | * assert($uuid->isNil() === false); 56 | * assert($uuid->getVariant() === UUID::VARIANT_RFC4122); 57 | * assert($uuid->getVersion() === UUID::VERSION_3_NAME_BASED_MD5); 58 | * 59 | * $uuid = UUID::v4(); 60 | * assert($uuid->isNil() === false); 61 | * assert($uuid->getVariant() === UUID::VARIANT_RFC4122); 62 | * assert($uuid->getVersion() === UUID::VERSION_4_RANDOM); 63 | * 64 | * $uuid = UUID::v5(UUID::NamespaceDNS(), 'php.net'); 65 | * assert($uuid->isNil() === false); 66 | * assert($uuid->getVariant() === UUID::VARIANT_RFC4122); 67 | * assert($uuid->getVersion() === UUID::VERSION_5_NAME_BASED_SHA1); 68 | * 69 | * $uuid = UUID::parse('urn:uuid:123E4567-E89B-12D3-A456-426655440000'); 70 | * assert($uuid->isNil() === false); 71 | * assert($uuid->getVariant() === UUID::VARIANT_RFC4122); 72 | * assert($uuid->getVersion() === UUID::VERSION_1_TIME_BASED); 73 | * 74 | * assert($uuid->toBinary() === "\x12\x3E\x45\x67\xE8\x9B\x12\xD3\xA4\x56\x42\x66\x55\x44\x00\x00"); 75 | * assert($uuid->toHex() === '123e4567e89b12d3a456426655440000'); 76 | * assert($uuid->toString() === '123e4567-e89b-12d3-a456-426655440000'); 77 | * 78 | * ?> 79 | * ``` 80 | * 81 | * Comparison of UUIDs is possible with the default comparison operators of 82 | * PHP. 83 | * 84 | * ``` 85 | * 111 | * ``` 112 | * 113 | * [rfc]: https://tools.ietf.org/html/rfc4122 114 | * [wiki]: https://en.wikipedia.org/wiki/Universally_unique_identifier 115 | * @since 7.2 116 | * @see https://php.net/uuid 117 | */ 118 | final class UUID { 119 | /** 120 | * Code for the (reserved) NCS variant of UUIDs. 121 | * 122 | * @since 7.2 123 | * @see https://tools.ietf.org/html/rfc4122#section-4.1.1 124 | */ 125 | const VARIANT_NCS = 0; 126 | 127 | /** 128 | * Code for the RFC 4122 variant of UUIDs. 129 | * 130 | * This implementation generates UUIDs of this variant only. 131 | * 132 | * @since 7.2 133 | * @see https://tools.ietf.org/html/rfc4122#section-4.1.1 134 | */ 135 | const VARIANT_RFC4122 = 1; 136 | 137 | /** 138 | * Code for the (reserved) Microsoft variant of UUIDs, the GUIDs. 139 | * 140 | * @since 7.2 141 | * @see https://tools.ietf.org/html/rfc4122#section-4.1.1 142 | */ 143 | const VARIANT_MICROSOFT = 2; 144 | 145 | /** 146 | * Version code for the future reserved variant of UUIDs. 147 | * 148 | * @since 7.2 149 | * @see https://tools.ietf.org/html/rfc4122#section-4.1.1 150 | */ 151 | const VARIANT_FUTURE_RESERVED = 3; 152 | 153 | /** 154 | * Version code for date-time and IEEE 802 MAC address UUIDs. 155 | * 156 | * Generation of this version is not supported by this implementation due 157 | * to security concerns. Version 4 UUIDs are a good replacement for version 158 | * 1 UUIDs without the privacy/security concerns (see [Wikipedia][wiki]). 159 | * 160 | * [wiki]: https://en.wikipedia.org/wiki/Universally_unique_identifier 161 | * @since 7.2 162 | * @see https://tools.ietf.org/html/rfc4122#section-4.2 163 | * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_.28date-time_and_MAC_address.29 164 | */ 165 | const VERSION_1_TIME_BASED = 1; 166 | 167 | /** 168 | * Version code for date-time and IEEE 802 MAC address UUIDs (DCE security 169 | * algorithm). 170 | * 171 | * Generation of this version is not supported by this implementation due 172 | * to security concerns, and uniqueness limitations for applications with 173 | * high allocations. Version 4 UUIDs are a good replacement for version 2 174 | * UUIDs without the privacy/security concerns (see [Wikipedia][wiki]), and 175 | * they support high allocations. 176 | * 177 | * [wiki]: https://en.wikipedia.org/wiki/Universally_unique_identifier 178 | * @since 7.2 179 | * @see https://tools.ietf.org/html/rfc4122#section-4.2 180 | * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_2_.28date-time_and_MAC_Address.2C_DCE_security_version.29 181 | */ 182 | const VERSION_2_DCE_SECURITY = 2; 183 | 184 | /** 185 | * Version code for namespace/name-based MD5 hashed UUIDs. 186 | * 187 | * @since 7.2 188 | * @see v3 189 | * @see https://tools.ietf.org/html/rfc4122#section-4.3 190 | * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_.28namespace_name-based.29 191 | */ 192 | const VERSION_3_NAME_BASED_MD5 = 3; 193 | 194 | /** 195 | * Version code for random UUIDs. 196 | * 197 | * @since 7.2 198 | * @see v4 199 | * @see https://tools.ietf.org/html/rfc4122#section-4.4 200 | * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29 201 | */ 202 | const VERSION_4_RANDOM = 4; 203 | 204 | /** 205 | * Version code for namespace/name-based SHA1 hashed UUIDs. 206 | * 207 | * @since 7.2 208 | * @see v5 209 | * @see https://tools.ietf.org/html/rfc4122#section-4.3 210 | * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_.28namespace_name-based.29 211 | */ 212 | const VERSION_5_NAME_BASED_SHA1 = 5; 213 | 214 | /** 215 | * This UUID's 128 bit integer value as 16 byte binary string. 216 | * 217 | * @since 7.2 218 | * @see toBinary 219 | * @var string 220 | */ 221 | private $bytes; 222 | 223 | /** 224 | * Use {@see fromBinary} or {@see parse} to construct a new instance. 225 | */ 226 | private function __construct() { 227 | // NOOP 228 | } 229 | 230 | /** 231 | * Construct new UUID instance from binary string of exactly 16 bytes. 232 | * 233 | * Any string of 16 bytes is accepted by this named constructor. This 234 | * enables the construction of UUIDs of any variant and version, regardless 235 | * of the {@see parse} implementation. 236 | * 237 | * ## Examples 238 | * ``` 239 | * getVariant() === UUID::VARIANT_FUTURE_RESERVED); 247 | * 248 | * ?> 249 | * ``` 250 | * 251 | * @since 7.2 252 | * @see https://php.net/uuid.fromBinary 253 | * @see toBinary 254 | * @param string $input string of exactly 16 bytes to construct the instance from. 255 | * @return \UUID UUID constructed from the binary input. 256 | * @throws \InvalidArgumentException if the input is not 16 bytes long. 257 | */ 258 | public static function fromBinary($input) { 259 | if (strlen($input) !== 16) { 260 | throw new InvalidArgumentException('Expected exactly 16 bytes, but got ' . strlen($input)); 261 | } 262 | 263 | $uuid = new UUID; 264 | $uuid->bytes = $input; 265 | return $uuid; 266 | } 267 | 268 | /** 269 | * Parse the given string as UUID. 270 | * 271 | * The following UUID representations are parsable: 272 | * 273 | * - hexadecimal (`00000000111122223333444444444444`), 274 | * - string (`00000000-1111-2222-3333-444444444444`), 275 | * - URNs (`urn:uuid:00000000-1111-2222-3333-444444444444`), and 276 | * - Microsoft (`{00000000-1111-2222-3333-444444444444}`). 277 | * 278 | * Leading and trailing whitespace, namely spaces (` `) and tabs (`\t`), is 279 | * ignored, so are leading opening braces (`{`) and trailing closing braces 280 | * (`}`). Hyphens (`-`) are ignored everywhere. The parsing algorithm 281 | * follows the [robustness principle][wrp] and is not meant for validation. 282 | * 283 | * ## Examples 284 | * ``` 285 | * getMessage() === 'Expected hexadecimal digit, but found '{' (0x7b)'); 303 | * } 304 | * 305 | * ?> 306 | * ``` 307 | * 308 | * [wrp]: https://en.wikipedia.org/wiki/Robustness_principle 309 | * @since 7.2 310 | * @see https://php.net/uuid.parse 311 | * @see toHex 312 | * @see toString 313 | * @param string $input to parse as UUID and construct the instance from. 314 | * @return \UUID UUID constructed from the parsed input. 315 | * @throws \UUIDParseException if parsing of the input fails. 316 | */ 317 | public static function parse($input) { 318 | $input = ltrim($input, " \t{-"); 319 | 320 | if (strpos($input, 'urn:uuid:') === 0) { 321 | $input = (string) substr($input, 9); 322 | } 323 | 324 | $input = rtrim($input, " \t}-"); 325 | 326 | if (strlen($input) < 32) { 327 | throw new UUIDParseException('Expected at least 32 hexadecimal digits, but got ' . strlen($input), $input); 328 | } 329 | 330 | $input = str_replace('-', '', $input); 331 | $binary = @hex2bin($input); 332 | if ($binary === false) { 333 | for ($position = 0, $limit = strlen($input); $position < $limit; ++$position) { 334 | $char = $input{$position}; 335 | if ( 336 | ($char < '0' || $char > '9') && 337 | ($char < 'a' || $char > 'f') && 338 | ($char < 'A' || $char > 'F') 339 | ) { 340 | throw new UUIDParseException(sprintf( 341 | "Expected hexadecimal digit, but found '{$char}' (0x%02x)", 342 | ord($char) 343 | ), $input, $position); 344 | } 345 | } 346 | } 347 | 348 | $length = strlen($binary === false ? $input : $binary); 349 | if ($length < 16) { 350 | throw new UUIDParseException('Expected at least 32 hexadecimal digits, but got ' . ($length * 2), $input, $length - 1); 351 | } 352 | if ($length > 16) { 353 | throw new UUIDParseException('Expected no more than 32 hexadecimal digits', $input, $length - 1); 354 | } 355 | 356 | $uuid = new UUID; 357 | $uuid->bytes = $binary; 358 | return $uuid; 359 | } 360 | 361 | /** 362 | * Construct new version 3 UUID. 363 | * 364 | * > RFC 4122 recommends {@see v5} over this one and states that version 3 365 | * > UUIDs should be used if backwards compatibility is required only. This 366 | * > is because MD5 has a higher collision probability compared to SHA1, 367 | * > which is used by version 5; regardless of the truncation! 368 | * 369 | * Version 3 UUIDs are generated by MD5 hashing the concatenated 370 | * namespace's byte representation and the given name. The namespace itself 371 | * must be another UUID. This can be any UUID, or one of the predefined 372 | * ones: 373 | * 374 | * - {@see NamespaceDNS} 375 | * - {@see NamespaceOID} 376 | * - {@see NamespaceURL} 377 | * - {@see NamespaceX500} 378 | * 379 | * A particular name within the same namespace always results in the same 380 | * version 3 UUID, across all RFC 4122 compliant UUID implementations. 381 | * However, the namespace and name cannot be determined from the UUID 382 | * alone. 383 | * 384 | * ## Examples 385 | * ``` 386 | * isNil() === false); 391 | * assert($uuid->getVariant() === UUID::VARIANT_RFC4122); 392 | * assert($uuid->getVersion() === UUID::VERSION_3_NAME_BASED_MD5); 393 | * assert($uuid->toString() === '11a38b9a-b3da-360f-9353-a5a725514269'); 394 | * assert($uuid == UUID::v3(UUID::NamespaceDNS(), 'php.net')); 395 | * 396 | * ?> 397 | * ``` 398 | * 399 | * @since 7.2 400 | * @see https://php.net/uuid.v3 401 | * @see https://tools.ietf.org/html/rfc4122#section-4.3 402 | * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_.28namespace_name-based.29 403 | * @param \UUID $namespace to construct the UUID in. 404 | * @param string $name to construct the UUID from. 405 | * @return \UUID UUID constructed from the name in the namespace. 406 | * @throws \Exception if the namespace does not encapsulate a valid UUID. 407 | */ 408 | public static function v3(self $namespace, $name) { 409 | $uuid = new UUID; 410 | 411 | $uuid->bytes = md5($namespace->toBinary() . $name, true); 412 | $uuid->bytes{6} = chr((ord($uuid->bytes{6}) & 0b00001111) | 0b00110000); 413 | $uuid->bytes{8} = chr((ord($uuid->bytes{8}) & 0b00111111) | 0b10000000); 414 | 415 | return $uuid; 416 | } 417 | 418 | /** 419 | * Construct new version 4 UUID. 420 | * 421 | * Version 4 UUIDs are randomly generated from the best available random 422 | * source. The selection of that source is determined by PHP's 423 | * {@see random_bytes} implementation. Some systems may be bad at 424 | * generating sufficient entropy, e.g. virtual machines. This might lead to 425 | * collisions faster than desired. If this is the case, the {@see v5} 426 | * version should be used. 427 | * 428 | * ## Examples 429 | * ``` 430 | * isNil() === false); 435 | * assert($uuid->getVariant() === UUID::VARIANT_RFC4122); 436 | * assert($uuid->getVersion() === UUID::VERSION_4_RANDOM); 437 | * assert($uuid != UUID::v4()); 438 | * 439 | * ?> 440 | * ``` 441 | * 442 | * @since 7.2 443 | * @see https://php.net/uuid.v4 444 | * @see https://tools.ietf.org/html/rfc4122#section-4.4 445 | * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29 446 | * @return \UUID UUID constructed from random data. 447 | * @throws \Exception if it was not possible to gather sufficient entropy. 448 | */ 449 | public static function v4() { 450 | $uuid = new UUID; 451 | 452 | $uuid->bytes = random_bytes(16); 453 | $uuid->bytes{6} = chr((ord($uuid->bytes{6}) & 0b00001111) | 0b01000000); 454 | $uuid->bytes{8} = chr((ord($uuid->bytes{8}) & 0b00111111) | 0b10000000); 455 | 456 | return $uuid; 457 | } 458 | 459 | /** 460 | * Construct new version 5 UUID. 461 | * 462 | * Version 5 UUIDs are generated by MD5 hashing the concatenated 463 | * namespace's byte representation and the given name. The namespace itself 464 | * must be another UUID. This can be any UUID, or one of the predefined 465 | * ones: 466 | * 467 | * - {@see NamespaceDNS} 468 | * - {@see NamespaceOID} 469 | * - {@see NamespaceURL} 470 | * - {@see NamespaceX500} 471 | * 472 | * A particular name within the same namespace always results in the same 473 | * version 5 UUID, across all RFC 4122 compliant UUID implementations. 474 | * However, the namespace and name cannot be determined from the UUID 475 | * alone. 476 | * 477 | * ## Examples 478 | * ``` 479 | * isNil() === false); 484 | * assert($uuid->getVariant() === UUID::VARIANT_RFC4122); 485 | * assert($uuid->getVersion() === UUID::VERSION_5_NAME_BASED_SHA1); 486 | * assert($uuid->toString() === 'c4a760a8-dbcf-5254-a0d9-6a4474bd1b62'); 487 | * assert($uuid == UUID::v5(UUID::NamespaceDNS(), 'php.net')); 488 | * 489 | * ?> 490 | * ``` 491 | * 492 | * @since 7.2 493 | * @see https://php.net/uuid.v5 494 | * @see @see https://tools.ietf.org/html/rfc4122#section-4.3 495 | * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_.28namespace_name-based.29 496 | * @param \UUID $namespace to construct the UUID in. 497 | * @param string $name to construct the UUID from. 498 | * @return \UUID UUID constructed from the name in the namespace. 499 | * @throws \Exception if the namespace does not encapsulate a valid UUID. 500 | */ 501 | public static function v5(self $namespace, $name) { 502 | $uuid = new UUID; 503 | 504 | $uuid->bytes = substr(sha1($namespace->toBinary() . $name, true), 0, 16); 505 | $uuid->bytes{6} = chr((ord($uuid->bytes{6}) & 0b00001111) | 0b01010000); 506 | $uuid->bytes{8} = chr((ord($uuid->bytes{8}) & 0b00111111) | 0b10000000); 507 | 508 | return $uuid; 509 | } 510 | 511 | /** 512 | * Construct new Domain Name System (DNS) namespace UUID instance. 513 | * 514 | * @since 7.2 515 | * @see https://php.net/uuid.NamespaceDNS 516 | * @see https://tools.ietf.org/html/rfc4122#appendix-C 517 | * @see https://en.wikipedia.org/wiki/Domain_Name_System 518 | * @return \UUID Predefined DNS namespace UUID. 519 | */ 520 | public static function NamespaceDNS() { 521 | $uuid = new UUID; 522 | $uuid->bytes = "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8"; 523 | return $uuid; 524 | } 525 | 526 | /** 527 | * Construct new Object Identifier (OID) namespace UUID instance. 528 | * 529 | * @since 7.2 530 | * @see https://php.net/uuid.NamespaceOID 531 | * @see https://tools.ietf.org/html/rfc4122#appendix-C 532 | * @see https://en.wikipedia.org/wiki/Object_identifier 533 | * @return \UUID Predefined OID namespace UUID. 534 | */ 535 | public static function NamespaceOID() { 536 | $uuid = new UUID; 537 | $uuid->bytes = "\x6b\xa7\xb8\x12\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8"; 538 | return $uuid; 539 | } 540 | 541 | /** 542 | * Construct new Uniform Resource Locator (URL) namespace UUID instance. 543 | * 544 | * @since 7.2 545 | * @see https://php.net/uuid.NamespaceURL 546 | * @see https://tools.ietf.org/html/rfc4122#appendix-C 547 | * @see https://en.wikipedia.org/wiki/URL 548 | * @return \UUID Predefined URL namespace UUID. 549 | */ 550 | public static function NamespaceURL() { 551 | $uuid = new UUID; 552 | $uuid->bytes = "\x6b\xa7\xb8\x11\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8"; 553 | return $uuid; 554 | } 555 | 556 | /** 557 | * Construct new X.500 Distinguished Names (X.500 DN) namespace UUID 558 | * instance. The names that are to be hashed in this namespace can be in 559 | * DER or a text output format. 560 | * 561 | * @since 7.2 562 | * @see https://php.net/uuid.NamespaceX500 563 | * @see https://tools.ietf.org/html/rfc4122#appendix-C 564 | * @see https://en.wikipedia.org/wiki/X.500 565 | * @see https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol 566 | * @return \UUID Predefined X.500 namespace UUID instance. 567 | */ 568 | public static function NamespaceX500() { 569 | $uuid = new UUID; 570 | $uuid->bytes = "\x6b\xa7\xb8\x14\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8"; 571 | return $uuid; 572 | } 573 | 574 | /** 575 | * Construct special nil UUID that has all 128 bits set to zero. 576 | * 577 | * @since 7.2 578 | * @see https://php.net/uuid.Nil 579 | * @see https://tools.ietf.org/html/rfc4122#section-4.1.7 580 | * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Nil_UUID 581 | * @return \UUID Predefined special nil UUID. 582 | */ 583 | public static function Nil() { 584 | $uuid = new UUID; 585 | $uuid->bytes = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 586 | return $uuid; 587 | } 588 | 589 | /** 590 | * Callback for dynamic adding of properties which throws an {@see Exception} 591 | * upon every invocation, direct or indirect. This is necessary to protect 592 | * the promised immutability of this object. Not doing so could lead to 593 | * problems with the comparison operators, since PHP always compares all 594 | * properties. 595 | * 596 | * @since 7.2 597 | * @see https://php.net/uuid.__set 598 | * @param mixed $_ 599 | * @param mixed $__ 600 | * @return void 601 | * @throws \Exception upon every invocation, direct or indirect. 602 | */ 603 | public function __set($_, $__) { 604 | throw new Exception('Cannot set dynamic properties on immutable ' . __CLASS__ . ' object'); 605 | } 606 | 607 | /** 608 | * Deserialization callback. 609 | * 610 | * @since 7.2 611 | * @see https://php.net/uuid.__wakeup 612 | * @see unserialize() 613 | * @return void 614 | * @throws \UnexpectedValueException if the value of the {@see bytes} 615 | * property is not of type string, or not exactly 16 bytes long. 616 | */ 617 | public function __wakeup() { 618 | if (is_string($this->bytes) === false) { 619 | $type = 'unknown'; 620 | 621 | if (is_array($this->bytes)) { 622 | $type = 'array'; 623 | } 624 | elseif ($this->bytes === true || $this->bytes === false) { 625 | $type = 'boolean'; 626 | } 627 | elseif (is_float($this->bytes)) { 628 | $type = 'float'; 629 | } 630 | elseif (is_int($this->bytes)) { 631 | $type = 'integer'; 632 | } 633 | elseif ($this->bytes === null) { 634 | $type = 'null'; 635 | } 636 | elseif (is_object($this->bytes)) { 637 | $type = 'object'; 638 | } 639 | elseif (is_resource($this->bytes)) { 640 | $type = 'resource'; 641 | } 642 | 643 | throw new UnexpectedValueException('Expected ' . __CLASS__ . '::$bytes value to be of type string, but found ' . $type); 644 | } 645 | 646 | if (strlen($this->bytes) !== 16) { 647 | throw new UnexpectedValueException('Expected ' . __CLASS__ . '::$bytes value to be exactly 16 bytes long, but found ' . strlen($this->bytes)); 648 | } 649 | } 650 | 651 | /** 652 | * Get the variant associated with this UUID. 653 | * 654 | * The variant specifies the internal data layout of a UUID. This 655 | * implementation generates {@see UUID::VARIANT_RFC4122} UUIDs only, 656 | * however, parsing and construction of other variants is supported. 657 | * 658 | * @since 7.2 659 | * @see https://php.net/uuid.getVariant 660 | * @see https://tools.ietf.org/html/rfc4122#section-4.1.1 661 | * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Variants 662 | * @see UUID::VARIANT_NCS 663 | * @see UUID::VARIANT_RFC4122 664 | * @see UUID::VARIANT_MICROSOFT 665 | * @see UUID::VARIANT_FUTURE_RESERVED 666 | * @return int An integer in [0, 3] where each value corresponds to one of 667 | * the `UUID::VARIANT_*` class constants. 668 | * @throws \Exception if this instance does not encapsulate a valid UUID. 669 | */ 670 | public function getVariant() { 671 | $bin = $this->toBinary(); 672 | $ord = ord($bin{8}); 673 | if (($ord & 0xC0) === 0x80) return static::VARIANT_RFC4122; 674 | if (($ord & 0xE0) === 0xC0) return static::VARIANT_MICROSOFT; 675 | if (($ord & 0x80) === 0x00) return static::VARIANT_NCS; 676 | return static::VARIANT_FUTURE_RESERVED; 677 | } 678 | 679 | /** 680 | * Get the version associated with this UUID. 681 | * 682 | * The version specifies which algorithm was used to generate the UUID. 683 | * Note that the version might not be meaningful if another variant than 684 | * the {@see UUID::VARIANT_RFC4122} was used to generate the UUID. This 685 | * implementation generates {@see UUID::VARIANT_RFC4122} UUIDs only, but 686 | * allows parsing and construction of other variants. 687 | * 688 | * @since 7.2 689 | * @see https://php.net/uuid.getVersion 690 | * @see https://tools.ietf.org/html/rfc4122#section-4.1.3 691 | * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions 692 | * @see UUID::VERSION_1_TIME_BASED 693 | * @see UUID::VERSION_2_DCE_SECURITY 694 | * @see UUID::VERSION_3_NAME_BASED_MD5 695 | * @see UUID::VERSION_4_RANDOM 696 | * @see UUID::VERSION_5_NAME_BASED_SHA1 697 | * @return int An integer in [0, 15], the values [1, 5] correspond to one 698 | * of the `UUID::VERSION_*` class constants. The others are not defined 699 | * in RFC 4122. 700 | * @throws \Exception if this instance does not encapsulate a valid UUID. 701 | */ 702 | public function getVersion() { 703 | $bin = $this->toBinary(); 704 | return ord($bin{6}) >> 4; 705 | } 706 | 707 | /** 708 | * Check if this UUID is the special nil UUID that has all 128 bits set to 709 | * zero. 710 | * 711 | * @since 7.2 712 | * @see https://php.net/uuid.isNil 713 | * @see https://tools.ietf.org/html/rfc4122#section-4.1.7 714 | * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Nil_UUID 715 | * @see Nil 716 | * @return bool **TRUE** if this is the special nil UUID; **FALSE** 717 | * otherwise. 718 | * @throws \Exception if this instance does not encapsulate a valid UUID. 719 | */ 720 | public function isNil() { 721 | return $this->toBinary() === "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 722 | } 723 | 724 | /** 725 | * Convert the UUID to its binary representation. 726 | * 727 | * The binary representation of a UUID is a string of exactly 16 bytes. It 728 | * is the format that is used internally. It is also the format that should 729 | * be used to store UUIDs in a database (e.g. in MySQL as `BINARY(16)` 730 | * column). The resulting string, if generated by this implementation, is 731 | * always in network byte order. 732 | * 733 | * ## Examples 734 | * ``` 735 | * toBinary() === "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8"); 738 | * 739 | * ?> 740 | * ``` 741 | * 742 | * @since 7.2 743 | * @see https://php.net/uuid.toBinary 744 | * @return string Binary representation of the UUID. 745 | * @throws \Exception if this instance does not encapsulate a valid UUID. 746 | */ 747 | public function toBinary() { 748 | if (is_string($this->bytes) === false) { 749 | $type = 'unknown'; 750 | 751 | if (is_array($this->bytes)) { 752 | $type = 'array'; 753 | } 754 | elseif ($this->bytes === true || $this->bytes === false) { 755 | $type = 'boolean'; 756 | } 757 | elseif (is_float($this->bytes)) { 758 | $type = 'float'; 759 | } 760 | elseif (is_int($this->bytes)) { 761 | $type = 'integer'; 762 | } 763 | elseif ($this->bytes === null) { 764 | $type = 'null'; 765 | } 766 | elseif (is_object($this->bytes)) { 767 | $type = 'object'; 768 | } 769 | elseif (is_resource($this->bytes)) { 770 | $type = 'resource'; 771 | } 772 | 773 | throw new \Exception('Expected ' . __CLASS__ . '::$bytes value to be of type string, but found ' . $type); 774 | } 775 | 776 | if (strlen($this->bytes) !== 16) { 777 | throw new \Exception('Expected ' . __CLASS__ . '::$bytes value to be exactly 16 bytes long, but found ' . strlen($this->bytes)); 778 | } 779 | 780 | return $this->bytes; 781 | } 782 | 783 | /** 784 | * Convert the UUID to its hexadecimal representation. 785 | * 786 | * The hexadecimal representation of a UUID are 32 hexadecimal digits. The 787 | * hexadecimal digits `a` through `f` are always formatted as lower case 788 | * characters, in accordance with RFC 4122. 789 | * 790 | * ## Examples 791 | * ``` 792 | * toHex() === '6ba7b8109dad11d180b400c04fd430c8'); 795 | * 796 | * ?> 797 | * ``` 798 | * 799 | * @since 7.2 800 | * @see https://php.net/uuid.toHex 801 | * @return string Hexadecimal representation of the UUID. 802 | * @throws \Exception if this instance does not encapsulate a valid UUID. 803 | */ 804 | public function toHex() { 805 | return bin2hex($this->toBinary()); 806 | } 807 | 808 | /** 809 | * Convert the UUID to its string representation. 810 | * 811 | * The string representation of a UUID are 32 hexadecimal digits separated 812 | * by a hyphen into five groups of 8, 4, 4, 4, and 12 digits. The 813 | * hexadecimal digits `a` through `f` are always formatted as lower case 814 | * characters, in accordance with RFC 4122. 815 | * 816 | * ## Examples 817 | * ``` 818 | * toString() === '6ba7b810-9dad-11d1-80b4-00c04fd430c8'); 821 | * 822 | * ?> 823 | * ``` 824 | * 825 | * @since 7.2 826 | * @see https://php.net/uuid.toString 827 | * @see https://tools.ietf.org/html/rfc4122#page-4 828 | * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Format 829 | * @return string String representation of the UUID. 830 | * @throws \Exception if this instance does not encapsulate a valid UUID. 831 | */ 832 | public function toString() { 833 | $hex = $this->toHex(); 834 | 835 | return substr($hex, 0, 8) . '-' . 836 | substr($hex, 8, 4) . '-' . 837 | substr($hex, 12, 4) . '-' . 838 | substr($hex, 16, 4) . '-' . 839 | substr($hex, 20); 840 | } 841 | 842 | /** 843 | * Callback for cloning of objects. This method is private and effectively 844 | * disables cloning of this object, since it makes no sense to clone 845 | * immutable objects. 846 | * 847 | * @return void 848 | * @throws \Exception upon every invocation. 849 | */ 850 | private function __clone() { 851 | throw new Exception('Cannot clone immutable ' . __CLASS__ . ' object'); 852 | } 853 | } 854 | } 855 | --------------------------------------------------------------------------------