diff --git a/src/snowflake/src/IdGeneratorInterface.php b/src/snowflake/src/IdGeneratorInterface.php index 91f12c6b1..e21780d28 100644 --- a/src/snowflake/src/IdGeneratorInterface.php +++ b/src/snowflake/src/IdGeneratorInterface.php @@ -19,4 +19,6 @@ interface IdGeneratorInterface const LEVEL_MILLISECOND = 2; public function generate(?Meta $meta = null): int; + + public function degenerate(int $id): Meta; } diff --git a/src/snowflake/src/Meta.php b/src/snowflake/src/Meta.php index 77f3a8029..8dd73e954 100644 --- a/src/snowflake/src/Meta.php +++ b/src/snowflake/src/Meta.php @@ -26,11 +26,6 @@ class Meta const MACHINE_ID_BITS = 7; - /** - * @var int - */ - public $beginSecond; - /** * @var int [0, 15] */ @@ -51,7 +46,7 @@ class Meta */ public $sequence; - public function __construct(int $beginSecond, int $businessId, int $dataCenterId, int $machineId, int $sequence) + public function __construct(int $businessId, int $dataCenterId, int $machineId, int $sequence) { if ($businessId < 0 || $businessId > $this->maxBusinessId()) { throw new SnowflakeException('Business Id can\'t be greater than 15 or less than 0'); @@ -66,29 +61,28 @@ class Meta throw new SnowflakeException('Sequence can\'t be greater than 4096 or less than 0'); } - $this->beginSecond = $beginSecond; $this->businessId = $businessId; $this->dataCenterId = $dataCenterId; $this->machineId = $machineId; $this->sequence = $sequence; } - private function maxMachineId() + protected function maxMachineId() { return -1 ^ (-1 << self::MACHINE_ID_BITS); } - private function maxDataCenterId() + protected function maxDataCenterId() { return -1 ^ (-1 << self::DATA_CENTER_ID_BITS); } - private function maxBusinessId() + protected function maxBusinessId() { return -1 ^ (-1 << self::BUSINESS_ID_BITS); } - private function maxSequence() + protected function maxSequence() { return -1 ^ (-1 << self::SEQUENCE_BITS); } diff --git a/src/snowflake/src/RandomMetaGenerator.php b/src/snowflake/src/RandomMetaGenerator.php index 3880cacb1..2b409bffe 100644 --- a/src/snowflake/src/RandomMetaGenerator.php +++ b/src/snowflake/src/RandomMetaGenerator.php @@ -23,6 +23,6 @@ class RandomMetaGenerator implements MetaGeneratorInterface $machineId = rand(0, 127); $sequence = ($this->sequence++) % 4096; - return new Meta(15657528167, $businessId, $dataCenterId, $machineId, $sequence); + return new Meta($businessId, $dataCenterId, $machineId, $sequence); } } diff --git a/src/snowflake/src/Snowflake.php b/src/snowflake/src/Snowflake.php index 9ecd3f1ed..59478021d 100644 --- a/src/snowflake/src/Snowflake.php +++ b/src/snowflake/src/Snowflake.php @@ -29,10 +29,11 @@ class Snowflake implements IdGeneratorInterface */ protected $beginSecond; - public function __construct(MetaGeneratorInterface $metaGenerator, int $level = self::LEVEL_MILLISECOND) + public function __construct(MetaGeneratorInterface $metaGenerator, int $level = self::LEVEL_MILLISECOND, int $beginSecond = 1565712000) { $this->metaGenerator = $metaGenerator; $this->level = $level; + $this->beginSecond = $level == self::LEVEL_SECOND ? $beginSecond : $beginSecond * 1000; } public function generate(?Meta $meta = null): int @@ -41,12 +42,52 @@ class Snowflake implements IdGeneratorInterface $timestamp = $this->getTimestamp(); - $t = ($timestamp - $this->getBeginTimestamp($meta)) << (Meta::SEQUENCE_BITS + Meta::MACHINE_ID_BITS + Meta::DATA_CENTER_ID_BITS + Meta::BUSINESS_ID_BITS); - $b = $meta->businessId << (Meta::SEQUENCE_BITS + Meta::MACHINE_ID_BITS + Meta::DATA_CENTER_ID_BITS); - $dc = $meta->dataCenterId << (Meta::SEQUENCE_BITS + Meta::MACHINE_ID_BITS); - $worker = $meta->machineId << Meta::SEQUENCE_BITS; + $timestamp = ($timestamp - $this->beginSecond) << $this->getTimestampShift(); + $businessId = $meta->businessId << $this->getBusinessIdShift(); + $dataCenterId = $meta->dataCenterId << $this->getDataCenterShift(); + $machineId = $meta->machineId << $this->getMachineIdShift(); - return $t | $b | $dc | $worker | $meta->sequence; + return $timestamp | $businessId | $dataCenterId | $machineId | $meta->sequence; + } + + public function degenerate(int $id): Meta + { + $timestamp = $id >> $this->getTimestampShift(); + $businessId = $id >> $this->getBusinessIdShift(); + $dataCenterId = $id >> $this->getDataCenterShift(); + $machineId = $id >> $this->getMachineIdShift(); + + return new Meta( + $timestamp << Meta::BUSINESS_ID_BITS ^ $businessId, + $businessId << Meta::DATA_CENTER_ID_BITS ^ $dataCenterId, + $dataCenterId << Meta::MACHINE_ID_BITS ^ $machineId, + $machineId << Meta::SEQUENCE_BITS ^ $id + ); + } + + protected function getTimestampShift() + { + return Meta::SEQUENCE_BITS + Meta::MACHINE_ID_BITS + Meta::DATA_CENTER_ID_BITS + Meta::BUSINESS_ID_BITS; + } + + protected function getBusinessIdShift() + { + return Meta::SEQUENCE_BITS + Meta::MACHINE_ID_BITS + Meta::DATA_CENTER_ID_BITS; + } + + protected function getDataCenterShift() + { + return Meta::SEQUENCE_BITS + Meta::MACHINE_ID_BITS; + } + + protected function getMachineIdShift() + { + return Meta::SEQUENCE_BITS; + } + + protected function getMaxNumber(int $shift) + { + return -1 ^ (-1 << $shift); } protected function getTimestamp(): int @@ -57,19 +98,6 @@ class Snowflake implements IdGeneratorInterface return intval(microtime(true) * 1000); } - protected function getBeginTimestamp(Meta $meta) - { - if (is_int($this->beginSecond)) { - return $this->beginSecond; - } - - if ($this->level == self::LEVEL_SECOND) { - return $meta->beginSecond; - } - - return $this->beginSecond = intval($meta->beginSecond * 1000); - } - protected function meta(?Meta $meta = null): Meta { if (is_null($meta)) { diff --git a/src/snowflake/tests/GeneratorTest.php b/src/snowflake/tests/GeneratorTest.php index e2667ccb1..d8cea86e1 100644 --- a/src/snowflake/tests/GeneratorTest.php +++ b/src/snowflake/tests/GeneratorTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace HyperfTest\Snowflake; +use Hyperf\Snowflake\Meta; use Hyperf\Snowflake\RandomMetaGenerator; use Hyperf\Snowflake\Snowflake; use PHPUnit\Framework\TestCase; @@ -27,4 +28,13 @@ class GeneratorTest extends TestCase $generator = new Snowflake(new RandomMetaGenerator()); $this->assertTrue(is_int($generator->generate())); } + + public function testDegenerate() + { + $generator = new Snowflake(new RandomMetaGenerator()); + + $id = $generator->generate(); + + $this->assertInstanceOf(Meta::class, $generator->degenerate($id)); + } }