123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- <?php
- /**
- * Author: Tony Chen
- */
- namespace app\common\until;
- /**
- * @file
- * Provide basic v3, v4, and v5 UUID functionality for PHP.
- *
- * Created by Matt Farina on 2011-11-29.
- */
- use \Exception;
- /**
- * This class creates RFC 4122 compliant Universally Unique Identifiers (UUID).
- *
- * The generated UUIDs can be version 3, 4, or 5. Functionality is provided to
- * validate UUIDs as well as validate name based UUIDs.
- *
- * @see https://tools.ietf.org/html/rfc4122
- * @see https://en.wikipedia.org/wiki/UUID
- * @see https://github.com/lootils/uuid
- *
- * @author Matt Farina <matt@mattfarina.com>
- * @copyright MIT License
- * @version 1.0
- */
- class Uuid {
- /**
- * @var string DNS namespace from RFC 4122 appendix C.
- */
- const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
- /**
- * @var string URL namespace from RFC 4122 appendix C.
- */
- const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
- /**
- * @var string ISO OID namespace from RFC 4122 appendix C.
- */
- const OID = '6ba7b812-9dad-11d1-80b4-00c04fd430c8';
- /**
- * @var string X.500 namespace from RFC 4122 appendix C.
- */
- const X500 = '6ba7b814-9dad-11d1-80b4-00c04fd430c8';
- /**
- * @var string NULL UUID string from RFC 4122.
- */
- const NIL = '00000000-0000-0000-0000-000000000000';
- /**
- * @var UUID Version 3.
- */
- const V3 = '3';
- /**
- * @var UUID Version 4.
- */
- const V4 = '4';
- /**
- * @var UUID Version 5.
- */
- const V5 = '5';
- /**
- * @var string the first 32 bits of the UUID.
- */
- protected $time_low = NULL;
- /**
- * @var string the next 16 bits of the UUID.
- */
- protected $time_mid = NULL;
- /**
- * @var string the next 16 bits of the UUID.
- */
- protected $time_hi_version = NULL;
- /**
- * @var string the next 8 bits of the UUID.
- */
- protected $clock_seq_hi_variant = NULL;
- /**
- * @var string the next 8 bits of the UUID.
- */
- protected $clock_seq_low = NULL;
- /**
- * @var string the last 48 bits of the UUID.
- */
- protected $node = NULL;
- /**
- * @var string the version of the UUID.
- */
- protected $version = NULL;
- /**
- * @var string the namespace used when the UUID was generated.
- */
- protected $namespace = NULL;
- /**
- * @var string the name used when the UUID was generated.
- */
- protected $name = NULL;
- /**
- * Return a list of UUID fields. These can be used with the getField() method.
- *
- * @return array
- * A list of fields on the UUID. These can be used with the getField method.
- */
- public function listFields() {
- return [
- 'time_low',
- 'time_mid',
- 'time_hi_version',
- 'clock_seq_hi_variant',
- 'clock_seq_low',
- 'node',
- ];
- }
- /**
- * Get the value of a UUID field.
- *
- * @param string $name
- * The name of a field to get the vurrent value. The values are returned in
- * hex format.
- */
- public function getField($name) {
- if (!in_array($name, $this->listFields())) {
- throw new Exception('A field value was requested for an invalid field name.');
- }
- return $this->$name;
- }
- /**
- * Get the UUID version for this UUID.
- *
- * @return string
- * The UUID Version number.
- */
- public function getVersion() {
- return $this->version;
- }
- /**
- * Get the UUID namespace for this UUID.
- *
- * @return string
- * The UUID namespace if known.
- */
- public function getNamespace() {
- return $this->namespace;
- }
- /**
- * Get the UUID name for this UUID.
- *
- * @return string
- * The UUID name if known.
- */
- public function getName() {
- return $this->name;
- }
- /**
- * Set the version of the UUID.
- *
- * @param string $version
- * 3, 4, or 5 which are the possible suppored versions.
- */
- protected function setVersion($version) {
- if ($version == self::V3 || $version == self::V4 || $version == self::V5) {
- $this->version = $version;
- } else {
- throw new Exception('An invalid UUID version was specified.');
- }
- }
- /**
- * Get the URN for a URI.
- */
- public function getURN() {
- return 'urn:uuid:' . $this;
- }
- /**
- * Get the UUID.
- *
- * @return string
- * A string containing a properly formatted UUID.
- */
- public function getUuid() {
- return $this->time_low . '-' . $this->time_mid . '-' . $this->time_hi_version . '-' . $this->clock_seq_hi_variant . $this->clock_seq_low . '-' . $this->node;
- }
- /**
- * Construct a new UUID object.
- *
- * There are a number of ways this UUID object could be generated.
- * - By the hex UUID: '{12345678-1234-5678-1234-567812345678}'
- * - Via the fields passed in as an array. For a list of fields see the method
- * listFields().
- * - From a URN: 'urn:uuid:12345678-1234-5678-1234-567812345678'
- *
- * @param mixed $uuid
- * The UUID in a format that can be parsed.
- * @param string $version const
- * (optional but recommended) The UUID version. If none specified we simply don't know it.
- * @param string $namespace
- * (optional) The namespace used when the UUID was generated. This is useful with v3 and v5 UUIDs.
- * @param string $name
- * (optional) The name used when the UUID was generated. This is useful with v3 and v5 UUIDs.
- */
- public function __construct($uuid, $version = NULL, $namespace = NULL, $name = NULL) {
- $this->parse($uuid);
- if (!is_null($version)) {
- $this->setVersion($version);
- }
- $this->namespace = $namespace;
- $this->name = $name;
- }
- /**
- * Parse the UUID from the available formats.
- *
- * @todo this should be written prettier. For realz.
- */
- protected function parse($uuid) {
- // The UUID as a standard string was passed in.
- if (is_string($uuid)) {
- if (substr($uuid, 0, 1) === '{' && substr($uuid, -1, 1) === '}') {
- $string = substr($uuid, 1, strlen($uuid) - 2);
- $this->parseStringToParts($string);
- } // The case where a URL was supplied.
- else if (substr($uuid, 0, 9) === 'urn:uuid:') {
- $string = substr($uuid, 9);
- $this->parseStringToParts($string);
- } else {
- throw new Exception('The UUID string supplied could not be parsed.');
- }
- } else if (is_array($uuid)) {
- if (count($uuid) != 6) {
- throw new Exception('The UUID array supplied could not be parsed.');
- }
- // For the case where a UUID is passed in via the format:
- // array('35e872b4', '190a', '5faa', 'a0', 'f6', '09da0d4f9c01');
- if (isset($uuid[0]) && !empty($uuid[0])) {
- $this->time_low = $uuid[0];
- $this->time_mid = $uuid[1];
- $this->time_hi_version = $uuid[2];
- $this->clock_seq_hi_variant = $uuid[3];
- $this->clock_seq_low = $uuid[4];
- $this->node = $uuid[5];
- }
- // For the case where the UUID is passed in via the format:
- // array(
- // 'time_low' => '35e872b4',
- // 'time_mid' => '190a',
- // 'time_hi_version' => '5faa',
- // 'clock_seq_hi_variant' => 'a0',
- // 'clock_seq_low' => 'f6',
- // 'node' => '09da0d4f9c01',
- // );
- else if (isset($uuid['time_low']) && !empty($uuid['time_low'])) {
- $this->time_low = $uuid['time_low'];
- $this->time_mid = $uuid['time_mid'];
- $this->time_hi_version = $uuid['time_hi_version'];
- $this->clock_seq_hi_variant = $uuid['clock_seq_hi_variant'];
- $this->clock_seq_low = $uuid['clock_seq_low'];
- $this->node = $uuid['node'];
- } else {
- throw new Exception('The UUID array supplied could not be parsed.');
- }
- } else {
- throw new Exception('The UUID supplied could not be parsed.');
- }
- }
- /**
- * Parse a string in the form of 12345678-1234-5678-1234-567812345678.
- */
- protected function parseStringToParts($string) {
- $parts = explode('-', $string);
- if (count($parts) != 5) {
- throw new Exception('The UUID string supplied could not be parsed.');
- }
- foreach ($parts as $id => $part) {
- switch ($id) {
- case 0:
- $this->time_low = $part;
- break;
- case 1:
- $this->time_mid = $part;
- break;
- case 2:
- $this->time_hi_version = $part;
- break;
- case 3:
- $this->clock_seq_hi_variant = substr($part, 0, 2);
- $this->clock_seq_low = substr($part, 2);
- break;
- case 4:
- $this->node = $part;
- break;
- }
- }
- }
- /**
- * Display the UUID as a string in the format 6ba7b810-9dad-11d1-80b4-00c04fd430c8.
- */
- public function __toString() {
- return $this->getUuid();
- }
- /**
- * Validate if a UUID has a valid format.
- *
- * @param string $uuid
- * The string to validate if it is in the proper UUID format.
- *
- * @return bool TRUE if the format is valid and FALSE otherwise.
- */
- public static function isValid($uuid) {
- return (preg_match('/^\{?[0-9a-f]{8}\-?[0-9a-f]{4}\-?[0-9a-f]{4}\-?[0-9a-f]{4}\-?[0-9a-f]{12}\}?$/i', $uuid) === 1);
- }
- /**
- * Version 5 UUIDs are based on a namespace and name.
- *
- * If you have the same namespace and name you can recreate the namespace.
- * V5 UUIDs are prefered over v3. V5 is based on sha1 while v3 is based on md5.
- *
- * @see https://en.wikipedia.org/wiki/UUID#Version_5_.28SHA-1_hash.29
- *
- * @param string $namespace
- * The UUID of the given namespace.
- * @param string $name
- * The name we are creating the UUID for.
- */
- public static function createV5($namespace, $name) {
- // If the namespace is not a valid UUID we throw an error.
- if (!self::isValid($namespace)) {
- throw new Exception('The UUID provided for the namespace is not valid.');
- }
- $bin = self::bin($namespace);
- $hash = sha1($bin . $name);
- return new self (sprintf('{%08s-%04s-%04x-%04x-%12s}',
- // 32 bits for "time_low"
- substr($hash, 0, 8),
- // 16 bits for "time_mid"
- substr($hash, 8, 4),
- // 16 bits for "time_hi_and_version",
- // four most significant bits holds version number 5
- (hexdec(substr($hash, 12, 4)) & 0x0fff) | 0x5000,
- // 16 bits, 8 bits for "clk_seq_hi_res",
- // 8 bits for "clk_seq_low",
- // two most significant bits holds zero and one for variant DCE1.1
- (hexdec(substr($hash, 16, 4)) & 0x3fff) | 0x8000,
- // 48 bits for "node"
- substr($hash, 20, 12)
- ), self::V5, $namespace, $name);
- }
- /**
- * Version 4 UUIDs are random.
- *
- * @see https://en.wikipedia.org/wiki/UUID#Version_4_.28random.29
- *
- * @return self
- * A properly formatted v4 UUID.
- */
- public static function createV4() {
- return new self (sprintf('{%04x%04x-%04x-%04x-%04x-%04x%04x%04x}',
- // 32 bits for "time_low"
- mt_rand(0, 65535), mt_rand(0, 65535),
- // 16 bits for "time_mid"
- mt_rand(0, 65535),
- // 12 bits before the 0100 of (version) 4 for "time_hi_and_version"
- mt_rand(0, 4095) | 0x4000,
- // 16 bits, 8 bits for "clk_seq_hi_res",
- // 8 bits for "clk_seq_low",
- // two most significant bits holds zero and one for variant DCE1.1
- mt_rand(0, 0x3fff) | 0x8000,
- // 48 bits for "node"
- mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535)
- ));
- }
- /**
- * Version 3 UUID are based on namespace and name utilizing a md5 hash.
- *
- * If you are considering using v3 consider using v5 instead as that is what
- * is recommended.
- *
- * @see https://en.wikipedia.org/wiki/UUID#Version_3_.28MD5_hash.29
- */
- public static function createV3($namespace, $name) {
- // If the namespace is not a valid UUID we throw an error.
- if (!self::isValid($namespace)) {
- throw new Exception('The UUID provided for the namespace is not valid.');
- }
- $bin = self::bin($namespace);
- $hash = md5($bin . $name);
- return new self (sprintf('{%08s-%04s-%04x-%04x-%12s}',
- // 32 bits for "time_low"
- substr($hash, 0, 8),
- // 16 bits for "time_mid"
- substr($hash, 8, 4),
- // 16 bits for "time_hi_and_version",
- // four most significant bits holds version number 3
- (hexdec(substr($hash, 12, 4)) & 0x0fff) | 0x3000,
- // 16 bits, 8 bits for "clk_seq_hi_res",
- // 8 bits for "clk_seq_low",
- // two most significant bits holds zero and one for variant DCE1.1
- (hexdec(substr($hash, 16, 4)) & 0x3fff) | 0x8000,
- // 48 bits for "node"
- substr($hash, 20, 12)
- ), self::V3, $namespace, $name);
- }
- /**
- * Utility function to convert hex into bin for a UUID.
- *
- * @param string $uuid
- * A UUID to convert into binary format.
- *
- * @return string
- * A UUID in binary format.
- */
- public static function bin($uuid) {
- if (!self::isValid($uuid)) {
- throw new Exception('The UUID provided for the namespace is not valid.');
- }
- // Get hexadecimal components of namespace
- $hex = str_replace(['-', '{', '}'], '', $uuid);
- $bin = '';
- // Convert to bits
- for ($i = 0; $i < strlen($hex); $i += 2) {
- $bin .= chr(hexdec($hex[$i] . $hex[$i + 1]));
- }
- return $bin;
- }
- }
|