-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Add Pure-PHP ULID Generator (Spec-Compliant & Monotonic) #19972
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Signed-off-by: zaenalcoders <zaenal.virus@gmail.com>
…e functions Signed-off-by: zaenalcoders <zaenal.virus@gmail.com>
9a76d1e to
0c91351
Compare
|
I think this is interesting. I wasn't familiar with ULID but seems like
something we can implement.
Nice work on the pull request, thanks especially for having tests.
I'm curious what the others think, but this looks good to me.
🤔 Maybe it should target 6.x, which we're working hard towards releasing
as the next major version.
|
|
Hi @ibennetch, Thank you for the feedback and kind words! I’ll update this pull request to target the 6.x branch as suggested and ensure it aligns with the upcoming major release. Appreciate your guidance and time reviewing this. |
I am a bit confused as it already targets master, and QA_6_0 did not start yet |
You're quite right - I somehow misread the email notification. I'm not sure what I thought I saw - I definitely misread it as being for the QA branch - but you are of course correct. |
src/InsertEdit.php
Outdated
| } | ||
|
|
||
| if ($editField->function === 'ULID') { | ||
| /* generate ULID */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't add comments that convey no information.
src/InsertEdit.php
Outdated
|
|
||
| if ($editField->function === 'ULID') { | ||
| /* generate ULID */ | ||
| $ulid = (string) Ulid::generate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| $ulid = (string) Ulid::generate(); | |
| $ulid = Ulid::generate(); |
src/Ulid.php
Outdated
| private static function encodeRandom(array $bytes): string | ||
| { | ||
| $binary = ''; | ||
| foreach ($bytes as $b) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| foreach ($bytes as $b) { | |
| foreach ($bytes as $byte) { |
The word is short enough that you don't need to be cryptic with it.
src/Ulid.php
Outdated
| $result = '0'; | ||
| } else { | ||
| while ($value > 0) { | ||
| $result = $alphabet[$value % 32] . $result; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| $result = $alphabet[$value % 32] . $result; | |
| $result = self::ALPHABET[$value % 32] . $result; |
Unnecessary reassignments are confusing in the long run.
src/Ulid.php
Outdated
| $result = ''; | ||
|
|
||
| if ($value === 0) { | ||
| $result = '0'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this line necessary?
tests/unit/UlidTest.php
Outdated
| } | ||
| } | ||
|
|
||
| // ULIDs created one after another should be in order thanks to the monotonic logic. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, I don't think any of these comments add any value. They just repeat what the test already does.
tests/unit/UlidTest.php
Outdated
| $u1 = Ulid::generate(); | ||
| $u2 = Ulid::generate(); | ||
|
|
||
| $this->assertLessThan( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this assertion accept strings? Is there a reason to use strcmp here?
tests/unit/UlidTest.php
Outdated
| // Wait for about 2ms to make sure the next ULID lands in a new millisecond. | ||
| usleep(2000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or you could take the timestamp as an optional parameter to the generate method.
tests/unit/UlidTest.php
Outdated
| ); | ||
| } | ||
|
|
||
| // The first 10 characters are the timestamp. If you generate a ULID a little later, that part should go up. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment like this belongs in the class where the functionality is implemented, not in the test case.
tests/unit/UlidTest.php
Outdated
| public function testMultipleUlidsAreUnique(): void | ||
| { | ||
| $generated = []; | ||
|
|
||
| for ($i = 0; $i < 1000; $i++) { | ||
| $u = Ulid::generate(); | ||
| $this->assertArrayNotHasKey( | ||
| $u, | ||
| $generated, | ||
| "Duplicate ULID found: {$u}" | ||
| ); | ||
| $generated[$u] = true; | ||
| } | ||
|
|
||
| $this->assertCount( | ||
| 1000, | ||
| $generated, | ||
| 'All ULIDs generated in batch should be unique' | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A unit test should be reproducible. If it fails, we need to be able to apply a fix and retest with the same parameters. I doubt this test fulfils this requirement. Testing random behaviour may be useful during development, but it isn't helpful in a test suite.
Signed-off-by: zaenalcoders <zaenal.virus@gmail.com>
Signed-off-by: zaenalcoders <zaenal.virus@gmail.com>
Add Pure-PHP ULID Generator (Spec-Compliant & Monotonic)
Summary
This pull request introduces a fully self-contained ULID (Universally Unique Lexicographically Sortable Identifier) generator for phpMyAdmin.
The implementation is written entirely in pure PHP, adds no external dependencies, and follows the official ULID specification, including timestamp structure, randomness generation, and Crockford Base32 encoding.
Motivation
phpMyAdmin currently supports UUID generation but does not provide a built-in ULID generator.
ULID offers several advantages:
Adding a built-in ULID generator improves developer experience without increasing dependency footprint.
Design & Implementation
Specification-Compliant
This generator follows the official ULID specification:
https://github.com/ulid/spec
Monotonic Behavior
When multiple ULIDs are generated within the same millisecond, the randomness portion is incremented to maintain:
Dependency-Free
Although inspired by
symfony/uidandrobinvdvleuten/ulid, this implementation is entirely rewritten to keep phpMyAdmin dependency-light.Structure & Coding Standards
New Files
Class
libraries/classes/Ulid.phpPublic API
Ulid::generate(): stringGenerates a 26-character ULID encoded using Crockford Base32.
Tests
Test File
test/classes/UlidTest.phpTest Coverage Includes:
All tests are pure PHPUnit and require no external libraries.
Impact
Conclusion
This PR adds a modern, reliable, and specification-compliant ULID generator to phpMyAdmin, without altering existing behavior or adding dependencies.
It strengthens phpMyAdmin's utility for modern development workflows.