Skip to content

Commit

Permalink
Merge pull request #16 from infocyph/feature/update
Browse files Browse the repository at this point in the history
Fix+GUID
  • Loading branch information
abmmhasan authored Jun 5, 2024
2 parents d83afd2 + 75d3207 commit 7ab4ae2
Show file tree
Hide file tree
Showing 11 changed files with 105 additions and 40 deletions.
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
![Packagist Version](https://img.shields.io/packagist/v/infocyph/uid)
![Packagist PHP Version Support](https://img.shields.io/packagist/php-v/infocyph/uid)
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/infocyph/uid)
![visitors](https://visitor-badge.laobi.icu/badge?page_id=infocyph.com)

An AIO Unique ID generator written in PHP. Supports,
- UUID (RFC 4122 + Unofficial/Draft)
Expand Down Expand Up @@ -172,6 +173,14 @@ $timeInterface = new DateTime(); // DateTime implements DateTimeInterface
\Infocyph\UID\UUID::v8($node); // check additional section for, how to generate one
```

#### GUID

GUID generator, works in all platform. Generate:
```php
\Infocyph\UID\UUID::guid()
```
_Note: Sending false in only parameter will return the string enclosed with Braces_

#### Additional

- Generate node for further use (with version: 1, 6, 7, 8)
Expand Down Expand Up @@ -212,7 +221,7 @@ $timeInterface = new DateTime(); // DateTime implements DateTimeInterface
// alternatively
\Infocyph\UID\snowflake();
```
- Parse Snowflake ID (get the timestamp, sequence, worker_id, datacenter_id from any Snowflake ID):
- Parse Snowflake ID (get the timestamp, sequence, worker_id, datacenter_id):
```php
// Parse Snowflake ID
// returns [time => DateTimeInterface object, sequence, worker_id, datacenter_id]
Expand All @@ -237,7 +246,7 @@ $timeInterface = new DateTime(); // DateTime implements DateTimeInterface
// alternatively
\Infocyph\UID\sonyflake();
```
- Parse Sonyflake ID (get the timestamp, sequence, machine_id from any Snowflake ID):
- Parse Sonyflake ID (get the timestamp, sequence, machine_id):

```php
// Parse Sonyflake ID
Expand Down
4 changes: 4 additions & 0 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector;

return static function (RectorConfig $rectorConfig): void {
Expand All @@ -12,4 +13,7 @@
$rectorConfig->sets([
constant("Rector\Set\ValueObject\LevelSetList::UP_TO_PHP_82")
]);
$rectorConfig->skip([
StringClassNameToClassConstantRector::class
]);
};
4 changes: 1 addition & 3 deletions src/GetSequence.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ private static function sequence(int $dateTime, string $machineId, string $type)
touch(self::$fileLocation);
}
$handle = fopen(self::$fileLocation, "r+");
if (!flock($handle, LOCK_EX)) {
throw new FileLockException('Could not acquire lock on ' . self::$fileLocation);
}
flock($handle, LOCK_EX) || throw new FileLockException('Could not acquire lock on ' . self::$fileLocation);
$line = fgetcsv($handle);
$sequence = 0;
if ($line && ($line[0] = (int)$line[0]) <= $dateTime) {
Expand Down
36 changes: 18 additions & 18 deletions src/ULID.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,31 +28,31 @@ public static function generate(?DateTimeInterface $dateTime = null): string
{
$time = (int)($dateTime ?? new DateTimeImmutable('now'))->format('Uv');

$isDuplicate = $time === static::$lastGenTime;
static::$lastGenTime = $time;
$isDuplicate = $time === self::$lastGenTime;
self::$lastGenTime = $time;

// Generate time characters
$timeChars = '';
for ($i = static::$timeLength - 1; $i >= 0; $i--) {
$mod = $time % static::$encodingLength;
$timeChars = static::$encodingChars[$mod] . $timeChars;
$time = ($time - $mod) / static::$encodingLength;
for ($i = self::$timeLength - 1; $i >= 0; $i--) {
$mod = $time % self::$encodingLength;
$timeChars = self::$encodingChars[$mod] . $timeChars;
$time = ($time - $mod) / self::$encodingLength;
}

// Generate random characters
$randChars = '';
if (!$isDuplicate) {
for ($i = 0; $i < static::$randomLength; $i++) {
static::$lastRandChars[$i] = random_int(0, 31);
for ($i = 0; $i < self::$randomLength; $i++) {
self::$lastRandChars[$i] = random_int(0, 31);
}
} else {
for ($i = static::$randomLength - 1; $i >= 0 && static::$lastRandChars[$i] === 31; $i--) {
static::$lastRandChars[$i] = 0;
for ($i = self::$randomLength - 1; $i >= 0 && self::$lastRandChars[$i] === 31; $i--) {
self::$lastRandChars[$i] = 0;
}
static::$lastRandChars[$i]++;
self::$lastRandChars[$i]++;
}
for ($i = 0; $i < static::$randomLength; $i++) {
$randChars .= static::$encodingChars[static::$lastRandChars[$i]];
for ($i = 0; $i < self::$randomLength; $i++) {
$randChars .= self::$encodingChars[self::$lastRandChars[$i]];
}

return $timeChars . $randChars;
Expand All @@ -67,19 +67,19 @@ public static function generate(?DateTimeInterface $dateTime = null): string
*/
public static function getTime(string $ulid): DateTimeImmutable
{
if (!static::isValid($ulid)) {
if (!self::isValid($ulid)) {
throw new ULIDException('Invalid ULID string');
}

$timeChars = str_split(strrev(substr($ulid, 0, static::$timeLength)));
$timeChars = str_split(strrev(substr($ulid, 0, self::$timeLength)));

$time = 0;
foreach ($timeChars as $index => $char) {
$encodingIndex = strripos(static::$encodingChars, $char);
$time += ($encodingIndex * static::$encodingLength ** $index);
$encodingIndex = strripos(self::$encodingChars, $char);
$time += ($encodingIndex * self::$encodingLength ** $index);
}

$time = str_split($time, static::$timeLength);
$time = str_split($time, self::$timeLength);

if ($time[0] > (time() + (86400 * 365 * 10))) {
throw new ULIDException('Invalid ULID string: timestamp too large');
Expand Down
34 changes: 32 additions & 2 deletions src/UUID.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,27 @@ public static function v8(string $node = null): string
return self::output(8, $string);
}

/**
* Generates a GUID (Globally Unique Identifier) string.
*
* @param bool $trim Whether to trim the curly braces from the GUID string. Default is true.
* @return string The generated GUID string.
* @throws Exception
*/
public static function guid(bool $trim = true): string
{
if (function_exists('com_create_guid') === true) {
$data = com_create_guid();
return $trim ? trim($data, '{}') : $data;
}

$data = random_bytes(16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
$data = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
return $trim ? $data : "\{$data\}";
}

/**
* Generate unique node.
*
Expand All @@ -192,9 +213,11 @@ public static function getNode(): string
*/
public static function parse(string $uuid): array
{
$uuid = trim($uuid, '{}');
$data = [
'isValid' => self::isValid($uuid),
'version' => null,
'variant' => null,
'time' => null,
'node' => null
];
Expand All @@ -204,10 +227,17 @@ public static function parse(string $uuid): array
}

$uuidData = explode('-', $uuid);
$variantN = hexdec($uuidData[3][0]);
$data['version'] = (int)$uuidData[2][0];
$data['time'] = in_array($data['version'], [1, 6, 7, 8]) ? self::getTime($uuidData, $data['version']) : null;
$data['node'] = $uuidData[4];

$data['variant'] = match (true) {
$variantN <= 7 => 'NCS',
$variantN >= 8 && $variantN <= 11 => 'DCE 1.1, ISO/IEC 11578:1996',
$variantN === 12 || $variantN === 13 => 'Microsoft GUID',
$variantN === 14 => 'Reserved',
default => 'Unknown'
};
return $data;
}

Expand Down Expand Up @@ -235,7 +265,7 @@ private static function prepareNode(int $version, string $node = null): string
*/
private static function isValid(string $uuid): bool
{
return (bool)preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-\d[0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i', $uuid);
return (bool)preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-\d[0-9a-f]{3}-[089a-e][0-9a-f]{3}-[0-9a-f]{12}$/i', $uuid);
}

/**
Expand Down
24 changes: 19 additions & 5 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,21 @@ function uuid8(string $node = null): string
}
}

if (!function_exists(\Infocyph\UID\ulid::class)) {
if (!function_exists('Infocyph\UID\guid')) {
/**
* Generates a GUID (Globally Unique Identifier) string.
*
* @param bool $trim Whether to trim the curly braces from the GUID string. Default is true.
* @return string The generated GUID string.
* @throws Exception
*/
function guid(bool $trim = true): string
{
return UUID::guid($trim);
}
}

if (!function_exists('Infocyph\UID\ulid')) {
/**
* Generates ULID.
*
Expand All @@ -122,7 +136,7 @@ function ulid(?DateTimeInterface $dateTime = null): string
}
}

if (!function_exists(\Infocyph\UID\snowflake::class)) {
if (!function_exists('Infocyph\UID\snowflake')) {
/**
* Generates Snowflake ID.
*
Expand All @@ -137,7 +151,7 @@ function snowflake(int $datacenter = 0, int $workerId = 0): string
}
}

if (!function_exists(\Infocyph\UID\TBSL::class)) {
if (!function_exists('Infocyph\UID\sonyflake')) {
/**
* Generates Sonyflake ID.
*
Expand All @@ -147,11 +161,11 @@ function snowflake(int $datacenter = 0, int $workerId = 0): string
*/
function sonyflake(int $machineId = 0): string
{
return TBSL::generate($machineId);
return Sonyflake::generate($machineId);
}
}

if (!function_exists(\Infocyph\UID\tbsl::class)) {
if (!function_exists('Infocyph\UID\tbsl')) {
/**
* Generates TBSL ID.
*
Expand Down
2 changes: 1 addition & 1 deletion tests/SnowflakeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
test('Basic', function () {
$sf = Snowflake::generate();
$parsed = Snowflake::parse($sf);
expect($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time() + 1)
expect($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time())
->and($parsed['worker_id'])->toBe(0)
->and($parsed['datacenter_id'])->toBe(0);
});
Expand Down
2 changes: 1 addition & 1 deletion tests/SonyflakeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
test('Basic', function () {
$sf = Sonyflake::generate();
$parsed = Sonyflake::parse($sf);
expect($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time() + 1)
expect($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time())
->and($parsed['machine_id'])->toBe(0);
});

2 changes: 1 addition & 1 deletion tests/TBSLTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
test('Basic', function () {
$sf = TBSL::generate();
$parsed = TBSL::parse($sf);
expect($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time() + 1)
expect($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time())
->and($parsed['machineId'])->toBe('00');
});

6 changes: 3 additions & 3 deletions tests/ULIDTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

test('Basic', function () {
$ulid = ULID::generate();
expect($ulid)->toBeString();
expect(ULID::isValid($ulid))->toBeTrue()
->and(ULID::getTime($ulid)->getTimestamp())->toBeBetween(time() - 1, time() + 1);
expect($ulid)->toBeString()
->and(ULID::isValid($ulid))->toBeTrue()
->and(ULID::getTime($ulid)->getTimestamp())->toBeBetween(time() - 1, time());
});
18 changes: 14 additions & 4 deletions tests/UUIDTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
->and($parsed['version'])->toBe(1)
->and($parsed['time'])->not()->toBeNull()
->and($parsed['node'])->toBeString()->not()->toBeNull()
->and($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time() + 1);
->and($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time());
});

$ns = UUID::v4();
Expand Down Expand Up @@ -53,7 +53,7 @@
->and($parsed['version'])->toBe(6)
->and($parsed['time'])->not()->toBeNull()
->and($parsed['node'])->toBeString()->not()->toBeNull()
->and($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time() + 1);
->and($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time());
});

test('UUID v7', function () {
Expand All @@ -64,7 +64,7 @@
->and($parsed['version'])->toBe(7)
->and($parsed['time'])->not()->toBeNull()
->and($parsed['node'])->toBeString()->not()->toBeNull()
->and($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time() + 1);
->and($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time());
});

test('UUID v8', function () {
Expand All @@ -75,6 +75,16 @@
->and($parsed['version'])->toBe(8)
->and($parsed['time'])->not()->toBeNull()
->and($parsed['node'])->toBeString()->not()->toBeNull()
->and($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time() + 1);
->and($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time());
});

test('GUID', function () {
$uid = UUID::guid();
expect($uid)->toBeString();
$parsed = UUID::parse($uid);
expect($parsed['isValid'])->toBeTrue()
->and($parsed['version'])->toBe(4)
->and($parsed['time'])->toBeNull()
->and($parsed['node'])->toBeString()->not()->toBeNull();
});

0 comments on commit 7ab4ae2

Please sign in to comment.