From 5004b6d9f99d450425ac165e9a1f999f69c85502 Mon Sep 17 00:00:00 2001 From: Quentin Schuler Date: Tue, 7 Jul 2026 22:11:47 +0200 Subject: [PATCH] Let the user build its own randomizer engine to fix the seed and get reproductible results. --- src/Container/Container.php | 18 ++++++++++- src/Container/Traits/HasExtensions.php | 13 +++++--- src/Container/Traits/HasModifiers.php | 2 +- src/Extensions/Extension.php | 2 +- src/Faker.php | 13 ++++++-- src/Providers/Provider.php | 2 +- .../Extensions/NumberTestExtension.php | 2 +- tests/Unit/Extensions/HashExtensionTest.php | 32 +++++++++++++++++++ .../Unit/Extensions/InternetExtensionTest.php | 16 +++++----- tests/Unit/Extensions/TestCase.php | 15 ++++++++- 10 files changed, 94 insertions(+), 21 deletions(-) diff --git a/src/Container/Container.php b/src/Container/Container.php index 760d2d8..3b1d0ce 100644 --- a/src/Container/Container.php +++ b/src/Container/Container.php @@ -3,6 +3,7 @@ namespace Xefi\Faker\Container; use Closure; +use Random\Engine; use Xefi\Faker\Container\Traits\HasExtensions; use Xefi\Faker\Container\Traits\HasLocale; use Xefi\Faker\Container\Traits\HasModifiers; @@ -47,13 +48,20 @@ class Container */ protected static string $containerMixinManifestPath = './faker_mixin.php'; + /** + * @var Engine|null + */ + private ?Engine $engine; + /** * Create the container instance. * * @return void */ - public function __construct(bool $shouldBuildContainerMixin = true) + public function __construct(?Engine $engine = null, bool $shouldBuildContainerMixin = true) { + $this->engine = $engine; + if (!$this->areExtensionsInitialized()) { $this->registerConfiguredProviders(); @@ -101,6 +109,14 @@ public static function basePath(string $basePath) static::$basePath = $basePath; } + /** + * @return Engine|null + */ + public function getEngine(): ?Engine + { + return $this->engine; + } + /** * Build container mixin manifest. */ diff --git a/src/Container/Traits/HasExtensions.php b/src/Container/Traits/HasExtensions.php index 475691b..79c7827 100644 --- a/src/Container/Traits/HasExtensions.php +++ b/src/Container/Traits/HasExtensions.php @@ -2,6 +2,7 @@ namespace Xefi\Faker\Container\Traits; +use Random\Engine; use Random\Randomizer; use Xefi\Faker\Container\Container; use Xefi\Faker\Container\Enum\Locales; @@ -27,14 +28,15 @@ trait HasExtensions /** * Resolve an array of extensions through the container. * - * @param array $extensions + * @param array $extensions + * @param Engine|null $engine * * @return $this */ - public function resolveExtensions(array $extensions): self + public function resolveExtensions(array $extensions, ?Engine $engine = null): self { foreach ($extensions as $extension) { - $this->resolve($extension); + $this->resolve($extension, $engine); } return $this; @@ -44,12 +46,13 @@ public function resolveExtensions(array $extensions): self * Add an extension, resolving through the application. * * @param Extension|string $extension + * @param Engine|null $engine * * @return Container */ - protected function resolve(\Xefi\Faker\Extensions\Extension|string $extension): Container + protected function resolve(\Xefi\Faker\Extensions\Extension|string $extension, ?Engine $engine = null): Container { - $instance = $extension instanceof Extension ? $extension : new $extension(new Randomizer()); + $instance = $extension instanceof Extension ? $extension : new $extension(new Randomizer($engine)); // If the extension supports locale variations if (method_exists($instance, 'getLocale')) { diff --git a/src/Container/Traits/HasModifiers.php b/src/Container/Traits/HasModifiers.php index 324acd3..8dc88b9 100644 --- a/src/Container/Traits/HasModifiers.php +++ b/src/Container/Traits/HasModifiers.php @@ -27,7 +27,7 @@ trait HasModifiers */ public function nullable(int $weight = 50): self { - $this->modifiers[] = new NullableModifier(new Randomizer(), $weight); + $this->modifiers[] = new NullableModifier(new Randomizer($this->engine), $weight); return $this; } diff --git a/src/Extensions/Extension.php b/src/Extensions/Extension.php index fe03b67..8f999f6 100644 --- a/src/Extensions/Extension.php +++ b/src/Extensions/Extension.php @@ -9,7 +9,7 @@ class Extension { protected Randomizer $randomizer; - public function __construct(Randomizer $randomizer) + final public function __construct(Randomizer $randomizer) { $this->randomizer = $randomizer; } diff --git a/src/Faker.php b/src/Faker.php index 240dd07..1fb77b5 100644 --- a/src/Faker.php +++ b/src/Faker.php @@ -2,6 +2,7 @@ namespace Xefi\Faker; +use Random\Engine; use Xefi\Faker\Container\Container; /** @@ -16,14 +17,22 @@ class Faker */ protected ?string $locale; - public function __construct(?string $locale = null) + /** + * The current Randomizer engine. + * + * @var Engine|null + */ + protected ?Engine $engine; + + public function __construct(?string $locale = null, ?Engine $engine = null) { $this->locale = $locale; + $this->engine = $engine; } public function __call(string $method, array $parameters) { // We simply redirect calls to container to create a new container for each faker call - return (new Container())->locale($this->locale)->{$method}(...$parameters); + return (new Container($this->engine))->locale($this->locale)->{$method}(...$parameters); } } diff --git a/src/Providers/Provider.php b/src/Providers/Provider.php index 06cc1c5..0fad516 100644 --- a/src/Providers/Provider.php +++ b/src/Providers/Provider.php @@ -16,7 +16,7 @@ class Provider public function extensions(array $extensions) { Container::starting(function (Container $container) use ($extensions) { - $container->resolveExtensions($extensions); + $container->resolveExtensions($extensions, $container->getEngine()); }); } diff --git a/tests/Support/Extensions/NumberTestExtension.php b/tests/Support/Extensions/NumberTestExtension.php index f91e57b..f2c11bd 100644 --- a/tests/Support/Extensions/NumberTestExtension.php +++ b/tests/Support/Extensions/NumberTestExtension.php @@ -13,6 +13,6 @@ public function returnOne() public function returnNumberBetween($min, $max) { - return rand($min, $max); + return $this->randomizer->getInt($min, $max); } } diff --git a/tests/Unit/Extensions/HashExtensionTest.php b/tests/Unit/Extensions/HashExtensionTest.php index 83e816e..17b0886 100644 --- a/tests/Unit/Extensions/HashExtensionTest.php +++ b/tests/Unit/Extensions/HashExtensionTest.php @@ -11,6 +11,14 @@ public function testSha1(): void $this->assertMatchesRegularExpression('/^[a-z0-9]{40}$/', $result); } + public function testSha1IsSeedable(): void + { + $this->assertEquals( + $this->createFresh()->sha1(), + $this->createFresh()->sha1(), + ); + } + public function testSha256(): void { $result = $this->faker->sha256(); @@ -18,6 +26,14 @@ public function testSha256(): void $this->assertMatchesRegularExpression('/^[a-z0-9]{64}$/', $result); } + public function testSha256IsSeedable(): void + { + $this->assertEquals( + $this->createFresh()->sha256(), + $this->createFresh()->sha256(), + ); + } + public function testSha512(): void { $result = $this->faker->sha512(); @@ -25,10 +41,26 @@ public function testSha512(): void $this->assertMatchesRegularExpression('/^[a-z0-9]{128}$/', $result); } + public function testSha512IsSeedable(): void + { + $this->assertEquals( + $this->createFresh()->sha512(), + $this->createFresh()->sha512(), + ); + } + public function testMd5(): void { $result = $this->faker->md5(); $this->assertMatchesRegularExpression('/^[a-fA-F0-9]{32}$/', $result); } + + public function testMd5IsSeedable(): void + { + $this->assertEquals( + $this->createFresh()->md5(), + $this->createFresh()->md5(), + ); + } } diff --git a/tests/Unit/Extensions/InternetExtensionTest.php b/tests/Unit/Extensions/InternetExtensionTest.php index 4247cb7..8ab10f1 100644 --- a/tests/Unit/Extensions/InternetExtensionTest.php +++ b/tests/Unit/Extensions/InternetExtensionTest.php @@ -19,7 +19,7 @@ protected function setUp(): void public function testSdl(): void { - $faker = new Container(false); + $faker = new Container(null, false); $results = []; @@ -34,7 +34,7 @@ public function testSdl(): void public function testTld(): void { - $faker = new Container(false); + $faker = new Container(null, false); $results = []; @@ -47,7 +47,7 @@ public function testTld(): void public function testDomain(): void { - $faker = new Container(false); + $faker = new Container(null, false); $results = []; @@ -62,7 +62,7 @@ public function testDomain(): void public function testIp(): void { - $faker = new Container(false); + $faker = new Container(null, false); for ($i = 0; $i < 50; $i++) { $result = $faker->ip(); @@ -73,7 +73,7 @@ public function testIp(): void public function testIpv4(): void { - $faker = new Container(false); + $faker = new Container(null, false); $results = []; @@ -88,7 +88,7 @@ public function testIpv4(): void public function testIpv6(): void { - $faker = new Container(false); + $faker = new Container(null, false); $results = []; @@ -103,7 +103,7 @@ public function testIpv6(): void public function testMacAddress(): void { - $faker = new Container(false); + $faker = new Container(null, false); $results = []; @@ -118,7 +118,7 @@ public function testMacAddress(): void public function testEmail(): void { - $faker = new Container(false); + $faker = new Container(null, false); $results = []; diff --git a/tests/Unit/Extensions/TestCase.php b/tests/Unit/Extensions/TestCase.php index 0c43c0c..38131fd 100644 --- a/tests/Unit/Extensions/TestCase.php +++ b/tests/Unit/Extensions/TestCase.php @@ -2,6 +2,7 @@ namespace Xefi\Faker\Tests\Unit\Extensions; +use Random\Engine\Mt19937; use Xefi\Faker\Container\Container; use Xefi\Faker\FakerServiceProvider; @@ -15,6 +16,18 @@ protected function setUp(): void (new FakerServiceProvider())->boot(); - $this->faker = new Container(false); + $this->faker = new Container(new Mt19937(19937), false); + } + + protected function createFresh(): Container + { + $this->faker->forgetBootstrappers(); + $this->faker->forgetExtensions(); + $this->faker->forgetModifiers(); + $this->faker->forgetStrategies(); + + (new FakerServiceProvider())->boot(); + + return new Container(new Mt19937(19937), false); } }