Skip to content

Commit

Permalink
uuid methods
Browse files Browse the repository at this point in the history
  • Loading branch information
taylorotwell committed Jan 16, 2018
1 parent 7ba0c22 commit 3d39604
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 0 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"doctrine/dbal": "~2.6",
"filp/whoops": "^2.1.4",
"mockery/mockery": "~1.0",
"moontoast/math": "^1.1",
"orchestra/testbench-core": "3.6.*",
"pda/pheanstalk": "~3.0",
"phpunit/phpunit": "~6.0",
Expand Down
35 changes: 35 additions & 0 deletions src/Illuminate/Support/Str.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

namespace Illuminate\Support;

use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidFactory;
use Illuminate\Support\Traits\Macroable;
use Ramsey\Uuid\Generator\CombGenerator;
use Ramsey\Uuid\Codec\TimestampFirstCombCodec;

class Str
{
Expand Down Expand Up @@ -517,6 +521,37 @@ public static function ucfirst($string)
return static::upper(static::substr($string, 0, 1)).static::substr($string, 1);
}

/**
* Generate a UUID (version 4).
*
* @return \Ramsey\Uuid\Uuid
*/
public static function uuid()
{
return Uuid::uuid4();
}

/**
* Generate a time-ordered UUID (version 4).
*
* @return \Ramsey\Uuid\Uuid
*/
public static function orderedUuid()
{
$factory = new UuidFactory;

$factory->setRandomGenerator(new CombGenerator(
$factory->getRandomGenerator(),
$factory->getNumberConverter()
));

$factory->setCodec(new TimestampFirstCombCodec(
$factory->getUuidBuilder()
));

return $factory->uuid4();
}

/**
* Returns the replacements for the ascii method.
*
Expand Down
7 changes: 7 additions & 0 deletions tests/Support/SupportStrTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Illuminate\Tests\Support;

use Ramsey\Uuid\Uuid;
use Illuminate\Support\Str;
use PHPUnit\Framework\TestCase;

Expand Down Expand Up @@ -285,6 +286,12 @@ public function testUcfirst()
$this->assertEquals('Мама', Str::ucfirst('мама'));
$this->assertEquals('Мама мыла раму', Str::ucfirst('мама мыла раму'));
}

public function testUuid()
{
$this->assertInstanceOf(Uuid::class, Str::uuid());
$this->assertInstanceOf(Uuid::class, Str::orderedUuid());
}
}

class StringableObjectStub
Expand Down

1 comment on commit 3d39604

@Garbee
Copy link
Contributor

@Garbee Garbee commented on 3d39604 Apr 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taylorotwell There is a pretty big problem here. People are expecting orderedUuid to return an ID that takes time into account. However, this is not possible by the spec of UUID's.

First, simply take a look at the implementation of the v4 generator to see it never uses the codec for time. It just gets random seed data, then hex's it, and then generates the UUID from that. The setCodec call here to try and make it use time, is never accounted for in the pathway to make a v4 id.

Second, the UUID specification itself (section 4.4) supports this notion.

The version 4 UUID is meant for generating UUIDs from truly-random or
pseudo-random numbers.

Introducing a component of time, makes it into a v1 ID. Which is determined by time where v4's are supposed to be as completely random as possible. With no deterministic input.

You need to return a v1 UUID to get an output that can be ordered. V4's can't and shouldn't fulfill this need.


Fix paths:

First, we could add a new method that is called uuidOrdered. This would return Uuid::v1(); and be done. Then the current method would just return a v4 without all the extra logic, but trigger a warning to let developers know to convert to v1 types using the new method to get what they want and that the current method will be removed in the future since it isn't useful.

Second, Simply let the current method trigger an error about the impending v1 switch. Then switch in 5.8 to return v1 types.

The first method is preferable for a few reasons:

  • Developers know right away what the problem is and how to migrate.
  • It allows for systems to more immediately get correct identifiers to start working compared to having to wait to 5.8.
  • The new method name allows for type hint resolution to see both versions in one stroke, instead of needing to know what you're looking for or have fuzzy search for helpers.

The second method allows incorrect code to continue to manifest with no direct resolution path until multiple releases to come. This is not good for applications that are depending on the timestamp being included in the identifier.


So, this is a "comb uuid" type thing. Which means it isn't a true UUID but something that looks like one, can validate as one, but has some deterministic seeded data.

Also, the v1 idea for sorting doesn't work at all. Looking back into that, the timestamp is seeded across the bits. So it makes them unsortable since they aren't placed together. Since that helps create the problem this does which is sections that can be determined.

So this is fine technically. Just a confusing docblock since it didn't explain right away that it isn't generating a true v4 UUID but an alternate type that emulates the same structure.

Please sign in to comment.