142 lines
4.3 KiB
PHP
142 lines
4.3 KiB
PHP
<?php
|
|
|
|
/**
|
|
* This file is part of the ramsey/uuid library
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*
|
|
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
|
|
* @license http://opensource.org/licenses/MIT MIT
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Ramsey\Uuid\Generator;
|
|
|
|
use Ramsey\Uuid\Converter\NumberConverterInterface;
|
|
use Ramsey\Uuid\Exception\DceSecurityException;
|
|
use Ramsey\Uuid\Provider\DceSecurityProviderInterface;
|
|
use Ramsey\Uuid\Type\Hexadecimal;
|
|
use Ramsey\Uuid\Type\Integer as IntegerObject;
|
|
use Ramsey\Uuid\Uuid;
|
|
|
|
use function hex2bin;
|
|
use function in_array;
|
|
use function pack;
|
|
use function str_pad;
|
|
use function strlen;
|
|
use function substr_replace;
|
|
|
|
use const STR_PAD_LEFT;
|
|
|
|
/**
|
|
* DceSecurityGenerator generates strings of binary data based on a local
|
|
* domain, local identifier, node ID, clock sequence, and the current time
|
|
*/
|
|
class DceSecurityGenerator implements DceSecurityGeneratorInterface
|
|
{
|
|
private const DOMAINS = [
|
|
Uuid::DCE_DOMAIN_PERSON,
|
|
Uuid::DCE_DOMAIN_GROUP,
|
|
Uuid::DCE_DOMAIN_ORG,
|
|
];
|
|
|
|
/**
|
|
* Upper bounds for the clock sequence in DCE Security UUIDs.
|
|
*/
|
|
private const CLOCK_SEQ_HIGH = 63;
|
|
|
|
/**
|
|
* Lower bounds for the clock sequence in DCE Security UUIDs.
|
|
*/
|
|
private const CLOCK_SEQ_LOW = 0;
|
|
|
|
public function __construct(
|
|
private NumberConverterInterface $numberConverter,
|
|
private TimeGeneratorInterface $timeGenerator,
|
|
private DceSecurityProviderInterface $dceSecurityProvider
|
|
) {
|
|
}
|
|
|
|
public function generate(
|
|
int $localDomain,
|
|
?IntegerObject $localIdentifier = null,
|
|
?Hexadecimal $node = null,
|
|
?int $clockSeq = null
|
|
): string {
|
|
if (!in_array($localDomain, self::DOMAINS)) {
|
|
throw new DceSecurityException(
|
|
'Local domain must be a valid DCE Security domain'
|
|
);
|
|
}
|
|
|
|
if ($localIdentifier && $localIdentifier->isNegative()) {
|
|
throw new DceSecurityException(
|
|
'Local identifier out of bounds; it must be a value between 0 and 4294967295'
|
|
);
|
|
}
|
|
|
|
if ($clockSeq > self::CLOCK_SEQ_HIGH || $clockSeq < self::CLOCK_SEQ_LOW) {
|
|
throw new DceSecurityException(
|
|
'Clock sequence out of bounds; it must be a value between 0 and 63'
|
|
);
|
|
}
|
|
|
|
switch ($localDomain) {
|
|
case Uuid::DCE_DOMAIN_ORG:
|
|
if ($localIdentifier === null) {
|
|
throw new DceSecurityException(
|
|
'A local identifier must be provided for the org domain'
|
|
);
|
|
}
|
|
|
|
break;
|
|
case Uuid::DCE_DOMAIN_PERSON:
|
|
if ($localIdentifier === null) {
|
|
$localIdentifier = $this->dceSecurityProvider->getUid();
|
|
}
|
|
|
|
break;
|
|
case Uuid::DCE_DOMAIN_GROUP:
|
|
default:
|
|
if ($localIdentifier === null) {
|
|
$localIdentifier = $this->dceSecurityProvider->getGid();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
$identifierHex = $this->numberConverter->toHex($localIdentifier->toString());
|
|
|
|
// The maximum value for the local identifier is 0xffffffff, or
|
|
// 4294967295. This is 8 hexadecimal digits, so if the length of
|
|
// hexadecimal digits is greater than 8, we know the value is greater
|
|
// than 0xffffffff.
|
|
if (strlen($identifierHex) > 8) {
|
|
throw new DceSecurityException(
|
|
'Local identifier out of bounds; it must be a value between 0 and 4294967295'
|
|
);
|
|
}
|
|
|
|
$domainByte = pack('n', $localDomain)[1];
|
|
$identifierBytes = (string) hex2bin(str_pad($identifierHex, 8, '0', STR_PAD_LEFT));
|
|
|
|
if ($node instanceof Hexadecimal) {
|
|
$node = $node->toString();
|
|
}
|
|
|
|
// Shift the clock sequence 8 bits to the left, so it matches 0x3f00.
|
|
if ($clockSeq !== null) {
|
|
$clockSeq = $clockSeq << 8;
|
|
}
|
|
|
|
$bytes = $this->timeGenerator->generate($node, $clockSeq);
|
|
|
|
// Replace bytes in the time-based UUID with DCE Security values.
|
|
$bytes = substr_replace($bytes, $identifierBytes, 0, 4);
|
|
|
|
return substr_replace($bytes, $domainByte, 9, 1);
|
|
}
|
|
}
|