Skip to content

Commit

Permalink
feat: support version 8, custom UUIDs
Browse files Browse the repository at this point in the history
  • Loading branch information
ramsey committed Nov 5, 2022
1 parent 0ba1ffb commit 55d8833
Show file tree
Hide file tree
Showing 22 changed files with 332 additions and 30 deletions.
1 change: 1 addition & 0 deletions docs/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Reference
reference/rfc4122-uuidv5
reference/rfc4122-uuidv6
reference/rfc4122-uuidv7
reference/rfc4122-uuidv8
reference/guid-fields
reference/guid-guid
reference/nonstandard-fields
Expand Down
13 changes: 13 additions & 0 deletions docs/reference/rfc4122-uuidv8.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.. _reference.rfc4122.uuidv8:

===============
Rfc4122\\UuidV8
===============

.. php:namespace:: Ramsey\Uuid\Rfc4122
.. php:class:: UuidV8
Implements :php:interface:`Ramsey\\Uuid\\Rfc4122\\UuidInterface`.

UuidV8 represents a :ref:`version 8, custom UUID <rfc4122.version8>`.
7 changes: 6 additions & 1 deletion docs/rfc4122.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ RFC 4122 UUIDs
rfc4122/version5
rfc4122/version6
rfc4122/version7
rfc4122/version8

`RFC 4122`_ defines five versions of UUID, while a `new Internet-Draft under
review`_ defines three new versions. Each version has different generation
Expand Down Expand Up @@ -57,6 +58,10 @@ Version 7: Unix Epoch Time
sortable UUID without the privacy and entropy concerns associated with
version 1 and version 6 UUIDs. For more details, see :ref:`rfc4122.version7`.

Version 8: Custom
This version of UUID allows applications to generate custom identifiers in
an RFC-compatible format. For more details, see :doc:`rfc4122/version8`.


.. _RFC 4122: https://tools.ietf.org/html/rfc4122
.. _new Internet-Draft under review: https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04
.. _new Internet-Draft under review: https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00
2 changes: 1 addition & 1 deletion docs/rfc4122/version6.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,6 @@ need the benefit of a monotonically increasing unique identifier, see
:ref:`rfc4122.version7`.


.. _Internet-Draft under review: https://datatracker.ietf.org/doc/draft-peabody-dispatch-new-uuid-format/
.. _Internet-Draft under review: https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.6
.. _two problems that have long existed: https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/
.. _RFC 4122: https://tools.ietf.org/html/rfc4122
2 changes: 1 addition & 1 deletion docs/rfc4122/version7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ This will print something like this:
ULID, unless it was converted from a version 7 UUID.

.. _ULIDs: https://github.com/ulid/spec
.. _Internet-Draft under review: https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.2
.. _Internet-Draft under review: https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.7
.. _two problems that have long existed: https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/
.. _Crockford's Base 32 algorithm: https://www.crockford.com/base32.html
.. _tuupola/base32: https://packagist.org/packages/tuupola/base32
51 changes: 51 additions & 0 deletions docs/rfc4122/version8.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.. _rfc4122.version8:

=================
Version 8: Custom
=================

.. note::

Version 8, custom UUIDs are a new format of UUID, proposed in an
`Internet-Draft under review`_ at the IETF. While the draft is still going
through the IETF process, the version 7 format is not expected to change
in any way that breaks compatibility.

Version 8 UUIDs allow applications to create custom UUIDs in an RFC-compatible
way. The only requirement is the version and variant bits must be set according
to the UUID specification. The bytes provided may contain any value according to
your application's needs. Be aware, however, that other applications may not
understand the semantics of the value.

.. warning::

The bytes should be a 16-byte octet string, an open blob of data that you
may fill with 128 bits of information. However, bits 48 through 51 will be
replaced with the UUID version field, and bits 64 and 65 will be replaced
with the UUID variant. You must not rely on these bits for your application
needs.

.. code-block:: php
:caption: Generate a version 8, custom UUID
:name: rfc4122.version8.example
use Ramsey\Uuid\Uuid;
$uuid = Uuid::uuid8("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff");
printf(
"UUID: %s\nVersion: %d\n",
$uuid->toString(),
$uuid->getFields()->getVersion()
);
This will generate a version 8 UUID and print out its string representation.
It will look something like this:

.. code-block:: text
UUID: 00112233-4455-8677-8899-aabbccddeeff
Version: 8
.. _Internet-Draft under review: https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.8
19 changes: 3 additions & 16 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,6 @@
<code>$timeGenerator</code>
</PropertyNotSetInConstructor>
</file>
<file src="src/Fields/SerializableFieldsTrait.php">
<UnusedMethodCall occurrences="1">
<code>unserialize</code>
</UnusedMethodCall>
</file>
<file src="src/Generator/PeclUuidNameGenerator.php">
<ImpureFunctionCall occurrences="3">
<code>uuid_generate_md5</code>
Expand All @@ -66,11 +61,6 @@
<code>$this</code>
</ImpureVariable>
</file>
<file src="src/Lazy/LazyUuidFromString.php">
<UnusedMethodCall occurrences="1">
<code>unserialize</code>
</UnusedMethodCall>
</file>
<file src="src/Nonstandard/UuidBuilder.php">
<ImpureVariable occurrences="3">
<code>$this</code>
Expand All @@ -96,7 +86,9 @@
</MixedArrayAssignment>
</file>
<file src="src/Rfc4122/UuidBuilder.php">
<ImpureVariable occurrences="19">
<ImpureVariable occurrences="21">
<code>$this</code>
<code>$this</code>
<code>$this</code>
<code>$this</code>
<code>$this</code>
Expand All @@ -123,11 +115,6 @@
<code>NonstandardUuidV6</code>
</DeprecatedClass>
</file>
<file src="src/Type/Decimal.php">
<UnusedMethodCall occurrences="1">
<code>unserialize</code>
</UnusedMethodCall>
</file>
<file src="src/Type/Hexadecimal.php">
<UnusedMethodCall occurrences="1">
<code>unserialize</code>
Expand Down
2 changes: 1 addition & 1 deletion src/Rfc4122/MaxTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* The max UUID is special form of UUID that is specified to have all 128 bits
* set to one. It is the inverse of the nil UUID.
*
* @link https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.4 Max UUID
* @link https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.10 Max UUID
*
* @psalm-immutable
*/
Expand Down
2 changes: 2 additions & 0 deletions src/Rfc4122/UuidBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ public function build(CodecInterface $codec, string $bytes): UuidInterface
return new UuidV6($fields, $this->numberConverter, $codec, $this->timeConverter);
case Uuid::UUID_TYPE_UNIX_TIME:
return new UuidV7($fields, $this->numberConverter, $codec, $this->unixTimeConverter);
case Uuid::UUID_TYPE_CUSTOM:
return new UuidV8($fields, $this->numberConverter, $codec, $this->timeConverter);
}

throw new UnsupportedOperationException(
Expand Down
2 changes: 1 addition & 1 deletion src/Rfc4122/UuidV6.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* Reordered time, or version 6, UUIDs include timestamp, clock sequence, and
* node values that are combined into a 128-bit unsigned integer
*
* @link https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.1 UUID Version 6
* @link https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.6 UUID Version 6
*
* @psalm-immutable
*/
Expand Down
6 changes: 4 additions & 2 deletions src/Rfc4122/UuidV7.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
use Ramsey\Uuid\Uuid;

/**
* Gregorian time, or version 1, UUIDs include timestamp, clock sequence, and node
* values that are combined into a 128-bit unsigned integer
* Unix Epoch time, or version 7, UUIDs include a timestamp in milliseconds
* since the Unix Epoch, along with random bytes
*
* @link https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.7 UUID Version 7
*
* @psalm-immutable
*/
Expand Down
65 changes: 65 additions & 0 deletions src/Rfc4122/UuidV8.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

/**
* This file is part of the ramsey/uuid library
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
* @license http://opensource.org/licenses/MIT MIT
*/

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Ramsey\Uuid\Uuid;

/**
* Version 8, Custom UUIDs provide an RFC 4122 compatible format for
* experimental or vendor-specific uses
*
* The only requirement for version 8 UUIDs is that the version and variant bits
* must be set. Otherwise, implementations are free to set the other bits
* according to their needs. As a result, the uniqueness of version 8 UUIDs is
* implementation-specific and should not be assumed.
*
* @link https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.8 UUID Version 8
*
* @psalm-immutable
*/
final class UuidV8 extends Uuid implements UuidInterface
{
/**
* Creates a version 8 (custom) UUID
*
* @param Rfc4122FieldsInterface $fields The fields from which to construct a UUID
* @param NumberConverterInterface $numberConverter The number converter to use
* for converting hex values to/from integers
* @param CodecInterface $codec The codec to use when encoding or decoding
* UUID strings
* @param TimeConverterInterface $timeConverter The time converter to use
* for converting timestamps extracted from a UUID to unix timestamps
*/
public function __construct(
Rfc4122FieldsInterface $fields,
NumberConverterInterface $numberConverter,
CodecInterface $codec,
TimeConverterInterface $timeConverter
) {
if ($fields->getVersion() !== Uuid::UUID_TYPE_CUSTOM) {
throw new InvalidArgumentException(
'Fields used to create a UuidV8 must represent a '
. 'version 8 (custom) UUID'
);
}

parent::__construct($fields, $numberConverter, $codec, $timeConverter);
}
}
2 changes: 1 addition & 1 deletion src/Rfc4122/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
final class Validator implements ValidatorInterface
{
private const VALID_PATTERN = '\A[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-'
. '[1-7][0-9A-Fa-f]{3}-[ABab89][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}\z';
. '[1-8][0-9A-Fa-f]{3}-[ABab89][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}\z';

/**
* @psalm-return non-empty-string
Expand Down
2 changes: 1 addition & 1 deletion src/Rfc4122/VersionTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private function isCorrectVersion(): bool
Uuid::UUID_TYPE_TIME, Uuid::UUID_TYPE_DCE_SECURITY,
Uuid::UUID_TYPE_HASH_MD5, Uuid::UUID_TYPE_RANDOM,
Uuid::UUID_TYPE_HASH_SHA1, Uuid::UUID_TYPE_REORDERED_TIME,
Uuid::UUID_TYPE_UNIX_TIME => true,
Uuid::UUID_TYPE_UNIX_TIME, Uuid::UUID_TYPE_CUSTOM => true,
default => false,
};
}
Expand Down
41 changes: 38 additions & 3 deletions src/Uuid.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class Uuid implements UuidInterface
* The max UUID is a special form of UUID that is specified to have all 128
* bits set to one
*
* @link https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.4 Max UUID
* @link https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.10 Max UUID
*/
public const MAX = 'ffffffff-ffff-ffff-ffff-ffffffffffff';

Expand Down Expand Up @@ -173,17 +173,22 @@ class Uuid implements UuidInterface
/**
* Version 6 (reordered time) UUID
*
* @link https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.1 UUID Version 6
* @link https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.6 UUID Version 6
*/
public const UUID_TYPE_REORDERED_TIME = 6;

/**
* Version 7 (Unix Epoch time) UUID
*
* @link https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.2 UUID Version 7
* @link https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.7 UUID Version 7
*/
public const UUID_TYPE_UNIX_TIME = 7;

/**
* @link https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.8 UUID Version 8
*/
public const UUID_TYPE_CUSTOM = 8;

/**
* DCE Security principal domain
*
Expand Down Expand Up @@ -690,4 +695,34 @@ public static function uuid7(?DateTimeInterface $dateTime = null): UuidInterface
'The provided factory does not support the uuid7() method',
);
}

/**
* Returns a version 8 (custom) UUID
*
* The bytes provided may contain any value according to your application's
* needs. Be aware, however, that other applications may not understand the
* semantics of the value.
*
* @param string $bytes A 16-byte octet string. This is an open blob
* of data that you may fill with 128 bits of information. Be aware,
* however, bits 48 through 51 will be replaced with the UUID version
* field, and bits 64 and 65 will be replaced with the UUID variant. You
* MUST NOT rely on these bits for your application needs.
*
* @return UuidInterface A UuidInterface instance that represents a
* version 8 UUID
*/
public static function uuid8(string $bytes): UuidInterface
{
$factory = self::getFactory();

if (method_exists($factory, 'uuid8')) {
/** @var UuidInterface */
return $factory->uuid8($bytes);
}

throw new UnsupportedOperationException(
'The provided factory does not support the uuid8() method',
);
}
}
21 changes: 21 additions & 0 deletions src/UuidFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,27 @@ public function uuid7(?DateTimeInterface $dateTime = null): UuidInterface
return $this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_UNIX_TIME);
}

/**
* Returns a version 8 (Custom) UUID
*
* The bytes provided may contain any value according to your application's
* needs. Be aware, however, that other applications may not understand the
* semantics of the value.
*
* @param string $bytes A 16-byte octet string. This is an open blob
* of data that you may fill with 128 bits of information. Be aware,
* however, bits 48 through 51 will be replaced with the UUID version
* field, and bits 64 and 65 will be replaced with the UUID variant. You
* MUST NOT rely on these bits for your application needs.
*
* @return UuidInterface A UuidInterface instance that represents a
* version 8 UUID
*/
public function uuid8(string $bytes): UuidInterface
{
return $this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_CUSTOM);
}

/**
* Returns a Uuid created from the provided byte string
*
Expand Down

0 comments on commit 55d8833

Please sign in to comment.