| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 | <?php/* * This file is part of the godruoyi/php-snowflake. * * (c) Godruoyi <g@godruoyi.com> * * This source file is subject to the MIT license that is bundled. */namespace Godruoyi\Snowflake;class Snowflake{    const MAX_TIMESTAMP_LENGTH = 41;    const MAX_DATACENTER_LENGTH = 5;    const MAX_WORKID_LENGTH = 5;    const MAX_SEQUENCE_LENGTH = 12;    const MAX_FIRST_LENGTH = 1;    /**     * The data center id.     *     * @var int     */    protected $datacenter;    /**     * The worker id.     *     * @var int     */    protected $workerid;    /**     * The Sequence Resolver instance.     *     * @var \Godruoyi\Snowflake\SequenceResolver|null     */    protected $sequence;    /**     * The start timestamp.     *     * @var int     */    protected $startTime;    /**     * Default sequence resolver.     *     * @var \Godruoyi\Snowflake\SequenceResolver|null     */    protected $defaultSequenceResolver;    /**     * Build Snowflake Instance.     *     * @param int $datacenter     * @param int $workerid     */    public function __construct(int $datacenter = null, int $workerid = null)    {        $maxDataCenter = -1 ^ (-1 << self::MAX_DATACENTER_LENGTH);        $maxWorkId = -1 ^ (-1 << self::MAX_WORKID_LENGTH);        // If not set datacenter or workid, we will set a default value to use.        $this->datacenter = $datacenter > $maxDataCenter || $datacenter < 0 ? mt_rand(0, 31) : $datacenter;        $this->workerid = $workerid > $maxWorkId || $workerid < 0 ? mt_rand(0, 31) : $workerid;    }    /**     * Get snowflake id.     *     * @return int     */    public function id()    {        $currentTime = $this->getCurrentMicrotime();        while (($sequence = $this->callResolver($currentTime)) > (-1 ^ (-1 << self::MAX_SEQUENCE_LENGTH))) {            usleep(1);            $currentTime = $this->getCurrentMicrotime();        }        $workerLeftMoveLength = self::MAX_SEQUENCE_LENGTH;        $datacenterLeftMoveLength = self::MAX_WORKID_LENGTH + $workerLeftMoveLength;        $timestampLeftMoveLength = self::MAX_DATACENTER_LENGTH + $datacenterLeftMoveLength;        return (string) ((($currentTime - $this->getStartTimeStamp()) << $timestampLeftMoveLength)            | ($this->datacenter << $datacenterLeftMoveLength)            | ($this->workerid << $workerLeftMoveLength)            | ($sequence));    }    /**     * Parse snowflake id.     *     * @param string $id     *     * @return array     */    public function parseId(string $id, $transform = false): array    {        $id = decbin($id);        $data = [            'timestamp' => substr($id, 0, -22),            'sequence' => substr($id, -12),            'workerid' => substr($id, -17, 5),            'datacenter' => substr($id, -22, 5),        ];        return $transform ? array_map(function ($value) {            return bindec($value);        }, $data) : $data;    }    /**     * Get current microtime timestamp.     *     * @return int     */    public function getCurrentMicrotime()    {        return floor(microtime(true) * 1000) | 0;    }    /**     * Set start time (millisecond).     *     * @param int $startTime     */    public function setStartTimeStamp(int $startTime)    {        $missTime = $this->getCurrentMicrotime() - $startTime;        if ($missTime < 0 || $missTime > ($maxTimeDiff = ((1 << self::MAX_TIMESTAMP_LENGTH) - 1))) {            throw new \Exception('The starttime cannot be greater than current time and the maximum time difference is '.$maxTimeDiff);        }        $this->startTime = $startTime;        return $this;    }    /**     * Get start timestamp (millisecond), If not set default to 2019-08-08 08:08:08.     *     * @return int     */    public function getStartTimeStamp()    {        if ($this->startTime > 0) {            return $this->startTime;        }        // We set a default start time if you not set.        $defaultTime = '2019-08-08 08:08:08';        return strtotime($defaultTime) * 1000;    }    /**     * Set Sequence Resolver.     *     * @param SequenceResolver|callable $sequence     */    public function setSequenceResolver($sequence)    {        $this->sequence = $sequence;        return $this;    }    /**     * Get Sequence Resolver.     *     * @return \Godruoyi\Snowflake\SequenceResolver|callable|null     */    public function getSequenceResolver()    {        return $this->sequence;    }    /**     * Get Default Sequence Resolver.     *     * @return \Godruoyi\Snowflake\SequenceResolver     */    public function getDefaultSequenceResolver(): SequenceResolver    {        return $this->defaultSequenceResolver ?: $this->defaultSequenceResolver = new RandomSequenceResolver();    }    /**     * Call resolver.     *     * @param callable|\Godruoyi\Snowflake\SequenceResolver $resolver     * @param int                                           $maxSequence     *     * @return int     */    protected function callResolver($currentTime)    {        $resolver = $this->getSequenceResolver();        if (is_callable($resolver)) {            return $resolver($currentTime);        }        return is_null($resolver) || !($resolver instanceof SequenceResolver)            ? $this->getDefaultSequenceResolver()->sequence($currentTime)            : $resolver->sequence($currentTime);    }}
 |