Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
83.33% covered (warning)
83.33%
10 / 12
66.67% covered (warning)
66.67%
4 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Uuid
83.33% covered (warning)
83.33%
10 / 12
66.67% covered (warning)
66.67%
4 / 6
8.30
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 v5
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 v4
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isValid
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 format
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 bytes
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
3.14
1<?php
2
3declare(strict_types=1);
4
5namespace LambdaTwelve\OneRecord\Model;
6
7use InvalidArgumentException;
8
9/**
10 * The two UUID flavours the package needs: version 5 (name-based, so the same
11 * input always yields the same id) and version 4 (random, from an injectable
12 * byte source so tests stay deterministic).
13 */
14final class Uuid
15{
16    /** RFC 4122 namespace for URLs; our names are IRIs. */
17    public const string NAMESPACE_URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
18
19    private function __construct() {}
20
21    public static function v5(string $namespace, string $name): string
22    {
23        $hash = sha1(self::bytes($namespace) . $name);
24
25        return self::format(substr($hash, 0, 32), 5);
26    }
27
28    /**
29     * @param callable(int): string $randomBytes
30     */
31    public static function v4(callable $randomBytes): string
32    {
33        return self::format(bin2hex($randomBytes(16)), 4);
34    }
35
36    public static function isValid(string $value): bool
37    {
38        return preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i', $value) === 1;
39    }
40
41    private static function format(string $hex, int $version): string
42    {
43        $hex = substr($hex, 0, 12) . dechex($version) . substr($hex, 13, 3)
44            . dechex((hexdec($hex[16]) & 0x3) | 0x8) . substr($hex, 17, 15);
45
46        return \sprintf('%s-%s-%s-%s-%s', substr($hex, 0, 8), substr($hex, 8, 4), substr($hex, 12, 4), substr($hex, 16, 4), substr($hex, 20, 12));
47    }
48
49    private static function bytes(string $uuid): string
50    {
51        $hex = str_replace('-', '', $uuid);
52        if (\strlen($hex) !== 32 || !ctype_xdigit($hex)) {
53            throw new InvalidArgumentException(\sprintf('Not a UUID: "%s".', $uuid));
54        }
55
56        return (string) hex2bin($hex);
57    }
58}