PageRenderTime 42ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/core/src/main/php/util/UUID.class.php

http://github.com/xp-framework/xp-framework
PHP | 325 lines | 160 code | 26 blank | 139 comment | 8 complexity | fac4c0d5570b2fead02d9c7cfd362863 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /* This class is part of the XP framework
  3. *
  4. * $Id$
  5. */
  6. uses('lang.types.Bytes');
  7. /**
  8. * Encapsulates UUIDs (Universally Unique IDentifiers), also known as
  9. * GUIDs (Globally Unique IDentifiers).
  10. *
  11. * <quote>
  12. * A UUID is an identifier that is unique across both space and time,
  13. * with respect to the space of all UUIDs. To be precise, the UUID
  14. * consists of a finite bit space. Thus the time value used for
  15. * constructing a UUID is limited and will roll over in the future
  16. * (approximately at A.D. 3400, based on the specified algorithm).
  17. * </quote>
  18. *
  19. * Creating UUIDs
  20. * --------------
  21. * <code>
  22. * UUID::timeUUID(); // Creates a new v1, time based, UUID
  23. * UUID::randomUUID(); // Creates a new v4, pseudo randomly generated, UUID
  24. * </code>
  25. *
  26. * Creating name-based UUIDs
  27. * -------------------------
  28. * <code>
  29. * UUID::md5UUID(UUID::$NS_DNS, 'example.com');
  30. * UUID::sha1UUID(UUID::$NS_DNS, 'example.com');
  31. * </code>
  32. *
  33. * Instanciation
  34. * -------------
  35. * UUIDs can be created from various input sources. The following are
  36. * all equivalent:
  37. *
  38. * <code>
  39. * new UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8');
  40. * new UUID('{6ba7b811-9dad-11d1-80b4-00c04fd430c8}');
  41. * new UUID('urn:uuid:6ba7b811-9dad-11d1-80b4-00c04fd430c8');
  42. * new UUID(new Bytes("k\xa7\xb8\x11\x9d\xad\x11\xd1\x80\xb4\x00\xc0O\xd40\xc8"));
  43. * </code>
  44. *
  45. * Output
  46. * -----
  47. * <code>
  48. * $uuid->hashCode(); // '6ba7b811-9dad-11d1-80b4-00c04fd430c8'
  49. * $uuid->toString(); // '{6ba7b811-9dad-11d1-80b4-00c04fd430c8}'
  50. * $uuid->getUrn(); // 'urn:uuid:6ba7b811-9dad-11d1-80b4-00c04fd430c8'
  51. * $uuid->getBytes(); // new Bytes("k\xa7\xb8\x11\x9d\xad\x11\xd1\x80\xb4\x00\xc0O\xd40\xc8")
  52. * </code>
  53. *
  54. * @test xp://net.xp_framework.unittest.util.UUIDTest
  55. * @see rfc://4122
  56. * @see http://www.ietf.org/internet-drafts/draft-mealling-uuid-urn-00.txt
  57. */
  58. class UUID extends Object {
  59. const FORMAT = '%04x%04x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x';
  60. public static
  61. $NS_DNS = NULL,
  62. $NS_URL = NULL,
  63. $NS_OID = NULL,
  64. $NS_X500 = NULL;
  65. public
  66. $time_low = 0,
  67. $time_mid = 0,
  68. $time_hi_and_version = 0,
  69. $clock_seq_low = 0,
  70. $clock_seq_hi_and_reserved = 0,
  71. $node = array();
  72. protected
  73. $version = NULL;
  74. static function __static() {
  75. self::$NS_DNS= new self('6ba7b810-9dad-11d1-80b4-00c04fd430c8');
  76. self::$NS_URL= new self('6ba7b811-9dad-11d1-80b4-00c04fd430c8');
  77. self::$NS_OID= new self('6ba7b812-9dad-11d1-80b4-00c04fd430c8');
  78. self::$NS_X500= new self('6ba7b814-9dad-11d1-80b4-00c04fd430c8');
  79. }
  80. /**
  81. * Create a UUID
  82. *
  83. * @param var arg
  84. * @throws lang.FormatException in case str is not a valid UUID string
  85. */
  86. public function __construct($arg) {
  87. if ($arg instanceof Bytes) {
  88. $this->populate(implode('-', unpack('H8a/H4b/H4c/H4d/H12e', $arg)));
  89. } else if (is_array($arg)) {
  90. $this->version= $arg[0];
  91. $this->time_low= $arg[1];
  92. $this->time_mid= $arg[2];
  93. $this->time_hi_and_version= $arg[3] | ($arg[0] << 12);
  94. $this->clock_seq_low= $arg[4] & 0xFF;
  95. $this->clock_seq_hi_and_reserved= (($arg[4] & 0x3F00) >> 8) | 0x80;
  96. $this->node= $arg[5];
  97. } else if (0 === strncasecmp($arg, 'urn:uuid', 8)) {
  98. $this->populate(substr($arg, 9));
  99. } else {
  100. $this->populate(trim($arg, '{}'));
  101. }
  102. }
  103. /**
  104. * Populate instance members from a given string
  105. *
  106. * @param string
  107. */
  108. private function populate($str) {
  109. // Parse. Use %04x%04x for "time_low" instead of "%08x" to overcome
  110. // sscanf()'s 32 bit limitation and do the multiplication manually.
  111. if (12 !== sscanf(
  112. $str,
  113. self::FORMAT,
  114. $l[0], $l[1],
  115. $this->time_mid,
  116. $this->time_hi_and_version,
  117. $this->clock_seq_hi_and_reserved,
  118. $this->clock_seq_low,
  119. $this->node[0],
  120. $this->node[1],
  121. $this->node[2],
  122. $this->node[3],
  123. $this->node[4],
  124. $this->node[5]
  125. )) {
  126. throw new FormatException($str.' is not a valid UUID string');
  127. }
  128. $this->time_low= $l[0] * 0x10000 + $l[1];
  129. // Detect version
  130. $this->version= ($this->time_hi_and_version >> 12) & 0xF;
  131. }
  132. /**
  133. * Create a version 1 UUID based upon time stamp and node identifier
  134. *
  135. * @return util.UUID
  136. * @see http://www.ietf.org/internet-drafts/draft-mealling-uuid-urn-00.txt section 4.1.4
  137. */
  138. public static function timeUUID() {
  139. // Get timestamp and convert it to UTC (based Oct 15, 1582).
  140. list($usec, $sec) = explode(' ', microtime());
  141. $t= ($sec * 10000000) + ($usec * 10) + 122192928000000000;
  142. $clock_seq= mt_rand();
  143. $h= md5(php_uname());
  144. return new self(array(
  145. 1,
  146. ($t & 0xFFFFFFFF),
  147. (($t >> 32) & 0xFFFF),
  148. (($t >> 48) & 0x0FFF),
  149. $clock_seq,
  150. array(
  151. hexdec(substr($h, 0x0, 2)),
  152. hexdec(substr($h, 0x2, 2)),
  153. hexdec(substr($h, 0x4, 2)),
  154. hexdec(substr($h, 0x6, 2)),
  155. hexdec(substr($h, 0x8, 2)),
  156. hexdec(substr($h, 0xB, 2))
  157. )
  158. ));
  159. }
  160. /**
  161. * Create a version 3 UUID based upon a name and a given namespace
  162. *
  163. * @param util.UUID namespace
  164. * @param string name
  165. * @return util.UUID
  166. */
  167. public static function md5UUID(self $namespace, $name) {
  168. $bytes= md5($namespace->getBytes().iconv(xp::ENCODING, 'utf-8', $name));
  169. return new self(array(
  170. 3,
  171. hexdec(substr($bytes, 0, 8)),
  172. hexdec(substr($bytes, 8, 4)),
  173. hexdec(substr($bytes, 12, 4)) & 0x0fff,
  174. hexdec(substr($bytes, 16, 4)) & 0x3fff | 0x8000,
  175. array_map('hexdec', str_split(substr($bytes, 20, 12), 2))
  176. ));
  177. }
  178. /**
  179. * Create a version 5 UUID based upon a name and a given namespace
  180. *
  181. * @param util.UUID namespace
  182. * @param string name
  183. * @return util.UUID
  184. */
  185. public static function sha1UUID(self $namespace, $name) {
  186. $bytes= sha1($namespace->getBytes().iconv(xp::ENCODING, 'utf-8', $name));
  187. return new self(array(
  188. 5,
  189. hexdec(substr($bytes, 0, 8)),
  190. hexdec(substr($bytes, 8, 4)),
  191. hexdec(substr($bytes, 12, 4)) & 0x0fff,
  192. hexdec(substr($bytes, 16, 4)) & 0x3fff | 0x8000,
  193. array_map('hexdec', str_split(substr($bytes, 20, 12), 2))
  194. ));
  195. }
  196. /**
  197. * Create a version 4 UUID based upon random bits
  198. *
  199. * @return util.UUID
  200. */
  201. public static function randomUUID() {
  202. return new self(array(
  203. 4,
  204. mt_rand(0, 0xffff) * 0x10000 + mt_rand(0, 0xffff),
  205. mt_rand(0, 0xffff),
  206. mt_rand(0, 0x0fff),
  207. mt_rand(0, 0x3fff) | 0x8000,
  208. sscanf(
  209. sprintf('%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)),
  210. '%02x%02x%02x%02x%02x%02x'
  211. )
  212. ));
  213. }
  214. /**
  215. * Returns version
  216. *
  217. * @return int
  218. */
  219. public function version() {
  220. return $this->version;
  221. }
  222. /**
  223. * Get bytes
  224. *
  225. * @return lang.types.Bytes
  226. */
  227. public function getBytes() {
  228. return new Bytes(pack('H32', str_replace('-', '', $this->hashCode())));
  229. }
  230. /**
  231. * Creates a urn representation
  232. *
  233. * @return string
  234. */
  235. public function getUrn() {
  236. return 'urn:uuid:'.$this->hashCode();
  237. }
  238. /**
  239. * Creates a string representation.
  240. *
  241. * Example: <tt>{f81d4fae-7dec-11d0-a765-00a0c91e6bf6}</tt>
  242. *
  243. * @return string
  244. */
  245. public function toString() {
  246. return '{'.$this->hashCode().'}';
  247. }
  248. /**
  249. * Returns a hashcode
  250. *
  251. * Example: <tt>f81d4fae-7dec-11d0-a765-00a0c91e6bf6</tt>
  252. *
  253. * @return string
  254. */
  255. public function hashCode() {
  256. $r= (int)($this->time_low / 0x10000);
  257. return sprintf(
  258. self::FORMAT,
  259. $r, $this->time_low - $r * 0x10000,
  260. $this->time_mid,
  261. $this->time_hi_and_version,
  262. $this->clock_seq_hi_and_reserved,
  263. $this->clock_seq_low,
  264. $this->node[0],
  265. $this->node[1],
  266. $this->node[2],
  267. $this->node[3],
  268. $this->node[4],
  269. $this->node[5]
  270. );
  271. }
  272. /**
  273. * Returns whether another instance is equal to this
  274. *
  275. * @param var cmp
  276. * @return bool
  277. */
  278. public function equals($cmp) {
  279. return $cmp instanceof self && $cmp->hashCode() === $this->hashCode();
  280. }
  281. /**
  282. * Serialization callback
  283. *
  284. * @return string[]
  285. */
  286. public function __sleep() {
  287. $this->value= $this->hashCode(); // Invent "value" member
  288. return array('value');
  289. }
  290. /**
  291. * Deserialization callback
  292. *
  293. */
  294. public function __wakeup() {
  295. $this->populate($this->value);
  296. unset($this->value);
  297. }
  298. }
  299. ?>