Making PHP generate a UUID with ease in 2023

Inhaltsverzeichnis
You want to make PHP generate UUIDs for you!?
I welcome you to todays post on „How to make PHP generate a UUID for you?“.
In this post i will partially dive into the thing called universally unique identifiers.
Those „special kind“ identifiers are – as the name obviously suggests – for uniquely identifying „things“.
There are different versions of UUIDs, but the main focus will lie on generating an UUID of the version 4.
Many projects nowadays require a new way of generating that unique identifier for like your entities.
It doesn’t matter if it’s stored in a good old MySQL Database, or in one of the document based alternatives.
Using a different approach than those normal „1, 2, ..“ ids has become more and more popular.
This doesn’t only apply for the developers side, customers and other involved parties actually want something else, too.
So let’s get into the background story and later into how we can actually create/generate one.
The background story – PHP generate UUID’s

Before we actually start thinking about code, we could also start from a little different angle.
So imagine having that graphical user interface, like most of the time, with some kind of list.
You got that usual screen with something like a table, which displays some data about the listed entities.
So far this isn’t really exciting, i know, but what comes into your mind, when thinking about the first column?
The first column inside that table, will usually display the unique identifier for that entity.
So basically this will be an incrementing number, which starts at like 1 and counting up.
Sure, there are some cases where people want to define another starting point like at 5000, but..
Here we are, already facing one of the common problems when using „normal“ numbers.
The thing with guessability
No matter if those numbers start at 300, or at 300000, you can always kinda guess, what’s the next one.
When you have one id, you can increment that by yourself and you could test „Hmm, what’s the next one about!?“.
This is literally one thing, where some customers and developers argue about doing it different.
The main reason for the customer would mostly be, to hide actual counts from the end user.
End users could guess from order number „5“, that this shop maybe just started.
This could lead the end user to actually interpret more, maybe negative things into that.
Thoughts like „Hmm, this shop did just start, maybe it’s a scam..“ could arise.
Being able to generate offline
Usually the approach of working with the database or entities in general, is like the following.
You have that basic overview screen, where you click this sweet „+„-button and voila.
After that, you will have your detail screen displayed, which is ready to get filled out.
You will most likely fill out that form in front of you and then press something like „confirm„, or „save“.
This will call some kind of api in the background, which then creates that new entity for you.
During that process, the database will be contacted as central unit, being responsible for generating the new id.
Now think about what happens, when you can’t contact the database, if you’re offline.
Well I think you are correct – usually nothing will happen..
At this point, UUIDs come in handy, as they can be generated offline and can still be unique!
Uniqueness accross systems & replications
Imagine you have that one online shop, where you already got thousands of orders, bills and more data.
Now imagine building up on that one shop, where you want to export and import the data in another system.
Well, if you’re using normal, incremental numbers, you could easily run into some id clashes.
With UUIDs on the other hand, this most likely won’t be the case (care for the different versions here).
By the word „universally“ of the full name „universally unique identifier“ this might get clearer.
As I already promised, I won’t go to deep on this aspect of UUID, maybe I’ll do so in a later post!
You can just keep in mind, that UUIDs will prevent clashing, even generated on different systems.
Disadvantages of using UUIDs

I think every upside on a specific topic, will mostly have a downside as well, so let’s talk about that.
On the one side, UUIDs will be that kind of added value from the aspects mentioned above.
The other side like performance, indexability, etc. counts as well, for sure.
Indexability and performance
When using uuids as keys inside your database, theres one big thing which primarily comes into your mind.
I’m talking about the „indexability“ and performance of actually storing the UUIDs inside your database tables.
I can’t go to deep here, as there are already so many opinions on the web, so I would recommend doing further research.
One of the most reasons I’ve heard about not using UUIDs is like hard dropping performance when used on multiple joins.
You should take a read on the web with search queries like „index fragmentation„.
Storage size
I’ve seen it many times, where people stored the UUIDs as like „CHAR(36)“, means strings consisting of 36 characters.
Actually, the UUIDs can be stored as a 128 bit = 16 byte, unsigned number, which is kinda more space optimized.
Speaking about storage size in general I think, nowadays storage is much cheaper.
Especially if you can provide a better performance, I would preferably go for that over storage.
For sure, this also depends on your use case and more things like budget, etc.
Anyways i’m a big fan of optimization, where it actually makes sense.
If effort doesn’t outplay the result as hard as I don’t know, then it would make sense for me, so..
Generally you could say, that an UUID needs 4 times more space than like a 4 byte integer.
It’s also really nice, that MySQL added more support for UUIDs on their version 8.
Readability
Another important aspect, which has its downside as well as its upside (as mentioned in „guessability„) is readability.
While it can be nice for some folks out there, that you can’t really read those identifiers as easy as simple numbers.
Other stress on having non readable identifiers, as like telling your customer on a request: „Hey, do you have an id for me?“.
For sure, this wouldn’t be a good experience, having your customer tell you like 36 characters.
Theres another thing coming along with that readability point, especially when talking about URLs.
You can tell by just looking at those URLs:
- /api/users/cc4f998b-29d8-4163-8bbe-2a44a1ccb2ac
- /api/users/55
- /api/users/my_nice_nickname
PHP generate a UUID – The Code
After discussing some aspects about using those UUIDs, let’s now actually talk about generating a UUID with PHP.
As I already mentioned, we can’t dive to deep into the different versions here, so I will focus on UUID version 4.
Many online code examples use the mt_rand PHP function, which is (as it states itself from the docs) not good for generating cryptographic thingys.
After finding different examples, I think this one here is the best one, being a mix of different answers.
In the first step we need to choose the „correct“ function for generating 16 random bytes.
Below PHPs major version 7, this is supported by using the „openssl_random_pseudo_bytes„-function.
Starting with major version 7 we can use the „random_bytes„-function.
With an easy ternary operation, it could look like this:
$data = PHP_MAJOR_VERSION < 7 ? openssl_random_pseudo_bytes(16) : random_bytes(16);
In the next step we are arranging the data, so that the version of the UUID, etc. actually matches:
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // Set version to 0100 $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // Set bits 6-7 to 10
In the last step we are displaying the actual UUID by returning it as formatted string:
$uuid = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
You could also split up that last step into multiple other calls, but I think this doesn’t really help visibility here.
So the complete function could look like this:
function get_guid() { $data = PHP_MAJOR_VERSION < 7 ? openssl_random_pseudo_bytes(16) : random_bytes(16); $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // Set version to 0100 $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // Set bits 6-7 to 10 return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); }
You could also expand further by going the „Microsoft way“, with the „com_create_guid„-function.
This function will only (as far as I know) work, if you’re on a Windows based server.
It will generate a UUID with prefixed and appended curly braces, hence the trim statement.
function get_guid() { if (function_exists('com_create_guid') === true) return trim(com_create_guid(), '{}'); $data = PHP_MAJOR_VERSION < 7 ? openssl_random_pseudo_bytes(16) : random_bytes(16); $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // Set version to 0100 $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // Set bits 6-7 to 10 return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); }