Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <fabien@symfony.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
 
12
namespace Symfony\Polyfill\Uuid;
13
 
14
/**
15
 * @internal
16
 *
17
 * @author Grégoire Pineau <lyrixx@lyrixx.info>
18
 */
19
final class Uuid
20
{
21
    public const UUID_VARIANT_NCS = 0;
22
    public const UUID_VARIANT_DCE = 1;
23
    public const UUID_VARIANT_MICROSOFT = 2;
24
    public const UUID_VARIANT_OTHER = 3;
25
    public const UUID_TYPE_DEFAULT = 0;
26
    public const UUID_TYPE_TIME = 1;
27
    public const UUID_TYPE_MD5 = 3;
28
    public const UUID_TYPE_DCE = 4; // Deprecated alias
29
    public const UUID_TYPE_NAME = 1; // Deprecated alias
30
    public const UUID_TYPE_RANDOM = 4;
31
    public const UUID_TYPE_SHA1 = 5;
32
    public const UUID_TYPE_NULL = -1;
33
    public const UUID_TYPE_INVALID = -42;
34
 
35
    // https://tools.ietf.org/html/rfc4122#section-4.1.4
36
    // 0x01b21dd213814000 is the number of 100-ns intervals between the
37
    // UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
38
    public const TIME_OFFSET_INT = 0x01B21DD213814000;
39
    public const TIME_OFFSET_BIN = "\x01\xb2\x1d\xd2\x13\x81\x40\x00";
40
    public const TIME_OFFSET_COM = "\xfe\x4d\xe2\x2d\xec\x7e\xc0\x00";
41
 
42
    public static function uuid_create($uuid_type = \UUID_TYPE_DEFAULT)
43
    {
44
        if (!is_numeric($uuid_type) && null !== $uuid_type) {
45
            trigger_error(sprintf('uuid_create() expects parameter 1 to be int, %s given', \gettype($uuid_type)), \E_USER_WARNING);
46
 
47
            return null;
48
        }
49
 
50
        switch ((int) $uuid_type) {
51
            case self::UUID_TYPE_NAME:
52
            case self::UUID_TYPE_TIME:
53
                return self::uuid_generate_time();
54
            case self::UUID_TYPE_DCE:
55
            case self::UUID_TYPE_RANDOM:
56
            case self::UUID_TYPE_DEFAULT:
57
                return self::uuid_generate_random();
58
            default:
59
                trigger_error(sprintf("Unknown/invalid UUID type '%d' requested, using default type instead", $uuid_type), \E_USER_WARNING);
60
 
61
                return self::uuid_generate_random();
62
        }
63
    }
64
 
65
    public static function uuid_generate_md5($uuid_ns, $name)
66
    {
67
        if (!\is_string($uuid_ns = self::toString($uuid_ns))) {
68
            trigger_error(sprintf('uuid_generate_md5() expects parameter 1 to be string, %s given', \gettype($uuid_ns)), \E_USER_WARNING);
69
 
70
            return null;
71
        }
72
 
73
        if (!\is_string($name = self::toString($name))) {
74
            trigger_error(sprintf('uuid_generate_md5() expects parameter 2 to be string, %s given', \gettype($name)), \E_USER_WARNING);
75
 
76
            return null;
77
        }
78
 
79
        if (!self::isValid($uuid_ns)) {
80
            if (80000 > \PHP_VERSION_ID) {
81
                return false;
82
            }
83
 
84
            throw new \ValueError('uuid_generate_md5(): Argument #1 ($uuid_ns) UUID expected');
85
        }
86
 
87
        $hash = md5(hex2bin(str_replace('-', '', $uuid_ns)).$name);
88
 
89
        return sprintf('%08s-%04s-3%03s-%04x-%012s',
90
            // 32 bits for "time_low"
91
            substr($hash, 0, 8),
92
            // 16 bits for "time_mid"
93
            substr($hash, 8, 4),
94
            // 16 bits for "time_hi_and_version",
95
            // four most significant bits holds version number 3
96
            substr($hash, 13, 3),
97
            // 16 bits:
98
            // * 8 bits for "clk_seq_hi_res",
99
            // * 8 bits for "clk_seq_low",
100
            hexdec(substr($hash, 16, 4)) & 0x3FFF | 0x8000,
101
            // 48 bits for "node"
102
            substr($hash, 20, 12)
103
        );
104
    }
105
 
106
    public static function uuid_generate_sha1($uuid_ns, $name)
107
    {
108
        if (!\is_string($uuid_ns = self::toString($uuid_ns))) {
109
            trigger_error(sprintf('uuid_generate_sha1() expects parameter 1 to be string, %s given', \gettype($uuid_ns)), \E_USER_WARNING);
110
 
111
            return null;
112
        }
113
 
114
        if (!\is_string($name = self::toString($name))) {
115
            trigger_error(sprintf('uuid_generate_sha1() expects parameter 2 to be string, %s given', \gettype($name)), \E_USER_WARNING);
116
 
117
            return null;
118
        }
119
 
120
        if (!self::isValid($uuid_ns)) {
121
            if (80000 > \PHP_VERSION_ID) {
122
                return false;
123
            }
124
 
125
            throw new \ValueError('uuid_generate_sha1(): Argument #1 ($uuid_ns) UUID expected');
126
        }
127
 
128
        $hash = sha1(hex2bin(str_replace('-', '', $uuid_ns)).$name);
129
 
130
        return sprintf('%08s-%04s-5%03s-%04x-%012s',
131
            // 32 bits for "time_low"
132
            substr($hash, 0, 8),
133
            // 16 bits for "time_mid"
134
            substr($hash, 8, 4),
135
            // 16 bits for "time_hi_and_version",
136
            // four most significant bits holds version number 5
137
            substr($hash, 13, 3),
138
            // 16 bits:
139
            // * 8 bits for "clk_seq_hi_res",
140
            // * 8 bits for "clk_seq_low",
141
            // WARNING: On old libuuid version, there is a bug. 0x0fff is used instead of 0x3fff
142
            // See https://github.com/karelzak/util-linux/commit/d6ddf07d31dfdc894eb8e7e6842aa856342c526e
143
            hexdec(substr($hash, 16, 4)) & 0x3FFF | 0x8000,
144
            // 48 bits for "node"
145
            substr($hash, 20, 12)
146
        );
147
    }
148
 
149
    public static function uuid_is_valid($uuid)
150
    {
151
        if (!\is_string($uuid = self::toString($uuid))) {
152
            trigger_error(sprintf('uuid_is_valid() expects parameter 1 to be string, %s given', \gettype($uuid)), \E_USER_WARNING);
153
 
154
            return null;
155
        }
156
 
157
        return self::isValid($uuid);
158
    }
159
 
160
    public static function uuid_compare($uuid1, $uuid2)
161
    {
162
        if (!\is_string($uuid1 = self::toString($uuid1))) {
163
            trigger_error(sprintf('uuid_compare() expects parameter 1 to be string, %s given', \gettype($uuid1)), \E_USER_WARNING);
164
 
165
            return null;
166
        }
167
 
168
        if (!\is_string($uuid2 = self::toString($uuid2))) {
169
            trigger_error(sprintf('uuid_compare() expects parameter 2 to be string, %s given', \gettype($uuid2)), \E_USER_WARNING);
170
 
171
            return null;
172
        }
173
 
174
        if (!self::isValid($uuid1)) {
175
            if (80000 > \PHP_VERSION_ID) {
176
                return false;
177
            }
178
 
179
            throw new \ValueError('uuid_compare(): Argument #1 ($uuid1) UUID expected');
180
        }
181
 
182
        if (!self::isValid($uuid2)) {
183
            if (80000 > \PHP_VERSION_ID) {
184
                return false;
185
            }
186
 
187
            throw new \ValueError('uuid_compare(): Argument #2 ($uuid2) UUID expected');
188
        }
189
 
190
        return strcasecmp($uuid1, $uuid2);
191
    }
192
 
193
    public static function uuid_is_null($uuid)
194
    {
195
        if (!\is_string($uuid = self::toString($uuid))) {
196
            trigger_error(sprintf('uuid_is_null() expects parameter 1 to be string, %s given', \gettype($uuid)), \E_USER_WARNING);
197
 
198
            return null;
199
        }
200
        if (80000 <= \PHP_VERSION_ID && !self::isValid($uuid)) {
201
            throw new \ValueError('uuid_is_null(): Argument #1 ($uuid) UUID expected');
202
        }
203
 
204
        return '00000000-0000-0000-0000-000000000000' === $uuid;
205
    }
206
 
207
    public static function uuid_type($uuid)
208
    {
209
        if (!\is_string($uuid = self::toString($uuid))) {
210
            trigger_error(sprintf('uuid_type() expects parameter 1 to be string, %s given', \gettype($uuid)), \E_USER_WARNING);
211
 
212
            return null;
213
        }
214
 
215
        if ('00000000-0000-0000-0000-000000000000' === $uuid) {
216
            return self::UUID_TYPE_NULL;
217
        }
218
 
219
        if (null === $parsed = self::parse($uuid)) {
220
            if (80000 > \PHP_VERSION_ID) {
221
                return false;
222
            }
223
 
224
            throw new \ValueError('uuid_type(): Argument #1 ($uuid) UUID expected');
225
        }
226
 
227
        return $parsed['version'];
228
    }
229
 
230
    public static function uuid_variant($uuid)
231
    {
232
        if (!\is_string($uuid = self::toString($uuid))) {
233
            trigger_error(sprintf('uuid_variant() expects parameter 1 to be string, %s given', \gettype($uuid)), \E_USER_WARNING);
234
 
235
            return null;
236
        }
237
 
238
        if ('00000000-0000-0000-0000-000000000000' === $uuid) {
239
            return self::UUID_TYPE_NULL;
240
        }
241
 
242
        if (null === $parsed = self::parse($uuid)) {
243
            if (80000 > \PHP_VERSION_ID) {
244
                return false;
245
            }
246
 
247
            throw new \ValueError('uuid_variant(): Argument #1 ($uuid) UUID expected');
248
        }
249
 
250
        if (($parsed['clock_seq'] & 0x8000) === 0) {
251
            return self::UUID_VARIANT_NCS;
252
        }
253
        if (($parsed['clock_seq'] & 0x4000) === 0) {
254
            return self::UUID_VARIANT_DCE;
255
        }
256
        if (($parsed['clock_seq'] & 0x2000) === 0) {
257
            return self::UUID_VARIANT_MICROSOFT;
258
        }
259
 
260
        return self::UUID_VARIANT_OTHER;
261
    }
262
 
263
    public static function uuid_time($uuid)
264
    {
265
        if (!\is_string($uuid = self::toString($uuid))) {
266
            trigger_error(sprintf('uuid_time() expects parameter 1 to be string, %s given', \gettype($uuid)), \E_USER_WARNING);
267
 
268
            return null;
269
        }
270
 
271
        $parsed = self::parse($uuid);
272
 
273
        if (self::UUID_TYPE_TIME !== ($parsed['version'] ?? null)) {
274
            if (80000 > \PHP_VERSION_ID) {
275
                return false;
276
            }
277
 
278
            throw new \ValueError('uuid_time(): Argument #1 ($uuid) UUID DCE TIME expected');
279
        }
280
 
281
        if (\PHP_INT_SIZE >= 8) {
282
            return intdiv(hexdec($parsed['time']) - self::TIME_OFFSET_INT, 10000000);
283
        }
284
 
285
        $time = str_pad(hex2bin($parsed['time']), 8, "\0", \STR_PAD_LEFT);
286
        $time = self::binaryAdd($time, self::TIME_OFFSET_COM);
287
        $time[0] = $time[0] & "\x7F";
288
 
289
        return (int) substr(self::toDecimal($time), 0, -7);
290
    }
291
 
292
    public static function uuid_mac($uuid)
293
    {
294
        if (!\is_string($uuid = self::toString($uuid))) {
295
            trigger_error(sprintf('uuid_mac() expects parameter 1 to be string, %s given', \gettype($uuid)), \E_USER_WARNING);
296
 
297
            return null;
298
        }
299
 
300
        $parsed = self::parse($uuid);
301
 
302
        if (self::UUID_TYPE_TIME !== ($parsed['version'] ?? null)) {
303
            if (80000 > \PHP_VERSION_ID) {
304
                return false;
305
            }
306
 
307
            throw new \ValueError('uuid_mac(): Argument #1 ($uuid) UUID DCE TIME expected');
308
        }
309
 
310
        return strtr($parsed['node'], 'ABCDEF', 'abcdef');
311
    }
312
 
313
    public static function uuid_parse($uuid)
314
    {
315
        if (!\is_string($uuid = self::toString($uuid))) {
316
            trigger_error(sprintf('uuid_parse() expects parameter 1 to be string, %s given', \gettype($uuid)), \E_USER_WARNING);
317
 
318
            return null;
319
        }
320
 
321
        if (!self::isValid($uuid)) {
322
            if (80000 > \PHP_VERSION_ID) {
323
                return false;
324
            }
325
 
326
            throw new \ValueError('uuid_parse(): Argument #1 ($uuid) UUID expected');
327
        }
328
 
329
        return hex2bin(str_replace('-', '', $uuid));
330
    }
331
 
332
    public static function uuid_unparse($bytes)
333
    {
334
        if (!\is_string($bytes = self::toString($bytes))) {
335
            trigger_error(sprintf('uuid_unparse() expects parameter 1 to be string, %s given', \gettype($bytes)), \E_USER_WARNING);
336
 
337
            return null;
338
        }
339
 
340
        if (16 !== \strlen($bytes)) {
341
            if (80000 > \PHP_VERSION_ID) {
342
                return false;
343
            }
344
 
345
            throw new \ValueError('uuid_unparse(): Argument #1 ($uuid) UUID expected');
346
        }
347
 
348
        $uuid = bin2hex($bytes);
349
        $uuid = substr_replace($uuid, '-', 8, 0);
350
        $uuid = substr_replace($uuid, '-', 13, 0);
351
        $uuid = substr_replace($uuid, '-', 18, 0);
352
 
353
        return substr_replace($uuid, '-', 23, 0);
354
    }
355
 
356
    private static function uuid_generate_random()
357
    {
358
        $uuid = bin2hex(random_bytes(16));
359
 
360
        return sprintf('%08s-%04s-4%03s-%04x-%012s',
361
            // 32 bits for "time_low"
362
            substr($uuid, 0, 8),
363
            // 16 bits for "time_mid"
364
            substr($uuid, 8, 4),
365
            // 16 bits for "time_hi_and_version",
366
            // four most significant bits holds version number 4
367
            substr($uuid, 13, 3),
368
            // 16 bits:
369
            // * 8 bits for "clk_seq_hi_res",
370
            // * 8 bits for "clk_seq_low",
371
            // two most significant bits holds zero and one for variant DCE1.1
372
            hexdec(substr($uuid, 16, 4)) & 0x3FFF | 0x8000,
373
            // 48 bits for "node"
374
            substr($uuid, 20, 12)
375
        );
376
    }
377
 
378
    /**
379
     * @see http://tools.ietf.org/html/rfc4122#section-4.2.2
380
     */
381
    private static function uuid_generate_time()
382
    {
383
        $time = microtime(false);
384
        $time = substr($time, 11).substr($time, 2, 7);
385
 
386
        if (\PHP_INT_SIZE >= 8) {
387
            $time = str_pad(dechex($time + self::TIME_OFFSET_INT), 16, '0', \STR_PAD_LEFT);
388
        } else {
389
            $time = str_pad(self::toBinary($time), 8, "\0", \STR_PAD_LEFT);
390
            $time = self::binaryAdd($time, self::TIME_OFFSET_BIN);
391
            $time = bin2hex($time);
392
        }
393
 
394
        // https://tools.ietf.org/html/rfc4122#section-4.1.5
395
        // We are using a random data for the sake of simplicity: since we are
396
        // not able to get a super precise timeOfDay as a unique sequence
397
        $clockSeq = random_int(0, 0x3FFF);
398
 
399
        static $node;
400
        if (null === $node) {
401
            if (\function_exists('apcu_fetch')) {
402
                $node = apcu_fetch('__symfony_uuid_node');
403
                if (false === $node) {
404
                    $node = sprintf('%06x%06x',
405
                        random_int(0, 0xFFFFFF) | 0x010000,
406
                        random_int(0, 0xFFFFFF)
407
                    );
408
                    apcu_store('__symfony_uuid_node', $node);
409
                }
410
            } else {
411
                $node = sprintf('%06x%06x',
412
                    random_int(0, 0xFFFFFF) | 0x010000,
413
                    random_int(0, 0xFFFFFF)
414
                );
415
            }
416
        }
417
 
418
        return sprintf('%08s-%04s-1%03s-%04x-%012s',
419
            // 32 bits for "time_low"
420
            substr($time, -8),
421
 
422
            // 16 bits for "time_mid"
423
            substr($time, -12, 4),
424
 
425
            // 16 bits for "time_hi_and_version",
426
            // four most significant bits holds version number 1
427
            substr($time, -15, 3),
428
 
429
            // 16 bits:
430
            // * 8 bits for "clk_seq_hi_res",
431
            // * 8 bits for "clk_seq_low",
432
            // two most significant bits holds zero and one for variant DCE1.1
433
            $clockSeq | 0x8000,
434
 
435
            // 48 bits for "node"
436
            $node
437
        );
438
    }
439
 
440
    private static function isValid($uuid)
441
    {
442
        return (bool) preg_match('{^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$}Di', $uuid);
443
    }
444
 
445
    private static function parse($uuid)
446
    {
447
        if (!preg_match('{^(?<time_low>[0-9a-f]{8})-(?<time_mid>[0-9a-f]{4})-(?<version>[0-9a-f])(?<time_hi>[0-9a-f]{3})-(?<clock_seq>[0-9a-f]{4})-(?<node>[0-9a-f]{12})$}Di', $uuid, $matches)) {
448
            return null;
449
        }
450
 
451
        return [
452
            'time' => '0'.$matches['time_hi'].$matches['time_mid'].$matches['time_low'],
453
            'version' => hexdec($matches['version']),
454
            'clock_seq' => hexdec($matches['clock_seq']),
455
            'node' => $matches['node'],
456
        ];
457
    }
458
 
459
    private static function toString($v)
460
    {
461
        if (\is_string($v) || null === $v || (\is_object($v) ? method_exists($v, '__toString') : \is_scalar($v))) {
462
            return (string) $v;
463
        }
464
 
465
        return $v;
466
    }
467
 
468
    private static function toBinary($digits)
469
    {
470
        $bytes = '';
471
        $count = \strlen($digits);
472
 
473
        while ($count) {
474
            $quotient = [];
475
            $remainder = 0;
476
 
477
            for ($i = 0; $i !== $count; ++$i) {
478
                $carry = $digits[$i] + $remainder * 10;
479
                $digit = $carry >> 8;
480
                $remainder = $carry & 0xFF;
481
 
482
                if ($digit || $quotient) {
483
                    $quotient[] = $digit;
484
                }
485
            }
486
 
487
            $bytes = \chr($remainder).$bytes;
488
            $count = \count($digits = $quotient);
489
        }
490
 
491
        return $bytes;
492
    }
493
 
494
    private static function toDecimal($bytes)
495
    {
496
        $digits = '';
497
        $bytes = array_values(unpack('C*', $bytes));
498
 
499
        while ($count = \count($bytes)) {
500
            $quotient = [];
501
            $remainder = 0;
502
 
503
            for ($i = 0; $i !== $count; ++$i) {
504
                $carry = $bytes[$i] + ($remainder << 8);
505
                $digit = (int) ($carry / 10);
506
                $remainder = $carry % 10;
507
 
508
                if ($digit || $quotient) {
509
                    $quotient[] = $digit;
510
                }
511
            }
512
 
513
            $digits = $remainder.$digits;
514
            $bytes = $quotient;
515
        }
516
 
517
        return $digits;
518
    }
519
 
520
    private static function binaryAdd($a, $b)
521
    {
522
        $sum = 0;
523
        for ($i = 7; 0 <= $i; --$i) {
524
            $sum += \ord($a[$i]) + \ord($b[$i]);
525
            $a[$i] = \chr($sum & 0xFF);
526
            $sum >>= 8;
527
        }
528
 
529
        return $a;
530
    }
531
}