diff --git a/.gitattributes b/.gitattributes index 88a22b9..ec91691 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,8 +1,8 @@ -tests export-ignore -docs export-ignore -.github export-ignore -.readthedocs.yaml export-ignore -captainhook.json export-ignore -phpunit.xml export-ignore -pint.json export-ignore -rector.php export-ignore +tests export-ignore +docs export-ignore +.github export-ignore +.readthedocs.yaml export-ignore +captainhook.json export-ignore +phpunit.xml export-ignore +pint.json export-ignore +rector.php export-ignore diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 550fca2..1161b61 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,6 @@ -version: 2 -updates: - - package-ecosystem: "composer" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "daily" +version: 2 +updates: + - package-ecosystem: "composer" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d07b23a..0fc349d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,40 +1,42 @@ -name: "build" - -on: - schedule: - - cron: '0 0 * * 0' - push: - branches: [ '*' ] - pull_request: - branches: [ "main", "master", "develop" ] - -jobs: - run: - runs-on: ${{ matrix.operating-system }} - strategy: - matrix: - operating-system: [ ubuntu-latest ] - php-versions: [ '8.2' , '8.3' ] - name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-versions }} - tools: composer:v2 - coverage: xdebug - - - name: Validate composer.json and composer.lock - run: composer validate --strict - - - name: Install dependencies - run: composer install --no-interaction --prefer-dist --no-progress - - - name: Package Audit - run: composer audit - - - name: Test +name: "Security & Standards" + +on: + schedule: + - cron: '0 0 * * 0' + push: + branches: [ '*' ] + pull_request: + branches: [ "main", "master", "develop" ] + +jobs: + run: + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: [ ubuntu-latest ] + php-versions: [ '8.2' , '8.3' ] + dependency-version: [ prefer-lowest, prefer-stable ] + + name: PHP ${{ matrix.php-versions }} - ${{ matrix.operating-system }} - ${{ matrix.dependency-version }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + tools: composer:v2 + coverage: xdebug + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Install dependencies + run: composer install --no-interaction --prefer-dist --no-progress + + - name: Package Audit + run: composer audit + + - name: Test run: composer tests \ No newline at end of file diff --git a/.gitignore b/.gitignore index 59ed94e..b3a9a9f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -vendor -.idea -composer.lock -git-story_media -test.php +vendor +.idea +composer.lock +git-story_media +test.php diff --git a/LICENSE b/LICENSE index 7ceb1ae..7cabe02 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2024 Infocyph - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2024 Infocyph + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index bbb8913..2403149 100644 --- a/README.md +++ b/README.md @@ -1,305 +1,305 @@ -# UID - -[![build](https://github.com/infocyph/UID/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/infocyph/UID/actions/workflows/build.yml) -[![Codacy Badge](https://app.codacy.com/project/badge/Grade/cec4c7ed0e274b3da4571973732a363e)](https://app.codacy.com/gh/infocyph/UID/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) -![Packagist Downloads](https://img.shields.io/packagist/dt/infocyph/uid) -[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) -![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) - -An AIO Unique ID generator written in PHP. Supports, -- UUID (RFC 4122 + Unofficial/Draft) -- ULID (ulid specification) -- Snowflake ID (Twitter Snowflake) -- Sonyflake ID (Snowflake Inspired, ported from Golang) -- TBSL (tbsl spec) - -## Table of contents - - - -* [Prerequisites](#prerequisites) -* [Installation](#installation) -* [Usage](#usage) - * [UUID](#uuid-universal-unique-identifier) - * [UUID v1](#uuid-v1-time-based-uuid) - * [UUID v3](#uuid-v3-namespace-based-uuid) - * [UUID v4](#uuid-v4-random-uuid) - * [UUID v5](#uuid-v5-namespace-based-uuid) - * [UUID v6](#uuid-v6-draft-basedunofficial-time-based-uuid) - * [UUID v7](#uuid-v7-draft-basedunofficial-time-based-uuid) - * [UUID v8](#uuid-v8-draft-basedunofficial-time-based-uuid-lexicographically-sortable) - * [Additional](#additional) - * [ULID](#ulid-universally-unique-lexicographically-sortable-identifier) - * [Snowflake ID](#snowflake-id) - * [Sonyflake ID](#sonyflake-id) - * [TBSL ID](#tbsl-time-based-keys-with-lexicographic-sorting-library-exclusive) -* [Benchmark](#benchmark) -* [Support](#support) -* [References](#references) - - - -## Prerequisites - -Language: PHP 8/+ - -## Installation - -``` -composer require infocyph/uid -``` - -## Usage - -### UUID (Universal Unique Identifier) - -The node specific UUID's `$node` parameter (1, 6, 7, 8) is optional. If not provided, it will be generated randomly. -But, if you wanna track the source of the UUIDs, you should use it (pre-define the node per server & pass it accordingly). - -#### UUID v1: Time-based UUID. - -- Generate v1 UUID -```php -// Get v1 UUID -\Infocyph\UID\UUID::v1(); -// alternatively can also use -\Infocyph\UID\uuid1(); -``` -- Pass your pre-generated node (for node specific UUID) -```php -\Infocyph\UID\UUID::v1($node); // check additional section for how to generate one -``` - -#### UUID v3: Namespace based UUID. - -- Generate v3 UUID -```php -// Get v3 UUID -\Infocyph\UID\UUID::v3('a pre-generated UUID', 'the string you wanna get UUID for'); -// alternatively can also use -\Infocyph\UID\uuid3(); -``` -- Get v3 UUID for predefined namespaces (RFC4122 #Appendix C) -```php -/** -* You can pass X500, URL, OID, DNS (check RFC4122 #Appendix C) -*/ -\Infocyph\UID\UUID::v3('url', 'abmmhasan.github.io'); -``` -- You can generate a UUID & use as namespace as well -```php -\Infocyph\UID\UUID::v3('fa1700dd-828c-4d1b-8e6d-a6104807da90', 'abmmhasan.github.io'); -``` - -#### UUID v4: Random UUID. - -- Generate v4 UUID -```php -// Get v4 UUID (completely random) -\Infocyph\UID\UUID::v4(); -// alternatively can also use -\Infocyph\UID\uuid4(); -``` - -#### UUID v5: Namespace based UUID. - -- Generate v5 UUID -```php -// Get v5 UUID -\Infocyph\UID\UUID::v5('a pre-generated UUID', 'the string you wanna get UUID for'); -// alternatively can also use -\Infocyph\UID\uuid5(); -``` -- Get v5 UUID for predefined namespaces (RFC4122 #Appendix C) -```php -/** -* You can pass X500, URL, OID, DNS (check RFC4122 #Appendix C) -*/ -\Infocyph\UID\UUID::v5('url', 'abmmhasan.github.io'); -``` -- You can generate a UUID & use as namespace as well -```php -\Infocyph\UID\UUID::v5('fa1700dd-828c-4d1b-8e6d-a6104807da90', 'abmmhasan.github.io'); -``` - -#### UUID v6 (draft-based/unofficial): Time-based UUID. - -- Generate v6 UUID -```php -// Get v6 UUID (Time based) -\Infocyph\UID\UUID::v6(); -// alternatively can also use -\Infocyph\UID\uuid6(); -``` -- Get v6 UUID using predefined node: -```php -\Infocyph\UID\UUID::v6($node); // check additional section for how to generate one -``` - -#### UUID v7 (draft-based/unofficial): Time-based UUID. - -- Generate v7 UUID -```php -// Get v7 UUID for current time -\Infocyph\UID\UUID::v7(); -// alternatively can also use -\Infocyph\UID\uuid7(); -``` -- Get v7 UUID using predefined node: -```php -\Infocyph\UID\UUID::v7(null, $node); // check additional section for, how to generate one -``` -- Or if you wanna get v7 UUID using predefined time: -```php -$timeInterface = new DateTime(); // DateTime implements DateTimeInterface -\Infocyph\UID\UUID::v7($timeInterface); -``` -- You can combine both parameters together as well. - -#### UUID v8 (draft-based/unofficial): Time-based UUID. Lexicographically sortable. - -- Generate v8 UUID -```php -// Get v8 UUID -\Infocyph\UID\UUID::v8(); -// alternatively can also use -\Infocyph\UID\uuid8(); -``` -- Get v8 UUID using predefined node: -```php -\Infocyph\UID\UUID::v8($node); // check additional section for, how to generate one -``` - -#### Additional - -- Generate node for further use (with version: 1, 6, 7, 8) -```php -\Infocyph\UID\UUID::getNode(); -``` -- Parse any UUID string: -```php -\Infocyph\UID\UUID::parse($uuid); // returns ['isValid', 'version', 'time', 'node'] -``` - -### ULID (Universally Unique Lexicographically Sortable Identifier) - -- Generating ULID is very simple, -```php -\Infocyph\UID\ULID::generate(); -``` -- Or if you wanna get ULID for specific time: -```php -\Infocyph\UID\ULID::generate(new DateTimeImmutable('2020-01-01 00:00:00')); -``` -- Extract datetime from any ULID string: -```php -\Infocyph\UID\ULID::getTime($ulid); // returns DateTimeInterface object -``` -- Validate any ULID string: -```php -\Infocyph\UID\ULID::isValid($ulid); // true/false -``` - -### Snowflake ID - -- Generate a new Snowflake ID (You can also pass your pre-generated worker_id & datacenter_id for server/module detection): -```php -// Get Snowflake ID -// optionally you can set worker_id & datacenter_id, for server/module detection -\Infocyph\UID\Snowflake::generate(); -// alternatively -\Infocyph\UID\snowflake(); -``` -- Parse Snowflake ID (get the timestamp, sequence, worker_id, datacenter_id from any Snowflake ID): -```php -// Parse Snowflake ID -// returns [time => DateTimeInterface object, sequence, worker_id, datacenter_id] -\Infocyph\UID\Snowflake::parse($snowflake); -``` -- Specify start time for Snowflake ID (a Snowflake ID is unique upto 69 years from the start date): -```php -// By default, the start time is set to `2020-01-01 00:00:00`, which is changeable -// but if changed, this should always stay same as long as your project lives -// & must call this before any Snowflake call (generate/parse) -\Infocyph\UID\Snowflake::setStartTimeStamp('2000-01-01 00:00:00'); -``` - -### Sonyflake ID - -- Generate a new Sonyflake ID (You can also pass your pre-generated machine_id for server detection): - -```php -// Get Sonyflake ID -// optionally set machine_id, for server detection -\Infocyph\UID\Sonyflake::generate(); -// alternatively -\Infocyph\UID\sonyflake(); -``` -- Parse Sonyflake ID (get the timestamp, sequence, machine_id from any Snowflake ID): - -```php -// Parse Sonyflake ID -// returns [time => DateTimeInterface object, sequence, machine_id] -\Infocyph\UID\Sonyflake::parse($sonyflake); -``` -- Specify start time for Sonyflake ID (a Sonyflake ID is unique upto 174 years from the start date): - -```php -// By default, the start time is set to `2020-01-01 00:00:00`, which is changeable -// but if changed, this should always stay same as long as your project lives -// & must call this before any Sonyflake call (generate/parse) -\Infocyph\UID\Sonyflake::setStartTimeStamp('2000-01-01 00:00:00'); -``` - -### TBSL: Time-Based Keys with Lexicographic Sorting (library exclusive) - -- Get TBSL ID (You can also pass your pre-generated machine_id for server detection): - -```php -// Get TBSL ID -// optionally set machine_id, for server detection -\Infocyph\UID\TBSL::generate(); -// alternatively -\Infocyph\UID\tbsl(); -``` -- Parse TBSL ID (get the timestamp, machine_id): -```php -// Parse TBSL -// returns [isValid, time => DateTimeInterface object, machine_id] -\Infocyph\UID\TBSL::parse($tbsl); -``` - -## Benchmark - -| Type | Generation time (ms) | -|:---------------------------|:---------------------------------------------------------------------------------:| -| UUID v1 (random node) | 0.00411 (ramsey/Uuid: 0.18753) | -| UUID v1 (fixed node) | 0.00115 (ramsey/Uuid: 0.17386) | -| UUID v3 (custom namespace) | 0.00257 (ramsey/Uuid: 0.03015) | -| UUID v4 | 0.00362 (ramsey/Uuid: 0.16501) | -| UUID v5 (custom namespace) | 0.00108 (ramsey/Uuid: 0.03658) | -| UUID v6 (random node) | 0.00444 (ramsey/Uuid: 0.17469) | -| UUID v6 (fixed node) | 0.00164 (ramsey/Uuid: 0.17382) | -| UUID v7 (random node) | 0.00503 (ramsey/Uuid: 0.16278) | -| UUID v7 (fixed node)** | 0.00154 (ramsey/Uuid: 0.18753) | -| UUID v8 (random node) | 0.00505 (ramsey/Uuid: N/A) | -| UUID v8 (fixed node) | 0.00209 (ramsey/Uuid: 0.16029 _*predefined random node, not usable as signature_) | -| ULID | 0.00506 (robinvdvleuten/php-ulid: 0.00508) | -| TBSL | 0.0034 (library unique) | -| Snowflake | 0.13951 (godruoyi/php-snowflake: 0.14856) | -| Sonyflake | 0.13821 (godruoyi/php-snowflake: 0.14583) | - -## Support - -Having trouble? Create an issue! - -## References - -- UUID (RFC4122): https://tools.ietf.org/html/rfc4122 -- UUID (Drafts/Proposals): https://datatracker.ietf.org/doc/draft-ietf-uuidrev-rfc4122bis -- ULID: https://github.com/ulid/spec -- Snowflake ID: https://github.com/twitter-archive/snowflake/tree/snowflake-2010 -- Sonyflake ID: https://github.com/sony/sonyflake -- TBSL ID: https://github.com/infocyph/UID/blob/main/TBSL.md +# UID + +[![build](https://github.com/infocyph/UID/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/infocyph/UID/actions/workflows/build.yml) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/cec4c7ed0e274b3da4571973732a363e)](https://app.codacy.com/gh/infocyph/UID/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) +![Packagist Downloads](https://img.shields.io/packagist/dt/infocyph/uid) +[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) +![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) + +An AIO Unique ID generator written in PHP. Supports, +- UUID (RFC 4122 + Unofficial/Draft) +- ULID (ulid specification) +- Snowflake ID (Twitter Snowflake) +- Sonyflake ID (Snowflake Inspired, ported from Golang) +- TBSL (tbsl spec) + +## Table of contents + + + +* [Prerequisites](#prerequisites) +* [Installation](#installation) +* [Usage](#usage) + * [UUID](#uuid-universal-unique-identifier) + * [UUID v1](#uuid-v1-time-based-uuid) + * [UUID v3](#uuid-v3-namespace-based-uuid) + * [UUID v4](#uuid-v4-random-uuid) + * [UUID v5](#uuid-v5-namespace-based-uuid) + * [UUID v6](#uuid-v6-draft-basedunofficial-time-based-uuid) + * [UUID v7](#uuid-v7-draft-basedunofficial-time-based-uuid) + * [UUID v8](#uuid-v8-draft-basedunofficial-time-based-uuid-lexicographically-sortable) + * [Additional](#additional) + * [ULID](#ulid-universally-unique-lexicographically-sortable-identifier) + * [Snowflake ID](#snowflake-id) + * [Sonyflake ID](#sonyflake-id) + * [TBSL ID](#tbsl-time-based-keys-with-lexicographic-sorting-library-exclusive) +* [Benchmark](#benchmark) +* [Support](#support) +* [References](#references) + + + +## Prerequisites + +Language: PHP 8/+ + +## Installation + +``` +composer require infocyph/uid +``` + +## Usage + +### UUID (Universal Unique Identifier) + +The node specific UUID's `$node` parameter (1, 6, 7, 8) is optional. If not provided, it will be generated randomly. +But, if you wanna track the source of the UUIDs, you should use it (pre-define the node per server & pass it accordingly). + +#### UUID v1: Time-based UUID. + +- Generate v1 UUID +```php +// Get v1 UUID +\Infocyph\UID\UUID::v1(); +// alternatively can also use +\Infocyph\UID\uuid1(); +``` +- Pass your pre-generated node (for node specific UUID) +```php +\Infocyph\UID\UUID::v1($node); // check additional section for how to generate one +``` + +#### UUID v3: Namespace based UUID. + +- Generate v3 UUID +```php +// Get v3 UUID +\Infocyph\UID\UUID::v3('a pre-generated UUID', 'the string you wanna get UUID for'); +// alternatively can also use +\Infocyph\UID\uuid3(); +``` +- Get v3 UUID for predefined namespaces (RFC4122 #Appendix C) +```php +/** +* You can pass X500, URL, OID, DNS (check RFC4122 #Appendix C) +*/ +\Infocyph\UID\UUID::v3('url', 'abmmhasan.github.io'); +``` +- You can generate a UUID & use as namespace as well +```php +\Infocyph\UID\UUID::v3('fa1700dd-828c-4d1b-8e6d-a6104807da90', 'abmmhasan.github.io'); +``` + +#### UUID v4: Random UUID. + +- Generate v4 UUID +```php +// Get v4 UUID (completely random) +\Infocyph\UID\UUID::v4(); +// alternatively can also use +\Infocyph\UID\uuid4(); +``` + +#### UUID v5: Namespace based UUID. + +- Generate v5 UUID +```php +// Get v5 UUID +\Infocyph\UID\UUID::v5('a pre-generated UUID', 'the string you wanna get UUID for'); +// alternatively can also use +\Infocyph\UID\uuid5(); +``` +- Get v5 UUID for predefined namespaces (RFC4122 #Appendix C) +```php +/** +* You can pass X500, URL, OID, DNS (check RFC4122 #Appendix C) +*/ +\Infocyph\UID\UUID::v5('url', 'abmmhasan.github.io'); +``` +- You can generate a UUID & use as namespace as well +```php +\Infocyph\UID\UUID::v5('fa1700dd-828c-4d1b-8e6d-a6104807da90', 'abmmhasan.github.io'); +``` + +#### UUID v6 (draft-based/unofficial): Time-based UUID. + +- Generate v6 UUID +```php +// Get v6 UUID (Time based) +\Infocyph\UID\UUID::v6(); +// alternatively can also use +\Infocyph\UID\uuid6(); +``` +- Get v6 UUID using predefined node: +```php +\Infocyph\UID\UUID::v6($node); // check additional section for how to generate one +``` + +#### UUID v7 (draft-based/unofficial): Time-based UUID. + +- Generate v7 UUID +```php +// Get v7 UUID for current time +\Infocyph\UID\UUID::v7(); +// alternatively can also use +\Infocyph\UID\uuid7(); +``` +- Get v7 UUID using predefined node: +```php +\Infocyph\UID\UUID::v7(null, $node); // check additional section for, how to generate one +``` +- Or if you wanna get v7 UUID using predefined time: +```php +$timeInterface = new DateTime(); // DateTime implements DateTimeInterface +\Infocyph\UID\UUID::v7($timeInterface); +``` +- You can combine both parameters together as well. + +#### UUID v8 (draft-based/unofficial): Time-based UUID. Lexicographically sortable. + +- Generate v8 UUID +```php +// Get v8 UUID +\Infocyph\UID\UUID::v8(); +// alternatively can also use +\Infocyph\UID\uuid8(); +``` +- Get v8 UUID using predefined node: +```php +\Infocyph\UID\UUID::v8($node); // check additional section for, how to generate one +``` + +#### Additional + +- Generate node for further use (with version: 1, 6, 7, 8) +```php +\Infocyph\UID\UUID::getNode(); +``` +- Parse any UUID string: +```php +\Infocyph\UID\UUID::parse($uuid); // returns ['isValid', 'version', 'time', 'node'] +``` + +### ULID (Universally Unique Lexicographically Sortable Identifier) + +- Generating ULID is very simple, +```php +\Infocyph\UID\ULID::generate(); +``` +- Or if you wanna get ULID for specific time: +```php +\Infocyph\UID\ULID::generate(new DateTimeImmutable('2020-01-01 00:00:00')); +``` +- Extract datetime from any ULID string: +```php +\Infocyph\UID\ULID::getTime($ulid); // returns DateTimeInterface object +``` +- Validate any ULID string: +```php +\Infocyph\UID\ULID::isValid($ulid); // true/false +``` + +### Snowflake ID + +- Generate a new Snowflake ID (You can also pass your pre-generated worker_id & datacenter_id for server/module detection): +```php +// Get Snowflake ID +// optionally you can set worker_id & datacenter_id, for server/module detection +\Infocyph\UID\Snowflake::generate(); +// alternatively +\Infocyph\UID\snowflake(); +``` +- Parse Snowflake ID (get the timestamp, sequence, worker_id, datacenter_id from any Snowflake ID): +```php +// Parse Snowflake ID +// returns [time => DateTimeInterface object, sequence, worker_id, datacenter_id] +\Infocyph\UID\Snowflake::parse($snowflake); +``` +- Specify start time for Snowflake ID (a Snowflake ID is unique upto 69 years from the start date): +```php +// By default, the start time is set to `2020-01-01 00:00:00`, which is changeable +// but if changed, this should always stay same as long as your project lives +// & must call this before any Snowflake call (generate/parse) +\Infocyph\UID\Snowflake::setStartTimeStamp('2000-01-01 00:00:00'); +``` + +### Sonyflake ID + +- Generate a new Sonyflake ID (You can also pass your pre-generated machine_id for server detection): + +```php +// Get Sonyflake ID +// optionally set machine_id, for server detection +\Infocyph\UID\Sonyflake::generate(); +// alternatively +\Infocyph\UID\sonyflake(); +``` +- Parse Sonyflake ID (get the timestamp, sequence, machine_id from any Snowflake ID): + +```php +// Parse Sonyflake ID +// returns [time => DateTimeInterface object, sequence, machine_id] +\Infocyph\UID\Sonyflake::parse($sonyflake); +``` +- Specify start time for Sonyflake ID (a Sonyflake ID is unique upto 174 years from the start date): + +```php +// By default, the start time is set to `2020-01-01 00:00:00`, which is changeable +// but if changed, this should always stay same as long as your project lives +// & must call this before any Sonyflake call (generate/parse) +\Infocyph\UID\Sonyflake::setStartTimeStamp('2000-01-01 00:00:00'); +``` + +### TBSL: Time-Based Keys with Lexicographic Sorting (library exclusive) + +- Get TBSL ID (You can also pass your pre-generated machine_id for server detection): + +```php +// Get TBSL ID +// optionally set machine_id, for server detection +\Infocyph\UID\TBSL::generate(); +// alternatively +\Infocyph\UID\tbsl(); +``` +- Parse TBSL ID (get the timestamp, machine_id): +```php +// Parse TBSL +// returns [isValid, time => DateTimeInterface object, machine_id] +\Infocyph\UID\TBSL::parse($tbsl); +``` + +## Benchmark + +| Type | Generation time (ms) | +|:---------------------------|:---------------------------------------------------------------------------------:| +| UUID v1 (random node) | 0.00411 (ramsey/Uuid: 0.18753) | +| UUID v1 (fixed node) | 0.00115 (ramsey/Uuid: 0.17386) | +| UUID v3 (custom namespace) | 0.00257 (ramsey/Uuid: 0.03015) | +| UUID v4 | 0.00362 (ramsey/Uuid: 0.16501) | +| UUID v5 (custom namespace) | 0.00108 (ramsey/Uuid: 0.03658) | +| UUID v6 (random node) | 0.00444 (ramsey/Uuid: 0.17469) | +| UUID v6 (fixed node) | 0.00164 (ramsey/Uuid: 0.17382) | +| UUID v7 (random node) | 0.00503 (ramsey/Uuid: 0.16278) | +| UUID v7 (fixed node)** | 0.00154 (ramsey/Uuid: 0.18753) | +| UUID v8 (random node) | 0.00505 (ramsey/Uuid: N/A) | +| UUID v8 (fixed node) | 0.00209 (ramsey/Uuid: 0.16029 _*predefined random node, not usable as signature_) | +| ULID | 0.00506 (robinvdvleuten/php-ulid: 0.00508) | +| TBSL | 0.0034 (library unique) | +| Snowflake | 0.13951 (godruoyi/php-snowflake: 0.14856) | +| Sonyflake | 0.13821 (godruoyi/php-snowflake: 0.14583) | + +## Support + +Having trouble? Create an issue! + +## References + +- UUID (RFC4122): https://tools.ietf.org/html/rfc4122 +- UUID (Drafts/Proposals): https://datatracker.ietf.org/doc/draft-ietf-uuidrev-rfc4122bis +- ULID: https://github.com/ulid/spec +- Snowflake ID: https://github.com/twitter-archive/snowflake/tree/snowflake-2010 +- Sonyflake ID: https://github.com/sony/sonyflake +- TBSL ID: https://github.com/infocyph/UID/blob/main/TBSL.md diff --git a/SECURITY.md b/SECURITY.md index 56d709e..f9b2b64 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,14 +1,14 @@ -# Security Policy - -![Libraries.io dependency status for GitHub repo](https://img.shields.io/librariesio/github/infocyph/uid) - -## Supported Versions - -| Version | Supported | -|---------|--------------------| -| 2.x | :white_check_mark: | -| 1.x | :x: | - -## Reporting a Vulnerability - -Report any vulnerabilities to the [security advisory tracker](https://github.com/infocyph/uid/issues)! +# Security Policy + +![Libraries.io dependency status for GitHub repo](https://img.shields.io/librariesio/github/infocyph/uid) + +## Supported Versions + +| Version | Supported | +|---------|--------------------| +| 2.x | :white_check_mark: | +| 1.x | :x: | + +## Reporting a Vulnerability + +Report any vulnerabilities to the [security advisory tracker](https://github.com/infocyph/uid/issues)! diff --git a/TBSL.md b/TBSL.md index be27ae8..66a2d18 100644 --- a/TBSL.md +++ b/TBSL.md @@ -1,9 +1,9 @@ -# TBSL - -A 20 character alphanumeric ID generated by a time-based lexicographically sortable algorithm. It stores time in first -13 characters and machine ID in 14th-15th characters. Rest 5 is randomly generated. - -| Time Identifier | Machine Identifier | Random Character | -|:---------------:|:------------------:|:----------------:| -| 13 | 2 | 5 | - +# TBSL + +A 20 character alphanumeric ID generated by a time-based lexicographically sortable algorithm. It stores time in first +13 characters and machine ID in 14th-15th characters. Rest 5 is randomly generated. + +| Time Identifier | Machine Identifier | Random Character | +|:---------------:|:------------------:|:----------------:| +| 13 | 2 | 5 | + diff --git a/composer.json b/composer.json index c15d147..d21dc55 100644 --- a/composer.json +++ b/composer.json @@ -1,77 +1,77 @@ -{ - "name": "infocyph/uid", - "description": "UUID (RFC 4122 + Unofficial/Draft), ULID, Snowflake ID, Sonyflake ID, TBSL (library exclusive) generator!", - "type": "library", - "license": "MIT", - "keywords": [ - "uuid", - "ulid", - "snowflake", - "sonyflake", - "tbsl", - "id", - "key", - "unique", - "rfc4122" - ], - "authors": [ - { - "name": "abmmhasan", - "email": "abmmhasan@gmail.com" - } - ], - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Infocyph\\UID\\": "src/" - } - }, - "minimum-stability": "stable", - "prefer-stable": true, - "require": { - "php": ">=8.0", - "ext-bcmath": "*" - }, - "config": { - "sort-packages": true, - "optimize-autoloader": true, - "allow-plugins": { - "pestphp/pest-plugin": true - } - }, - "replace": { - "abmmhasan/uuid": "*" - }, - "require-dev": { - "captainhook/captainhook": "^5.23", - "laravel/pint": "^1.16", - "pestphp/pest": "^2.34", - "rector/rector": "^1.1", - "symfony/var-dumper": "^7.0" - }, - "scripts": { - "test:code": "pest --parallel --processes=10", - "test:refactor": "rector process --dry-run", - "test:lint": "pint --test", - "test:hook": [ - "captainhook hook:post-checkout", - "captainhook hook:pre-commit", - "captainhook hook:post-commit", - "captainhook hook:post-merge", - "captainhook hook:post-rewrite", - "captainhook hook:pre-push" - ], - "tests": [ - "@test:code", - "@test:lint", - "@test:refactor" - ], - "git:hook": "captainhook install --only-enabled -nf", - "test": "pest", - "refactor": "rector process", - "lint": "pint", - "post-install-cmd": "@git:hook" - } -} +{ + "name": "infocyph/uid", + "description": "UUID (RFC 4122 + Unofficial/Draft), ULID, Snowflake ID, Sonyflake ID, TBSL (library exclusive) generator!", + "type": "library", + "license": "MIT", + "keywords": [ + "uuid", + "ulid", + "snowflake", + "sonyflake", + "tbsl", + "id", + "key", + "unique", + "rfc4122" + ], + "authors": [ + { + "name": "abmmhasan", + "email": "abmmhasan@gmail.com" + } + ], + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Infocyph\\UID\\": "src/" + } + }, + "minimum-stability": "stable", + "prefer-stable": true, + "require": { + "php": ">=8.0", + "ext-bcmath": "*" + }, + "config": { + "sort-packages": true, + "optimize-autoloader": true, + "allow-plugins": { + "pestphp/pest-plugin": true + } + }, + "replace": { + "abmmhasan/uuid": "*" + }, + "require-dev": { + "captainhook/captainhook": "^5.23", + "laravel/pint": "^1.16", + "pestphp/pest": "^2.34", + "rector/rector": "^1.1", + "symfony/var-dumper": "^7.0" + }, + "scripts": { + "test:code": "pest --parallel --processes=10", + "test:refactor": "rector process --dry-run", + "test:lint": "pint --test", + "test:hook": [ + "captainhook hook:post-checkout", + "captainhook hook:pre-commit", + "captainhook hook:post-commit", + "captainhook hook:post-merge", + "captainhook hook:post-rewrite", + "captainhook hook:pre-push" + ], + "tests": [ + "@test:code", + "@test:lint", + "@test:refactor" + ], + "git:hook": "captainhook install --only-enabled -nf", + "test": "pest", + "refactor": "rector process", + "lint": "pint", + "post-autoload-dump": "@git:hook" + } +} diff --git a/pint.json b/pint.json index e44f7b4..cd88583 100644 --- a/pint.json +++ b/pint.json @@ -1,10 +1,10 @@ -{ - "preset": "psr12", - "exclude": [ - "tests" - ], - "notPath": [ - "rector.php", - "test.php" - ] +{ + "preset": "psr12", + "exclude": [ + "tests" + ], + "notPath": [ + "rector.php", + "test.php" + ] } \ No newline at end of file diff --git a/tests/ArchTest.php b/tests/ArchTest.php index 609b6b6..b189a79 100644 --- a/tests/ArchTest.php +++ b/tests/ArchTest.php @@ -1,9 +1,9 @@ -each->not()->toBeUsed(); -}); - -test('No echo statements', function () { - expect(['echo', 'print'])->each->not()->toBeUsed(); -}); +each->not()->toBeUsed(); +}); + +test('No echo statements', function () { + expect(['echo', 'print'])->each->not()->toBeUsed(); +}); diff --git a/tests/SnowflakeTest.php b/tests/SnowflakeTest.php index f0d0003..3dfe24d 100644 --- a/tests/SnowflakeTest.php +++ b/tests/SnowflakeTest.php @@ -1,12 +1,12 @@ -getTimestamp())->toBeBetween(time() - 1, time() + 1) - ->and($parsed['worker_id'])->toBe(0) - ->and($parsed['datacenter_id'])->toBe(0); -}); - +getTimestamp())->toBeBetween(time() - 1, time() + 1) + ->and($parsed['worker_id'])->toBe(0) + ->and($parsed['datacenter_id'])->toBe(0); +}); + diff --git a/tests/SonyflakeTest.php b/tests/SonyflakeTest.php index 0b49334..4ed6b88 100644 --- a/tests/SonyflakeTest.php +++ b/tests/SonyflakeTest.php @@ -1,11 +1,11 @@ -getTimestamp())->toBeBetween(time() - 1, time() + 1) - ->and($parsed['machine_id'])->toBe(0); -}); - +getTimestamp())->toBeBetween(time() - 1, time() + 1) + ->and($parsed['machine_id'])->toBe(0); +}); + diff --git a/tests/TBSLTest.php b/tests/TBSLTest.php index 04b3778..df43b19 100644 --- a/tests/TBSLTest.php +++ b/tests/TBSLTest.php @@ -1,11 +1,11 @@ -getTimestamp())->toBeBetween(time() - 1, time() + 1) - ->and($parsed['machineId'])->toBe('00'); -}); - +getTimestamp())->toBeBetween(time() - 1, time() + 1) + ->and($parsed['machineId'])->toBe('00'); +}); + diff --git a/tests/ULIDTest.php b/tests/ULIDTest.php index d530a89..99df658 100644 --- a/tests/ULIDTest.php +++ b/tests/ULIDTest.php @@ -1,10 +1,10 @@ -toBeString(); - expect(ULID::isValid($ulid))->toBeTrue() - ->and(ULID::getTime($ulid)->getTimestamp())->toBeBetween(time() - 1, time() + 1); -}); +toBeString(); + expect(ULID::isValid($ulid))->toBeTrue() + ->and(ULID::getTime($ulid)->getTimestamp())->toBeBetween(time() - 1, time() + 1); +}); diff --git a/tests/UUIDTest.php b/tests/UUIDTest.php index b6f412f..c72b82c 100644 --- a/tests/UUIDTest.php +++ b/tests/UUIDTest.php @@ -1,80 +1,80 @@ -toBeString(); - $parsed = UUID::parse($uid); - expect($parsed['isValid'])->toBeTrue() - ->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); -}); - -$ns = UUID::v4(); - -test('UUID v3', function () use ($ns) { - $uid = UUID::v3($ns, 'my-string'); - expect($uid)->toBeString(); - $parsed = UUID::parse($uid); - expect($parsed['isValid'])->toBeTrue() - ->and($parsed['version'])->toBe(3) - ->and($parsed['time'])->toBeNull() - ->and($parsed['node'])->toBeString()->not()->toBeNull(); -}); - -test('UUID v4', function () { - $uid = UUID::v4(); - 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(); -}); - -test('UUID v5', function () use ($ns) { - $uid = UUID::v5($ns, 'my-string'); - expect($uid)->toBeString(); - $parsed = UUID::parse($uid); - expect($parsed['isValid'])->toBeTrue() - ->and($parsed['version'])->toBe(5) - ->and($parsed['time'])->toBeNull() - ->and($parsed['node'])->toBeString()->not()->toBeNull(); -}); - -test('UUID v6', function () { - $uid = UUID::v6(); - expect($uid)->toBeString(); - $parsed = UUID::parse($uid); - expect($parsed['isValid'])->toBeTrue() - ->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); -}); - -test('UUID v7', function () { - $uid = UUID::v7(); - expect($uid)->toBeString(); - $parsed = UUID::parse($uid); - expect($parsed['isValid'])->toBeTrue() - ->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); -}); - -test('UUID v8', function () { - $uid = UUID::v8(); - expect($uid)->toBeString(); - $parsed = UUID::parse($uid); - expect($parsed['isValid'])->toBeTrue() - ->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); -}); - +toBeString(); + $parsed = UUID::parse($uid); + expect($parsed['isValid'])->toBeTrue() + ->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); +}); + +$ns = UUID::v4(); + +test('UUID v3', function () use ($ns) { + $uid = UUID::v3($ns, 'my-string'); + expect($uid)->toBeString(); + $parsed = UUID::parse($uid); + expect($parsed['isValid'])->toBeTrue() + ->and($parsed['version'])->toBe(3) + ->and($parsed['time'])->toBeNull() + ->and($parsed['node'])->toBeString()->not()->toBeNull(); +}); + +test('UUID v4', function () { + $uid = UUID::v4(); + 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(); +}); + +test('UUID v5', function () use ($ns) { + $uid = UUID::v5($ns, 'my-string'); + expect($uid)->toBeString(); + $parsed = UUID::parse($uid); + expect($parsed['isValid'])->toBeTrue() + ->and($parsed['version'])->toBe(5) + ->and($parsed['time'])->toBeNull() + ->and($parsed['node'])->toBeString()->not()->toBeNull(); +}); + +test('UUID v6', function () { + $uid = UUID::v6(); + expect($uid)->toBeString(); + $parsed = UUID::parse($uid); + expect($parsed['isValid'])->toBeTrue() + ->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); +}); + +test('UUID v7', function () { + $uid = UUID::v7(); + expect($uid)->toBeString(); + $parsed = UUID::parse($uid); + expect($parsed['isValid'])->toBeTrue() + ->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); +}); + +test('UUID v8', function () { + $uid = UUID::v8(); + expect($uid)->toBeString(); + $parsed = UUID::parse($uid); + expect($parsed['isValid'])->toBeTrue() + ->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); +}); +