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/05/31 13:17] – back draft zeriyoshi | rfc:rng_extension [2021/09/03 14:28] – zeriyoshi | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== PHP RFC: Add Random | + | ====== PHP RFC: Random |
- | * Version: | + | * Version: |
- | * Date: 2021-05-18 | + | * Date: 2021-09-02 |
* Author: Go Kudo < | * Author: Go Kudo < | ||
- | * Status: | + | * Status: |
- | * 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 ===== | ||
- | Implements Random class. | ||
- | This class implement to ext/ | + | Clean up the implementation, separate out the random number related functions as Random extension, and add an object scoped API. |
- | The PHP code that represents | + | All of the following functions will be moved to the newly created Random extension. |
- | <code php> | + | * lcg_value() |
- | const RANDOM_XORSHIFT128PLUS = ' | + | * srand() |
- | const RANDOM_MT19937 = ' | + | * rand() |
- | const RANDOM_SECURE = ' | + | * mt_srand() |
- | const RANDOM_USER = ' | + | * mt_rand() |
+ | * random_int() | ||
+ | * random_bytes() | ||
- | class Random | + | At the same time, the following internal APIs will also be relocated. If you want to use them, you can simply include ext/random/random.h. |
- | { | + | |
- | /** | + | |
- | * Get registered algorithm in array. | + | |
- | */ | + | |
- | public static function getAlgos(): array; | + | |
- | + | ||
- | | + | |
- | * Get algorithm information in array. | + | |
- | * if not registered, returns null. | + | |
- | */ | + | |
- | public static function getAlgoInfo(string $algo): ?array; | + | |
- | /** | + | |
- | | + | |
- | | + | * 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 |
- | * Returns raw generated number | + | |
- | * if on the 32-bit machine, then uses 64-bit RNG throw Exception. | + | |
- | * but can implicating truncate on random.ignore_generated_size_exceeded=1 INI directive. | + | |
- | */ | + | |
- | public function nextInt(): int {} | + | |
- | /** | + | |
- | | + | * MT_RAND_PHP |
- | * similar random_int() function. | + | |
- | */ | + | |
- | public function getInt(int $min, int $max): int {} | + | |
- | /** | + | To solve the scope problem, the following classes will be added |
- | * Generates a string within a given range. | + | |
- | * similar random_bytes() function. | + | |
- | */ | + | |
- | public function getBytes(int $length): string {} | + | |
- | /** | + | |
- | | + | * Random\NumberGenrator abstract class |
- | | + | * Random\NumberGenerator\XorShift128Plus class |
- | | + | * Random\NumberGenerator\MT19937 class |
- | | + | * Random\NumberGenerator\Secure class |
- | /** | + | The Random class is a utility class that provides functionality using random numbers. It provides the following methods, but does not provide an alternative to array_rand because it is too complex. |
- | * Shuffling the given string characters. | + | |
- | * similar str_shuffle(). | + | |
- | */ | + | |
- | public function shuffleString(string $string): string {} | + | |
- | /** | + | |
- | | + | * getBytes() |
- | | + | * shuffleArray() |
- | | + | * shuffleString() |
- | /** | + | This class can be used in the following way. |
- | * Unseriialize state from string | + | |
- | */ | + | |
- | public function __unserialize(array $data): void {} | + | |
- | | + | <code php> |
- | * Must override user-defined RNG class. | + | // functions |
- | * only uses inherits from the Random class with RANDOM_USER argument. | + | 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 | ||
+ | $mt = new Random\NumberGenerator\MT19937(1234); | ||
+ | $mt-> | ||
+ | $random = new Random($mt); | ||
+ | $random-> | ||
+ | $random-> | ||
+ | $random-> | ||
</ | </ | ||
- | This class retrieves and uses the RNG algorithm registered in the core, based on the string passed in the constructor argument | + | 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. | ||
- | * XorShift128+: | + | The serializability depends on the serializability of the contained $rng. |
- | * MT19937: 32-bit, reproducible, | + | |
- | * secure: 64-bit, non-reproducible, | + | |
- | * user: user defined. | + | |
- | By default, XorShift128+ is used. It can generate 64-bit values, is used by major browsers, and is fast and reliable. On the other hand, MT19937 is retained for compatibility. | + | <code php> |
+ | final class Random | ||
+ | { | ||
+ | private Random\NumberGenerator $randomNumberGenerator; | ||
- | " | + | public function __construct(? |
+ | public function getNumberGenerator(): | ||
+ | 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(): | ||
+ | public function __unserialize(array $data): void {} | ||
+ | } | ||
+ | </ | ||
- | " | + | The Random\NumberGenerator abstract |
<code php> | <code php> | ||
+ | namespace Random; | ||
- | class FixedNumberRandom extends Random | + | abstract |
{ | { | ||
- | protected | + | abstract public function generate(): |
+ | } | ||
+ | </ | ||
- | public function __construct() | + | By defining a class that extends Random\NumberGenerator, |
- | { | + | |
- | | + | <code php> |
- | | + | class UserDefinedRNG extends Random\NumberGenerator |
+ | { | ||
+ | | ||
- | | + | |
{ | { | ||
return ++$this-> | return ++$this-> | ||
Line 156: | 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 | + | foobar(new |
</ | </ | ||
- | Algorithms can be registered and unregistered using PHP's C API, and new implementations can be added via Extension. The following APIs are 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, |
- | /* Register new RNG algo */ | + | |
- | int php_random_class_algo_register(const php_random_class_algo *algo); | + | |
- | /* Unregister RNG algo */ | + | 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, |
- | void php_random_class_algo_unregister(const char *ident); | + | |
- | /* Find and get registered algo */ | + | 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 to obtain exactly the same result as mt_rand(), as shown below. This is due to historical reasons. |
- | const php_random_class_algo* php_random_class_algo_find(const zend_string *ident); | + | |
- | </ | + | |
- | Also, as with MT, various alternative APIs using object scope RNGs will be provided. | + | <code php> |
+ | $seed = 1234; | ||
- | <code c> | + | $mt = new Random\NumberGenerator\MT19937($seed); |
- | /* similar php_mt_rand() */ | + | mt_srand($seed); |
- | uint64_t php_random_class_next(php_random_class *random_class, | + | var_dump(mt_rand() === ($mt-> |
+ | </ | ||
- | /* similar php_mt_rand_range() */ | + | The following NumberGenerator class supports serialization. Secure is not serializable because it uses random_bytes internally and has no state. |
- | zend_long php_random_class_range(php_random_class *random_class, | + | |
- | /* similar php_array_data_shuffle() | + | |
- | void php_random_class_array_data_shuffle(php_random_class *random_class, | + | |
+ | * Random\NumberGenerator extends user-defined classes. | ||
- | /* similar php_string_shuffle() */ | + | Also, a new internal API will be implemented. |
- | void php_random_class_string_shuffle(php_random_class *random_class, zend_object *obj, char *str, zend_long len); | + | |
- | </ | + | |
- | Random class can be serialized or cloned if the algorithm supports it. This is useful for storing and reusing state. | + | * php_random_ng_next() |
+ | * php_random_ng_range() | ||
+ | * php_random_ng_array_data_shuffle() | ||
+ | * php_random_ng_string_shuffle() | ||
- | <code php> | + | A Stub showing these implementations can be found on the Pull-Request. It's probably easier to understand if you look at it. |
- | // serialize | + | |
- | $foo = new Random(); | + | |
- | for ($i = 0; $i < 10; $i++) { $foo-> | + | |
- | var_dump(unserialize(serialize($foo))-> | + | |
- | // clone | + | * [[https://github.com/ |
- | $bar = new Random(); | + | |
- | for ($i = 0; $i < 10; $i++) { $bar-> | + | |
- | $baz = clone $bar; | + | |
- | var_dump($baz-> | + | |
- | </code> | + | |
- | Using this feature, the first example can be rewritten as follows: | + | ===== Future Scope ===== |
- | <code php> | + | This proposal is just a first step to improve the situation of PHP's random number implementation. |
- | echo foo(1234, function (): void {}) . PHP_EOL; // Result: 1480009472 | + | |
- | echo foo(1234, function (): void { mt_rand(); }) . PHP_EOL; // Result: 1480009472 | + | |
- | function foo(int $seed, callable $bar): int { | + | If this proposal is approved, I will then propose the following changes |
- | $random | + | |
- | $result = $random-> | + | * Replace the state of the existing implementation with php_random_ng. |
- | $bar(); | + | * Changes |
- | $result += $random-> | + | * Deprecate srand() and mt_srand() (step by step) |
- | return $result; | + | |
- | } | + | |
- | </ | + | |
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
- | The following items will no longer be available: | ||
- | - " | + | The code that includes the following header file needs to be changed to ext/ |
- | | + | |
- | | + | * ext/ |
- | | + | * ext/ |
- | | + | * ext/ |
+ | * ext/ | ||
+ | |||
+ | The following class names have been reserved and will no longer be available | ||
+ | |||
+ | * " | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
===== Proposed PHP Version(s) ===== | ===== Proposed PHP Version(s) ===== | ||
- | 8.1 | + | 8.2 |
===== RFC Impact ===== | ===== RFC Impact ===== | ||
Line 247: | Line 242: | ||
==== New Constants ==== | ==== New Constants ==== | ||
- | * RANDOM_XORSHIFT128PLUS | + | none |
- | * RANDOM_MT19937 | + | |
- | * RANDOM_SECURE | + | |
- | * RANDOM_USER | + | |
==== php.ini Defaults ==== | ==== php.ini Defaults ==== | ||
- | * random.ignore_generated_size_exceeded=0 | + | none |
- | + | ||
- | If 1, implicit truncation will be performed when a 64-bit value is generated in a 32-bit environment. If 0, an exception will be thrown. | + | |
===== Open Issues ===== | ===== Open Issues ===== | ||
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