UuidFactory.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. <?php
  2. /**
  3. * This file is part of the ramsey/uuid library
  4. *
  5. * For the full copyright and license information, please view the LICENSE
  6. * file that was distributed with this source code.
  7. *
  8. * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
  9. * @license http://opensource.org/licenses/MIT MIT
  10. */
  11. declare(strict_types=1);
  12. namespace Ramsey\Uuid;
  13. use DateTimeInterface;
  14. use Ramsey\Uuid\Builder\UuidBuilderInterface;
  15. use Ramsey\Uuid\Codec\CodecInterface;
  16. use Ramsey\Uuid\Converter\NumberConverterInterface;
  17. use Ramsey\Uuid\Converter\TimeConverterInterface;
  18. use Ramsey\Uuid\Generator\DceSecurityGeneratorInterface;
  19. use Ramsey\Uuid\Generator\DefaultTimeGenerator;
  20. use Ramsey\Uuid\Generator\NameGeneratorInterface;
  21. use Ramsey\Uuid\Generator\RandomGeneratorInterface;
  22. use Ramsey\Uuid\Generator\TimeGeneratorInterface;
  23. use Ramsey\Uuid\Lazy\LazyUuidFromString;
  24. use Ramsey\Uuid\Provider\NodeProviderInterface;
  25. use Ramsey\Uuid\Provider\Time\FixedTimeProvider;
  26. use Ramsey\Uuid\Type\Hexadecimal;
  27. use Ramsey\Uuid\Type\Integer as IntegerObject;
  28. use Ramsey\Uuid\Type\Time;
  29. use Ramsey\Uuid\Validator\ValidatorInterface;
  30. use function bin2hex;
  31. use function hex2bin;
  32. use function pack;
  33. use function str_pad;
  34. use function strtolower;
  35. use function substr;
  36. use function substr_replace;
  37. use function unpack;
  38. use const STR_PAD_LEFT;
  39. class UuidFactory implements UuidFactoryInterface
  40. {
  41. /**
  42. * @var CodecInterface
  43. */
  44. private $codec;
  45. /**
  46. * @var DceSecurityGeneratorInterface
  47. */
  48. private $dceSecurityGenerator;
  49. /**
  50. * @var NameGeneratorInterface
  51. */
  52. private $nameGenerator;
  53. /**
  54. * @var NodeProviderInterface
  55. */
  56. private $nodeProvider;
  57. /**
  58. * @var NumberConverterInterface
  59. */
  60. private $numberConverter;
  61. /**
  62. * @var RandomGeneratorInterface
  63. */
  64. private $randomGenerator;
  65. /**
  66. * @var TimeConverterInterface
  67. */
  68. private $timeConverter;
  69. /**
  70. * @var TimeGeneratorInterface
  71. */
  72. private $timeGenerator;
  73. /**
  74. * @var UuidBuilderInterface
  75. */
  76. private $uuidBuilder;
  77. /**
  78. * @var ValidatorInterface
  79. */
  80. private $validator;
  81. /** @var bool whether the feature set was provided from outside, or we can operate under "default" assumptions */
  82. private $isDefaultFeatureSet;
  83. /**
  84. * @param FeatureSet $features A set of available features in the current environment
  85. */
  86. public function __construct(?FeatureSet $features = null)
  87. {
  88. $this->isDefaultFeatureSet = $features === null;
  89. $features = $features ?: new FeatureSet();
  90. $this->codec = $features->getCodec();
  91. $this->dceSecurityGenerator = $features->getDceSecurityGenerator();
  92. $this->nameGenerator = $features->getNameGenerator();
  93. $this->nodeProvider = $features->getNodeProvider();
  94. $this->numberConverter = $features->getNumberConverter();
  95. $this->randomGenerator = $features->getRandomGenerator();
  96. $this->timeConverter = $features->getTimeConverter();
  97. $this->timeGenerator = $features->getTimeGenerator();
  98. $this->uuidBuilder = $features->getBuilder();
  99. $this->validator = $features->getValidator();
  100. }
  101. /**
  102. * Returns the codec used by this factory
  103. */
  104. public function getCodec(): CodecInterface
  105. {
  106. return $this->codec;
  107. }
  108. /**
  109. * Sets the codec to use for this factory
  110. *
  111. * @param CodecInterface $codec A UUID encoder-decoder
  112. */
  113. public function setCodec(CodecInterface $codec): void
  114. {
  115. $this->isDefaultFeatureSet = false;
  116. $this->codec = $codec;
  117. }
  118. /**
  119. * Returns the name generator used by this factory
  120. */
  121. public function getNameGenerator(): NameGeneratorInterface
  122. {
  123. return $this->nameGenerator;
  124. }
  125. /**
  126. * Sets the name generator to use for this factory
  127. *
  128. * @param NameGeneratorInterface $nameGenerator A generator to generate
  129. * binary data, based on a namespace and name
  130. */
  131. public function setNameGenerator(NameGeneratorInterface $nameGenerator): void
  132. {
  133. $this->isDefaultFeatureSet = false;
  134. $this->nameGenerator = $nameGenerator;
  135. }
  136. /**
  137. * Returns the node provider used by this factory
  138. */
  139. public function getNodeProvider(): NodeProviderInterface
  140. {
  141. return $this->nodeProvider;
  142. }
  143. /**
  144. * Returns the random generator used by this factory
  145. */
  146. public function getRandomGenerator(): RandomGeneratorInterface
  147. {
  148. return $this->randomGenerator;
  149. }
  150. /**
  151. * Returns the time generator used by this factory
  152. */
  153. public function getTimeGenerator(): TimeGeneratorInterface
  154. {
  155. return $this->timeGenerator;
  156. }
  157. /**
  158. * Sets the time generator to use for this factory
  159. *
  160. * @param TimeGeneratorInterface $generator A generator to generate binary
  161. * data, based on the time
  162. */
  163. public function setTimeGenerator(TimeGeneratorInterface $generator): void
  164. {
  165. $this->isDefaultFeatureSet = false;
  166. $this->timeGenerator = $generator;
  167. }
  168. /**
  169. * Returns the DCE Security generator used by this factory
  170. */
  171. public function getDceSecurityGenerator(): DceSecurityGeneratorInterface
  172. {
  173. return $this->dceSecurityGenerator;
  174. }
  175. /**
  176. * Sets the DCE Security generator to use for this factory
  177. *
  178. * @param DceSecurityGeneratorInterface $generator A generator to generate
  179. * binary data, based on a local domain and local identifier
  180. */
  181. public function setDceSecurityGenerator(DceSecurityGeneratorInterface $generator): void
  182. {
  183. $this->isDefaultFeatureSet = false;
  184. $this->dceSecurityGenerator = $generator;
  185. }
  186. /**
  187. * Returns the number converter used by this factory
  188. */
  189. public function getNumberConverter(): NumberConverterInterface
  190. {
  191. return $this->numberConverter;
  192. }
  193. /**
  194. * Sets the random generator to use for this factory
  195. *
  196. * @param RandomGeneratorInterface $generator A generator to generate binary
  197. * data, based on some random input
  198. */
  199. public function setRandomGenerator(RandomGeneratorInterface $generator): void
  200. {
  201. $this->isDefaultFeatureSet = false;
  202. $this->randomGenerator = $generator;
  203. }
  204. /**
  205. * Sets the number converter to use for this factory
  206. *
  207. * @param NumberConverterInterface $converter A converter to use for working
  208. * with large integers (i.e. integers greater than PHP_INT_MAX)
  209. */
  210. public function setNumberConverter(NumberConverterInterface $converter): void
  211. {
  212. $this->isDefaultFeatureSet = false;
  213. $this->numberConverter = $converter;
  214. }
  215. /**
  216. * Returns the UUID builder used by this factory
  217. */
  218. public function getUuidBuilder(): UuidBuilderInterface
  219. {
  220. return $this->uuidBuilder;
  221. }
  222. /**
  223. * Sets the UUID builder to use for this factory
  224. *
  225. * @param UuidBuilderInterface $builder A builder for constructing instances
  226. * of UuidInterface
  227. */
  228. public function setUuidBuilder(UuidBuilderInterface $builder): void
  229. {
  230. $this->isDefaultFeatureSet = false;
  231. $this->uuidBuilder = $builder;
  232. }
  233. /**
  234. * @psalm-mutation-free
  235. */
  236. public function getValidator(): ValidatorInterface
  237. {
  238. return $this->validator;
  239. }
  240. /**
  241. * Sets the validator to use for this factory
  242. *
  243. * @param ValidatorInterface $validator A validator to use for validating
  244. * whether a string is a valid UUID
  245. */
  246. public function setValidator(ValidatorInterface $validator): void
  247. {
  248. $this->isDefaultFeatureSet = false;
  249. $this->validator = $validator;
  250. }
  251. /**
  252. * @psalm-pure
  253. */
  254. public function fromBytes(string $bytes): UuidInterface
  255. {
  256. return $this->codec->decodeBytes($bytes);
  257. }
  258. /**
  259. * @psalm-pure
  260. */
  261. public function fromString(string $uuid): UuidInterface
  262. {
  263. $uuid = strtolower($uuid);
  264. return $this->codec->decode($uuid);
  265. }
  266. /**
  267. * @psalm-pure
  268. */
  269. public function fromInteger(string $integer): UuidInterface
  270. {
  271. $hex = $this->numberConverter->toHex($integer);
  272. $hex = str_pad($hex, 32, '0', STR_PAD_LEFT);
  273. return $this->fromString($hex);
  274. }
  275. public function fromDateTime(
  276. DateTimeInterface $dateTime,
  277. ?Hexadecimal $node = null,
  278. ?int $clockSeq = null
  279. ): UuidInterface {
  280. $timeProvider = new FixedTimeProvider(
  281. new Time($dateTime->format('U'), $dateTime->format('u'))
  282. );
  283. $timeGenerator = new DefaultTimeGenerator(
  284. $this->nodeProvider,
  285. $this->timeConverter,
  286. $timeProvider
  287. );
  288. $nodeHex = $node ? $node->toString() : null;
  289. $bytes = $timeGenerator->generate($nodeHex, $clockSeq);
  290. return $this->uuidFromBytesAndVersion($bytes, 1);
  291. }
  292. /**
  293. * @inheritDoc
  294. */
  295. public function uuid1($node = null, ?int $clockSeq = null): UuidInterface
  296. {
  297. $bytes = $this->timeGenerator->generate($node, $clockSeq);
  298. return $this->uuidFromBytesAndVersion($bytes, 1);
  299. }
  300. public function uuid2(
  301. int $localDomain,
  302. ?IntegerObject $localIdentifier = null,
  303. ?Hexadecimal $node = null,
  304. ?int $clockSeq = null
  305. ): UuidInterface {
  306. $bytes = $this->dceSecurityGenerator->generate(
  307. $localDomain,
  308. $localIdentifier,
  309. $node,
  310. $clockSeq
  311. );
  312. return $this->uuidFromBytesAndVersion($bytes, 2);
  313. }
  314. /**
  315. * @inheritDoc
  316. * @psalm-pure
  317. */
  318. public function uuid3($ns, string $name): UuidInterface
  319. {
  320. return $this->uuidFromNsAndName($ns, $name, 3, 'md5');
  321. }
  322. public function uuid4(): UuidInterface
  323. {
  324. $bytes = $this->randomGenerator->generate(16);
  325. return $this->uuidFromBytesAndVersion($bytes, 4);
  326. }
  327. /**
  328. * @inheritDoc
  329. * @psalm-pure
  330. */
  331. public function uuid5($ns, string $name): UuidInterface
  332. {
  333. return $this->uuidFromNsAndName($ns, $name, 5, 'sha1');
  334. }
  335. public function uuid6(?Hexadecimal $node = null, ?int $clockSeq = null): UuidInterface
  336. {
  337. $nodeHex = $node ? $node->toString() : null;
  338. $bytes = $this->timeGenerator->generate($nodeHex, $clockSeq);
  339. // Rearrange the bytes, according to the UUID version 6 specification.
  340. $v6 = $bytes[6] . $bytes[7] . $bytes[4] . $bytes[5]
  341. . $bytes[0] . $bytes[1] . $bytes[2] . $bytes[3];
  342. $v6 = bin2hex($v6);
  343. // Drop the first four bits, while adding an empty four bits for the
  344. // version field. This allows us to reconstruct the correct time from
  345. // the bytes of this UUID.
  346. $v6Bytes = hex2bin(substr($v6, 1, 12) . '0' . substr($v6, -3));
  347. $v6Bytes .= substr($bytes, 8);
  348. return $this->uuidFromBytesAndVersion($v6Bytes, 6);
  349. }
  350. /**
  351. * Returns a Uuid created from the provided byte string
  352. *
  353. * Uses the configured builder and codec and the provided byte string to
  354. * construct a Uuid object.
  355. *
  356. * @param string $bytes The byte string from which to construct a UUID
  357. *
  358. * @return UuidInterface An instance of UuidInterface, created from the
  359. * provided bytes
  360. *
  361. * @psalm-pure
  362. */
  363. public function uuid(string $bytes): UuidInterface
  364. {
  365. return $this->uuidBuilder->build($this->codec, $bytes);
  366. }
  367. /**
  368. * Returns a version 3 or 5 namespaced Uuid
  369. *
  370. * @param string|UuidInterface $ns The namespace (must be a valid UUID)
  371. * @param string $name The name to hash together with the namespace
  372. * @param int $version The version of UUID to create (3 or 5)
  373. * @param string $hashAlgorithm The hashing algorithm to use when hashing
  374. * together the namespace and name
  375. *
  376. * @return UuidInterface An instance of UuidInterface, created by hashing
  377. * together the provided namespace and name
  378. *
  379. * @psalm-pure
  380. */
  381. private function uuidFromNsAndName($ns, string $name, int $version, string $hashAlgorithm): UuidInterface
  382. {
  383. if (!($ns instanceof UuidInterface)) {
  384. $ns = $this->fromString($ns);
  385. }
  386. $bytes = $this->nameGenerator->generate($ns, $name, $hashAlgorithm);
  387. return $this->uuidFromBytesAndVersion(substr($bytes, 0, 16), $version);
  388. }
  389. /**
  390. * Returns an RFC 4122 variant Uuid, created from the provided bytes and version
  391. *
  392. * @param string $bytes The byte string to convert to a UUID
  393. * @param int $version The RFC 4122 version to apply to the UUID
  394. *
  395. * @return UuidInterface An instance of UuidInterface, created from the
  396. * byte string and version
  397. *
  398. * @psalm-pure
  399. */
  400. private function uuidFromBytesAndVersion(string $bytes, int $version): UuidInterface
  401. {
  402. /** @var array $unpackedTime */
  403. $unpackedTime = unpack('n*', substr($bytes, 6, 2));
  404. $timeHi = (int) $unpackedTime[1];
  405. $timeHiAndVersion = pack('n*', BinaryUtils::applyVersion($timeHi, $version));
  406. /** @var array $unpackedClockSeq */
  407. $unpackedClockSeq = unpack('n*', substr($bytes, 8, 2));
  408. $clockSeqHi = (int) $unpackedClockSeq[1];
  409. $clockSeqHiAndReserved = pack('n*', BinaryUtils::applyVariant($clockSeqHi));
  410. $bytes = substr_replace($bytes, $timeHiAndVersion, 6, 2);
  411. $bytes = substr_replace($bytes, $clockSeqHiAndReserved, 8, 2);
  412. if ($this->isDefaultFeatureSet) {
  413. return LazyUuidFromString::fromBytes($bytes);
  414. }
  415. return $this->uuid($bytes);
  416. }
  417. }