diff --git a/src/Provider/Node/StaticNodeProvider.php b/src/Provider/Node/StaticNodeProvider.php new file mode 100644 index 0000000..b07a291 --- /dev/null +++ b/src/Provider/Node/StaticNodeProvider.php @@ -0,0 +1,79 @@ + + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Uuid\Provider\Node; + +use Ramsey\Uuid\Exception\InvalidArgumentException; +use Ramsey\Uuid\Provider\NodeProviderInterface; +use Ramsey\Uuid\Type\Hexadecimal; + +use function dechex; +use function hexdec; +use function str_pad; +use function substr; + +use const STR_PAD_LEFT; + +/** + * StaticNodeProvider provides a static node value with the multicast bit set + * + * @link http://tools.ietf.org/html/rfc4122#section-4.5 RFC 4122, ยง 4.5: Node IDs that Do Not Identify the Host + */ +class StaticNodeProvider implements NodeProviderInterface +{ + /** + * @var Hexadecimal + */ + private $node; + + /** + * @param Hexadecimal $node The static node value to use + */ + public function __construct(Hexadecimal $node) + { + if (strlen($node->toString()) > 12) { + throw new InvalidArgumentException( + 'Static node value cannot be greater than 12 hexadecimal characters' + ); + } + + $this->node = $this->setMulticastBit($node); + } + + /** + * @inheritDoc + */ + public function getNode() + { + return $this->node->toString(); + } + + /** + * Set the multicast bit for the static node value + */ + private function setMulticastBit(Hexadecimal $node): Hexadecimal + { + $nodeHex = str_pad($node->toString(), 12, '0', STR_PAD_LEFT); + $firstOctet = substr($nodeHex, 0, 2); + + $firstOctet = str_pad( + dechex(hexdec($firstOctet) | 0x01), + 2, + '0', + STR_PAD_LEFT + ); + + return new Hexadecimal($firstOctet . substr($nodeHex, 2)); + } +} diff --git a/src/Rfc4122/UuidV2.php b/src/Rfc4122/UuidV2.php index 7a3e8dc..fdbc1ec 100644 --- a/src/Rfc4122/UuidV2.php +++ b/src/Rfc4122/UuidV2.php @@ -22,6 +22,8 @@ use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface; use Ramsey\Uuid\Type\Integer as IntegerObject; use Ramsey\Uuid\Uuid; +use function hexdec; + /** * DCE Security version, or version 2, UUIDs include local domain identifier, * local ID for the specified domain, and node values that are combined into a diff --git a/tests/Provider/Node/StaticNodeProviderTest.php b/tests/Provider/Node/StaticNodeProviderTest.php new file mode 100644 index 0000000..72d0eec --- /dev/null +++ b/tests/Provider/Node/StaticNodeProviderTest.php @@ -0,0 +1,58 @@ +assertSame($expectedNode, $staticNode->getNode()); + } + + /** + * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification + */ + public function provideNodeForTest(): array + { + return [ + [ + 'node' => new Hexadecimal('0'), + 'expectedNode' => '010000000000', + ], + [ + 'node' => new Hexadecimal('1'), + 'expectedNode' => '010000000001', + ], + [ + 'node' => new Hexadecimal('f2ffffffffff'), + 'expectedNode' => 'f3ffffffffff', + ], + [ + 'node' => new Hexadecimal('ffffffffffff'), + 'expectedNode' => 'ffffffffffff', + ], + ]; + } + + public function testStaticNodeThrowsExceptionForTooLongNode(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( + 'Static node value cannot be greater than 12 hexadecimal characters' + ); + + new StaticNodeProvider(new Hexadecimal('1000000000000')); + } +}