1<?php
2
3/**
4 * This file is part of the ramsey/uuid library
5 *
6 * For the full copyright and license information, please view the LICENSE
7 * file that was distributed with this source code.
8 *
9 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
10 * @license http://opensource.org/licenses/MIT MIT
11 */
12
13declare(strict_types=1);
14
15namespace Ramsey\Uuid\Rfc4122;
16
17use Ramsey\Uuid\Exception\InvalidBytesException;
18use Ramsey\Uuid\Uuid;
19
20use function decbin;
21use function str_pad;
22use function strlen;
23use function strpos;
24use function substr;
25use function unpack;
26
27use const STR_PAD_LEFT;
28
29/**
30 * Provides common functionality for handling the variant, as defined by RFC 4122
31 *
32 * @psalm-immutable
33 */
34trait VariantTrait
35{
36    /**
37     * Returns the bytes that comprise the fields
38     */
39    abstract public function getBytes(): string;
40
41    /**
42     * Returns the variant identifier, according to RFC 4122, for the given bytes
43     *
44     * The following values may be returned:
45     *
46     * - `0` -- Reserved, NCS backward compatibility.
47     * - `2` -- The variant specified in RFC 4122.
48     * - `6` -- Reserved, Microsoft Corporation backward compatibility.
49     * - `7` -- Reserved for future definition.
50     *
51     * @link https://tools.ietf.org/html/rfc4122#section-4.1.1 RFC 4122, § 4.1.1: Variant
52     *
53     * @return int The variant identifier, according to RFC 4122
54     */
55    public function getVariant(): int
56    {
57        if (strlen($this->getBytes()) !== 16) {
58            throw new InvalidBytesException('Invalid number of bytes');
59        }
60
61        $parts = unpack('n*', $this->getBytes());
62
63        // $parts[5] is a 16-bit, unsigned integer containing the variant bits
64        // of the UUID. We convert this integer into a string containing a
65        // binary representation, padded to 16 characters. We analyze the first
66        // three characters (three most-significant bits) to determine the
67        // variant.
68        $binary = str_pad(
69            decbin((int) $parts[5]),
70            16,
71            '0',
72            STR_PAD_LEFT
73        );
74
75        $msb = substr($binary, 0, 3);
76
77        if ($msb === '111') {
78            $variant = Uuid::RESERVED_FUTURE;
79        } elseif ($msb === '110') {
80            $variant = Uuid::RESERVED_MICROSOFT;
81        } elseif (strpos($msb, '10') === 0) {
82            $variant = Uuid::RFC_4122;
83        } else {
84            $variant = Uuid::RESERVED_NCS;
85        }
86
87        return $variant;
88    }
89}
90