8889841cFields.php000066600000011646150513024430006474 0ustar00 * @license http://opensource.org/licenses/MIT MIT */ declare(strict_types=1); namespace Ramsey\Uuid\Guid; use Ramsey\Uuid\Exception\InvalidArgumentException; use Ramsey\Uuid\Fields\SerializableFieldsTrait; use Ramsey\Uuid\Rfc4122\FieldsInterface; use Ramsey\Uuid\Rfc4122\NilTrait; use Ramsey\Uuid\Rfc4122\VariantTrait; use Ramsey\Uuid\Rfc4122\VersionTrait; use Ramsey\Uuid\Type\Hexadecimal; use Ramsey\Uuid\Uuid; use function bin2hex; use function dechex; use function hexdec; use function pack; use function sprintf; use function str_pad; use function strlen; use function substr; use function unpack; use const STR_PAD_LEFT; /** * GUIDs are comprised of a set of named fields, according to RFC 4122 * * @see Guid * * @psalm-immutable */ final class Fields implements FieldsInterface { use NilTrait; use SerializableFieldsTrait; use VariantTrait; use VersionTrait; /** * @var string */ private $bytes; /** * @param string $bytes A 16-byte binary string representation of a UUID * * @throws InvalidArgumentException if the byte string is not exactly 16 bytes * @throws InvalidArgumentException if the byte string does not represent a GUID * @throws InvalidArgumentException if the byte string does not contain a valid version */ public function __construct(string $bytes) { if (strlen($bytes) !== 16) { throw new InvalidArgumentException( 'The byte string must be 16 bytes long; ' . 'received ' . strlen($bytes) . ' bytes' ); } $this->bytes = $bytes; if (!$this->isCorrectVariant()) { throw new InvalidArgumentException( 'The byte string received does not conform to the RFC ' . '4122 or Microsoft Corporation variants' ); } if (!$this->isCorrectVersion()) { throw new InvalidArgumentException( 'The byte string received does not contain a valid version' ); } } public function getBytes(): string { return $this->bytes; } public function getTimeLow(): Hexadecimal { // Swap the bytes from little endian to network byte order. /** @var array $hex */ $hex = unpack( 'H*', pack( 'v*', hexdec(bin2hex(substr($this->bytes, 2, 2))), hexdec(bin2hex(substr($this->bytes, 0, 2))) ) ); return new Hexadecimal((string) ($hex[1] ?? '')); } public function getTimeMid(): Hexadecimal { // Swap the bytes from little endian to network byte order. /** @var array $hex */ $hex = unpack( 'H*', pack( 'v', hexdec(bin2hex(substr($this->bytes, 4, 2))) ) ); return new Hexadecimal((string) ($hex[1] ?? '')); } public function getTimeHiAndVersion(): Hexadecimal { // Swap the bytes from little endian to network byte order. /** @var array $hex */ $hex = unpack( 'H*', pack( 'v', hexdec(bin2hex(substr($this->bytes, 6, 2))) ) ); return new Hexadecimal((string) ($hex[1] ?? '')); } public function getTimestamp(): Hexadecimal { return new Hexadecimal(sprintf( '%03x%04s%08s', hexdec($this->getTimeHiAndVersion()->toString()) & 0x0fff, $this->getTimeMid()->toString(), $this->getTimeLow()->toString() )); } public function getClockSeq(): Hexadecimal { $clockSeq = hexdec(bin2hex(substr($this->bytes, 8, 2))) & 0x3fff; return new Hexadecimal(str_pad(dechex($clockSeq), 4, '0', STR_PAD_LEFT)); } public function getClockSeqHiAndReserved(): Hexadecimal { return new Hexadecimal(bin2hex(substr($this->bytes, 8, 1))); } public function getClockSeqLow(): Hexadecimal { return new Hexadecimal(bin2hex(substr($this->bytes, 9, 1))); } public function getNode(): Hexadecimal { return new Hexadecimal(bin2hex(substr($this->bytes, 10))); } public function getVersion(): ?int { if ($this->isNil()) { return null; } /** @var array $parts */ $parts = unpack('n*', $this->bytes); return ((int) $parts[4] >> 4) & 0x00f; } private function isCorrectVariant(): bool { if ($this->isNil()) { return true; } $variant = $this->getVariant(); return $variant === Uuid::RFC_4122 || $variant === Uuid::RESERVED_MICROSOFT; } } GuidBuilder.php000066600000004600150513024430007455 0ustar00 * @license http://opensource.org/licenses/MIT MIT */ declare(strict_types=1); namespace Ramsey\Uuid\Guid; use Ramsey\Uuid\Builder\UuidBuilderInterface; use Ramsey\Uuid\Codec\CodecInterface; use Ramsey\Uuid\Converter\NumberConverterInterface; use Ramsey\Uuid\Converter\TimeConverterInterface; use Ramsey\Uuid\Exception\UnableToBuildUuidException; use Ramsey\Uuid\UuidInterface; use Throwable; /** * GuidBuilder builds instances of Guid * * @see Guid * * @psalm-immutable */ class GuidBuilder implements UuidBuilderInterface { /** * @var NumberConverterInterface */ private $numberConverter; /** * @var TimeConverterInterface */ private $timeConverter; /** * @param NumberConverterInterface $numberConverter The number converter to * use when constructing the Guid * @param TimeConverterInterface $timeConverter The time converter to use * for converting timestamps extracted from a UUID to Unix timestamps */ public function __construct( NumberConverterInterface $numberConverter, TimeConverterInterface $timeConverter ) { $this->numberConverter = $numberConverter; $this->timeConverter = $timeConverter; } /** * Builds and returns a Guid * * @param CodecInterface $codec The codec to use for building this Guid instance * @param string $bytes The byte string from which to construct a UUID * * @return Guid The GuidBuilder returns an instance of Ramsey\Uuid\Guid\Guid * * @psalm-pure */ public function build(CodecInterface $codec, string $bytes): UuidInterface { try { return new Guid( $this->buildFields($bytes), $this->numberConverter, $codec, $this->timeConverter ); } catch (Throwable $e) { throw new UnableToBuildUuidException($e->getMessage(), (int) $e->getCode(), $e); } } /** * Proxy method to allow injecting a mock, for testing */ protected function buildFields(string $bytes): Fields { return new Fields($bytes); } } Guid.php000066600000004377150513024430006161 0ustar00 * @license http://opensource.org/licenses/MIT MIT */ declare(strict_types=1); namespace Ramsey\Uuid\Guid; use Ramsey\Uuid\Codec\CodecInterface; use Ramsey\Uuid\Converter\NumberConverterInterface; use Ramsey\Uuid\Converter\TimeConverterInterface; use Ramsey\Uuid\Uuid; /** * Guid represents a UUID with "native" (little-endian) byte order * * From Wikipedia: * * > The first three fields are unsigned 32- and 16-bit integers and are subject * > to swapping, while the last two fields consist of uninterpreted bytes, not * > subject to swapping. This byte swapping applies even for versions 3, 4, and * > 5, where the canonical fields do not correspond to the content of the UUID. * * The first three fields of a GUID are encoded in little-endian byte order, * while the last three fields are in network (big-endian) byte order. This is * according to the history of the Microsoft definition of a GUID. * * According to the .NET Guid.ToByteArray method documentation: * * > Note that the order of bytes in the returned byte array is different from * > the string representation of a Guid value. The order of the beginning * > four-byte group and the next two two-byte groups is reversed, whereas the * > order of the last two-byte group and the closing six-byte group is the * > same. * * @link https://en.wikipedia.org/wiki/Universally_unique_identifier#Variants UUID Variants on Wikipedia * @link https://docs.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid Windows GUID structure * @link https://docs.microsoft.com/en-us/dotnet/api/system.guid .NET Guid Struct * @link https://docs.microsoft.com/en-us/dotnet/api/system.guid.tobytearray .NET Guid.ToByteArray Method * * @psalm-immutable */ final class Guid extends Uuid { public function __construct( Fields $fields, NumberConverterInterface $numberConverter, CodecInterface $codec, TimeConverterInterface $timeConverter ) { parent::__construct($fields, $numberConverter, $codec, $timeConverter); } }