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/09/03 14:02] – add more example zeriyoshi | rfc:rng_extension [2022/02/14 14:28] – fix stringShuffle -> shuffleString zeriyoshi | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== PHP RFC: Random Extension | + | ====== PHP RFC: Random Extension |
- | * Version: | + | * Version: |
- | * Date: 2021-09-02 | + | * Date: 2022-02-14 |
- | * Author: Go Kudo < | + | * Author: Go Kudo < |
* Status: Under Discussion | * Status: Under Discussion | ||
- | * Implementation: | + | * Implementation: |
* First Published at: http:// | * First Published at: http:// | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | Currently, | + | PHP implements |
- | 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, and the header file dependencies are complex. | + | Mersenne Twister, PHP's default RNG, provides a function mt_srand() to initialize with a user-specified seed value, but the scope is global, which may cause unintended user behavior. |
- | Second, the pseudo-random number generator makes use of global state. If a random number is consumed at an unexpected time, the reproducibility of the result | + | When a user executes mt_srand(), one would expect it to only affect result |
- | <code php> | + | * shuffle() |
- | echo foo(1234, function | + | * str_shuffle() |
- | echo foo(1234, function | + | * array_rand() |
- | function foo(int $seed, callable $bar): int { | + | For example, in the following code, the result |
- | mt_srand($seed); | + | |
- | $result | + | |
- | $bar(); | + | |
- | $result += mt_rand(); | + | |
- | return $result; | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | Reproducibility of random numbers can easily be lost if additional code is added later. | + | |
- | + | ||
- | 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. | + | |
- | + | ||
- | There is also the problem of functions that implicitly use the state stored in PHP's global state. | + | |
<code php> | <code php> | ||
mt_srand(1234); | mt_srand(1234); | ||
- | echo mt_rand() | + | $next = mt_rand(); |
mt_srand(1234); | mt_srand(1234); | ||
- | str_shuffle(' | + | $arr = range(0, 9); |
- | echo mt_rand() | + | shuffle($arr); |
+ | $next2 = mt_rand(); | ||
+ | |||
+ | die(" | ||
</ | </ | ||
- | ===== Proposal ===== | + | These behaviors were unintuitive and often led to unintended execution results, but were not that problematic for general web application use. |
- | Clean up the implementation, separate out the random number related functions | + | However, in more complex and repeatable applications (such as games), this can be a problem. |
- | All of the following functions will be moved to the newly created Random extension. | + | There is also the issue of state management difficulties with Fiber, which was added in PHP 8.1. Nikita had this to say: |
- | * lcg_value() | + | https:// |
- | * srand() | + | |
- | * rand() | + | |
- | * mt_srand() | + | |
- | * mt_rand() | + | |
- | * random_int() | + | |
- | * random_bytes() | + | |
- | At the same time, the following internal APIs will also be relocated. If you want to use them, you can simply include ext/ | + | In addition, |
+ | In recent years, many of the environments where PHP runs have been migrating to 64-bit platforms. | ||
+ | In order to generate more secure values, an RNG that can generate 64-bit wide values should be provided by the language. | ||
- | * php_random_int_throw() | + | ===== Proposal ===== |
- | * php_random_int_silent() | + | |
- | * 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() | + | |
- | The following PHP constants will now be provided by the Random | + | Implement |
+ | XorShift128Plus is a fast, high-quality RNG that is proven in major web browsers. | ||
+ | Many of the major hardware architectures are now 64-bit, so it makes sense to use this RNG. | ||
- | * MT_RAND_MT19937 | + | In addition to the new algorithm, the following classes will be added to fix the global scope issue. |
- | * MT_RAND_PHP | + | |
- | To solve the scope problem, the following classes will be added | + | * class Random\NumberGenerator\XorShift128Plus |
+ | * class Random\NumberGenerator\MersenneTwister | ||
+ | * class Random\NumberGenerator\CombinedLCG | ||
+ | * class Random\NumberGenerator\Secure (aka php_random_bytes()) | ||
- | * Random class | + | These classes will hold independent RNG state and will not affect the global scope. |
- | * Random\NumberGenrator abstract class | + | |
- | * Random\NumberGenerator\XorShift128Plus class | + | |
- | * Random\NumberGenerator\MT19937 class | + | |
- | * Random\NumberGenerator\Secure class | + | |
- | The Random | + | An interface |
+ | This interface has only a single generate() method which makes it possible to switch between RNG implementations depending on the situation, | ||
+ | allowing | ||
- | * getInt() | + | RNGs other than XorShift128Plus are based on the RNGs currently implemented in PHP. |
- | * getBytes() | + | |
- | * shuffleArray() | + | |
- | * shuffleString() | + | |
- | This class can be used in the following way. | + | The Random\Randomizer |
- | <code php> | + | This class provides the following methods: |
- | // 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 | + | * __constructor(\Random\NumberGenerator |
- | $mt = new Random\NumberGenerator\MT19937(1234); | + | * getInt(int |
- | $mt-> | + | * getBytes(int $length): string [replacement for random_bytes()] |
- | $random = new Random($mt); | + | * shuffleArray(array |
- | $random-> | + | |
- | $random-> | + | |
- | $random-> | + | |
- | </ | + | |
- | The Random class accepts an instance that inherits from Random\NumberGenerator as a constructor argument. | + | Method equivalent to array_rand() was not implemented at this time because the implementation is complex and can be easily implemented in userland if necessary. |
- | This class is final and cannot be cloned, but it can be serialized. | + | Examples |
- | This is to prevent $rng from being copied by reference to a property and causing unintended behavior. | + | |
- | + | ||
- | The serializability depends on the serializability | + | |
<code php> | <code php> | ||
- | final class Random | + | // Use different RNGs for different environments. |
- | { | + | $rng = $is_production |
- | private Random\NumberGenerator | + | ? new Random\NumberGenerator\Secure() |
- | + | : new Random\NumberGenerator\XorShift128Plus(1234); | |
- | | + | |
- | | + | |
- | 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 {} | + | |
- | public function __serialize(): array {} | + | $randomizer = new Random\Randomizer($rng); |
- | | + | $randomizer-> |
- | } | + | |
</ | </ | ||
- | |||
- | The Random\NumberGenerator abstract class has a single abstract method called generate(). | ||
<code php> | <code php> | ||
- | namespace Random; | + | // Safely migrate the existing mt_rand() state. |
- | abstract class NumberGenerator | + | // before |
- | { | + | mt_srand(1234, |
- | | + | foobar(); |
- | } | + | $result = str_shuffle(' |
- | </ | + | |
- | By defining a class that extends | + | // after |
- | + | $randomizer = new Random\Randomizer(new | |
- | <code php> | + | foobar(); |
- | class UserDefinedRNG extends | + | $result |
- | { | + | |
- | protected int $current = 0; | + | |
- | + | ||
- | public function generate(): int | + | |
- | { | + | |
- | return ++$this-> | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | function | + | |
- | for ($i = 0; $i < 9; $i++) { | + | |
- | echo $numberGenerator->generate(); | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | foobar(new UserDefinedRNG()); // Results: 123456789 | + | |
</ | </ | ||
- | It is also useful when you want to use a random | + | As a side effect of this RFC, the following PHP functions have been moved to the new ext/random |
- | The Random class creates | + | This is because ext/ |
+ | In addition, all RNG-related implementations will be moved to the new random extension in order to standardize the RNG implementation. | ||
- | 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, | + | * lcg_value() |
+ | * srand() | ||
+ | * rand() | ||
+ | * mt_srand() | ||
+ | * mt_rand() | ||
+ | * random_int() | ||
+ | * random_bytes() | ||
- | The Random\NumberGenerator\MT19937 class, which implements the MT19937 Mersenne twister, is also provided for backward compatibility or when a higher period is required. However, a 1-bit right shift is required | + | The following internal APIs will also be moved to the ext/random extension: |
- | <code php> | + | * php_random_int_throw() |
- | $seed = 1234; | + | * php_random_int_silent() |
+ | * 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() | ||
- | $mt = new Random\NumberGenerator\MT19937($seed); | + | All of these features are available from the extension by simply including a single ext/random/php_random.h |
- | mt_srand($seed); | + | |
- | var_dump(mt_rand() === ($mt-> | + | |
- | </ | + | |
- | The following | + | The following |
- | * Random\NumberGenerator\XorShift128Plus | + | * ext/ |
- | * Random\NumberGenerator\MT19937 | + | * ext/ |
- | * Random\NumberGenerator extends user-defined classes. | + | * ext/ |
- | + | * ext/standard/php_random.h | |
- | Also, a new internal API will be implemented. | + | |
- | + | ||
- | * php_random_ng_next() | + | |
- | * php_random_ng_range() | + | |
- | * php_random_ng_array_data_shuffle() | + | |
- | * php_random_ng_string_shuffle() | + | |
- | + | ||
- | A Stub showing these implementations can be found on the Pull-Request. It's probably easier to understand if you look at it. | + | |
- | + | ||
- | * [[https:// | + | |
===== Future Scope ===== | ===== Future Scope ===== | ||
- | This proposal is just a first step to improve | + | These are not within |
- | If this proposal is approved, I will then propose the following changes | + | |
- | + | * Deprecate | |
- | | + | |
- | * Replace random_bytes() with random_bytes() | + | |
- | * Deprecate | + | |
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
- | The code that includes the following | + | The following names have been reserved and will no longer be available |
- | + | ||
- | * ext/ | + | |
- | * ext/ | + | |
- | * ext/ | + | |
- | * ext/ | + | |
- | + | ||
- | The following class names have been reserved and will no longer be available | + | |
* " | * " | ||
* " | * " | ||
* " | * " | ||
- | * " | + | * " |
+ | * " | ||
* " | * " | ||
Line 236: | Line 166: | ||
==== To Existing Extensions ==== | ==== To Existing Extensions ==== | ||
- | none | + | In the future, it may be necessary to change the included header files to point to ext/ |
==== To Opcache ==== | ==== To Opcache ==== | ||
Line 251: | Line 181: | ||
===== Vote ===== | ===== Vote ===== | ||
- | Voting opens 2021-MM-DD and 2021-MM-DD at 00:00:00 EDT. 2/3 required to accept. | + | Voting opens 2022-MM-DD and 2021-MM-DD at 00:00:00 EDT. 2/3 required to accept. |
- | <doodle title=" | + | <doodle title=" |
* Yes | * Yes | ||
* No | * No | ||
Line 259: | Line 189: | ||
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
- | * https:// | + | * https:// |
rfc/rng_extension.txt · Last modified: 2022/08/01 16:52 by timwolla