diff --git a/src/Faker/Core/Uuid.php b/src/Faker/Core/Uuid.php new file mode 100644 index 0000000000..7311e287f5 --- /dev/null +++ b/src/Faker/Core/Uuid.php @@ -0,0 +1,56 @@ +numberBetween(0, 2147483647) . '#' . $number->numberBetween(0, 2147483647); + + // Hash the seed and convert to a byte array + $val = md5($seed, true); + $byte = array_values(unpack('C16', $val)); + + // extract fields from byte array + $tLo = ($byte[0] << 24) | ($byte[1] << 16) | ($byte[2] << 8) | $byte[3]; + $tMi = ($byte[4] << 8) | $byte[5]; + $tHi = ($byte[6] << 8) | $byte[7]; + $csLo = $byte[9]; + $csHi = $byte[8] & 0x3f | (1 << 7); + + // correct byte order for big edian architecture + if (pack('L', 0x6162797A) == pack('N', 0x6162797A)) { + $tLo = (($tLo & 0x000000ff) << 24) | (($tLo & 0x0000ff00) << 8) + | (($tLo & 0x00ff0000) >> 8) | (($tLo & 0xff000000) >> 24); + $tMi = (($tMi & 0x00ff) << 8) | (($tMi & 0xff00) >> 8); + $tHi = (($tHi & 0x00ff) << 8) | (($tHi & 0xff00) >> 8); + } + + // apply version number + $tHi &= 0x0fff; + $tHi |= (3 << 12); + + // cast to string + return sprintf( + '%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x', + $tLo, + $tMi, + $tHi, + $csHi, + $csLo, + $byte[10], + $byte[11], + $byte[12], + $byte[13], + $byte[14], + $byte[15] + ); + } +} diff --git a/src/Faker/Extension/ContainerBuilder.php b/src/Faker/Extension/ContainerBuilder.php index 6c34109ca8..82b2fbe7d1 100644 --- a/src/Faker/Extension/ContainerBuilder.php +++ b/src/Faker/Extension/ContainerBuilder.php @@ -66,6 +66,7 @@ public static function defaultExtensions(): array FileExtension::class => Core\File::class, NumberExtension::class => Core\Number::class, VersionExtension::class => Core\Version::class, + UuidExtension::class => Core\Uuid::class, ]; } diff --git a/src/Faker/Extension/UuidExtension.php b/src/Faker/Extension/UuidExtension.php new file mode 100644 index 0000000000..261ff447fb --- /dev/null +++ b/src/Faker/Extension/UuidExtension.php @@ -0,0 +1,16 @@ +ext(Extension\BloodExtension::class)->bloodGroup(); } + /** + * Get a random v3 uuid + * + * @example '7e57d004-2b97-0e7a-b45f-5387367791cd' + * + * @deprecated call uuid3() instead + */ + public function uuid(): string + { + trigger_deprecation( + 'fakerphp/faker', + '1.18', + 'Method uuid() is deprecated, call uuid3() instead' + ); + + return $this->uuid3(); + } + + /** + * Get a random v3 uuid + * + * @example '7e57d004-2b97-0e7a-b45f-5387367791cd' + */ + public function uuid3(): string + { + return $this->ext(Extension\UuidExtension::class)->uuid3(); + } + /** * Get a random EAN13 barcode. * diff --git a/test/Faker/Core/UuidTest.php b/test/Faker/Core/UuidTest.php new file mode 100644 index 0000000000..fd79bc11d3 --- /dev/null +++ b/test/Faker/Core/UuidTest.php @@ -0,0 +1,29 @@ +faker->uuid3(); + self::assertTrue($this->isUuid($uuid)); + } + + public function testUuidExpectedSeed() + { + if (pack('L', 0x6162797A) == pack('N', 0x6162797A)) { + self::markTestSkipped('Big Endian'); + } + $this->faker->seed(123); + self::assertEquals('8e2e0c84-50dd-367c-9e66-f3ab455c78d6', $this->faker->uuid3()); + self::assertEquals('073eb60a-902c-30ab-93d0-a94db371f6c8', $this->faker->uuid3()); + } + + protected function isUuid($uuid) + { + 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); + } +}