Skip to content

Commit 69dbada

Browse files
authored
Added the UUID extension to Core and deprecated the global uuid call with an uuid3 to specify version (#427)
1 parent bad8fc1 commit 69dbada

File tree

5 files changed

+130
-0
lines changed

5 files changed

+130
-0
lines changed

src/Faker/Core/Uuid.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
namespace Faker\Core;
4+
5+
use Faker\Extension\UuidExtension;
6+
7+
final class Uuid implements UuidExtension
8+
{
9+
public function uuid3(): string
10+
{
11+
$number = new Number();
12+
13+
// fix for compatibility with 32bit architecture; each mt_rand call is restricted to 32bit
14+
// two such calls will cause 64bits of randomness regardless of architecture
15+
$seed = $number->numberBetween(0, 2147483647) . '#' . $number->numberBetween(0, 2147483647);
16+
17+
// Hash the seed and convert to a byte array
18+
$val = md5($seed, true);
19+
$byte = array_values(unpack('C16', $val));
20+
21+
// extract fields from byte array
22+
$tLo = ($byte[0] << 24) | ($byte[1] << 16) | ($byte[2] << 8) | $byte[3];
23+
$tMi = ($byte[4] << 8) | $byte[5];
24+
$tHi = ($byte[6] << 8) | $byte[7];
25+
$csLo = $byte[9];
26+
$csHi = $byte[8] & 0x3f | (1 << 7);
27+
28+
// correct byte order for big edian architecture
29+
if (pack('L', 0x6162797A) == pack('N', 0x6162797A)) {
30+
$tLo = (($tLo & 0x000000ff) << 24) | (($tLo & 0x0000ff00) << 8)
31+
| (($tLo & 0x00ff0000) >> 8) | (($tLo & 0xff000000) >> 24);
32+
$tMi = (($tMi & 0x00ff) << 8) | (($tMi & 0xff00) >> 8);
33+
$tHi = (($tHi & 0x00ff) << 8) | (($tHi & 0xff00) >> 8);
34+
}
35+
36+
// apply version number
37+
$tHi &= 0x0fff;
38+
$tHi |= (3 << 12);
39+
40+
// cast to string
41+
return sprintf(
42+
'%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x',
43+
$tLo,
44+
$tMi,
45+
$tHi,
46+
$csHi,
47+
$csLo,
48+
$byte[10],
49+
$byte[11],
50+
$byte[12],
51+
$byte[13],
52+
$byte[14],
53+
$byte[15]
54+
);
55+
}
56+
}

src/Faker/Extension/ContainerBuilder.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public static function defaultExtensions(): array
6666
FileExtension::class => Core\File::class,
6767
NumberExtension::class => Core\Number::class,
6868
VersionExtension::class => Core\Version::class,
69+
UuidExtension::class => Core\Uuid::class,
6970
];
7071
}
7172

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Faker\Extension;
4+
5+
/**
6+
* @experimental This interface is experimental and does not fall under our BC promise
7+
*/
8+
interface UuidExtension extends Extension
9+
{
10+
/**
11+
* Generate name based md5 UUID (version 3).
12+
*
13+
* @example '7e57d004-2b97-0e7a-b45f-5387367791cd'
14+
*/
15+
public function uuid3(): string;
16+
}

src/Faker/Generator.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,34 @@ public function bloodGroup(): string
797797
return $this->ext(Extension\BloodExtension::class)->bloodGroup();
798798
}
799799

800+
/**
801+
* Get a random v3 uuid
802+
*
803+
* @example '7e57d004-2b97-0e7a-b45f-5387367791cd'
804+
*
805+
* @deprecated call uuid3() instead
806+
*/
807+
public function uuid(): string
808+
{
809+
trigger_deprecation(
810+
'fakerphp/faker',
811+
'1.18',
812+
'Method uuid() is deprecated, call uuid3() instead'
813+
);
814+
815+
return $this->uuid3();
816+
}
817+
818+
/**
819+
* Get a random v3 uuid
820+
*
821+
* @example '7e57d004-2b97-0e7a-b45f-5387367791cd'
822+
*/
823+
public function uuid3(): string
824+
{
825+
return $this->ext(Extension\UuidExtension::class)->uuid3();
826+
}
827+
800828
/**
801829
* Get a random EAN13 barcode.
802830
*

test/Faker/Core/UuidTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Faker\Test\Core;
4+
5+
use Faker\Test\TestCase;
6+
7+
final class UuidTest extends TestCase
8+
{
9+
public function testUuidReturnsUuid()
10+
{
11+
$uuid = $this->faker->uuid3();
12+
self::assertTrue($this->isUuid($uuid));
13+
}
14+
15+
public function testUuidExpectedSeed()
16+
{
17+
if (pack('L', 0x6162797A) == pack('N', 0x6162797A)) {
18+
self::markTestSkipped('Big Endian');
19+
}
20+
$this->faker->seed(123);
21+
self::assertEquals('8e2e0c84-50dd-367c-9e66-f3ab455c78d6', $this->faker->uuid3());
22+
self::assertEquals('073eb60a-902c-30ab-93d0-a94db371f6c8', $this->faker->uuid3());
23+
}
24+
25+
protected function isUuid($uuid)
26+
{
27+
return is_string($uuid) && (bool) preg_match('/^[a-f0-9]{8,8}-(?:[a-f0-9]{4,4}-){3,3}[a-f0-9]{12,12}$/i', $uuid);
28+
}
29+
}

0 commit comments

Comments
 (0)