rfc:rng_extension
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
rfc:rng_extension [2021/07/06 14:18] – update latest implementation zeriyoshi | rfc:rng_extension [2021/09/03 14:02] – add more example zeriyoshi | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== PHP RFC: Add Random Extension ====== | + | ====== PHP RFC: Random Extension |
- | * Version: | + | * Version: |
- | * Date: 2021-05-18 | + | * Date: 2021-09-02 |
* Author: Go Kudo < | * Author: Go Kudo < | ||
* Status: Under Discussion | * Status: Under Discussion | ||
- | * Implementation: | + | * Implementation: |
* First Published at: http:// | * First Published at: http:// | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | PHP is currently having problems with RNG reproducibility. | ||
- | PHP' | + | Currently, |
- | But, these functions still store the state in the global state of PHP and are not easily reproducible. Look at the following example. | + | The first is that there are many different implementations. Historically, the random number implementations have been separated into lcg.c, rand.c, mt_rand.c random.c respectively, |
+ | |||
+ | Second, the pseudo-random number generator makes use of global state. If a random number is consumed at an unexpected time, the reproducibility | ||
<code php> | <code php> | ||
Line 27: | Line 28: | ||
</ | </ | ||
- | As mentioned above, the reproducibility | + | Reproducibility |
In addition, the fiber extension was introduced in PHP 8.1. This makes it more difficult to keep track of the execution order. However, this problem has existed since the introduced of Generator. | In addition, the fiber extension was introduced in PHP 8.1. This makes it more difficult to keep track of the execution order. However, this problem has existed since the introduced of Generator. | ||
Line 43: | Line 44: | ||
===== Proposal ===== | ===== Proposal ===== | ||
- | Implement and bundled Random extension into PHP. | ||
- | The phpstub for the whole extension is as follows: | + | Clean up the implementation, |
- | <code php> | + | All of the following functions will be moved to the newly created Random extension. |
- | <?php | + | |
- | /** @generate-class-entries | + | |
- | /** @generate-function-entries | + | |
+ | | ||
+ | * mt_srand() | ||
+ | | ||
+ | * random_int() | ||
+ | | ||
- | namespace Random | + | At the same time, the following internal APIs will also be relocated. If you want to use them, you can simply include ext/ |
- | { | + | |
- | interface NumberGenerator | + | |
- | { | + | |
- | public function generate(): int; | + | |
- | } | + | |
- | } | + | |
- | namespace Random\NumberGenerator | + | * php_random_int_throw() |
- | { | + | * php_random_int_silent() |
- | class XorShift128Plus implements Random\NumberGenerator | + | * php_combined_lcg() |
- | { | + | * php_mt_srand() |
- | | + | * php_mt_rand() |
+ | * php_mt_rand_range() | ||
+ | * php_mt_rand_common() | ||
+ | * php_srand() | ||
+ | * php_rand() | ||
+ | * php_random_bytes() | ||
+ | * php_random_int() | ||
- | public function generate(): int {} | + | The following PHP constants will now be provided by the Random extension |
- | public function __serialize(): | + | * MT_RAND_MT19937 |
+ | * MT_RAND_PHP | ||
- | public function __unserialize(array $data): void {} | + | To solve the scope problem, the following classes will be added |
- | } | + | |
- | | + | * Random |
- | { | + | * Random\NumberGenrator abstract class |
- | /** @implementation-alias | + | * Random\NumberGenerator\XorShift128Plus |
- | | + | |
+ | * Random\NumberGenerator\Secure class | ||
- | /** @implementation-alias | + | The Random |
- | public function generate(): int {} | + | |
- | /** @implementation-alias Random\NumberGenerator\XorShift128Plus:: | + | |
- | | + | |
+ | | ||
+ | * shuffleString() | ||
- | /** @implementation-alias Random\NumberGenerator\XorShift128Plus:: | + | This class can be used in the following way. |
- | public function __unserialize(array $data): void {} | + | |
- | } | + | |
- | class Secure implements Random\NumberGenerator | + | <code php> |
- | { | + | // functions |
- | | + | mt_srand(1234); |
+ | mt_rand(); // generate random number | ||
+ | mt_rand(1, 10); // generate random number in range | ||
+ | str_shuffle(" | ||
+ | $arr = range(1, 10); | ||
+ | shuffle($arr); | ||
- | | + | // object |
- | public function generate(): int {} | + | $mt = new Random\NumberGenerator\MT19937(1234); |
- | } | + | $mt-> |
- | } | + | $random |
- | + | $random-> | |
- | namespace | + | $random-> |
- | { | + | $random-> |
- | final class Random | + | |
- | { | + | |
- | // FIXME: stub generator | + | |
- | | + | |
- | | + | |
- | + | ||
- | public function __construct(? | + | |
- | public function getNumberGenerator(): | + | |
- | public function nextInt(): int {} | + | |
- | | + | |
- | | + | |
- | | + | |
- | public function shuffleString(string $string): string {} | + | |
- | public function __serialize(): array {} | + | |
- | public function __unserialize(array $data): void {} | + | |
- | } | + | |
- | } | + | |
</ | </ | ||
- | Each RNG is implemented as a class in the Random\NumberGenerator | + | The Random |
- | The bundled RNGs are as follows: | + | This class is final and cannot be cloned, but it can be serialized. |
+ | This is to prevent $rng from being copied by reference to a property and causing unintended behavior. | ||
- | * Random\NumberGenerator\XorShift128Plus: | + | The serializability depends on the serializability of the contained $rng. |
- | * Random\NumberGenerator\MT19937: | + | |
- | * Random\NumberGenerator\Secure: | + | |
- | Random | + | <code php> |
- | However, when used XorShift128+ in a 32-bit environment, | + | final class Random |
+ | { | ||
+ | private Random\NumberGenerator $randomNumberGenerator; | ||
- | Note that (new Random(new Random\NumberGenerator\MT19937($seed)))-> | + | public function __construct(?Random\NumberGenerator $randomNumberGenerator = null) {} |
+ | public function getNumberGenerator(): Random\NumberGenerator | ||
+ | public function getInt(int $min, int $max): int {} | ||
+ | public function getBytes(int $length): string {} | ||
+ | public function shuffleArray(array $array): array {} | ||
+ | public function shuffleString(string $string): string {} | ||
- | Secure is practically equivalent to random_int() and random_bytes(), This is useful when secure | + | public function __serialize(): array {} |
+ | public function __unserialize(array | ||
+ | } | ||
+ | </ | ||
- | This class also supports RNGs defined in userland. It can be used by passing an instance of a class that implements the RandomNumberGenerator interface provided at the same time as the first argument.This is useful for unit testing or when you want to use a fixed number. | + | The Random\NumberGenerator abstract |
<code php> | <code php> | ||
+ | namespace Random; | ||
- | class UserDefinedRNG | + | abstract class NumberGenerator |
+ | { | ||
+ | abstract public function generate(): int {} | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | By defining a class that extends Random\NumberGenerator, | ||
+ | |||
+ | <code php> | ||
+ | class UserDefinedRNG | ||
{ | { | ||
protected int $current = 0; | protected int $current = 0; | ||
- | | + | |
public function generate(): int | public function generate(): int | ||
{ | { | ||
Line 148: | Line 159: | ||
} | } | ||
- | function foobar(Random $random): void { | + | function foobar(Random\NumberGenerator |
for ($i = 0; $i < 9; $i++) { | for ($i = 0; $i < 9; $i++) { | ||
- | echo $random->nextInt(); | + | echo $numberGenerator->generate(); |
} | } | ||
} | } | ||
- | foobar(new Random(new UserDefinedRNG())); // Results: 123456789 | + | foobar(new UserDefinedRNG()); |
</ | </ | ||
- | Also, as with MT, various alternative APIs using Random class will be provided. | + | It is also useful when you want to use a random number sequence with a fixed result, such as in testing. |
- | <code c> | + | The Random class creates and uses an instance of the default random number generator, Random\NumberGenerator\XorShift128Plus, |
- | /* similar php_mt_rand() */ | + | |
- | uint64_t php_random_next(php_random *php_random, bool shift); | + | |
- | /* similar php_mt_rand_range() */ | + | XorShift128Plus is an efficient, high-quality algorithm used in modern browsers and other applications. This algorithm is capable of generating a wider range of random numbers in a 64-bit environment. In a 32-bit environment, |
- | zend_long php_random_range(php_random *php_random, zend_long | + | |
- | /* similar php_array_data_shuffle() */ | + | The Random\NumberGenerator\MT19937 |
- | void php_random_array_data_shuffle(php_random *php_random, | + | |
- | + | ||
- | /* similar php_string_shuffle() */ | + | |
- | void php_random_string_shuffle(php_random *php_random, | + | |
- | </ | + | |
- | + | ||
- | The Random class can be serialized using the standard PHP serialization mechanism. But, if the $rng member | + | |
<code php> | <code php> | ||
- | // serialize | + | $seed = 1234; |
- | $foo = new Random(new Random\Numbergenerator\XorShift128Plus()); | + | |
- | for ($i = 0; $i < 10; $i++) { $foo-> | + | |
- | var_dump(unserialize(serialize($foo))-> | + | |
- | // can't serialize | + | $mt = new Random\NumberGenerator\MT19937($seed); |
- | $foo = new Random(new Random\Numbergenerator\Secure()); | + | mt_srand($seed); |
- | for ($i = 0; $i < 10; $i++) { $foo-> | + | var_dump(mt_rand() === ($mt->generate() >> 1)); // true |
- | var_dump(unserialize(serialize($foo))-> | + | |
</ | </ | ||
- | It is not possible to clone the Random | + | The following NumberGenerator |
- | <code php> | + | * Random\NumberGenerator\XorShift128Plus |
- | $foo = new Random(); | + | * Random\NumberGenerator\MT19937 |
+ | | ||
- | // can't direct clone | + | Also, a new internal API will be implemented. |
- | // $bar = clone $foo; | + | |
- | // safe | + | * php_random_ng_next() |
- | $bar = new Random(clone $foo-> | + | * php_random_ng_range() |
- | </ | + | * php_random_ng_array_data_shuffle() |
+ | * php_random_ng_string_shuffle() | ||
- | Using this feature, the first example | + | A Stub showing these implementations |
- | <code php> | + | * [[https://github.com/php/php-src/ |
- | echo foo(1234, function (): void {}) . PHP_EOL; | + | |
- | echo foo(1234, function (): void { mt_rand(); }) . PHP_EOL; | + | |
- | function foo(int $seed, callable $bar): int { | + | ===== Future Scope ===== |
- | $random | + | |
- | $result | + | |
- | $bar(); | + | |
- | $result += ($random-> | + | |
- | return $result; | + | |
- | } | + | |
- | </ | + | |
+ | This proposal is just a first step to improve the situation of PHP's random number implementation. | ||
+ | If this proposal is approved, I will then propose the following changes | ||
- | ===== Future Scope ===== | + | * Replace the state of the existing implementation with php_random_ng. |
+ | * Replace random_bytes() with random_bytes() for random numbers used in shuffle(), str_shuffle(), | ||
+ | * Deprecate srand() and mt_srand() (step by step) | ||
- | This RFC will be the basis for making PHP RNGs safe in the future. | + | ===== Backward Incompatible Changes ===== |
- | By first accepted this RFC, PHP gets a random number in the local scope. | + | The code that includes |
- | The Random class can also be used when new features are implemented that use random numbers. This has the effect of discouraging more implementations from using random | + | * ext/ |
+ | * ext/ | ||
+ | * ext/ | ||
+ | * ext/ | ||
- | More in the future, we can consider doing away with functions such as mt_srand(). These functions are simple and convenient, but they may unintentionally create implementations that depend on global scope. | + | The following class names have been reserved and will no longer be available |
- | + | ||
- | ===== Backward Incompatible Changes ===== | + | |
- | The following class name will no longer be available: | + | |
* " | * " | ||
Line 236: | Line 229: | ||
===== Proposed PHP Version(s) ===== | ===== Proposed PHP Version(s) ===== | ||
- | 8.1 | + | 8.2 |
- | + | ||
- | ===== FAQ ===== | + | |
- | ==== ==== | + | |
===== RFC Impact ===== | ===== RFC Impact ===== | ||
Line 269: | Line 259: | ||
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
- | * https:// | + | * https:// |
rfc/rng_extension.txt · Last modified: 2022/08/01 16:52 by timwolla