mirror of
https://github.com/php/php-src.git
synced 2026-04-11 10:03:18 +02:00
Merge branch 'PHP-8.2'
* PHP-8.2: Unify structure for ext/random's randomizer tests (#9410)
This commit is contained in:
@@ -13,6 +13,8 @@ $engines[] = new PcgOneseq128XslRr64(1234);
|
||||
$engines[] = new Xoshiro256StarStar(1234);
|
||||
|
||||
foreach ($engines as $engine) {
|
||||
echo $engine::class, PHP_EOL;
|
||||
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
$engine->generate();
|
||||
}
|
||||
@@ -21,9 +23,7 @@ foreach ($engines as $engine) {
|
||||
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
if ($engine->generate() !== $engine2->generate()) {
|
||||
$className = $engine::class;
|
||||
|
||||
die("failure: {$className} at {$i}");
|
||||
die("failure: state differs at {$i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,4 +32,7 @@ die('success');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\PcgOneseq128XslRr64
|
||||
Random\Engine\Xoshiro256StarStar
|
||||
success
|
||||
|
||||
@@ -4,18 +4,11 @@ Random: Engine: Serialization of user engines must preserve the sequence
|
||||
<?php
|
||||
|
||||
use Random\Engine;
|
||||
use Random\Engine\Test\TestShaEngine;
|
||||
|
||||
final class User64 implements Engine
|
||||
{
|
||||
private int $count = 0;
|
||||
require __DIR__ . "/../engines.inc";
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
return pack('P*', ++$this->count);
|
||||
}
|
||||
}
|
||||
|
||||
final class User32 implements Engine
|
||||
final class CountingEngine32 implements Engine
|
||||
{
|
||||
private int $count = 0;
|
||||
|
||||
@@ -26,12 +19,12 @@ final class User32 implements Engine
|
||||
}
|
||||
|
||||
$engines = [];
|
||||
if (PHP_INT_SIZE >= 8) {
|
||||
$engines[] = new User64();
|
||||
}
|
||||
$engines[] = new User32();
|
||||
$engines[] = new CountingEngine32();
|
||||
$engines[] = new TestShaEngine();
|
||||
|
||||
foreach ($engines as $engine) {
|
||||
echo $engine::class, PHP_EOL;
|
||||
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
$engine->generate();
|
||||
}
|
||||
@@ -40,9 +33,7 @@ foreach ($engines as $engine) {
|
||||
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
if ($engine->generate() !== $engine2->generate()) {
|
||||
$className = $engine::class;
|
||||
|
||||
die("failure: {$className} at {$i}");
|
||||
die("failure: state differs at {$i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,4 +42,6 @@ die('success');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
CountingEngine32
|
||||
Random\Engine\Test\TestShaEngine
|
||||
success
|
||||
|
||||
@@ -16,7 +16,7 @@ try {
|
||||
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
if ($engine->generate() !== $referenceEngine->generate()) {
|
||||
die('failure: state changed');
|
||||
die("failure: state differs at {$i}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,19 +6,10 @@ Random: Engine: Native engines can be wrapped without changing their sequence
|
||||
use Random\Engine;
|
||||
use Random\Engine\Mt19937;
|
||||
use Random\Engine\PcgOneseq128XslRr64;
|
||||
use Random\Engine\Test\TestWrapperEngine;
|
||||
use Random\Engine\Xoshiro256StarStar;
|
||||
|
||||
class WrapperEngine implements Engine
|
||||
{
|
||||
public function __construct(private readonly Engine $engine)
|
||||
{
|
||||
}
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
return $this->engine->generate();
|
||||
}
|
||||
}
|
||||
require __DIR__ . "/../engines.inc";
|
||||
|
||||
$engines = [];
|
||||
$engines[] = new Mt19937(1234);
|
||||
@@ -26,13 +17,14 @@ $engines[] = new PcgOneseq128XslRr64(1234);
|
||||
$engines[] = new Xoshiro256StarStar(1234);
|
||||
|
||||
foreach ($engines as $engine) {
|
||||
echo $engine::class, PHP_EOL;
|
||||
|
||||
$native_engine = clone $engine;
|
||||
$user_engine = new WrapperEngine(clone $engine);
|
||||
$user_engine = new TestWrapperEngine(clone $engine);
|
||||
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
if ($native_engine->generate() !== $user_engine->generate()) {
|
||||
$className = $engine::class;
|
||||
die("failure: {$className} at {$i}");
|
||||
die("failure: state differs at {$i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,4 +33,7 @@ die('success');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\PcgOneseq128XslRr64
|
||||
Random\Engine\Xoshiro256StarStar
|
||||
success
|
||||
|
||||
@@ -6,17 +6,17 @@ Random: Engine: Xoshiro256StarStar: The seed parameter must work as expected
|
||||
use Random\Engine\Xoshiro256StarStar;
|
||||
|
||||
echo "Random integer seed", PHP_EOL;
|
||||
$engine = new Xoshiro256StarStar(\random_int(\PHP_INT_MIN, \PHP_INT_MAX));
|
||||
$engine = new Xoshiro256StarStar(random_int(PHP_INT_MIN, PHP_INT_MAX));
|
||||
echo PHP_EOL, PHP_EOL;
|
||||
|
||||
echo "Random string seed", PHP_EOL;
|
||||
$engine = new Xoshiro256StarStar(\random_bytes(32));
|
||||
$engine = new Xoshiro256StarStar(random_bytes(32));
|
||||
echo PHP_EOL, PHP_EOL;
|
||||
|
||||
echo "Invalid data type", PHP_EOL;
|
||||
try {
|
||||
$engine = new Xoshiro256StarStar(1.0);
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
echo PHP_EOL, PHP_EOL;
|
||||
@@ -24,15 +24,15 @@ echo PHP_EOL, PHP_EOL;
|
||||
echo "Invalid string seed length", PHP_EOL;
|
||||
try {
|
||||
$engine = new Xoshiro256StarStar('foobar');
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
echo PHP_EOL, PHP_EOL;
|
||||
|
||||
echo "Null seed", PHP_EOL;
|
||||
try {
|
||||
$engine = new Random\Engine\Xoshiro256StarStar(\str_repeat("\x00", 32));
|
||||
} catch (\Throwable $e) {
|
||||
$engine = new Xoshiro256StarStar(str_repeat("\x00", 32));
|
||||
} catch (Throwable $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
echo PHP_EOL, PHP_EOL;
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
--TEST--
|
||||
Random: Randomizer: basic
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$engines = [];
|
||||
$engines[] = new Random\Engine\Mt19937(\random_int(\PHP_INT_MIN, \PHP_INT_MAX), MT_RAND_MT19937);
|
||||
$engines[] = new Random\Engine\Mt19937(\random_int(\PHP_INT_MIN, \PHP_INT_MAX), MT_RAND_PHP);
|
||||
$engines[] = new Random\Engine\PcgOneseq128XslRr64(\random_int(\PHP_INT_MIN, \PHP_INT_MAX));
|
||||
$engines[] = new Random\Engine\Xoshiro256StarStar(\random_int(\PHP_INT_MIN, \PHP_INT_MAX));
|
||||
$engines[] = new Random\Engine\Secure();
|
||||
$engines[] = new class () implements Random\Engine {
|
||||
public function generate(): string
|
||||
{
|
||||
return \random_bytes(16);
|
||||
}
|
||||
};
|
||||
class UserEngine implements Random\Engine
|
||||
{
|
||||
public function generate(): string
|
||||
{
|
||||
return \random_bytes(16);
|
||||
}
|
||||
}
|
||||
$engines[] = new UserEngine();
|
||||
|
||||
foreach ($engines as $engine) {
|
||||
$randomizer = new Random\Randomizer($engine);
|
||||
|
||||
// nextInt
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
try {
|
||||
$randomizer->nextInt();
|
||||
} catch (\Random\RandomException $e) {
|
||||
if ($e->getMessage() !== 'Generated value exceeds size of int') {
|
||||
die($engine::class . ": nextInt: failure: {$e->getMessage()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getInt
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$result = $randomizer->getInt(-50, 50);
|
||||
if ($result > 50 || $result < -50) {
|
||||
die($engine::class . ': getInt: failure');
|
||||
}
|
||||
}
|
||||
|
||||
// getBytes
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$length = \random_int(1, 1024);
|
||||
if (\strlen($randomizer->getBytes($length)) !== $length) {
|
||||
die($engine::class . ': getBytes: failure');
|
||||
}
|
||||
}
|
||||
|
||||
// shuffleArray
|
||||
$array = range(1, 1000);
|
||||
$shuffled_array = $randomizer->shuffleArray($array);
|
||||
(function () use ($array, $shuffled_array): void {
|
||||
for ($i = 0; $i < count($array); $i++) {
|
||||
if ($array[$i] !== $shuffled_array[$i]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
die($engine::class . ': shuffleArray: failure');
|
||||
})();
|
||||
|
||||
// shuffleBytes
|
||||
$string = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
|
||||
$shuffled_string = $randomizer->shuffleBytes($string);
|
||||
if ($string === $shuffled_string) {
|
||||
die($engine::class . ': shuffleBytes: failure');
|
||||
}
|
||||
}
|
||||
|
||||
die('success');
|
||||
?>
|
||||
--EXPECTF--
|
||||
success
|
||||
@@ -1,8 +1,11 @@
|
||||
--TEST--
|
||||
Random: Randomizer: pickArrayKeys
|
||||
Random: Randomizer: The Mt19937 engine and pickArrayKeys are consistent with array_rand()
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine\Mt19937;
|
||||
use Random\Randomizer;
|
||||
|
||||
$map = ['foo' => 0, 'bar' => 1, 'baz' => 2];
|
||||
$list = range(1, 10);
|
||||
|
||||
@@ -14,7 +17,7 @@ $mapPickTwoFunc = array_rand($map, 2);
|
||||
$listPickOneFunc = array_rand($list, 1);
|
||||
$listPickTwoFunc = array_rand($list, 2);
|
||||
|
||||
$randomizer = new \Random\Randomizer(new \Random\Engine\Mt19937(1234));
|
||||
$randomizer = new Randomizer(new Mt19937(1234));
|
||||
|
||||
[$mapPickOneMethod] = $randomizer->pickArrayKeys($map, 1);
|
||||
$mapPickTwoMethod = $randomizer->pickArrayKeys($map, 2);
|
||||
@@ -24,25 +27,30 @@ $listPickTwoMethod = $randomizer->pickArrayKeys($list, 2);
|
||||
|
||||
if ($mapPickOneFunc !== $mapPickOneMethod) {
|
||||
var_dump($mapPickOneFunc, $mapPickOneMethod);
|
||||
die('failure mapPickOne');
|
||||
|
||||
die('failure: mapPickOne');
|
||||
}
|
||||
|
||||
if ($mapPickTwoFunc !== $mapPickTwoMethod) {
|
||||
var_dump($mapPickTwoFunc, $mapPickTwoMethod);
|
||||
die('failure mapPickTwo');
|
||||
|
||||
die('failure: mapPickTwo');
|
||||
}
|
||||
|
||||
if ($listPickOneFunc !== $listPickOneMethod) {
|
||||
var_dump($listPickOneFunc, $listPickOneMethod);
|
||||
die('failure listPickOne');
|
||||
|
||||
die('failure: listPickOne');
|
||||
}
|
||||
|
||||
if ($listPickTwoFunc !== $listPickTwoMethod) {
|
||||
var_dump($listPickTwoFunc, $listPickOneMethod);
|
||||
die('failure listPickTwo');
|
||||
|
||||
die('failure: listPickTwo');
|
||||
}
|
||||
|
||||
die('success');
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
--EXPECT--
|
||||
success
|
||||
@@ -1,24 +0,0 @@
|
||||
--TEST--
|
||||
Random: Randomizer: Compatibility: Mt19937
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$randomizer = new \Random\Randomizer(new \Random\Engine\Mt19937(1234, \MT_RAND_PHP));
|
||||
\mt_srand(1234, \MT_RAND_PHP);
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
if ($randomizer->nextInt() !== \mt_rand()) {
|
||||
die('failure');
|
||||
}
|
||||
}
|
||||
|
||||
$randomizer = new \Random\Randomizer(new \Random\Engine\Mt19937(1234, \MT_RAND_MT19937));
|
||||
\mt_srand(1234, \MT_RAND_MT19937);
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
if ($randomizer->nextInt() !== \mt_rand()) {
|
||||
die('failure');
|
||||
}
|
||||
}
|
||||
|
||||
die('success');
|
||||
--EXPECT--
|
||||
success
|
||||
49
ext/random/tests/03_randomizer/compatibility_mt_rand.phpt
Normal file
49
ext/random/tests/03_randomizer/compatibility_mt_rand.phpt
Normal file
@@ -0,0 +1,49 @@
|
||||
--TEST--
|
||||
Random: Randomizer: The Mt19937 engine is a drop-in replacement for mt_rand()
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Randomizer;
|
||||
use Random\Engine\Mt19937;
|
||||
|
||||
echo "MT_RAND_PHP", PHP_EOL;
|
||||
|
||||
$randomizer = new Randomizer(new Mt19937(1234, MT_RAND_PHP));
|
||||
mt_srand(1234, MT_RAND_PHP);
|
||||
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
if ($randomizer->nextInt() !== mt_rand()) {
|
||||
die("failure: state differs at {$i} for nextInt()");
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
if ($randomizer->getInt(0, $i) !== mt_rand(0, $i)) {
|
||||
die("failure: state differs at {$i} for getInt()");
|
||||
}
|
||||
}
|
||||
|
||||
echo "MT_RAND_MT19937", PHP_EOL;
|
||||
|
||||
$randomizer = new Randomizer(new Mt19937(1234, MT_RAND_MT19937));
|
||||
mt_srand(1234, MT_RAND_MT19937);
|
||||
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
if ($randomizer->nextInt() !== mt_rand()) {
|
||||
die("failure: state differs at {$i} for nextInt()");
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
if ($randomizer->getInt(0, $i) !== mt_rand(0, $i)) {
|
||||
die("failure: state differs at {$i} for getInt()");
|
||||
}
|
||||
}
|
||||
|
||||
die('success');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
MT_RAND_PHP
|
||||
MT_RAND_MT19937
|
||||
success
|
||||
@@ -1,80 +1,43 @@
|
||||
--TEST--
|
||||
Random: Randomizer: Compatibility: user
|
||||
Random: Randomizer: Native engines can be wrapped without changing their sequence
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$native_randomizer = new \Random\Randomizer(new \Random\Engine\Mt19937(1234));
|
||||
$user_randomizer = new \Random\Randomizer(new class () implements \Random\Engine {
|
||||
public function __construct(private $engine = new \Random\Engine\Mt19937(1234))
|
||||
{
|
||||
}
|
||||
use Random\Engine;
|
||||
use Random\Engine\Mt19937;
|
||||
use Random\Engine\PcgOneseq128XslRr64;
|
||||
use Random\Engine\Test\TestWrapperEngine;
|
||||
use Random\Engine\Xoshiro256StarStar;
|
||||
use Random\Randomizer;
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
return $this->engine->generate();
|
||||
}
|
||||
});
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$native = $native_randomizer->nextInt();
|
||||
$user = $user_randomizer->nextInt();
|
||||
if ($native !== $user) {
|
||||
die("failure Mt19937 i: {$i} native: {$native} user: {$user}");
|
||||
}
|
||||
}
|
||||
require __DIR__ . "/../engines.inc";
|
||||
|
||||
try {
|
||||
$native_randomizer = new \Random\Randomizer(new \Random\Engine\PcgOneseq128XslRr64(1234));
|
||||
$user_randomizer = new \Random\Randomizer(new class () implements \Random\Engine {
|
||||
public function __construct(private $engine = new \Random\Engine\PcgOneseq128XslRr64(1234))
|
||||
{
|
||||
}
|
||||
$engines = [];
|
||||
$engines[] = new Mt19937(1234);
|
||||
$engines[] = new PcgOneseq128XslRr64(1234);
|
||||
$engines[] = new Xoshiro256StarStar(1234);
|
||||
|
||||
foreach ($engines as $engine) {
|
||||
echo $engine::class, PHP_EOL;
|
||||
|
||||
$native_randomizer = new Randomizer(clone $engine);
|
||||
$user_randomizer = new Randomizer(new TestWrapperEngine(clone $engine));
|
||||
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
$native = $native_randomizer->getInt(0, $i);
|
||||
$user = $user_randomizer->getInt(0, $i);
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
return $this->engine->generate();
|
||||
}
|
||||
});
|
||||
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$native = $native_randomizer->nextInt();
|
||||
$user = $user_randomizer->nextInt();
|
||||
if ($native !== $user) {
|
||||
die("failure PcgOneseq128XslRr64 i: {$i} native: {$native} user: {$user}");
|
||||
die("failure: state differs at {$i}");
|
||||
}
|
||||
}
|
||||
} catch (\Random\RandomException $e) {
|
||||
if ($e->getMessage() !== 'Generated value exceeds size of int') {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$native_randomizer = new \Random\Randomizer(new \Random\Engine\Xoshiro256StarStar(1234));
|
||||
$user_randomizer = new \Random\Randomizer(new class () implements \Random\Engine {
|
||||
public function __construct(private $engine = new \Random\Engine\Xoshiro256StarStar(1234))
|
||||
{
|
||||
}
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
return $this->engine->generate();
|
||||
}
|
||||
});
|
||||
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$native = $native_randomizer->nextInt();
|
||||
$user = $user_randomizer->nextInt();
|
||||
if ($native !== $user) {
|
||||
die("failure Xoshiro256StarStar i: {$i} native: {$native} user: {$user}");
|
||||
}
|
||||
}
|
||||
} catch (\Random\RandomException $e) {
|
||||
if ($e->getMessage() !== 'Generated value exceeds size of int') {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
die('success');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\PcgOneseq128XslRr64
|
||||
Random\Engine\Xoshiro256StarStar
|
||||
success
|
||||
|
||||
@@ -1,46 +1,47 @@
|
||||
--TEST--
|
||||
Random: Randomizer: Disallow manually calling __construct
|
||||
Random: Randomizer: Calling __construct() fails due to readonly $engine property
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
final class UserEngine implements \Random\Engine
|
||||
{
|
||||
public function generate(): string
|
||||
{
|
||||
return \random_byte(4); /* 32-bit */
|
||||
}
|
||||
}
|
||||
use Random\Engine;
|
||||
use Random\Engine\PcgOneseq128XslRr64;
|
||||
use Random\Engine\Test\TestShaEngine;
|
||||
use Random\Engine\Xoshiro256StarStar;
|
||||
use Random\Randomizer;
|
||||
|
||||
require __DIR__ . "/../engines.inc";
|
||||
|
||||
try {
|
||||
(new \Random\Randomizer())->__construct();
|
||||
} catch (\Error $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$r = new \Random\Randomizer(new \Random\Engine\Xoshiro256StarStar());
|
||||
$r->__construct(new \Random\Engine\PcgOneseq128XslRr64());
|
||||
} catch (\Error $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$r = new \Random\Randomizer(new \UserEngine());
|
||||
$r->__construct(new \UserEngine());
|
||||
} catch (\Error $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$r = new \Random\Randomizer(new \Random\Engine\Xoshiro256StarStar());
|
||||
$r->__construct(new \UserEngine());
|
||||
} catch (\Error $e) {
|
||||
(new Randomizer())->__construct();
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
var_dump($r->engine::class);
|
||||
try {
|
||||
$randomizer = new Randomizer(new Xoshiro256StarStar());
|
||||
$randomizer->__construct(new PcgOneseq128XslRr64());
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$randomizer = new Randomizer(new TestShaEngine("1234"));
|
||||
$randomizer->__construct(new TestShaEngine("1234"));
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$randomizer = new Randomizer(new Xoshiro256StarStar());
|
||||
$randomizer->__construct(new TestShaEngine("1234"));
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
var_dump($randomizer->engine::class);
|
||||
|
||||
die('success');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Cannot modify readonly property Random\Randomizer::$engine
|
||||
|
||||
58
ext/random/tests/03_randomizer/engine_unsafe_biased.phpt
Normal file
58
ext/random/tests/03_randomizer/engine_unsafe_biased.phpt
Normal file
@@ -0,0 +1,58 @@
|
||||
--TEST--
|
||||
Random: Randomizer: Heavily biased engines are detected and rejected
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine;
|
||||
use Random\Randomizer;
|
||||
|
||||
final class HeavilyBiasedEngine implements Engine
|
||||
{
|
||||
public function generate(): string
|
||||
{
|
||||
return str_repeat("\xff", PHP_INT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
function randomizer(): Randomizer
|
||||
{
|
||||
return new Randomizer(new HeavilyBiasedEngine());
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump(randomizer()->getInt(0, 1234));
|
||||
} catch (Random\BrokenRandomEngineError $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump(randomizer()->nextInt());
|
||||
} catch (Random\BrokenRandomEngineError $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump(bin2hex(randomizer()->getBytes(1)));
|
||||
} catch (Random\BrokenRandomEngineError $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump(randomizer()->shuffleArray(range(1, 1234)));
|
||||
} catch (Random\BrokenRandomEngineError $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump(randomizer()->shuffleBytes('foobar'));
|
||||
} catch (Random\BrokenRandomEngineError $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Failed to generate an acceptable random number in 50 attempts
|
||||
int(%d)
|
||||
string(2) "ff"
|
||||
Failed to generate an acceptable random number in 50 attempts
|
||||
Failed to generate an acceptable random number in 50 attempts
|
||||
@@ -0,0 +1,58 @@
|
||||
--TEST--
|
||||
Random: Randomizer: Engines returning an empty string are detected and rejected
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine;
|
||||
use Random\Randomizer;
|
||||
|
||||
final class EmptyStringEngine implements Engine
|
||||
{
|
||||
public function generate(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function randomizer(): Randomizer
|
||||
{
|
||||
return new Randomizer(new EmptyStringEngine());
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump(randomizer()->getInt(0, 1234));
|
||||
} catch (Random\BrokenRandomEngineError $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump(randomizer()->nextInt());
|
||||
} catch (Random\BrokenRandomEngineError $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump(bin2hex(randomizer()->getBytes(1)));
|
||||
} catch (Random\BrokenRandomEngineError $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump(randomizer()->shuffleArray(range(1, 1234)));
|
||||
} catch (Random\BrokenRandomEngineError $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump(randomizer()->shuffleBytes('foobar'));
|
||||
} catch (Random\BrokenRandomEngineError $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
A random engine must return a non-empty string
|
||||
A random engine must return a non-empty string
|
||||
A random engine must return a non-empty string
|
||||
A random engine must return a non-empty string
|
||||
A random engine must return a non-empty string
|
||||
23
ext/random/tests/03_randomizer/engine_unsafe_exits.phpt
Normal file
23
ext/random/tests/03_randomizer/engine_unsafe_exits.phpt
Normal file
@@ -0,0 +1,23 @@
|
||||
--TEST--
|
||||
Random: Randomizer: Engines calling exit() are gracefully handled
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine;
|
||||
use Random\Randomizer;
|
||||
|
||||
final class ExitingEngine implements Engine
|
||||
{
|
||||
public function generate(): string
|
||||
{
|
||||
exit("Exit\n");
|
||||
}
|
||||
}
|
||||
|
||||
$randomizer = new Randomizer(new ExitingEngine());
|
||||
|
||||
var_dump($randomizer->getBytes(1));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Exit
|
||||
28
ext/random/tests/03_randomizer/engine_unsafe_throws.phpt
Normal file
28
ext/random/tests/03_randomizer/engine_unsafe_throws.phpt
Normal file
@@ -0,0 +1,28 @@
|
||||
--TEST--
|
||||
Random: Randomizer: Engines throwing an exception are gracefully handled
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine;
|
||||
use Random\Randomizer;
|
||||
|
||||
final class ThrowingEngine implements Engine
|
||||
{
|
||||
public function generate(): string
|
||||
{
|
||||
throw new Exception('Error');
|
||||
}
|
||||
}
|
||||
|
||||
$randomizer = new Randomizer(new ThrowingEngine());
|
||||
|
||||
var_dump($randomizer->getBytes(1));
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Uncaught Exception: Error in %s:%d
|
||||
Stack trace:
|
||||
#0 [internal function]: ThrowingEngine->generate()
|
||||
#1 %s(%d): Random\Randomizer->getBytes(1)
|
||||
#2 {main}
|
||||
thrown in %s on line %d
|
||||
@@ -1,40 +0,0 @@
|
||||
--TEST--
|
||||
Random: Randomizer: getBytes
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$randomizer = new \Random\Randomizer (
|
||||
new class () implements \Random\Engine
|
||||
{
|
||||
private int $count = 0;
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
if ($this->count > 5) {
|
||||
die('overflow');
|
||||
}
|
||||
|
||||
return match ($this->count++) {
|
||||
0 => 'H',
|
||||
1 => 'e',
|
||||
2 => 'll',
|
||||
3 => 'o',
|
||||
4 => 'abcdefghijklmnopqrstuvwxyz', // 208 bits
|
||||
5 => 'success',
|
||||
default => \random_bytes(16),
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
echo $randomizer->getBytes(5) . PHP_EOL; // Hello
|
||||
|
||||
echo $randomizer->getBytes(8) . PHP_EOL; // abcdefgh (64 bits)
|
||||
|
||||
die($randomizer->getBytes(7));
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Hello
|
||||
abcdefgh
|
||||
success
|
||||
@@ -1,22 +0,0 @@
|
||||
--TEST--
|
||||
Random: Randomizer: User Engine results are correctly expanded for getInt()
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$randomizer = new \Random\Randomizer (
|
||||
new class () implements \Random\Engine
|
||||
{
|
||||
public $count = 0;
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
return "\x01\x02\x03\x04\x05\x06\x07\x08"[$this->count++];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
var_dump(bin2hex(pack('V', $randomizer->getInt(0, 0xFFFFFF))));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
string(8) "01020300"
|
||||
47
ext/random/tests/03_randomizer/methods/getBytes.phpt
Normal file
47
ext/random/tests/03_randomizer/methods/getBytes.phpt
Normal file
@@ -0,0 +1,47 @@
|
||||
--TEST--
|
||||
Random: Randomizer: getBytes(): Basic functionality
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine;
|
||||
use Random\Engine\Mt19937;
|
||||
use Random\Engine\PcgOneseq128XslRr64;
|
||||
use Random\Engine\Secure;
|
||||
use Random\Engine\Test\TestShaEngine;
|
||||
use Random\Engine\Xoshiro256StarStar;
|
||||
use Random\Randomizer;
|
||||
|
||||
require __DIR__ . "/../../engines.inc";
|
||||
|
||||
$engines = [];
|
||||
$engines[] = new Mt19937(null, MT_RAND_MT19937);
|
||||
$engines[] = new Mt19937(null, MT_RAND_PHP);
|
||||
$engines[] = new PcgOneseq128XslRr64();
|
||||
$engines[] = new Xoshiro256StarStar();
|
||||
$engines[] = new Secure();
|
||||
$engines[] = new TestShaEngine();
|
||||
|
||||
foreach ($engines as $engine) {
|
||||
echo $engine::class, PHP_EOL;
|
||||
|
||||
$randomizer = new Randomizer($engine);
|
||||
|
||||
// Using 10_000 is very slow.
|
||||
for ($i = 1; $i < 1_000; $i++) {
|
||||
if (\strlen($randomizer->getBytes($i)) !== $i) {
|
||||
die("failure: incorrect string length at {$i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
die('success');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\PcgOneseq128XslRr64
|
||||
Random\Engine\Xoshiro256StarStar
|
||||
Random\Engine\Secure
|
||||
Random\Engine\Test\TestShaEngine
|
||||
success
|
||||
@@ -0,0 +1,43 @@
|
||||
--TEST--
|
||||
Random: Randomizer: getBytes(): Returned bytes are consistently expanded
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine;
|
||||
use Random\Randomizer;
|
||||
|
||||
final class TestEngine implements Engine
|
||||
{
|
||||
private int $count = 0;
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
return match ($this->count++) {
|
||||
0 => 'H',
|
||||
1 => 'e',
|
||||
2 => 'll',
|
||||
3 => 'o',
|
||||
4 => 'abcdefghijklmnopqrstuvwxyz',
|
||||
5 => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
6 => 'success',
|
||||
default => throw new \Exception('Unhandled'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
$randomizer = new Randomizer(new TestEngine());
|
||||
|
||||
// 0-3: "Hello" - Insufficient bytes are concatenated.
|
||||
var_dump($randomizer->getBytes(5));
|
||||
|
||||
// 4-5: "abcdefghABC" - Returned values are truncated to 64-bits for technical reasons, thus dropping i-z.
|
||||
var_dump($randomizer->getBytes(11));
|
||||
|
||||
// 6: "success"
|
||||
var_dump($randomizer->getBytes(7));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
string(5) "Hello"
|
||||
string(11) "abcdefghABC"
|
||||
string(7) "success"
|
||||
54
ext/random/tests/03_randomizer/methods/getInt.phpt
Normal file
54
ext/random/tests/03_randomizer/methods/getInt.phpt
Normal file
@@ -0,0 +1,54 @@
|
||||
--TEST--
|
||||
Random: Randomizer: getInt(): Basic functionality
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine;
|
||||
use Random\Engine\Mt19937;
|
||||
use Random\Engine\PcgOneseq128XslRr64;
|
||||
use Random\Engine\Secure;
|
||||
use Random\Engine\Test\TestShaEngine;
|
||||
use Random\Engine\Xoshiro256StarStar;
|
||||
use Random\Randomizer;
|
||||
|
||||
require __DIR__ . "/../../engines.inc";
|
||||
|
||||
$engines = [];
|
||||
$engines[] = new Mt19937(null, MT_RAND_MT19937);
|
||||
$engines[] = new Mt19937(null, MT_RAND_PHP);
|
||||
$engines[] = new PcgOneseq128XslRr64();
|
||||
$engines[] = new Xoshiro256StarStar();
|
||||
$engines[] = new Secure();
|
||||
$engines[] = new TestShaEngine();
|
||||
|
||||
foreach ($engines as $engine) {
|
||||
echo $engine::class, PHP_EOL;
|
||||
|
||||
$randomizer = new Randomizer($engine);
|
||||
|
||||
// Basic range test.
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
$result = $randomizer->getInt(-$i, $i);
|
||||
|
||||
if ($result > $i || $result < -$i) {
|
||||
die("failure: out of range at {$i}");
|
||||
}
|
||||
}
|
||||
|
||||
// Test that extreme ranges do not throw.
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
$randomizer->getInt(PHP_INT_MIN, PHP_INT_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
die('success');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\PcgOneseq128XslRr64
|
||||
Random\Engine\Xoshiro256StarStar
|
||||
Random\Engine\Secure
|
||||
Random\Engine\Test\TestShaEngine
|
||||
success
|
||||
@@ -0,0 +1,25 @@
|
||||
--TEST--
|
||||
Random: Randomizer: getInt(): Returned values with insufficient bits are correctly expanded
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine;
|
||||
use Random\Randomizer;
|
||||
|
||||
final class ByteEngine implements Engine
|
||||
{
|
||||
public $count = 0;
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
return "\x01\x02\x03\x04\x05\x06\x07\x08"[$this->count++];
|
||||
}
|
||||
}
|
||||
|
||||
$randomizer = new Randomizer(new ByteEngine());
|
||||
|
||||
var_dump(bin2hex(pack('V', $randomizer->getInt(0, 0x00FF_FFFF))));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
string(8) "01020300"
|
||||
@@ -0,0 +1,27 @@
|
||||
--TEST--
|
||||
Random: Randomizer: getInt(): Returned values with insufficient bits are correctly expanded (64 Bit)
|
||||
--SKIPIF--
|
||||
<?php if (PHP_INT_SIZE != 8) die("skip 64-bit only"); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine;
|
||||
use Random\Randomizer;
|
||||
|
||||
final class ByteEngine implements Engine
|
||||
{
|
||||
public $count = 0;
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
return "\x01\x02\x03\x04\x05\x06\x07\x08"[$this->count++];
|
||||
}
|
||||
}
|
||||
|
||||
$randomizer = new Randomizer(new ByteEngine());
|
||||
|
||||
var_dump(bin2hex(pack('P', $randomizer->getInt(0, 0x00FF_FFFF_FFFF_FFFF))));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
string(16) "0102030405060700"
|
||||
50
ext/random/tests/03_randomizer/methods/nextInt.phpt
Normal file
50
ext/random/tests/03_randomizer/methods/nextInt.phpt
Normal file
@@ -0,0 +1,50 @@
|
||||
--TEST--
|
||||
Random: Randomizer: nextInt(): Basic functionality
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine;
|
||||
use Random\Engine\Mt19937;
|
||||
use Random\Engine\PcgOneseq128XslRr64;
|
||||
use Random\Engine\Secure;
|
||||
use Random\Engine\Test\TestShaEngine;
|
||||
use Random\Engine\Xoshiro256StarStar;
|
||||
use Random\Randomizer;
|
||||
|
||||
require __DIR__ . "/../../engines.inc";
|
||||
|
||||
$engines = [];
|
||||
$engines[] = new Mt19937(null, MT_RAND_MT19937);
|
||||
$engines[] = new Mt19937(null, MT_RAND_PHP);
|
||||
$engines[] = new PcgOneseq128XslRr64();
|
||||
$engines[] = new Xoshiro256StarStar();
|
||||
$engines[] = new Secure();
|
||||
$engines[] = new TestShaEngine();
|
||||
|
||||
foreach ($engines as $engine) {
|
||||
echo $engine::class, PHP_EOL;
|
||||
|
||||
$randomizer = new Randomizer($engine);
|
||||
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
try {
|
||||
$randomizer->nextInt();
|
||||
} catch (\Random\RandomException $e) {
|
||||
if ($e->getMessage() !== 'Generated value exceeds size of int' || PHP_INT_SIZE !== 4) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
die('success');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\PcgOneseq128XslRr64
|
||||
Random\Engine\Xoshiro256StarStar
|
||||
Random\Engine\Secure
|
||||
Random\Engine\Test\TestShaEngine
|
||||
success
|
||||
@@ -0,0 +1,21 @@
|
||||
--TEST--
|
||||
Random: Randomizer: nextInt(): Throws for 64 bit engines on 32 bit platforms
|
||||
--SKIPIF--
|
||||
<?php if (PHP_INT_SIZE == 8) die("skip 32-bit only"); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Randomizer;
|
||||
use Random\Engine\Xoshiro256StarStar;
|
||||
|
||||
$randomizer = new Randomizer(new Xoshiro256StarStar());
|
||||
|
||||
try {
|
||||
var_dump($randomizer->nextInt());
|
||||
} catch (Random\RandomException $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Generated value exceeds size of int
|
||||
84
ext/random/tests/03_randomizer/methods/pickArrayKeys.phpt
Normal file
84
ext/random/tests/03_randomizer/methods/pickArrayKeys.phpt
Normal file
@@ -0,0 +1,84 @@
|
||||
--TEST--
|
||||
Random: Randomizer: pickArrayKeys(): Basic functionality
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine;
|
||||
use Random\Engine\Mt19937;
|
||||
use Random\Engine\PcgOneseq128XslRr64;
|
||||
use Random\Engine\Secure;
|
||||
use Random\Engine\Test\TestShaEngine;
|
||||
use Random\Engine\Xoshiro256StarStar;
|
||||
use Random\Randomizer;
|
||||
|
||||
require __DIR__ . "/../../engines.inc";
|
||||
|
||||
$engines = [];
|
||||
$engines[] = new Mt19937(null, MT_RAND_MT19937);
|
||||
$engines[] = new Mt19937(null, MT_RAND_PHP);
|
||||
$engines[] = new PcgOneseq128XslRr64();
|
||||
$engines[] = new Xoshiro256StarStar();
|
||||
$engines[] = new Secure();
|
||||
$engines[] = new TestShaEngine();
|
||||
|
||||
$array1 = []; // list
|
||||
$array2 = []; // associative array with only strings
|
||||
$array3 = []; // mixed key array
|
||||
for ($i = 0; $i < 500; $i++) {
|
||||
$string = sha1((string)$i);
|
||||
|
||||
$array1[] = $i;
|
||||
$array2[$string] = $i;
|
||||
$array3[$string] = $i;
|
||||
$array3[$i] = $string;
|
||||
}
|
||||
|
||||
foreach ($engines as $engine) {
|
||||
echo $engine::class, PHP_EOL;
|
||||
|
||||
$randomizer = new Randomizer($engine);
|
||||
|
||||
for ($i = 1; $i < 100; $i++) {
|
||||
$result = $randomizer->pickArrayKeys($array1, $i);
|
||||
|
||||
if (array_unique($result) !== $result) {
|
||||
die("failure: duplicates returned at {$i} for array1");
|
||||
}
|
||||
|
||||
if (array_diff($result, array_keys($array1)) !== []) {
|
||||
die("failure: non-keys returned at {$i} for array1");
|
||||
}
|
||||
|
||||
$result = $randomizer->pickArrayKeys($array2, $i);
|
||||
|
||||
if (array_unique($result) !== $result) {
|
||||
die("failure: duplicates returned at {$i} for array2");
|
||||
}
|
||||
|
||||
if (array_diff($result, array_keys($array2)) !== []) {
|
||||
die("failure: non-keys returned at {$i} for array2");
|
||||
}
|
||||
|
||||
$result = $randomizer->pickArrayKeys($array3, $i);
|
||||
|
||||
if (array_unique($result) !== $result) {
|
||||
die("failure: duplicates returned at {$i} for array3");
|
||||
}
|
||||
|
||||
if (array_diff($result, array_keys($array3)) !== []) {
|
||||
die("failure: non-keys returned at {$i} for array3");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
die('success');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\PcgOneseq128XslRr64
|
||||
Random\Engine\Xoshiro256StarStar
|
||||
Random\Engine\Secure
|
||||
Random\Engine\Test\TestShaEngine
|
||||
success
|
||||
@@ -0,0 +1,49 @@
|
||||
--TEST--
|
||||
Random: Randomizer: pickArrayKeys(): Parameters are correctly validated
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Randomizer;
|
||||
|
||||
function randomizer(): Randomizer
|
||||
{
|
||||
return new Randomizer();
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump(randomizer()->pickArrayKeys("foo", 2));
|
||||
} catch (TypeError $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump(randomizer()->pickArrayKeys([], 0));
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump(randomizer()->pickArrayKeys(range(1, 3), 0));
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump(randomizer()->pickArrayKeys(range(1, 3), -1));
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump(randomizer()->pickArrayKeys(range(1, 3), 10));
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Random\Randomizer::pickArrayKeys(): Argument #1 ($array) must be of type array, string given
|
||||
Random\Randomizer::pickArrayKeys(): Argument #1 ($array) cannot be empty
|
||||
Random\Randomizer::pickArrayKeys(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array)
|
||||
Random\Randomizer::pickArrayKeys(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array)
|
||||
Random\Randomizer::pickArrayKeys(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array)
|
||||
53
ext/random/tests/03_randomizer/methods/shuffleArray.phpt
Normal file
53
ext/random/tests/03_randomizer/methods/shuffleArray.phpt
Normal file
@@ -0,0 +1,53 @@
|
||||
--TEST--
|
||||
Random: Randomizer: shuffleArray(): Basic functionality
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine;
|
||||
use Random\Engine\Mt19937;
|
||||
use Random\Engine\PcgOneseq128XslRr64;
|
||||
use Random\Engine\Secure;
|
||||
use Random\Engine\Test\TestShaEngine;
|
||||
use Random\Engine\Xoshiro256StarStar;
|
||||
use Random\Randomizer;
|
||||
|
||||
require __DIR__ . "/../../engines.inc";
|
||||
|
||||
$engines = [];
|
||||
$engines[] = new Mt19937(null, MT_RAND_MT19937);
|
||||
$engines[] = new Mt19937(null, MT_RAND_PHP);
|
||||
$engines[] = new PcgOneseq128XslRr64();
|
||||
$engines[] = new Xoshiro256StarStar();
|
||||
$engines[] = new Secure();
|
||||
$engines[] = new TestShaEngine();
|
||||
|
||||
foreach ($engines as $engine) {
|
||||
echo $engine::class, PHP_EOL;
|
||||
|
||||
$randomizer = new Randomizer($engine);
|
||||
|
||||
// This test is slow, test all numbers smaller than 50 and then in steps of 677 (which is prime).
|
||||
for ($i = 1; $i < 5_000; $i += ($i < 50 ? 1 : 677)) {
|
||||
$array = range(1, $i);
|
||||
|
||||
$result = $randomizer->shuffleArray($array);
|
||||
|
||||
sort($result);
|
||||
|
||||
if ($result !== $array) {
|
||||
die("failure: not a permutation at {$i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
die('success');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\PcgOneseq128XslRr64
|
||||
Random\Engine\Xoshiro256StarStar
|
||||
Random\Engine\Secure
|
||||
Random\Engine\Test\TestShaEngine
|
||||
success
|
||||
60
ext/random/tests/03_randomizer/methods/shuffleBytes.phpt
Normal file
60
ext/random/tests/03_randomizer/methods/shuffleBytes.phpt
Normal file
@@ -0,0 +1,60 @@
|
||||
--TEST--
|
||||
Random: Randomizer: shuffleBytes(): Basic functionality
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine;
|
||||
use Random\Engine\Mt19937;
|
||||
use Random\Engine\PcgOneseq128XslRr64;
|
||||
use Random\Engine\Secure;
|
||||
use Random\Engine\Test\TestShaEngine;
|
||||
use Random\Engine\Xoshiro256StarStar;
|
||||
use Random\Randomizer;
|
||||
|
||||
require __DIR__ . "/../../engines.inc";
|
||||
|
||||
$engines = [];
|
||||
$engines[] = new Mt19937(null, MT_RAND_MT19937);
|
||||
$engines[] = new Mt19937(null, MT_RAND_PHP);
|
||||
$engines[] = new PcgOneseq128XslRr64();
|
||||
$engines[] = new Xoshiro256StarStar();
|
||||
$engines[] = new Secure();
|
||||
$engines[] = new TestShaEngine();
|
||||
|
||||
function sort_bytes(string $bytes): string
|
||||
{
|
||||
$bytes = str_split($bytes);
|
||||
sort($bytes);
|
||||
return implode('', $bytes);
|
||||
}
|
||||
|
||||
foreach ($engines as $engine) {
|
||||
echo $engine::class, PHP_EOL;
|
||||
|
||||
$randomizer = new Randomizer($engine);
|
||||
|
||||
// This test is slow, test all numbers smaller than 50 and then in steps of 677 (which is prime).
|
||||
for ($i = 1; $i < 5_000; $i += ($i < 50 ? 1 : 677)) {
|
||||
$bytes = sort_bytes(random_bytes($i));
|
||||
|
||||
$result = $randomizer->shuffleBytes($bytes);
|
||||
|
||||
$result = sort_bytes($result);
|
||||
|
||||
if ($result !== $bytes) {
|
||||
die("failure: not a permutation at {$i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
die('success');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\PcgOneseq128XslRr64
|
||||
Random\Engine\Xoshiro256StarStar
|
||||
Random\Engine\Secure
|
||||
Random\Engine\Test\TestShaEngine
|
||||
success
|
||||
@@ -1,18 +0,0 @@
|
||||
--TEST--
|
||||
Random: Randomizer: nextInt() throws for too large values on 32 Bit
|
||||
--SKIPIF--
|
||||
<?php if (PHP_INT_SIZE == 8) die("skip 32-bit only"); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$randomizer = new \Random\Randomizer(new \Random\Engine\Xoshiro256StarStar());
|
||||
|
||||
try {
|
||||
var_dump($randomizer->nextInt());
|
||||
} catch (\Random\RandomException $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Generated value exceeds size of int
|
||||
@@ -1,52 +0,0 @@
|
||||
--TEST--
|
||||
Random: Randomizer: pickArrayKeys error pattern
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
var_dump((new \Random\Randomizer())->pickArrayKeys([], 0));
|
||||
} catch (\ValueError $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump((new \Random\Randomizer())->pickArrayKeys(range(1, 3), 0));
|
||||
} catch (\ValueError $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump((new \Random\Randomizer())->pickArrayKeys(range(1, 3), -1));
|
||||
} catch (\ValueError $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
var_dump((new \Random\Randomizer())->pickArrayKeys(range(1, 3), 10));
|
||||
} catch (\ValueError $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
var_dump((new \Random\Randomizer())->pickArrayKeys(range(1, 3), 3));
|
||||
var_dump((new \Random\Randomizer())->pickArrayKeys(range(1, 3), 2));
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Random\Randomizer::pickArrayKeys(): Argument #1 ($array) cannot be empty
|
||||
Random\Randomizer::pickArrayKeys(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array)
|
||||
Random\Randomizer::pickArrayKeys(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array)
|
||||
Random\Randomizer::pickArrayKeys(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array)
|
||||
array(3) {
|
||||
[0]=>
|
||||
int(%d)
|
||||
[1]=>
|
||||
int(%d)
|
||||
[2]=>
|
||||
int(%d)
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
int(%d)
|
||||
[1]=>
|
||||
int(%d)
|
||||
}
|
||||
@@ -1,43 +1,30 @@
|
||||
--TEST--
|
||||
Random: Randomizer: readonly engine
|
||||
Random: Randomizer: The engine property must be readonly
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$one = new \Random\Randomizer(
|
||||
new \Random\Engine\PcgOneseq128XslRr64(1234)
|
||||
);
|
||||
use Random\Engine\PcgOneseq128XslRr64;
|
||||
use Random\Engine\Xoshiro256StarStar;
|
||||
use Random\Randomizer;
|
||||
|
||||
$one_ng_clone = clone $one->engine;
|
||||
if ($one->engine->generate() !== $one_ng_clone->generate()) {
|
||||
die('invalid result');
|
||||
}
|
||||
$randomizer = new Randomizer(new PcgOneseq128XslRr64(1234));
|
||||
$referenceRandomizer = new Randomizer(new PcgOneseq128XslRr64(1234));
|
||||
|
||||
try {
|
||||
$one->engine = $one_ng_clone;
|
||||
} catch (\Throwable $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
$randomizer->engine = new Xoshiro256StarStar(1234);
|
||||
} catch (Throwable $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
$two = new \Random\Randomizer(
|
||||
new \Random\Engine\Secure()
|
||||
);
|
||||
|
||||
try {
|
||||
$two_ng_clone = clone $two->engine;
|
||||
} catch (\Throwable $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
if ($randomizer->getInt(0, $i) !== $referenceRandomizer->getInt(0, $i)) {
|
||||
die("failure: state differs at {$i}");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$two->engine = $one_ng_clone;
|
||||
} catch (\Throwable $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
die('success');
|
||||
|
||||
die('success')
|
||||
?>
|
||||
--EXPECT--
|
||||
Cannot modify readonly property Random\Randomizer::$engine
|
||||
Trying to clone an uncloneable object of class Random\Engine\Secure
|
||||
Cannot modify readonly property Random\Randomizer::$engine
|
||||
success
|
||||
|
||||
@@ -1,57 +1,50 @@
|
||||
--TEST--
|
||||
Random: Randomizer: serialize
|
||||
Random: Randomizer: Serialization of the Randomizer must preserve the sequence
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine;
|
||||
use Random\Engine\Mt19937;
|
||||
use Random\Engine\PcgOneseq128XslRr64;
|
||||
use Random\Engine\Test\TestShaEngine;
|
||||
use Random\Engine\Xoshiro256StarStar;
|
||||
use Random\Randomizer;
|
||||
|
||||
require __DIR__ . "/../engines.inc";
|
||||
|
||||
$engines = [];
|
||||
$engines[] = new \Random\Engine\Mt19937(\random_int(\PHP_INT_MIN, \PHP_INT_MAX), MT_RAND_MT19937);
|
||||
$engines[] = new \Random\Engine\Mt19937(\random_int(\PHP_INT_MIN, \PHP_INT_MAX), MT_RAND_PHP);
|
||||
$engines[] = new \Random\Engine\PcgOneseq128XslRr64(\random_int(\PHP_INT_MIN, \PHP_INT_MAX));
|
||||
$engines[] = new \Random\Engine\Xoshiro256StarStar(\random_int(\PHP_INT_MIN, \PHP_INT_MAX));
|
||||
$engines[] = new \Random\Engine\Secure();
|
||||
$generate = \random_bytes(16);
|
||||
$engines[] = new class () implements Random\Engine {
|
||||
public function generate(): string
|
||||
{
|
||||
global $generate;
|
||||
return $generate;
|
||||
}
|
||||
};
|
||||
class UserEngine implements Random\Engine
|
||||
{
|
||||
public function generate(): string
|
||||
{
|
||||
global $generate;
|
||||
return $generate;
|
||||
}
|
||||
}
|
||||
$engines[] = new UserEngine();
|
||||
$engines[] = new Mt19937(1234, MT_RAND_MT19937);
|
||||
$engines[] = new Mt19937(1234, MT_RAND_PHP);
|
||||
$engines[] = new PcgOneseq128XslRr64(1234);
|
||||
$engines[] = new Xoshiro256StarStar(1234);
|
||||
$engines[] = new TestShaEngine("1234");
|
||||
|
||||
foreach ($engines as $engine) {
|
||||
$randomizer = new Random\Randomizer($engine);
|
||||
$randomizer->getInt(\PHP_INT_MIN, \PHP_INT_MAX);
|
||||
try {
|
||||
$randomizer2 = unserialize(serialize($randomizer));
|
||||
} catch (\Exception $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
continue;
|
||||
echo $engine::class, PHP_EOL;
|
||||
|
||||
$randomizer = new Randomizer($engine);
|
||||
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
$randomizer->getInt(0, $i);
|
||||
}
|
||||
|
||||
if ($randomizer->getInt(\PHP_INT_MIN, \PHP_INT_MAX) !== $randomizer2->getInt(\PHP_INT_MIN, \PHP_INT_MAX)) {
|
||||
die($engine::class . ': failure.');
|
||||
}
|
||||
$randomizer2 = unserialize(serialize($randomizer));
|
||||
|
||||
echo $engine::class . ': success' . PHP_EOL;
|
||||
for ($i = 0; $i < 10_000; $i++) {
|
||||
if ($randomizer->getInt(0, $i) !== $randomizer2->getInt(0, $i)) {
|
||||
$className = $engine::class;
|
||||
|
||||
die("failure: state differs at {$i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
die('success');
|
||||
?>
|
||||
--EXPECTF--
|
||||
Random\Engine\Mt19937: success
|
||||
Random\Engine\Mt19937: success
|
||||
Random\Engine\PcgOneseq128XslRr64: success
|
||||
Random\Engine\Xoshiro256StarStar: success
|
||||
Serialization of 'Random\Engine\Secure' is not allowed
|
||||
Serialization of 'Random\Engine@anonymous' is not allowed
|
||||
UserEngine: success
|
||||
--EXPECT--
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\Mt19937
|
||||
Random\Engine\PcgOneseq128XslRr64
|
||||
Random\Engine\Xoshiro256StarStar
|
||||
Random\Engine\Test\TestShaEngine
|
||||
success
|
||||
|
||||
17
ext/random/tests/03_randomizer/serialize_disallowed.phpt
Normal file
17
ext/random/tests/03_randomizer/serialize_disallowed.phpt
Normal file
@@ -0,0 +1,17 @@
|
||||
--TEST--
|
||||
Random: Randomizer: Serialization of the Randomizer fails if the engine is not serializable
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Engine\Secure;
|
||||
use Random\Randomizer;
|
||||
|
||||
try {
|
||||
serialize(new Randomizer(new Secure()));
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Serialization of 'Random\Engine\Secure' is not allowed
|
||||
@@ -1,19 +0,0 @@
|
||||
--TEST--
|
||||
Random: Randomizer: User: Engine exits
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$randomizer = (new \Random\Randomizer(
|
||||
new class () implements \Random\Engine {
|
||||
public function generate(): string
|
||||
{
|
||||
exit("Exit\n");
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
var_dump($randomizer->getBytes(1));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Exit
|
||||
@@ -1,24 +0,0 @@
|
||||
--TEST--
|
||||
Random: Randomizer: User: Engine throws
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$randomizer = (new \Random\Randomizer(
|
||||
new class () implements \Random\Engine {
|
||||
public function generate(): string
|
||||
{
|
||||
throw new Exception('Error');
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
var_dump($randomizer->getBytes(1));
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Uncaught Exception: Error in %s:%d
|
||||
Stack trace:
|
||||
#0 [internal function]: Random\Engine@anonymous->generate()
|
||||
#1 %s(%d): Random\Randomizer->getBytes(1)
|
||||
#2 {main}
|
||||
thrown in %s on line %d
|
||||
@@ -1,141 +0,0 @@
|
||||
--TEST--
|
||||
Random: Randomizer: User: Engine unsafe
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
use Random\Randomizer;
|
||||
|
||||
final class EmptyStringEngine implements \Random\Engine {
|
||||
public function generate(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
final class HeavilyBiasedEngine implements \Random\Engine {
|
||||
public function generate(): string
|
||||
{
|
||||
return \str_repeat("\xff", PHP_INT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
echo "=====================", PHP_EOL;
|
||||
|
||||
foreach ([
|
||||
EmptyStringEngine::class,
|
||||
HeavilyBiasedEngine::class,
|
||||
] as $engine) {
|
||||
echo $engine, PHP_EOL, "=====================", PHP_EOL, PHP_EOL;
|
||||
|
||||
try {
|
||||
var_dump((new Randomizer(new $engine()))->getInt(0, 123));
|
||||
} catch (Throwable $e) {
|
||||
echo $e, PHP_EOL;
|
||||
}
|
||||
|
||||
echo PHP_EOL, "-------", PHP_EOL, PHP_EOL;
|
||||
|
||||
try {
|
||||
var_dump((new Randomizer(new $engine()))->nextInt());
|
||||
} catch (Throwable $e) {
|
||||
echo $e, PHP_EOL;
|
||||
}
|
||||
|
||||
echo PHP_EOL, "-------", PHP_EOL, PHP_EOL;
|
||||
|
||||
try {
|
||||
var_dump(bin2hex((new Randomizer(new $engine()))->getBytes(1)));
|
||||
} catch (Throwable $e) {
|
||||
echo $e, PHP_EOL;
|
||||
}
|
||||
|
||||
echo PHP_EOL, "-------", PHP_EOL, PHP_EOL;
|
||||
|
||||
try {
|
||||
var_dump((new Randomizer(new $engine()))->shuffleArray(\range(1, 10)));
|
||||
} catch (Throwable $e) {
|
||||
echo $e, PHP_EOL;
|
||||
}
|
||||
|
||||
echo PHP_EOL, "-------", PHP_EOL, PHP_EOL;
|
||||
|
||||
try {
|
||||
var_dump((new Randomizer(new $engine()))->shuffleBytes('foobar'));
|
||||
} catch (Throwable $e) {
|
||||
echo $e, PHP_EOL;
|
||||
}
|
||||
|
||||
echo PHP_EOL, "=====================", PHP_EOL;
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
=====================
|
||||
EmptyStringEngine
|
||||
=====================
|
||||
|
||||
Random\BrokenRandomEngineError: A random engine must return a non-empty string in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): Random\Randomizer->getInt(0, 123)
|
||||
#1 {main}
|
||||
|
||||
-------
|
||||
|
||||
Random\BrokenRandomEngineError: A random engine must return a non-empty string in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): Random\Randomizer->nextInt()
|
||||
#1 {main}
|
||||
|
||||
-------
|
||||
|
||||
Random\BrokenRandomEngineError: A random engine must return a non-empty string in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): Random\Randomizer->getBytes(1)
|
||||
#1 {main}
|
||||
|
||||
-------
|
||||
|
||||
Random\BrokenRandomEngineError: A random engine must return a non-empty string in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): Random\Randomizer->shuffleArray(Array)
|
||||
#1 {main}
|
||||
|
||||
-------
|
||||
|
||||
Random\BrokenRandomEngineError: A random engine must return a non-empty string in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): Random\Randomizer->shuffleBytes('foobar')
|
||||
#1 {main}
|
||||
|
||||
=====================
|
||||
HeavilyBiasedEngine
|
||||
=====================
|
||||
|
||||
Random\BrokenRandomEngineError: Failed to generate an acceptable random number in 50 attempts in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): Random\Randomizer->getInt(0, 123)
|
||||
#1 {main}
|
||||
|
||||
-------
|
||||
|
||||
int(%d)
|
||||
|
||||
-------
|
||||
|
||||
string(2) "ff"
|
||||
|
||||
-------
|
||||
|
||||
Random\BrokenRandomEngineError: Failed to generate an acceptable random number in 50 attempts in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): Random\Randomizer->shuffleArray(Array)
|
||||
#1 {main}
|
||||
|
||||
-------
|
||||
|
||||
Random\BrokenRandomEngineError: Failed to generate an acceptable random number in 50 attempts in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): Random\Randomizer->shuffleBytes('foobar')
|
||||
#1 {main}
|
||||
|
||||
=====================
|
||||
74
ext/random/tests/engines.inc
Normal file
74
ext/random/tests/engines.inc
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Random\Engine\Test;
|
||||
|
||||
use Random\Engine;
|
||||
|
||||
final class TestShaEngine implements Engine
|
||||
{
|
||||
private string $state;
|
||||
|
||||
public function __construct(?string $state = null)
|
||||
{
|
||||
if ($state !== null) {
|
||||
$this->state = $state;
|
||||
} else {
|
||||
$this->state = random_bytes(20);
|
||||
}
|
||||
}
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
$this->state = sha1($this->state, true);
|
||||
|
||||
return substr($this->state, 0, 8);
|
||||
}
|
||||
}
|
||||
|
||||
final class TestWrapperEngine implements Engine
|
||||
{
|
||||
public function __construct(private readonly Engine $engine)
|
||||
{
|
||||
}
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
return $this->engine->generate();
|
||||
}
|
||||
}
|
||||
|
||||
final class TestXoshiro128PlusPlusEngine implements Engine
|
||||
{
|
||||
public function __construct(
|
||||
private int $s0,
|
||||
private int $s1,
|
||||
private int $s2,
|
||||
private int $s3
|
||||
) {
|
||||
}
|
||||
|
||||
private static function rotl($x, $k)
|
||||
{
|
||||
return (($x << $k) | ($x >> (32 - $k))) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
$result = (self::rotl(($this->s0 + $this->s3) & 0xFFFFFFFF, 7) + $this->s0) & 0xFFFFFFFF;
|
||||
|
||||
$t = ($this->s1 << 9) & 0xFFFFFFFF;
|
||||
|
||||
$this->s2 ^= $this->s0;
|
||||
$this->s3 ^= $this->s1;
|
||||
$this->s1 ^= $this->s2;
|
||||
$this->s0 ^= $this->s3;
|
||||
|
||||
$this->s2 ^= $t;
|
||||
|
||||
$this->s3 = self::rotl($this->s3, 11);
|
||||
|
||||
return pack('V', $result);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user