UuidTest.php 72 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947
  1. <?php
  2. declare(strict_types=1);
  3. namespace Ramsey\Uuid\Test;
  4. use BadMethodCallException;
  5. use Brick\Math\BigDecimal;
  6. use Brick\Math\RoundingMode;
  7. use DateTimeImmutable;
  8. use DateTimeInterface;
  9. use Mockery;
  10. use Mockery\MockInterface;
  11. use PHPUnit\Framework\MockObject\MockObject;
  12. use Ramsey\Uuid\Builder\DefaultUuidBuilder;
  13. use Ramsey\Uuid\Codec\StringCodec;
  14. use Ramsey\Uuid\Codec\TimestampFirstCombCodec;
  15. use Ramsey\Uuid\Codec\TimestampLastCombCodec;
  16. use Ramsey\Uuid\Converter\Number\BigNumberConverter;
  17. use Ramsey\Uuid\Converter\TimeConverterInterface;
  18. use Ramsey\Uuid\Exception\DateTimeException;
  19. use Ramsey\Uuid\Exception\InvalidArgumentException;
  20. use Ramsey\Uuid\Exception\InvalidUuidStringException;
  21. use Ramsey\Uuid\Exception\UnsupportedOperationException;
  22. use Ramsey\Uuid\FeatureSet;
  23. use Ramsey\Uuid\Generator\CombGenerator;
  24. use Ramsey\Uuid\Generator\RandomGeneratorFactory;
  25. use Ramsey\Uuid\Generator\RandomGeneratorInterface;
  26. use Ramsey\Uuid\Guid\Guid;
  27. use Ramsey\Uuid\Lazy\LazyUuidFromString;
  28. use Ramsey\Uuid\Provider\Node\RandomNodeProvider;
  29. use Ramsey\Uuid\Provider\Time\FixedTimeProvider;
  30. use Ramsey\Uuid\Rfc4122\Fields;
  31. use Ramsey\Uuid\Rfc4122\FieldsInterface;
  32. use Ramsey\Uuid\Rfc4122\UuidV1;
  33. use Ramsey\Uuid\Type\Hexadecimal;
  34. use Ramsey\Uuid\Type\Time;
  35. use Ramsey\Uuid\Uuid;
  36. use Ramsey\Uuid\UuidFactory;
  37. use Ramsey\Uuid\UuidFactoryInterface;
  38. use Ramsey\Uuid\UuidInterface;
  39. use Ramsey\Uuid\Validator\GenericValidator;
  40. use Ramsey\Uuid\Validator\ValidatorInterface;
  41. use Stringable;
  42. use stdClass;
  43. use function base64_decode;
  44. use function base64_encode;
  45. use function gmdate;
  46. use function hex2bin;
  47. use function json_encode;
  48. use function serialize;
  49. use function str_pad;
  50. use function strlen;
  51. use function strtotime;
  52. use function strtoupper;
  53. use function substr;
  54. use function uniqid;
  55. use function unserialize;
  56. use function usleep;
  57. class UuidTest extends TestCase
  58. {
  59. protected function setUp(): void
  60. {
  61. Uuid::setFactory(new UuidFactory());
  62. }
  63. public function testFromString(): void
  64. {
  65. $this->assertSame(
  66. 'ff6f8cb0-c57d-11e1-9b21-0800200c9a66',
  67. Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66')
  68. ->toString()
  69. );
  70. }
  71. public function testFromHexadecimal(): void
  72. {
  73. $hex = new Hexadecimal('0x1EA78DEB37CE625E8F1A025041000001');
  74. $uuid = Uuid::fromHexadecimal($hex);
  75. $this->assertInstanceOf(Uuid::class, $uuid);
  76. $this->assertEquals('1ea78deb-37ce-625e-8f1a-025041000001', $uuid->toString());
  77. }
  78. public function testFromHexadecimalShort(): void
  79. {
  80. $hex = new Hexadecimal('0x1EA78DEB37CE625E8F1A0250410000');
  81. $this->expectException(InvalidUuidStringException::class);
  82. $this->expectExceptionMessage('Invalid UUID string:');
  83. Uuid::fromHexadecimal($hex);
  84. }
  85. public function testFromHexadecimalThrowsWhenMethodDoesNotExist(): void
  86. {
  87. $factory = Mockery::mock(UuidFactoryInterface::class);
  88. Uuid::setFactory($factory);
  89. $hex = new Hexadecimal('0x1EA78DEB37CE625E8F1A025041000001');
  90. $this->expectException(BadMethodCallException::class);
  91. $this->expectExceptionMessage('The method fromHexadecimal() does not exist on the provided factory');
  92. Uuid::fromHexadecimal($hex);
  93. }
  94. /**
  95. * Tests that UUID and GUID's have the same textual representation but not
  96. * the same binary representation.
  97. */
  98. public function testFromGuidString(): void
  99. {
  100. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  101. Uuid::setFactory(new UuidFactory(new FeatureSet(true)));
  102. $guid = Guid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  103. // UUID's and GUID's share the same textual representation.
  104. $this->assertSame($uuid->toString(), $guid->toString());
  105. // But not the same binary representation.
  106. $this->assertNotSame($uuid->getBytes(), $guid->getBytes());
  107. }
  108. public function testFromStringWithCurlyBraces(): void
  109. {
  110. $uuid = Uuid::fromString('{ff6f8cb0-c57d-11e1-9b21-0800200c9a66}');
  111. $this->assertSame('ff6f8cb0-c57d-11e1-9b21-0800200c9a66', $uuid->toString());
  112. }
  113. public function testFromStringWithInvalidUuidString(): void
  114. {
  115. $this->expectException(InvalidUuidStringException::class);
  116. $this->expectExceptionMessage('Invalid UUID string:');
  117. Uuid::fromString('ff6f8cb0-c57d-11e1-9b21');
  118. }
  119. public function testFromStringWithLeadingNewLine(): void
  120. {
  121. $this->expectException(InvalidUuidStringException::class);
  122. $this->expectExceptionMessage('Invalid UUID string:');
  123. Uuid::fromString("\nd0d5f586-21d1-470c-8088-55c8857728dc");
  124. }
  125. public function testFromStringWithTrailingNewLine(): void
  126. {
  127. $this->expectException(InvalidUuidStringException::class);
  128. $this->expectExceptionMessage('Invalid UUID string:');
  129. Uuid::fromString("d0d5f586-21d1-470c-8088-55c8857728dc\n");
  130. }
  131. public function testFromStringWithUrn(): void
  132. {
  133. $uuid = Uuid::fromString('urn:uuid:ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  134. $this->assertSame('ff6f8cb0-c57d-11e1-9b21-0800200c9a66', $uuid->toString());
  135. }
  136. public function testFromStringWithEmptyString(): void
  137. {
  138. $this->expectException(InvalidUuidStringException::class);
  139. $this->expectExceptionMessage('Invalid UUID string: ');
  140. Uuid::fromString('');
  141. }
  142. public function testFromStringUppercase(): void
  143. {
  144. $uuid = Uuid::fromString('FF6F8CB0-C57D-11E1-9B21-0800200C9A66');
  145. $this->assertSame('ff6f8cb0-c57d-11e1-9b21-0800200c9a66', $uuid->toString());
  146. }
  147. public function testFromStringLazyUuidFromUppercase(): void
  148. {
  149. $this->assertInstanceOf(LazyUuidFromString::class, Uuid::fromString('FF6F8CB0-C57D-11E1-9B21-0800200C9A66'));
  150. }
  151. public function testFromStringWithNilUuid(): void
  152. {
  153. $uuid = Uuid::fromString(Uuid::NIL);
  154. /** @var Fields $fields */
  155. $fields = $uuid->getFields();
  156. $this->assertSame('00000000-0000-0000-0000-000000000000', $uuid->toString());
  157. $this->assertTrue($fields->isNil());
  158. $this->assertFalse($fields->isMax());
  159. }
  160. public function testFromStringWithMaxUuid(): void
  161. {
  162. $uuid = Uuid::fromString(Uuid::MAX);
  163. /** @var Fields $fields */
  164. $fields = $uuid->getFields();
  165. $this->assertSame('ffffffff-ffff-ffff-ffff-ffffffffffff', $uuid->toString());
  166. $this->assertFalse($fields->isNil());
  167. $this->assertTrue($fields->isMax());
  168. }
  169. public function testGetBytes(): void
  170. {
  171. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  172. $this->assertSame(16, strlen($uuid->getBytes()));
  173. $this->assertSame('/2+MsMV9EeGbIQgAIAyaZg==', base64_encode($uuid->getBytes()));
  174. }
  175. public function testGetClockSeqHiAndReserved(): void
  176. {
  177. /** @var Uuid $uuid */
  178. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  179. $this->assertSame('155', $uuid->getClockSeqHiAndReserved());
  180. }
  181. public function testGetClockSeqHiAndReservedHex(): void
  182. {
  183. /** @var Uuid $uuid */
  184. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  185. $this->assertSame('9b', $uuid->getClockSeqHiAndReservedHex());
  186. }
  187. public function testGetClockSeqLow(): void
  188. {
  189. /** @var Uuid $uuid */
  190. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  191. $this->assertSame('33', $uuid->getClockSeqLow());
  192. }
  193. public function testGetClockSeqLowHex(): void
  194. {
  195. /** @var Uuid $uuid */
  196. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  197. $this->assertSame('21', $uuid->getClockSeqLowHex());
  198. }
  199. public function testGetClockSequence(): void
  200. {
  201. /** @var Uuid $uuid */
  202. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  203. $this->assertSame('6945', $uuid->getClockSequence());
  204. }
  205. public function testGetClockSequenceHex(): void
  206. {
  207. /** @var Uuid $uuid */
  208. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  209. $this->assertSame('1b21', $uuid->getClockSequenceHex());
  210. }
  211. public function testGetDateTime(): void
  212. {
  213. // Check a recent date
  214. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  215. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  216. $this->assertSame('2012-07-04T02:14:34+00:00', $uuid->getDateTime()->format('c'));
  217. $this->assertSame('1341368074.491000', $uuid->getDateTime()->format('U.u'));
  218. // Check an old date
  219. $uuid = Uuid::fromString('0901e600-0154-1000-9b21-0800200c9a66');
  220. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  221. $this->assertSame('1582-10-16T16:34:04+00:00', $uuid->getDateTime()->format('c'));
  222. $this->assertSame('-12219146756.000000', $uuid->getDateTime()->format('U.u'));
  223. // Check a future date
  224. $uuid = Uuid::fromString('ff9785f6-ffff-1fff-9669-00007ffffffe');
  225. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  226. $this->assertSame('5236-03-31T21:20:59+00:00', $uuid->getDateTime()->format('c'));
  227. $this->assertSame('103072857659.999999', $uuid->getDateTime()->format('U.u'));
  228. // Check the last possible time supported by v1 UUIDs
  229. // See inline comments in
  230. // {@see \Ramsey\Uuid\Test\Converter\Time\GenericTimeConverterTest::provideCalculateTime()}
  231. $uuid = Uuid::fromString('fffffffa-ffff-1fff-8b1e-acde48001122');
  232. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  233. $this->assertSame('5236-03-31T21:21:00+00:00', $uuid->getDateTime()->format('c'));
  234. $this->assertSame('103072857660.684697', $uuid->getDateTime()->format('U.u'));
  235. // Check the oldest date
  236. $uuid = Uuid::fromString('00000000-0000-1000-9669-00007ffffffe');
  237. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  238. $this->assertSame('1582-10-15T00:00:00+00:00', $uuid->getDateTime()->format('c'));
  239. $this->assertSame('-12219292800.000000', $uuid->getDateTime()->format('U.u'));
  240. // The Unix epoch
  241. $uuid = Uuid::fromString('13814000-1dd2-11b2-9669-00007ffffffe');
  242. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  243. $this->assertSame('1970-01-01T00:00:00+00:00', $uuid->getDateTime()->format('c'));
  244. $this->assertSame('0.000000', $uuid->getDateTime()->format('U.u'));
  245. }
  246. public function testGetDateTimeForUuidV6(): void
  247. {
  248. // Check a recent date
  249. $uuid = Uuid::fromString('1e1c57df-f6f8-6cb0-9b21-0800200c9a66');
  250. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  251. $this->assertSame('2012-07-04T02:14:34+00:00', $uuid->getDateTime()->format('c'));
  252. $this->assertSame('1341368074.491000', $uuid->getDateTime()->format('U.u'));
  253. // Check an old date
  254. $uuid = Uuid::fromString('00001540-901e-6600-9b21-0800200c9a66');
  255. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  256. $this->assertSame('1582-10-16T16:34:04+00:00', $uuid->getDateTime()->format('c'));
  257. $this->assertSame('-12219146756.000000', $uuid->getDateTime()->format('U.u'));
  258. // Check a future date
  259. $uuid = Uuid::fromString('ffffffff-f978-65f6-9669-00007ffffffe');
  260. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  261. $this->assertSame('5236-03-31T21:20:59+00:00', $uuid->getDateTime()->format('c'));
  262. $this->assertSame('103072857659.999999', $uuid->getDateTime()->format('U.u'));
  263. // Check the last possible time supported by UUIDs
  264. // See inline comments in
  265. // {@see \Ramsey\Uuid\Test\Converter\Time\GenericTimeConverterTest::provideCalculateTime()}
  266. $uuid = Uuid::fromString('ffffffff-ffff-6ffa-8b1e-acde48001122');
  267. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  268. $this->assertSame('5236-03-31T21:21:00+00:00', $uuid->getDateTime()->format('c'));
  269. $this->assertSame('103072857660.684697', $uuid->getDateTime()->format('U.u'));
  270. // Check the oldest date
  271. $uuid = Uuid::fromString('00000000-0000-6000-9669-00007ffffffe');
  272. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  273. $this->assertSame('1582-10-15T00:00:00+00:00', $uuid->getDateTime()->format('c'));
  274. $this->assertSame('-12219292800.000000', $uuid->getDateTime()->format('U.u'));
  275. // The Unix epoch
  276. $uuid = Uuid::fromString('1b21dd21-3814-6000-9669-00007ffffffe');
  277. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  278. $this->assertSame('1970-01-01T00:00:00+00:00', $uuid->getDateTime()->format('c'));
  279. $this->assertSame('0.000000', $uuid->getDateTime()->format('U.u'));
  280. }
  281. public function testGetDateTimeFromNonVersion1Uuid(): void
  282. {
  283. // Using a version 4 UUID to test
  284. $uuid = Uuid::fromString('bf17b594-41f2-474f-bf70-4c90220f75de');
  285. $this->expectException(UnsupportedOperationException::class);
  286. $this->expectExceptionMessage('Not a time-based UUID');
  287. $uuid->getDateTime();
  288. }
  289. public function testGetFields(): void
  290. {
  291. /** @var Uuid $uuid */
  292. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  293. $this->assertInstanceOf(FieldsInterface::class, $uuid->getFields());
  294. }
  295. public function testGetFieldsHex(): void
  296. {
  297. $fields = [
  298. 'time_low' => 'ff6f8cb0',
  299. 'time_mid' => 'c57d',
  300. 'time_hi_and_version' => '11e1',
  301. 'clock_seq_hi_and_reserved' => '9b',
  302. 'clock_seq_low' => '21',
  303. 'node' => '0800200c9a66',
  304. ];
  305. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  306. $this->assertSame($fields, $uuid->getFieldsHex());
  307. }
  308. public function testGetLeastSignificantBits(): void
  309. {
  310. /** @var Uuid $uuid */
  311. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  312. $this->assertSame('11178224546741000806', $uuid->getLeastSignificantBits());
  313. }
  314. public function testGetLeastSignificantBitsHex(): void
  315. {
  316. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  317. $this->assertSame('9b210800200c9a66', $uuid->getLeastSignificantBitsHex());
  318. }
  319. public function testGetMostSignificantBits(): void
  320. {
  321. /** @var Uuid $uuid */
  322. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  323. $this->assertSame('18406084892941947361', $uuid->getMostSignificantBits());
  324. }
  325. public function testGetMostSignificantBitsHex(): void
  326. {
  327. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  328. $this->assertSame('ff6f8cb0c57d11e1', $uuid->getMostSignificantBitsHex());
  329. }
  330. public function testGetNode(): void
  331. {
  332. /** @var Uuid $uuid */
  333. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  334. $this->assertSame('8796630719078', $uuid->getNode());
  335. }
  336. public function testGetNodeHex(): void
  337. {
  338. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  339. $this->assertSame('0800200c9a66', $uuid->getNodeHex());
  340. }
  341. public function testGetTimeHiAndVersion(): void
  342. {
  343. /** @var Uuid $uuid */
  344. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  345. $this->assertSame('4577', $uuid->getTimeHiAndVersion());
  346. }
  347. public function testGetTimeHiAndVersionHex(): void
  348. {
  349. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  350. $this->assertSame('11e1', $uuid->getTimeHiAndVersionHex());
  351. }
  352. public function testGetTimeLow(): void
  353. {
  354. /** @var Uuid $uuid */
  355. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  356. $this->assertSame('4285500592', $uuid->getTimeLow());
  357. }
  358. public function testGetTimeLowHex(): void
  359. {
  360. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  361. $this->assertSame('ff6f8cb0', $uuid->getTimeLowHex());
  362. }
  363. public function testGetTimeMid(): void
  364. {
  365. /** @var Uuid $uuid */
  366. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  367. $this->assertSame('50557', $uuid->getTimeMid());
  368. }
  369. public function testGetTimeMidHex(): void
  370. {
  371. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  372. $this->assertSame('c57d', $uuid->getTimeMidHex());
  373. }
  374. public function testGetTimestamp(): void
  375. {
  376. // Check for a recent date
  377. /** @var Uuid $uuid */
  378. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  379. $this->assertSame('135606608744910000', $uuid->getTimestamp());
  380. // Check for an old date
  381. /** @var Uuid $uuid */
  382. $uuid = Uuid::fromString('0901e600-0154-1000-9b21-0800200c9a66');
  383. $this->assertSame('1460440000000', $uuid->getTimestamp());
  384. }
  385. public function testGetTimestampHex(): void
  386. {
  387. // Check for a recent date
  388. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  389. $this->assertSame('1e1c57dff6f8cb0', $uuid->getTimestampHex());
  390. // Check for an old date
  391. $uuid = Uuid::fromString('0901e600-0154-1000-9b21-0800200c9a66');
  392. $this->assertSame('00001540901e600', $uuid->getTimestampHex());
  393. }
  394. public function testGetTimestampFromNonVersion1Uuid(): void
  395. {
  396. // Using a version 4 UUID to test
  397. /** @var Uuid $uuid */
  398. $uuid = Uuid::fromString('bf17b594-41f2-474f-bf70-4c90220f75de');
  399. $this->expectException(UnsupportedOperationException::class);
  400. $this->expectExceptionMessage('Not a time-based UUID');
  401. $uuid->getTimestamp();
  402. }
  403. public function testGetTimestampHexFromNonVersion1Uuid(): void
  404. {
  405. // Using a version 4 UUID to test
  406. $uuid = Uuid::fromString('bf17b594-41f2-474f-bf70-4c90220f75de');
  407. $this->expectException(UnsupportedOperationException::class);
  408. $this->expectExceptionMessage('Not a time-based UUID');
  409. $uuid->getTimestampHex();
  410. }
  411. public function testGetUrn(): void
  412. {
  413. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  414. $this->assertSame('urn:uuid:ff6f8cb0-c57d-11e1-9b21-0800200c9a66', $uuid->getUrn());
  415. }
  416. /**
  417. * @param non-empty-string $uuid
  418. *
  419. * @dataProvider provideVariousVariantUuids
  420. */
  421. public function testGetVariantForVariousVariantUuids(string $uuid, int $variant): void
  422. {
  423. $uuid = Uuid::fromString($uuid);
  424. $this->assertSame($variant, $uuid->getVariant());
  425. }
  426. /**
  427. * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification
  428. */
  429. public function provideVariousVariantUuids(): array
  430. {
  431. return [
  432. ['ff6f8cb0-c57d-11e1-0b21-0800200c9a66', Uuid::RESERVED_NCS],
  433. ['ff6f8cb0-c57d-11e1-1b21-0800200c9a66', Uuid::RESERVED_NCS],
  434. ['ff6f8cb0-c57d-11e1-2b21-0800200c9a66', Uuid::RESERVED_NCS],
  435. ['ff6f8cb0-c57d-11e1-3b21-0800200c9a66', Uuid::RESERVED_NCS],
  436. ['ff6f8cb0-c57d-11e1-4b21-0800200c9a66', Uuid::RESERVED_NCS],
  437. ['ff6f8cb0-c57d-11e1-5b21-0800200c9a66', Uuid::RESERVED_NCS],
  438. ['ff6f8cb0-c57d-11e1-6b21-0800200c9a66', Uuid::RESERVED_NCS],
  439. ['ff6f8cb0-c57d-11e1-7b21-0800200c9a66', Uuid::RESERVED_NCS],
  440. ['ff6f8cb0-c57d-11e1-8b21-0800200c9a66', Uuid::RFC_4122],
  441. ['ff6f8cb0-c57d-11e1-9b21-0800200c9a66', Uuid::RFC_4122],
  442. ['ff6f8cb0-c57d-11e1-ab21-0800200c9a66', Uuid::RFC_4122],
  443. ['ff6f8cb0-c57d-11e1-bb21-0800200c9a66', Uuid::RFC_4122],
  444. ['ff6f8cb0-c57d-11e1-cb21-0800200c9a66', Uuid::RESERVED_MICROSOFT],
  445. ['ff6f8cb0-c57d-11e1-db21-0800200c9a66', Uuid::RESERVED_MICROSOFT],
  446. ['ff6f8cb0-c57d-11e1-eb21-0800200c9a66', Uuid::RESERVED_FUTURE],
  447. ['ff6f8cb0-c57d-11e1-fb21-0800200c9a66', Uuid::RESERVED_FUTURE],
  448. ];
  449. }
  450. public function testGetVersionForVersion1(): void
  451. {
  452. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  453. $this->assertSame(1, $uuid->getVersion());
  454. }
  455. public function testGetVersionForVersion2(): void
  456. {
  457. $uuid = Uuid::fromString('6fa459ea-ee8a-2ca4-894e-db77e160355e');
  458. $this->assertSame(2, $uuid->getVersion());
  459. }
  460. public function testGetVersionForVersion3(): void
  461. {
  462. $uuid = Uuid::fromString('6fa459ea-ee8a-3ca4-894e-db77e160355e');
  463. $this->assertSame(3, $uuid->getVersion());
  464. }
  465. public function testGetVersionForVersion4(): void
  466. {
  467. $uuid = Uuid::fromString('6fabf0bc-603a-42f2-925b-d9f779bd0032');
  468. $this->assertSame(4, $uuid->getVersion());
  469. }
  470. public function testGetVersionForVersion5(): void
  471. {
  472. $uuid = Uuid::fromString('886313e1-3b8a-5372-9b90-0c9aee199e5d');
  473. $this->assertSame(5, $uuid->getVersion());
  474. }
  475. public function testToString(): void
  476. {
  477. // Check with a recent date
  478. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  479. $this->assertSame('ff6f8cb0-c57d-11e1-9b21-0800200c9a66', $uuid->toString());
  480. $this->assertSame('ff6f8cb0-c57d-11e1-9b21-0800200c9a66', (string) $uuid);
  481. $this->assertSame(
  482. 'ff6f8cb0-c57d-11e1-9b21-0800200c9a66',
  483. (static fn (Stringable $uuid) => (string) $uuid)($uuid)
  484. );
  485. // Check with an old date
  486. $uuid = Uuid::fromString('0901e600-0154-1000-9b21-0800200c9a66');
  487. $this->assertSame('0901e600-0154-1000-9b21-0800200c9a66', $uuid->toString());
  488. $this->assertSame('0901e600-0154-1000-9b21-0800200c9a66', (string) $uuid);
  489. $this->assertSame(
  490. '0901e600-0154-1000-9b21-0800200c9a66',
  491. (static fn (Stringable $uuid) => (string) $uuid)($uuid)
  492. );
  493. }
  494. public function testUuid1(): void
  495. {
  496. $uuid = Uuid::uuid1();
  497. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  498. $this->assertSame(2, $uuid->getVariant());
  499. $this->assertSame(1, $uuid->getVersion());
  500. }
  501. public function testUuid1WithNodeAndClockSequence(): void
  502. {
  503. /** @var Uuid $uuid */
  504. $uuid = Uuid::uuid1('0800200c9a66', 0x1669);
  505. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  506. $this->assertSame(2, $uuid->getVariant());
  507. $this->assertSame(1, $uuid->getVersion());
  508. $this->assertSame('5737', $uuid->getClockSequence());
  509. $this->assertSame('8796630719078', $uuid->getNode());
  510. $this->assertSame('9669-0800200c9a66', substr($uuid->toString(), 19));
  511. }
  512. public function testUuid1WithHexadecimalObjectNodeAndClockSequence(): void
  513. {
  514. /** @var Uuid $uuid */
  515. $uuid = Uuid::uuid1(new Hexadecimal('0800200c9a66'), 0x1669);
  516. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  517. $this->assertSame(2, $uuid->getVariant());
  518. $this->assertSame(1, $uuid->getVersion());
  519. $this->assertSame('5737', $uuid->getClockSequence());
  520. $this->assertSame('8796630719078', $uuid->getNode());
  521. $this->assertSame('9669-0800200c9a66', substr($uuid->toString(), 19));
  522. }
  523. public function testUuid1WithHexadecimalNode(): void
  524. {
  525. /** @var Uuid $uuid */
  526. $uuid = Uuid::uuid1('7160355e');
  527. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  528. $this->assertSame(2, $uuid->getVariant());
  529. $this->assertSame(1, $uuid->getVersion());
  530. $this->assertSame('00007160355e', $uuid->getNodeHex());
  531. $this->assertSame('1902130526', $uuid->getNode());
  532. }
  533. public function testUuid1WithHexadecimalObjectNode(): void
  534. {
  535. /** @var Uuid $uuid */
  536. $uuid = Uuid::uuid1(new Hexadecimal('7160355e'));
  537. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  538. $this->assertSame(2, $uuid->getVariant());
  539. $this->assertSame(1, $uuid->getVersion());
  540. $this->assertSame('00007160355e', $uuid->getNodeHex());
  541. $this->assertSame('1902130526', $uuid->getNode());
  542. }
  543. public function testUuid1WithMixedCaseHexadecimalNode(): void
  544. {
  545. /** @var Uuid $uuid */
  546. $uuid = Uuid::uuid1('71B0aD5e');
  547. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  548. $this->assertSame(2, $uuid->getVariant());
  549. $this->assertSame(1, $uuid->getVersion());
  550. $this->assertSame('000071b0ad5e', $uuid->getNodeHex());
  551. $this->assertSame('1907404126', $uuid->getNode());
  552. }
  553. public function testUuid1WithOutOfBoundsNode(): void
  554. {
  555. $this->expectException(InvalidArgumentException::class);
  556. $this->expectExceptionMessage('Invalid node value');
  557. Uuid::uuid1('9223372036854775808');
  558. }
  559. public function testUuid1WithNonHexadecimalNode(): void
  560. {
  561. $this->expectException(InvalidArgumentException::class);
  562. $this->expectExceptionMessage('Invalid node value');
  563. Uuid::uuid1('db77e160355g');
  564. }
  565. public function testUuid1WithNon48bitNumber(): void
  566. {
  567. $this->expectException(InvalidArgumentException::class);
  568. $this->expectExceptionMessage('Invalid node value');
  569. Uuid::uuid1('db77e160355ef');
  570. }
  571. public function testUuid1WithRandomNode(): void
  572. {
  573. Uuid::setFactory(new UuidFactory(new FeatureSet(false, false, false, true)));
  574. $uuid = Uuid::uuid1();
  575. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  576. $this->assertSame(2, $uuid->getVariant());
  577. $this->assertSame(1, $uuid->getVersion());
  578. }
  579. public function testUuid1WithUserGeneratedRandomNode(): void
  580. {
  581. $uuid = Uuid::uuid1(new Hexadecimal((string) (new RandomNodeProvider())->getNode()));
  582. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  583. $this->assertSame(2, $uuid->getVariant());
  584. $this->assertSame(1, $uuid->getVersion());
  585. }
  586. public function testUuid6(): void
  587. {
  588. $uuid = Uuid::uuid6();
  589. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  590. $this->assertSame(2, $uuid->getVariant());
  591. $this->assertSame(6, $uuid->getVersion());
  592. }
  593. public function testUuid6WithNodeAndClockSequence(): void
  594. {
  595. $uuid = Uuid::uuid6(new Hexadecimal('0800200c9a66'), 0x1669);
  596. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  597. $this->assertSame(2, $uuid->getVariant());
  598. $this->assertSame(6, $uuid->getVersion());
  599. $this->assertSame('1669', $uuid->getClockSequenceHex());
  600. $this->assertSame('0800200c9a66', $uuid->getNodeHex());
  601. $this->assertSame('9669-0800200c9a66', substr($uuid->toString(), 19));
  602. }
  603. public function testUuid6WithHexadecimalNode(): void
  604. {
  605. $uuid = Uuid::uuid6(new Hexadecimal('7160355e'));
  606. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  607. $this->assertSame(2, $uuid->getVariant());
  608. $this->assertSame(6, $uuid->getVersion());
  609. $this->assertSame('00007160355e', $uuid->getNodeHex());
  610. }
  611. public function testUuid6WithMixedCaseHexadecimalNode(): void
  612. {
  613. $uuid = Uuid::uuid6(new Hexadecimal('71B0aD5e'));
  614. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  615. $this->assertSame(2, $uuid->getVariant());
  616. $this->assertSame(6, $uuid->getVersion());
  617. $this->assertSame('000071b0ad5e', $uuid->getNodeHex());
  618. }
  619. public function testUuid6WithOutOfBoundsNode(): void
  620. {
  621. $this->expectException(InvalidArgumentException::class);
  622. $this->expectExceptionMessage('Invalid node value');
  623. Uuid::uuid6(new Hexadecimal('9223372036854775808'));
  624. }
  625. public function testUuid6WithNon48bitNumber(): void
  626. {
  627. $this->expectException(InvalidArgumentException::class);
  628. $this->expectExceptionMessage('Invalid node value');
  629. Uuid::uuid6(new Hexadecimal('db77e160355ef'));
  630. }
  631. public function testUuid6WithRandomNode(): void
  632. {
  633. Uuid::setFactory(new UuidFactory(new FeatureSet(false, false, false, true)));
  634. $uuid = Uuid::uuid6();
  635. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  636. $this->assertSame(2, $uuid->getVariant());
  637. $this->assertSame(6, $uuid->getVersion());
  638. }
  639. public function testUuid6WithUserGeneratedRandomNode(): void
  640. {
  641. $uuid = Uuid::uuid6(new Hexadecimal((string) (new RandomNodeProvider())->getNode()));
  642. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  643. $this->assertSame(2, $uuid->getVariant());
  644. $this->assertSame(6, $uuid->getVersion());
  645. }
  646. public function testUuid7(): void
  647. {
  648. $uuid = Uuid::uuid7();
  649. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  650. $this->assertSame(2, $uuid->getVariant());
  651. $this->assertSame(7, $uuid->getVersion());
  652. }
  653. public function testUuid7ThrowsExceptionForUnsupportedFactory(): void
  654. {
  655. /** @var UuidFactoryInterface&MockInterface $factory */
  656. $factory = Mockery::mock(UuidFactoryInterface::class);
  657. Uuid::setFactory($factory);
  658. $this->expectException(UnsupportedOperationException::class);
  659. $this->expectExceptionMessage('The provided factory does not support the uuid7() method');
  660. Uuid::uuid7();
  661. }
  662. public function testUuid7WithDateTime(): void
  663. {
  664. $dateTime = new DateTimeImmutable('@281474976710.655');
  665. $uuid = Uuid::uuid7($dateTime);
  666. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  667. $this->assertSame(2, $uuid->getVariant());
  668. $this->assertSame(7, $uuid->getVersion());
  669. $this->assertSame(
  670. '10889-08-02T05:31:50.655+00:00',
  671. $uuid->getDateTime()->format(DateTimeInterface::RFC3339_EXTENDED),
  672. );
  673. }
  674. public function testUuid7SettingTheClockBackwards(): void
  675. {
  676. $dates = [
  677. new DateTimeImmutable('now'),
  678. new DateTimeImmutable('last year'),
  679. new DateTimeImmutable('1979-01-01 00:00:00.000000'),
  680. ];
  681. foreach ($dates as $dateTime) {
  682. $previous = Uuid::uuid7($dateTime);
  683. for ($i = 0; $i < 25; $i++) {
  684. $uuid = Uuid::uuid7($dateTime);
  685. $this->assertGreaterThan(0, $uuid->compareTo($previous));
  686. $this->assertSame($dateTime->format('Y-m-d H:i'), $uuid->getDateTime()->format('Y-m-d H:i'));
  687. $previous = $uuid;
  688. }
  689. }
  690. }
  691. public function testUuid7WithMinimumDateTime(): void
  692. {
  693. $dateTime = new DateTimeImmutable('1979-01-01 00:00:00.000000');
  694. $uuid = Uuid::uuid7($dateTime);
  695. $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
  696. $this->assertSame(2, $uuid->getVariant());
  697. $this->assertSame(7, $uuid->getVersion());
  698. $this->assertSame(
  699. '1979-01-01T00:00:00.000+00:00',
  700. $uuid->getDateTime()->format(DateTimeInterface::RFC3339_EXTENDED),
  701. );
  702. }
  703. public function testUuid7EachUuidIsMonotonicallyIncreasing(): void
  704. {
  705. $previous = Uuid::uuid7();
  706. for ($i = 0; $i < 25; $i++) {
  707. $uuid = Uuid::uuid7();
  708. $now = gmdate('Y-m-d H:i');
  709. $this->assertGreaterThan(0, $uuid->compareTo($previous));
  710. $this->assertSame($now, $uuid->getDateTime()->format('Y-m-d H:i'));
  711. $previous = $uuid;
  712. }
  713. }
  714. public function testUuid7EachUuidFromSameDateTimeIsMonotonicallyIncreasing(): void
  715. {
  716. $dateTime = new DateTimeImmutable();
  717. $previous = Uuid::uuid7($dateTime);
  718. for ($i = 0; $i < 25; $i++) {
  719. $uuid = Uuid::uuid7($dateTime);
  720. $this->assertGreaterThan(0, $uuid->compareTo($previous));
  721. $this->assertSame($dateTime->format('Y-m-d H:i'), $uuid->getDateTime()->format('Y-m-d H:i'));
  722. $previous = $uuid;
  723. }
  724. }
  725. public function testUuid8(): void
  726. {
  727. $uuid = Uuid::uuid8("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff");
  728. $this->assertSame(2, $uuid->getVariant());
  729. $this->assertSame(8, $uuid->getVersion());
  730. }
  731. public function testUuid8ThrowsExceptionForUnsupportedFactory(): void
  732. {
  733. /** @var UuidFactoryInterface&MockInterface $factory */
  734. $factory = Mockery::mock(UuidFactoryInterface::class);
  735. Uuid::setFactory($factory);
  736. $this->expectException(UnsupportedOperationException::class);
  737. $this->expectExceptionMessage('The provided factory does not support the uuid8() method');
  738. Uuid::uuid8("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff");
  739. }
  740. /**
  741. * Tests known version-3 UUIDs
  742. *
  743. * Taken from the Python UUID tests in
  744. * http://hg.python.org/cpython/file/2f4c4db9aee5/Lib/test/test_uuid.py
  745. *
  746. * @param non-empty-string $uuid
  747. * @param non-empty-string $ns
  748. *
  749. * @dataProvider provideUuid3WithKnownUuids
  750. */
  751. public function testUuid3WithKnownUuids(string $uuid, string $ns, string $name): void
  752. {
  753. $uobj1 = Uuid::uuid3($ns, $name);
  754. $uobj2 = Uuid::uuid3(Uuid::fromString($ns), $name);
  755. $this->assertSame(2, $uobj1->getVariant());
  756. $this->assertSame(3, $uobj1->getVersion());
  757. $this->assertSame(Uuid::fromString($uuid)->toString(), $uobj1->toString());
  758. $this->assertTrue($uobj1->equals($uobj2));
  759. }
  760. /**
  761. * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification
  762. */
  763. public function provideUuid3WithKnownUuids(): array
  764. {
  765. return [
  766. [
  767. 'uuid' => '6fa459ea-ee8a-3ca4-894e-db77e160355e',
  768. 'ns' => Uuid::NAMESPACE_DNS,
  769. 'name' => 'python.org',
  770. ],
  771. [
  772. 'uuid' => '9fe8e8c4-aaa8-32a9-a55c-4535a88b748d',
  773. 'ns' => Uuid::NAMESPACE_URL,
  774. 'name' => 'http://python.org/',
  775. ],
  776. [
  777. 'uuid' => 'dd1a1cef-13d5-368a-ad82-eca71acd4cd1',
  778. 'ns' => Uuid::NAMESPACE_OID,
  779. 'name' => '1.3.6.1',
  780. ],
  781. [
  782. 'uuid' => '658d3002-db6b-3040-a1d1-8ddd7d189a4d',
  783. 'ns' => Uuid::NAMESPACE_X500,
  784. 'name' => 'c=ca',
  785. ],
  786. ];
  787. }
  788. public function testUuid4(): void
  789. {
  790. $uuid = Uuid::uuid4();
  791. $this->assertSame(2, $uuid->getVariant());
  792. $this->assertSame(4, $uuid->getVersion());
  793. }
  794. /**
  795. * Tests that generated UUID's using timestamp last COMB are sequential
  796. */
  797. public function testUuid4TimestampLastComb(): void
  798. {
  799. $mock = $this->getMockBuilder(RandomGeneratorInterface::class)->getMock();
  800. $mock->expects($this->any())
  801. ->method('generate')
  802. ->willReturnCallback(function ($length) {
  803. // Makes first fields of UUIDs equal
  804. return hex2bin(str_pad('', $length * 2, '0'));
  805. });
  806. $factory = new UuidFactory();
  807. $generator = new CombGenerator($mock, $factory->getNumberConverter());
  808. $codec = new TimestampLastCombCodec($factory->getUuidBuilder());
  809. $factory->setRandomGenerator($generator);
  810. $factory->setCodec($codec);
  811. $previous = $factory->uuid4();
  812. for ($i = 0; $i < 1000; $i++) {
  813. usleep(100);
  814. $uuid = $factory->uuid4();
  815. $this->assertGreaterThan($previous->toString(), $uuid->toString());
  816. $previous = $uuid;
  817. }
  818. }
  819. /**
  820. * Tests that generated UUID's using timestamp first COMB are sequential
  821. */
  822. public function testUuid4TimestampFirstComb(): void
  823. {
  824. $mock = $this->getMockBuilder(RandomGeneratorInterface::class)->getMock();
  825. $mock->expects($this->any())
  826. ->method('generate')
  827. ->willReturnCallback(function ($length) {
  828. // Makes first fields of UUIDs equal
  829. return hex2bin(str_pad('', $length * 2, '0'));
  830. });
  831. $factory = new UuidFactory();
  832. $generator = new CombGenerator($mock, $factory->getNumberConverter());
  833. $codec = new TimestampFirstCombCodec($factory->getUuidBuilder());
  834. $factory->setRandomGenerator($generator);
  835. $factory->setCodec($codec);
  836. $previous = $factory->uuid4();
  837. for ($i = 0; $i < 1000; $i++) {
  838. usleep(100);
  839. $uuid = $factory->uuid4();
  840. $this->assertGreaterThan($previous->toString(), $uuid->toString());
  841. $previous = $uuid;
  842. }
  843. }
  844. /**
  845. * Test that COMB UUID's have a version 4 flag
  846. */
  847. public function testUuid4CombVersion(): void
  848. {
  849. $factory = new UuidFactory();
  850. $generator = new CombGenerator(
  851. (new RandomGeneratorFactory())->getGenerator(),
  852. $factory->getNumberConverter()
  853. );
  854. $factory->setRandomGenerator($generator);
  855. $uuid = $factory->uuid4();
  856. $this->assertSame(4, $uuid->getVersion());
  857. }
  858. /**
  859. * Tests known version-5 UUIDs
  860. *
  861. * Taken from the Python UUID tests in
  862. * http://hg.python.org/cpython/file/2f4c4db9aee5/Lib/test/test_uuid.py
  863. *
  864. * @param non-empty-string $uuid
  865. * @param non-empty-string $ns
  866. *
  867. * @dataProvider provideUuid5WithKnownUuids
  868. */
  869. public function testUuid5WithKnownUuids(string $uuid, string $ns, string $name): void
  870. {
  871. $uobj1 = Uuid::uuid5($ns, $name);
  872. $uobj2 = Uuid::uuid5(Uuid::fromString($ns), $name);
  873. $this->assertSame(2, $uobj1->getVariant());
  874. $this->assertSame(5, $uobj1->getVersion());
  875. $this->assertSame(Uuid::fromString($uuid)->toString(), $uobj1->toString());
  876. $this->assertTrue($uobj1->equals($uobj2));
  877. }
  878. /**
  879. * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification
  880. */
  881. public function provideUuid5WithKnownUuids(): array
  882. {
  883. return [
  884. [
  885. 'uuid' => '886313e1-3b8a-5372-9b90-0c9aee199e5d',
  886. 'ns' => Uuid::NAMESPACE_DNS,
  887. 'name' => 'python.org',
  888. ],
  889. [
  890. 'uuid' => '4c565f0d-3f5a-5890-b41b-20cf47701c5e',
  891. 'ns' => Uuid::NAMESPACE_URL,
  892. 'name' => 'http://python.org/',
  893. ],
  894. [
  895. 'uuid' => '1447fa61-5277-5fef-a9b3-fbc6e44f4af3',
  896. 'ns' => Uuid::NAMESPACE_OID,
  897. 'name' => '1.3.6.1',
  898. ],
  899. [
  900. 'uuid' => 'cc957dd1-a972-5349-98cd-874190002798',
  901. 'ns' => Uuid::NAMESPACE_X500,
  902. 'name' => 'c=ca',
  903. ],
  904. ];
  905. }
  906. public function testCompareTo(): void
  907. {
  908. // $uuid1 and $uuid2 are identical
  909. $uuid1 = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  910. $uuid2 = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  911. // The next three UUIDs are used for comparing msb and lsb in
  912. // the compareTo() method
  913. // msb are less than $uuid4, lsb are greater than $uuid5
  914. $uuid3 = Uuid::fromString('44cca71e-d13d-11e1-a959-c8bcc8a476f4');
  915. // msb are greater than $uuid3, lsb are equal to those in $uuid3
  916. $uuid4 = Uuid::fromString('44cca71e-d13d-11e2-a959-c8bcc8a476f4');
  917. // msb are equal to those in $uuid3, lsb are less than in $uuid3
  918. $uuid5 = Uuid::fromString('44cca71e-d13d-11e1-a959-c8bcc8a476f3');
  919. $this->assertSame(0, $uuid1->compareTo($uuid2));
  920. $this->assertSame(0, $uuid2->compareTo($uuid1));
  921. $this->assertSame(-1, $uuid3->compareTo($uuid4));
  922. $this->assertSame(1, $uuid4->compareTo($uuid3));
  923. $this->assertSame(-1, $uuid5->compareTo($uuid3));
  924. $this->assertSame(1, $uuid3->compareTo($uuid5));
  925. }
  926. public function testCompareToReturnsZeroWhenDifferentCases(): void
  927. {
  928. $uuidString = 'ff6f8cb0-c57d-11e1-9b21-0800200c9a66';
  929. // $uuid1 and $uuid2 are identical
  930. $uuid1 = Uuid::fromString($uuidString);
  931. $uuid2 = Uuid::fromString(strtoupper($uuidString));
  932. $this->assertSame(0, $uuid1->compareTo($uuid2));
  933. $this->assertSame(0, $uuid2->compareTo($uuid1));
  934. }
  935. public function testEqualsReturnsTrueWhenDifferentCases(): void
  936. {
  937. $uuidString = 'ff6f8cb0-c57d-11e1-9b21-0800200c9a66';
  938. // $uuid1 and $uuid2 are identical
  939. $uuid1 = Uuid::fromString($uuidString);
  940. $uuid2 = Uuid::fromString(strtoupper($uuidString));
  941. $this->assertTrue($uuid1->equals($uuid2));
  942. $this->assertTrue($uuid2->equals($uuid1));
  943. }
  944. public function testEquals(): void
  945. {
  946. $uuid1 = Uuid::uuid5(Uuid::NAMESPACE_DNS, 'python.org');
  947. $uuid2 = Uuid::uuid5(Uuid::NAMESPACE_DNS, 'python.org');
  948. $uuid3 = Uuid::uuid5(Uuid::NAMESPACE_DNS, 'php.net');
  949. $this->assertTrue($uuid1->equals($uuid2));
  950. $this->assertFalse($uuid1->equals($uuid3));
  951. $this->assertFalse($uuid1->equals(new stdClass()));
  952. }
  953. public function testCalculateUuidTime(): void
  954. {
  955. $timeOfDay = new FixedTimeProvider(new Time(1348845514, 277885));
  956. $featureSet = new FeatureSet();
  957. $featureSet->setTimeProvider($timeOfDay);
  958. // For usec = 277885
  959. Uuid::setFactory(new UuidFactory($featureSet));
  960. $uuidA = Uuid::uuid1(0x00007ffffffe, 0x1669);
  961. $this->assertSame('c4dbe7e2-097f-11e2-9669-00007ffffffe', (string) $uuidA);
  962. $this->assertSame('c4dbe7e2', $uuidA->getTimeLowHex());
  963. $this->assertSame('097f', $uuidA->getTimeMidHex());
  964. $this->assertSame('11e2', $uuidA->getTimeHiAndVersionHex());
  965. // For usec = 0
  966. $timeOfDay->setUsec(0);
  967. $uuidB = Uuid::uuid1(0x00007ffffffe, 0x1669);
  968. $this->assertSame('c4b18100-097f-11e2-9669-00007ffffffe', (string) $uuidB);
  969. $this->assertSame('c4b18100', $uuidB->getTimeLowHex());
  970. $this->assertSame('097f', $uuidB->getTimeMidHex());
  971. $this->assertSame('11e2', $uuidB->getTimeHiAndVersionHex());
  972. // For usec = 999999
  973. $timeOfDay->setUsec(999999);
  974. $uuidC = Uuid::uuid1(0x00007ffffffe, 0x1669);
  975. $this->assertSame('c54a1776-097f-11e2-9669-00007ffffffe', (string) $uuidC);
  976. $this->assertSame('c54a1776', $uuidC->getTimeLowHex());
  977. $this->assertSame('097f', $uuidC->getTimeMidHex());
  978. $this->assertSame('11e2', $uuidC->getTimeHiAndVersionHex());
  979. }
  980. public function testCalculateUuidTimeUpperLowerBounds(): void
  981. {
  982. // 5235-03-31T21:20:59+00:00
  983. $timeOfDay = new FixedTimeProvider(new Time('103072857659', '999999'));
  984. $featureSet = new FeatureSet();
  985. $featureSet->setTimeProvider($timeOfDay);
  986. Uuid::setFactory(new UuidFactory($featureSet));
  987. $uuidA = Uuid::uuid1(0x00007ffffffe, 0x1669);
  988. $this->assertSame('ff9785f6-ffff-1fff-9669-00007ffffffe', (string) $uuidA);
  989. $this->assertSame('ff9785f6', $uuidA->getTimeLowHex());
  990. $this->assertSame('ffff', $uuidA->getTimeMidHex());
  991. $this->assertSame('1fff', $uuidA->getTimeHiAndVersionHex());
  992. // 1582-10-15T00:00:00+00:00
  993. $timeOfDay = new FixedTimeProvider(new Time('-12219292800', '0'));
  994. $featureSet->setTimeProvider($timeOfDay);
  995. Uuid::setFactory(new UuidFactory($featureSet));
  996. $uuidB = Uuid::uuid1(0x00007ffffffe, 0x1669);
  997. $this->assertSame('00000000-0000-1000-9669-00007ffffffe', (string) $uuidB);
  998. $this->assertSame('00000000', $uuidB->getTimeLowHex());
  999. $this->assertSame('0000', $uuidB->getTimeMidHex());
  1000. $this->assertSame('1000', $uuidB->getTimeHiAndVersionHex());
  1001. }
  1002. /**
  1003. * Iterates over a 3600-second period and tests to ensure that, for each
  1004. * second in the period, the 32-bit and 64-bit versions of the UUID match
  1005. */
  1006. public function test32BitMatch64BitForOneHourPeriod(): void
  1007. {
  1008. $currentTime = strtotime('2012-12-11T00:00:00+00:00');
  1009. $endTime = $currentTime + 3600;
  1010. $timeOfDay = new FixedTimeProvider(new Time($currentTime, 0));
  1011. $smallIntFeatureSet = new FeatureSet(false, true);
  1012. $smallIntFeatureSet->setTimeProvider($timeOfDay);
  1013. $smallIntFactory = new UuidFactory($smallIntFeatureSet);
  1014. $featureSet = new FeatureSet();
  1015. $featureSet->setTimeProvider($timeOfDay);
  1016. $factory = new UuidFactory($featureSet);
  1017. while ($currentTime <= $endTime) {
  1018. foreach ([0, 50000, 250000, 500000, 750000, 999999] as $usec) {
  1019. $timeOfDay->setSec($currentTime);
  1020. $timeOfDay->setUsec($usec);
  1021. $uuid32 = $smallIntFactory->uuid1(0x00007ffffffe, 0x1669);
  1022. $uuid64 = $factory->uuid1(0x00007ffffffe, 0x1669);
  1023. $this->assertTrue(
  1024. $uuid32->equals($uuid64),
  1025. 'Breaks at ' . gmdate('r', $currentTime)
  1026. . "; 32-bit: {$uuid32->toString()}, 64-bit: {$uuid64->toString()}"
  1027. );
  1028. // Assert that the time matches
  1029. $usecAdd = BigDecimal::of($usec)->dividedBy('1000000', 14, RoundingMode::HALF_UP);
  1030. $testTime = BigDecimal::of($currentTime)->plus($usecAdd)->toScale(0, RoundingMode::DOWN);
  1031. $this->assertSame((string) $testTime, (string) $uuid64->getDateTime()->getTimestamp());
  1032. $this->assertSame((string) $testTime, (string) $uuid32->getDateTime()->getTimestamp());
  1033. }
  1034. $currentTime++;
  1035. }
  1036. }
  1037. /**
  1038. * This method should respond to the result of the factory
  1039. */
  1040. public function testIsValid(): void
  1041. {
  1042. $argument = uniqid('passed argument ');
  1043. /** @var MockObject & ValidatorInterface $validator */
  1044. $validator = $this->getMockBuilder(ValidatorInterface::class)->getMock();
  1045. $validator->expects($this->once())->method('validate')->with($argument)->willReturn(true);
  1046. /** @var UuidFactory $factory */
  1047. $factory = Uuid::getFactory();
  1048. $factory->setValidator($validator);
  1049. $this->assertTrue(Uuid::isValid($argument));
  1050. // reset the static validator
  1051. $factory->setValidator(new GenericValidator());
  1052. }
  1053. public function testUsingNilAsValidUuid(): void
  1054. {
  1055. self::assertSame(
  1056. '0cb17687-6ec7-324b-833a-f1d101a7edb7',
  1057. Uuid::uuid3(Uuid::NIL, 'randomtext')
  1058. ->toString()
  1059. );
  1060. self::assertSame(
  1061. '3b24c15b-1273-5628-ade4-fc67c6ede500',
  1062. Uuid::uuid5(Uuid::NIL, 'randomtext')
  1063. ->toString()
  1064. );
  1065. }
  1066. public function testFromBytes(): void
  1067. {
  1068. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  1069. $bytes = $uuid->getBytes();
  1070. $fromBytesUuid = Uuid::fromBytes($bytes);
  1071. $this->assertTrue($uuid->equals($fromBytesUuid));
  1072. }
  1073. public function testGuidBytesMatchesUuidWithSameString(): void
  1074. {
  1075. $uuidFactory = new UuidFactory(new FeatureSet(false));
  1076. $guidFactory = new UuidFactory(new FeatureSet(true));
  1077. $uuid = $uuidFactory->fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  1078. $bytes = $uuid->getBytes();
  1079. // Swap the order of the bytes for a GUID.
  1080. $guidBytes = $bytes[3] . $bytes[2] . $bytes[1] . $bytes[0];
  1081. $guidBytes .= $bytes[5] . $bytes[4];
  1082. $guidBytes .= $bytes[7] . $bytes[6];
  1083. $guidBytes .= substr($bytes, 8);
  1084. $guid = $guidFactory->fromBytes($guidBytes);
  1085. $this->assertSame($uuid->toString(), $guid->toString());
  1086. $this->assertTrue($uuid->equals($guid));
  1087. }
  1088. public function testGuidBytesProducesSameGuidString(): void
  1089. {
  1090. $guidFactory = new UuidFactory(new FeatureSet(true));
  1091. $guid = $guidFactory->fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  1092. $bytes = $guid->getBytes();
  1093. $parsedGuid = $guidFactory->fromBytes($bytes);
  1094. $this->assertSame($guid->toString(), $parsedGuid->toString());
  1095. $this->assertTrue($guid->equals($parsedGuid));
  1096. }
  1097. public function testFromBytesArgumentTooShort(): void
  1098. {
  1099. $this->expectException(InvalidArgumentException::class);
  1100. Uuid::fromBytes('thisisveryshort');
  1101. }
  1102. public function testFromBytesArgumentTooLong(): void
  1103. {
  1104. $this->expectException(InvalidArgumentException::class);
  1105. Uuid::fromBytes('thisisabittoolong');
  1106. }
  1107. public function testFromInteger(): void
  1108. {
  1109. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  1110. $integer = $uuid->getInteger()->toString();
  1111. $fromIntegerUuid = Uuid::fromInteger($integer);
  1112. $this->assertTrue($uuid->equals($fromIntegerUuid));
  1113. }
  1114. public function testFromDateTime(): void
  1115. {
  1116. /** @var UuidV1 $uuid */
  1117. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-8b21-0800200c9a66');
  1118. $dateTime = $uuid->getDateTime();
  1119. $fromDateTimeUuid = Uuid::fromDateTime($dateTime, new Hexadecimal('0800200c9a66'), 2849);
  1120. $this->assertTrue($uuid->equals($fromDateTimeUuid));
  1121. }
  1122. /**
  1123. * This test ensures that Ramsey\Uuid passes the same test cases
  1124. * as the Python UUID library.
  1125. *
  1126. * @param non-empty-string $string
  1127. * @param non-empty-string $curly
  1128. * @param non-empty-string $hex
  1129. * @param string[] $fields
  1130. * @param non-empty-string $urn
  1131. *
  1132. * @dataProvider providePythonTests
  1133. */
  1134. public function testUuidPassesPythonTests(
  1135. string $string,
  1136. string $curly,
  1137. string $hex,
  1138. string $bytes,
  1139. string $int,
  1140. array $fields,
  1141. string $urn,
  1142. string $time,
  1143. string $clockSeq,
  1144. int $variant,
  1145. ?int $version
  1146. ): void {
  1147. $uuids = [
  1148. Uuid::fromString($string),
  1149. Uuid::fromString($curly),
  1150. Uuid::fromString($hex),
  1151. Uuid::fromBytes(base64_decode($bytes)),
  1152. Uuid::fromString($urn),
  1153. Uuid::fromInteger($int),
  1154. ];
  1155. /** @var UuidInterface $uuid */
  1156. foreach ($uuids as $uuid) {
  1157. $this->assertSame($string, $uuid->toString());
  1158. $this->assertSame($hex, $uuid->getHex()->toString());
  1159. $this->assertSame(base64_decode($bytes), $uuid->getBytes());
  1160. $this->assertSame($int, $uuid->getInteger()->toString());
  1161. $this->assertSame($fields, $uuid->getFieldsHex());
  1162. $this->assertSame($fields['time_low'], $uuid->getTimeLowHex());
  1163. $this->assertSame($fields['time_mid'], $uuid->getTimeMidHex());
  1164. $this->assertSame($fields['time_hi_and_version'], $uuid->getTimeHiAndVersionHex());
  1165. $this->assertSame($fields['clock_seq_hi_and_reserved'], $uuid->getClockSeqHiAndReservedHex());
  1166. $this->assertSame($fields['clock_seq_low'], $uuid->getClockSeqLowHex());
  1167. $this->assertSame($fields['node'], $uuid->getNodeHex());
  1168. $this->assertSame($urn, $uuid->getUrn());
  1169. if ($uuid->getVersion() === 1) {
  1170. $this->assertSame($time, $uuid->getTimestampHex());
  1171. }
  1172. $this->assertSame($clockSeq, $uuid->getClockSequenceHex());
  1173. $this->assertSame($variant, $uuid->getVariant());
  1174. $this->assertSame($version, $uuid->getVersion());
  1175. }
  1176. }
  1177. /**
  1178. * Taken from the Python UUID tests in
  1179. * http://hg.python.org/cpython/file/2f4c4db9aee5/Lib/test/test_uuid.py
  1180. *
  1181. * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification
  1182. */
  1183. public function providePythonTests(): array
  1184. {
  1185. // This array is taken directly from the Python tests, more or less.
  1186. return [
  1187. [
  1188. 'string' => '00000000-0000-0000-0000-000000000000',
  1189. 'curly' => '{00000000-0000-0000-0000-000000000000}',
  1190. 'hex' => '00000000000000000000000000000000',
  1191. 'bytes' => 'AAAAAAAAAAAAAAAAAAAAAA==',
  1192. 'int' => '0',
  1193. 'fields' => [
  1194. 'time_low' => '00000000',
  1195. 'time_mid' => '0000',
  1196. 'time_hi_and_version' => '0000',
  1197. 'clock_seq_hi_and_reserved' => '00',
  1198. 'clock_seq_low' => '00',
  1199. 'node' => '000000000000',
  1200. ],
  1201. 'urn' => 'urn:uuid:00000000-0000-0000-0000-000000000000',
  1202. 'time' => '0',
  1203. 'clock_seq' => '0000',
  1204. // This is a departure from the Python tests. The Python tests
  1205. // are technically "correct" because all bits are set to zero,
  1206. // so it stands to reason that the variant is also zero, but
  1207. // that leads to this being considered a "Reserved NCS" variant,
  1208. // and that is not the case. RFC 4122 defines this special UUID,
  1209. // so it is an RFC 4122 variant.
  1210. 'variant' => Uuid::RFC_4122,
  1211. 'version' => null,
  1212. ],
  1213. [
  1214. 'string' => '00010203-0405-0607-0809-0a0b0c0d0e0f',
  1215. 'curly' => '{00010203-0405-0607-0809-0a0b0c0d0e0f}',
  1216. 'hex' => '000102030405060708090a0b0c0d0e0f',
  1217. 'bytes' => 'AAECAwQFBgcICQoLDA0ODw==',
  1218. 'int' => '5233100606242806050955395731361295',
  1219. 'fields' => [
  1220. 'time_low' => '00010203',
  1221. 'time_mid' => '0405',
  1222. 'time_hi_and_version' => '0607',
  1223. 'clock_seq_hi_and_reserved' => '08',
  1224. 'clock_seq_low' => '09',
  1225. 'node' => '0a0b0c0d0e0f',
  1226. ],
  1227. 'urn' => 'urn:uuid:00010203-0405-0607-0809-0a0b0c0d0e0f',
  1228. 'time' => '607040500010203',
  1229. 'clock_seq' => '0809',
  1230. 'variant' => Uuid::RESERVED_NCS,
  1231. 'version' => null,
  1232. ],
  1233. [
  1234. 'string' => '02d9e6d5-9467-382e-8f9b-9300a64ac3cd',
  1235. 'curly' => '{02d9e6d5-9467-382e-8f9b-9300a64ac3cd}',
  1236. 'hex' => '02d9e6d59467382e8f9b9300a64ac3cd',
  1237. 'bytes' => 'Atnm1ZRnOC6Pm5MApkrDzQ==',
  1238. 'int' => '3789866285607910888100818383505376205',
  1239. 'fields' => [
  1240. 'time_low' => '02d9e6d5',
  1241. 'time_mid' => '9467',
  1242. 'time_hi_and_version' => '382e',
  1243. 'clock_seq_hi_and_reserved' => '8f',
  1244. 'clock_seq_low' => '9b',
  1245. 'node' => '9300a64ac3cd',
  1246. ],
  1247. 'urn' => 'urn:uuid:02d9e6d5-9467-382e-8f9b-9300a64ac3cd',
  1248. 'time' => '82e946702d9e6d5',
  1249. 'clock_seq' => '0f9b',
  1250. 'variant' => Uuid::RFC_4122,
  1251. 'version' => Uuid::UUID_TYPE_HASH_MD5,
  1252. ],
  1253. [
  1254. 'string' => '12345678-1234-5678-1234-567812345678',
  1255. 'curly' => '{12345678-1234-5678-1234-567812345678}',
  1256. 'hex' => '12345678123456781234567812345678',
  1257. 'bytes' => 'EjRWeBI0VngSNFZ4EjRWeA==',
  1258. 'int' => '24197857161011715162171839636988778104',
  1259. 'fields' => [
  1260. 'time_low' => '12345678',
  1261. 'time_mid' => '1234',
  1262. 'time_hi_and_version' => '5678',
  1263. 'clock_seq_hi_and_reserved' => '12',
  1264. 'clock_seq_low' => '34',
  1265. 'node' => '567812345678',
  1266. ],
  1267. 'urn' => 'urn:uuid:12345678-1234-5678-1234-567812345678',
  1268. 'time' => '678123412345678',
  1269. 'clock_seq' => '1234',
  1270. 'variant' => Uuid::RESERVED_NCS,
  1271. 'version' => null,
  1272. ],
  1273. [
  1274. 'string' => '6ba7b810-9dad-11d1-80b4-00c04fd430c8',
  1275. 'curly' => '{6ba7b810-9dad-11d1-80b4-00c04fd430c8}',
  1276. 'hex' => '6ba7b8109dad11d180b400c04fd430c8',
  1277. 'bytes' => 'a6e4EJ2tEdGAtADAT9QwyA==',
  1278. 'int' => '143098242404177361603877621312831893704',
  1279. 'fields' => [
  1280. 'time_low' => '6ba7b810',
  1281. 'time_mid' => '9dad',
  1282. 'time_hi_and_version' => '11d1',
  1283. 'clock_seq_hi_and_reserved' => '80',
  1284. 'clock_seq_low' => 'b4',
  1285. 'node' => '00c04fd430c8',
  1286. ],
  1287. 'urn' => 'urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8',
  1288. 'time' => '1d19dad6ba7b810',
  1289. 'clock_seq' => '00b4',
  1290. 'variant' => Uuid::RFC_4122,
  1291. 'version' => Uuid::UUID_TYPE_TIME,
  1292. ],
  1293. [
  1294. 'string' => '6ba7b811-9dad-11d1-80b4-00c04fd430c8',
  1295. 'curly' => '{6ba7b811-9dad-11d1-80b4-00c04fd430c8}',
  1296. 'hex' => '6ba7b8119dad11d180b400c04fd430c8',
  1297. 'bytes' => 'a6e4EZ2tEdGAtADAT9QwyA==',
  1298. 'int' => '143098242483405524118141958906375844040',
  1299. 'fields' => [
  1300. 'time_low' => '6ba7b811',
  1301. 'time_mid' => '9dad',
  1302. 'time_hi_and_version' => '11d1',
  1303. 'clock_seq_hi_and_reserved' => '80',
  1304. 'clock_seq_low' => 'b4',
  1305. 'node' => '00c04fd430c8',
  1306. ],
  1307. 'urn' => 'urn:uuid:6ba7b811-9dad-11d1-80b4-00c04fd430c8',
  1308. 'time' => '1d19dad6ba7b811',
  1309. 'clock_seq' => '00b4',
  1310. 'variant' => Uuid::RFC_4122,
  1311. 'version' => Uuid::UUID_TYPE_TIME,
  1312. ],
  1313. [
  1314. 'string' => '6ba7b812-9dad-11d1-80b4-00c04fd430c8',
  1315. 'curly' => '{6ba7b812-9dad-11d1-80b4-00c04fd430c8}',
  1316. 'hex' => '6ba7b8129dad11d180b400c04fd430c8',
  1317. 'bytes' => 'a6e4Ep2tEdGAtADAT9QwyA==',
  1318. 'int' => '143098242562633686632406296499919794376',
  1319. 'fields' => [
  1320. 'time_low' => '6ba7b812',
  1321. 'time_mid' => '9dad',
  1322. 'time_hi_and_version' => '11d1',
  1323. 'clock_seq_hi_and_reserved' => '80',
  1324. 'clock_seq_low' => 'b4',
  1325. 'node' => '00c04fd430c8',
  1326. ],
  1327. 'urn' => 'urn:uuid:6ba7b812-9dad-11d1-80b4-00c04fd430c8',
  1328. 'time' => '1d19dad6ba7b812',
  1329. 'clock_seq' => '00b4',
  1330. 'variant' => Uuid::RFC_4122,
  1331. 'version' => Uuid::UUID_TYPE_TIME,
  1332. ],
  1333. [
  1334. 'string' => '6ba7b814-9dad-11d1-80b4-00c04fd430c8',
  1335. 'curly' => '{6ba7b814-9dad-11d1-80b4-00c04fd430c8}',
  1336. 'hex' => '6ba7b8149dad11d180b400c04fd430c8',
  1337. 'bytes' => 'a6e4FJ2tEdGAtADAT9QwyA==',
  1338. 'int' => '143098242721090011660934971687007695048',
  1339. 'fields' => [
  1340. 'time_low' => '6ba7b814',
  1341. 'time_mid' => '9dad',
  1342. 'time_hi_and_version' => '11d1',
  1343. 'clock_seq_hi_and_reserved' => '80',
  1344. 'clock_seq_low' => 'b4',
  1345. 'node' => '00c04fd430c8',
  1346. ],
  1347. 'urn' => 'urn:uuid:6ba7b814-9dad-11d1-80b4-00c04fd430c8',
  1348. 'time' => '1d19dad6ba7b814',
  1349. 'clock_seq' => '00b4',
  1350. 'variant' => Uuid::RFC_4122,
  1351. 'version' => Uuid::UUID_TYPE_TIME,
  1352. ],
  1353. [
  1354. 'string' => '7d444840-9dc0-11d1-b245-5ffdce74fad2',
  1355. 'curly' => '{7d444840-9dc0-11d1-b245-5ffdce74fad2}',
  1356. 'hex' => '7d4448409dc011d1b2455ffdce74fad2',
  1357. 'bytes' => 'fURIQJ3AEdGyRV/9znT60g==',
  1358. 'int' => '166508041112410060672666770310773930706',
  1359. 'fields' => [
  1360. 'time_low' => '7d444840',
  1361. 'time_mid' => '9dc0',
  1362. 'time_hi_and_version' => '11d1',
  1363. 'clock_seq_hi_and_reserved' => 'b2',
  1364. 'clock_seq_low' => '45',
  1365. 'node' => '5ffdce74fad2',
  1366. ],
  1367. 'urn' => 'urn:uuid:7d444840-9dc0-11d1-b245-5ffdce74fad2',
  1368. 'time' => '1d19dc07d444840',
  1369. 'clock_seq' => '3245',
  1370. 'variant' => Uuid::RFC_4122,
  1371. 'version' => Uuid::UUID_TYPE_TIME,
  1372. ],
  1373. [
  1374. 'string' => 'e902893a-9d22-3c7e-a7b8-d6e313b71d9f',
  1375. 'curly' => '{e902893a-9d22-3c7e-a7b8-d6e313b71d9f}',
  1376. 'hex' => 'e902893a9d223c7ea7b8d6e313b71d9f',
  1377. 'bytes' => '6QKJOp0iPH6nuNbjE7cdnw==',
  1378. 'int' => '309723290945582129846206211755626405279',
  1379. 'fields' => [
  1380. 'time_low' => 'e902893a',
  1381. 'time_mid' => '9d22',
  1382. 'time_hi_and_version' => '3c7e',
  1383. 'clock_seq_hi_and_reserved' => 'a7',
  1384. 'clock_seq_low' => 'b8',
  1385. 'node' => 'd6e313b71d9f',
  1386. ],
  1387. 'urn' => 'urn:uuid:e902893a-9d22-3c7e-a7b8-d6e313b71d9f',
  1388. 'time' => 'c7e9d22e902893a',
  1389. 'clock_seq' => '27b8',
  1390. 'variant' => Uuid::RFC_4122,
  1391. 'version' => Uuid::UUID_TYPE_HASH_MD5,
  1392. ],
  1393. [
  1394. 'string' => 'eb424026-6f54-4ef8-a4d0-bb658a1fc6cf',
  1395. 'curly' => '{eb424026-6f54-4ef8-a4d0-bb658a1fc6cf}',
  1396. 'hex' => 'eb4240266f544ef8a4d0bb658a1fc6cf',
  1397. 'bytes' => '60JAJm9UTvik0Ltlih/Gzw==',
  1398. 'int' => '312712571721458096795100956955942831823',
  1399. 'fields' => [
  1400. 'time_low' => 'eb424026',
  1401. 'time_mid' => '6f54',
  1402. 'time_hi_and_version' => '4ef8',
  1403. 'clock_seq_hi_and_reserved' => 'a4',
  1404. 'clock_seq_low' => 'd0',
  1405. 'node' => 'bb658a1fc6cf',
  1406. ],
  1407. 'urn' => 'urn:uuid:eb424026-6f54-4ef8-a4d0-bb658a1fc6cf',
  1408. 'time' => 'ef86f54eb424026',
  1409. 'clock_seq' => '24d0',
  1410. 'variant' => Uuid::RFC_4122,
  1411. 'version' => Uuid::UUID_TYPE_RANDOM,
  1412. ],
  1413. [
  1414. 'string' => 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6',
  1415. 'curly' => '{f81d4fae-7dec-11d0-a765-00a0c91e6bf6}',
  1416. 'hex' => 'f81d4fae7dec11d0a76500a0c91e6bf6',
  1417. 'bytes' => '+B1Prn3sEdCnZQCgyR5r9g==',
  1418. 'int' => '329800735698586629295641978511506172918',
  1419. 'fields' => [
  1420. 'time_low' => 'f81d4fae',
  1421. 'time_mid' => '7dec',
  1422. 'time_hi_and_version' => '11d0',
  1423. 'clock_seq_hi_and_reserved' => 'a7',
  1424. 'clock_seq_low' => '65',
  1425. 'node' => '00a0c91e6bf6',
  1426. ],
  1427. 'urn' => 'urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6',
  1428. 'time' => '1d07decf81d4fae',
  1429. 'clock_seq' => '2765',
  1430. 'variant' => Uuid::RFC_4122,
  1431. 'version' => Uuid::UUID_TYPE_TIME,
  1432. ],
  1433. [
  1434. 'string' => 'fffefdfc-fffe-fffe-fffe-fffefdfcfbfa',
  1435. 'curly' => '{fffefdfc-fffe-fffe-fffe-fffefdfcfbfa}',
  1436. 'hex' => 'fffefdfcfffefffefffefffefdfcfbfa',
  1437. 'bytes' => '//79/P/+//7//v/+/fz7+g==',
  1438. 'int' => '340277133821575024845345576078114880506',
  1439. 'fields' => [
  1440. 'time_low' => 'fffefdfc',
  1441. 'time_mid' => 'fffe',
  1442. 'time_hi_and_version' => 'fffe',
  1443. 'clock_seq_hi_and_reserved' => 'ff',
  1444. 'clock_seq_low' => 'fe',
  1445. 'node' => 'fffefdfcfbfa',
  1446. ],
  1447. 'urn' => 'urn:uuid:fffefdfc-fffe-fffe-fffe-fffefdfcfbfa',
  1448. 'time' => 'ffefffefffefdfc',
  1449. 'clock_seq' => '3ffe',
  1450. 'variant' => Uuid::RESERVED_FUTURE,
  1451. 'version' => null,
  1452. ],
  1453. [
  1454. 'string' => 'ffffffff-ffff-ffff-ffff-ffffffffffff',
  1455. 'curly' => '{ffffffff-ffff-ffff-ffff-ffffffffffff}',
  1456. 'hex' => 'ffffffffffffffffffffffffffffffff',
  1457. 'bytes' => '/////////////////////w==',
  1458. 'int' => '340282366920938463463374607431768211455',
  1459. 'fields' => [
  1460. 'time_low' => 'ffffffff',
  1461. 'time_mid' => 'ffff',
  1462. 'time_hi_and_version' => 'ffff',
  1463. 'clock_seq_hi_and_reserved' => 'ff',
  1464. 'clock_seq_low' => 'ff',
  1465. 'node' => 'ffffffffffff',
  1466. ],
  1467. 'urn' => 'urn:uuid:ffffffff-ffff-ffff-ffff-ffffffffffff',
  1468. 'time' => 'fffffffffffffff',
  1469. // This is a departure from the Python tests. The Python tests
  1470. // are technically "correct" because all bits are set to one,
  1471. // which ends up calculating the variant as 7, or "Reserved
  1472. // Future," but that is not the case, and now that max UUIDs
  1473. // are defined as a special type, within the RFC 4122 variant
  1474. // rules, we also consider it an RFC 4122 variant.
  1475. //
  1476. // Similarly, Python's tests think the clock sequence should be
  1477. // 0x3fff because of the bit shifting performed on this field.
  1478. // However, since all the bits in this UUID are defined as being
  1479. // set to one, we will consider the clock sequence as 0xffff,
  1480. // which all bits set to one.
  1481. 'clock_seq' => 'ffff',
  1482. 'variant' => Uuid::RFC_4122,
  1483. 'version' => null,
  1484. ],
  1485. ];
  1486. }
  1487. /**
  1488. * @covers Ramsey\Uuid\Uuid::jsonSerialize
  1489. */
  1490. public function testJsonSerialize(): void
  1491. {
  1492. $uuid = Uuid::uuid1();
  1493. $this->assertSame('"' . $uuid->toString() . '"', json_encode($uuid));
  1494. }
  1495. public function testSerialize(): void
  1496. {
  1497. $uuid = Uuid::uuid4();
  1498. $serialized = serialize($uuid);
  1499. /** @var UuidInterface $unserializedUuid */
  1500. $unserializedUuid = unserialize($serialized);
  1501. $this->assertTrue($uuid->equals($unserializedUuid));
  1502. }
  1503. public function testSerializeWithOldStringFormat(): void
  1504. {
  1505. $serialized = 'C:26:"Ramsey\Uuid\Rfc4122\UuidV4":36:{b3cd586a-e3ca-44f3-988c-f4d666c1bf4d}';
  1506. /** @var UuidInterface $unserializedUuid */
  1507. $unserializedUuid = unserialize($serialized);
  1508. $this->assertSame('b3cd586a-e3ca-44f3-988c-f4d666c1bf4d', $unserializedUuid->toString());
  1509. }
  1510. public function testUuid3WithEmptyNamespace(): void
  1511. {
  1512. $this->expectException(InvalidArgumentException::class);
  1513. $this->expectExceptionMessage('Invalid UUID string:');
  1514. Uuid::uuid3('', '');
  1515. }
  1516. public function testUuid3WithEmptyName(): void
  1517. {
  1518. $uuid = Uuid::uuid3(Uuid::NIL, '');
  1519. $this->assertSame('4ae71336-e44b-39bf-b9d2-752e234818a5', $uuid->toString());
  1520. }
  1521. public function testUuid3WithZeroName(): void
  1522. {
  1523. $uuid = Uuid::uuid3(Uuid::NIL, '0');
  1524. $this->assertSame('19826852-5007-3022-a72a-212f66e9fac3', $uuid->toString());
  1525. }
  1526. public function testUuid5WithEmptyNamespace(): void
  1527. {
  1528. $this->expectException(InvalidArgumentException::class);
  1529. $this->expectExceptionMessage('Invalid UUID string:');
  1530. Uuid::uuid5('', '');
  1531. }
  1532. public function testUuid5WithEmptyName(): void
  1533. {
  1534. $uuid = Uuid::uuid5(Uuid::NIL, '');
  1535. $this->assertSame('e129f27c-5103-5c5c-844b-cdf0a15e160d', $uuid->toString());
  1536. }
  1537. public function testUuid5WithZeroName(): void
  1538. {
  1539. $uuid = Uuid::uuid5(Uuid::NIL, '0');
  1540. $this->assertSame('b6c54489-38a0-5f50-a60a-fd8d76219cae', $uuid->toString());
  1541. }
  1542. /**
  1543. * @depends testGetVersionForVersion1
  1544. */
  1545. public function testUuidVersionConstantForVersion1(): void
  1546. {
  1547. $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
  1548. $this->assertSame($uuid->getVersion(), Uuid::UUID_TYPE_TIME);
  1549. }
  1550. /**
  1551. * @depends testGetVersionForVersion2
  1552. */
  1553. public function testUuidVersionConstantForVersion2(): void
  1554. {
  1555. $uuid = Uuid::fromString('6fa459ea-ee8a-2ca4-894e-db77e160355e');
  1556. $this->assertSame($uuid->getVersion(), Uuid::UUID_TYPE_DCE_SECURITY);
  1557. }
  1558. /**
  1559. * @depends testGetVersionForVersion3
  1560. */
  1561. public function testUuidVersionConstantForVersion3(): void
  1562. {
  1563. $uuid = Uuid::fromString('6fa459ea-ee8a-3ca4-894e-db77e160355e');
  1564. $this->assertSame($uuid->getVersion(), Uuid::UUID_TYPE_HASH_MD5);
  1565. }
  1566. /**
  1567. * @depends testGetVersionForVersion4
  1568. */
  1569. public function testUuidVersionConstantForVersion4(): void
  1570. {
  1571. $uuid = Uuid::fromString('6fabf0bc-603a-42f2-925b-d9f779bd0032');
  1572. $this->assertSame($uuid->getVersion(), Uuid::UUID_TYPE_RANDOM);
  1573. }
  1574. /**
  1575. * @depends testGetVersionForVersion5
  1576. */
  1577. public function testUuidVersionConstantForVersion5(): void
  1578. {
  1579. $uuid = Uuid::fromString('886313e1-3b8a-5372-9b90-0c9aee199e5d');
  1580. $this->assertSame($uuid->getVersion(), Uuid::UUID_TYPE_HASH_SHA1);
  1581. }
  1582. public function testUuidVersionConstantForVersion6(): void
  1583. {
  1584. $uuid = Uuid::fromString('886313e1-3b8a-6372-9b90-0c9aee199e5d');
  1585. $this->assertSame($uuid->getVersion(), Uuid::UUID_TYPE_PEABODY);
  1586. $this->assertSame($uuid->getVersion(), Uuid::UUID_TYPE_REORDERED_TIME);
  1587. }
  1588. public function testUuidVersionConstantForVersion7(): void
  1589. {
  1590. $uuid = Uuid::fromString('886313e1-3b8a-7372-9b90-0c9aee199e5d');
  1591. $this->assertSame($uuid->getVersion(), Uuid::UUID_TYPE_UNIX_TIME);
  1592. }
  1593. public function testGetDateTimeThrowsExceptionWhenDateTimeCannotParseDate(): void
  1594. {
  1595. $numberConverter = new BigNumberConverter();
  1596. $timeConverter = Mockery::mock(TimeConverterInterface::class);
  1597. $timeConverter
  1598. ->shouldReceive('convertTime')
  1599. ->once()
  1600. ->andReturn(new Time(1234567890, '1234567'));
  1601. $builder = new DefaultUuidBuilder($numberConverter, $timeConverter);
  1602. $codec = new StringCodec($builder);
  1603. $factory = new UuidFactory();
  1604. $factory->setCodec($codec);
  1605. $uuid = $factory->fromString('b1484596-25dc-11ea-978f-2e728ce88125');
  1606. $this->expectException(DateTimeException::class);
  1607. $this->expectExceptionMessage(
  1608. 'Failed to parse time string (@1234567890.1234567) at position 18 (7): Unexpected character'
  1609. );
  1610. $uuid->getDateTime();
  1611. }
  1612. /**
  1613. * @param mixed[] $args
  1614. *
  1615. * @dataProvider provideStaticMethods
  1616. */
  1617. public function testStaticCreationMethodsReturnSpecificUuidInstances(
  1618. string $staticMethod,
  1619. array $args = []
  1620. ): void {
  1621. $this->assertInstanceOf(LazyUuidFromString::class, Uuid::$staticMethod(...$args));
  1622. }
  1623. /**
  1624. * @param mixed[] $args
  1625. *
  1626. * @dataProvider provideStaticMethods
  1627. */
  1628. public function testUuidInstancesBuiltFromStringAreEquivalentToTheirGeneratedCounterparts(
  1629. string $staticMethod,
  1630. array $args = []
  1631. ): void {
  1632. $generated = Uuid::$staticMethod(...$args);
  1633. self::assertSame(
  1634. (string) $generated,
  1635. (string) Uuid::fromString($generated->toString())
  1636. );
  1637. }
  1638. /**
  1639. * @param mixed[] $args
  1640. *
  1641. * @dataProvider provideStaticMethods
  1642. */
  1643. public function testUuidInstancesBuiltFromBytesAreEquivalentToTheirGeneratedCounterparts(
  1644. string $staticMethod,
  1645. array $args = []
  1646. ): void {
  1647. $generated = Uuid::$staticMethod(...$args);
  1648. self::assertSame(
  1649. (string) $generated,
  1650. (string) Uuid::fromBytes($generated->getBytes())
  1651. );
  1652. }
  1653. /**
  1654. * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification
  1655. */
  1656. public function provideStaticMethods(): array
  1657. {
  1658. return [
  1659. ['uuid1'],
  1660. ['uuid2', [Uuid::DCE_DOMAIN_PERSON]],
  1661. ['uuid3', [Uuid::NIL, 'foobar']],
  1662. ['uuid4'],
  1663. ['uuid5', [Uuid::NIL, 'foobar']],
  1664. ];
  1665. }
  1666. }