Completed
Push — custom-comb ( 1f8dae...6fdabb )
by Marcel
28:25 queued 26:08
created

CustomCombGenerator::maxTimestamp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace UMA\Uuid;
6
7
class CustomCombGenerator implements UuidGenerator
8
{
9
    /**
10
     * @var UuidGenerator
11
     */
12
    private $generator;
13
14
    /**
15
     * @var int
16
     */
17
    private $exponent;
18
19
    /**
20
     * @var int
21
     */
22
    private $epoch;
23
24
    /**
25
     * @var int
26
     */
27
    private $span;
28
29
    /**
30
     * @param UuidGenerator      $generator
31
     * @param \DateTimeImmutable $epoch
32
     * @param int                $span
33
     * @param int                $granularity
34
     *
35
     * @throws \InvalidArgumentException When $epoch is a date in the future.
36
     * @throws \InvalidArgumentException When $span is outside the [1, 16] range.
37
     * @throws \InvalidArgumentException When $granularity is outside the [0, 6] range.
38
     */
39 8
    public function __construct(UuidGenerator $generator, \DateTimeImmutable $epoch, int $span, int $granularity)
40
    {
41 8
        if (new \DateTimeImmutable('now') < $epoch) {
42
            throw new \InvalidArgumentException('$epoch must be in the past. Got timestamp: ' . $epoch->getTimestamp());
43
        }
44
45 8
        if (!\in_array($span, \range(1, 16), true)) {
46
            throw new \InvalidArgumentException('$span must be in the [1, 16] range. Got: ' . $span);
47
        }
48
49 8
        if (!\in_array($granularity, \range(0, 6), true)) {
50 1
            throw new \InvalidArgumentException('$granularity must be in the [0, 6] range. Got: ' . $granularity);
51
        }
52
53 7
        $this->generator = $generator;
54 7
        $this->exponent = 10 ** $granularity;
55 7
        $this->epoch = $epoch->getTimestamp() * $this->exponent;
0 ignored issues
show
Documentation Bug introduced by
It seems like $epoch->getTimestamp() * $this->exponent can also be of type double. However, the property $epoch is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
56 7
        $this->span = 2 * $span;
57 7
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62 1
    public function generate(string $name = null): Uuid
63
    {
64 1
        $head = $this->procrust($this->timestamp());
65 1
        $tail = \substr($this->generator->generate()->asBytes(), -16 + ($this->span / 2));
66
67 1
        return Uuid::fromBytes($head . $tail);
68
    }
69
70
    /**
71
     * Returns the exact date on which the 48 most significant bits of
72
     * the UUIDs will overflow for the chosen $granularity.
73
     *
74
     * The higher the granularity the better is the output of the
75
     * generator, but the overflow date also looms sooner.
76
     */
77 7
    public function getOverflowDate(): \DateTimeImmutable
78
    {
79 7
        $maxTimestamp = (int)(($this->maxTimestamp() - $this->epoch)/$this->exponent);
80
81 7
        return new \DateTimeImmutable("@$maxTimestamp UTC");
82
    }
83
84
    /**
85
     * Returns $timestamp "procrusted" to 6 bytes.
86
     *
87
     * If the timestamp is smaller than 6 bytes, leading 0 bits are appended.
88
     * If the timestamp is larger than 6 bytes, its least significant bits are chopped off.
89
     *
90
     * The returned string is raw binary (each character encodes 8 bits)
91
     * and has always the same size -- 6 bytes.
92
     *
93
     * @example '59b7d71f'      => 0x000059b7d71f
94
     * @example '3812e6738'     => 0x0003812e6738
95
     * @example '230bd00838'    => 0x00230bd00838
96
     * @example '15e76205236'   => 0x015e76205236
97
     * @example 'db09d433621'   => 0x0db09d433621
98
     * @example '88e624a01d4c'  => 0x88e624a01d4c
99
     * @example '558fd6e4124fb' => 0x558fd6e4124f
100
     */
101 1
    private function procrust(string $timestamp): string
102
    {
103 1
        return \pack("H{$this->span}", \str_pad(\substr($timestamp, 0, $this->span), $this->span, '0', STR_PAD_LEFT));
104
    }
105
106
    /**
107
     * Returns the current unix timestamp as a hex-encoded string (that is, each character
108
     * encodes 4 bits) with variable precision, ranging from second to microsecond.
109
     *
110
     * The length of the string varies depending on the $granularity chosen. This is how
111
     * the exact same reading from microtime() looks like for all 7 possible granularity
112
     * levels (0 through 6):
113
     *
114
     * @example '59b7d71f'
115
     * @example '3812e6738'
116
     * @example '230bd00838'
117
     * @example '15e76205236'
118
     * @example 'db09d433621'
119
     * @example '88e624a01d4c'
120
     * @example '558fd6e4124fb'
121
     */
122 8
    private function timestamp(): string
123
    {
124 8
        return \dechex((int)(\microtime(true) * $this->exponent) - $this->epoch);
125
    }
126
127 7
    private function maxTimestamp(): int
128
    {
129 7
        return \hexdec(\str_repeat('f', \max($this->span, \strlen($this->timestamp()))));
130
    }
131
}
132