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/06/01 14:01] – go under discussion 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: 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 ===== | ||
- | Implements Random class and RandomNumberGenerator interface. | ||
- | These class / interface implement in 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. |
+ | |||
+ | * lcg_value() | ||
+ | * 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/ | ||
+ | |||
+ | * php_random_int_throw() | ||
+ | * 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 | ||
+ | |||
+ | * MT_RAND_MT19937 | ||
+ | * MT_RAND_PHP | ||
+ | |||
+ | To solve the scope problem, the following classes will be added | ||
+ | |||
+ | * Random class | ||
+ | * 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 | ||
+ | |||
+ | * getInt() | ||
+ | * getBytes() | ||
+ | * shuffleArray() | ||
+ | * shuffleString() | ||
+ | |||
+ | This class can be used in the following way. | ||
<code php> | <code php> | ||
- | const RANDOM_XORSHIFT128PLUS = ' | + | // functions |
- | const RANDOM_MT19937 = ' | + | mt_srand(1234); |
- | const RANDOM_SECURE | + | mt_rand(); // generate random number |
+ | mt_rand(1, 10); // generate random number in range | ||
+ | str_shuffle(" | ||
+ | $arr = range(1, 10); | ||
+ | shuffle($arr); // shuffle array items (pass by reference) | ||
- | interface RandomNumberGenerator | + | // object |
- | { | + | $mt = new Random\NumberGenerator\MT19937(1234); |
- | /** | + | $mt-> |
- | * Generate a number. | + | $random = new Random($mt); |
- | */ | + | $random-> |
- | | + | $random-> |
- | } | + | $random-> |
+ | </ | ||
- | final class Random | + | The Random |
- | { | + | |
- | private ? | + | |
- | /** | + | This class is final and cannot be cloned, but it can be serialized. |
- | * Get registered algorithm string in array. | + | This is to prevent |
- | */ | + | |
- | public static function getAlgos(): array; | + | |
- | + | ||
- | /** | + | |
- | * Get algorithm information in array. | + | |
- | * if not registered, returns null. | + | |
- | */ | + | |
- | public static function getAlgoInfo(string | + | |
- | /** | + | The serializability depends on the serializability of the contained |
- | * Constructor. | + | |
- | * if $seed is null, generating seed by php_random_bytes(), | + | |
- | */ | + | |
- | public function __construct(string|RandomNumberGenerator $algo = RANDOM_XORSHIFT128PLUS, | + | |
- | /** | + | <code php> |
- | * Returns raw generated number by RNG. | + | final class Random |
- | * if on the 32-bit machine, least 32-bit will be truncated. | + | { |
- | */ | + | |
- | | + | |
- | | + | |
- | * Generates a number within a given range. | + | |
- | * similar random_int() function. | + | |
- | */ | + | |
public function getInt(int $min, int $max): int {} | public function getInt(int $min, int $max): int {} | ||
- | |||
- | /** | ||
- | * Generates a string within a given range. | ||
- | * similar random_bytes() function. | ||
- | */ | ||
public function getBytes(int $length): string {} | public function getBytes(int $length): string {} | ||
- | |||
- | /** | ||
- | * Shuffling the given array items. | ||
- | * similar shuffle(), but non-pass-by-reference. | ||
- | */ | ||
public function shuffleArray(array $array): array {} | public function shuffleArray(array $array): array {} | ||
- | |||
- | /** | ||
- | * Shuffling the given string characters. | ||
- | * similar str_shuffle(). | ||
- | */ | ||
public function shuffleString(string $string): string {} | public function shuffleString(string $string): string {} | ||
- | /** | ||
- | * Serializing state to string if algo's supported. | ||
- | */ | ||
public function __serialize(): | public function __serialize(): | ||
- | |||
- | /** | ||
- | * Unseriialize state from string in algo's supported. | ||
- | */ | ||
public function __unserialize(array $data): void {} | public function __unserialize(array $data): void {} | ||
} | } | ||
- | |||
</ | </ | ||
- | This class retrieves and uses the RNG algorithm registered in the core, based on the string passed in the constructor argument $algo. | + | The Random\NumberGenerator abstract |
- | The bundled RNGs are as follows: | + | <code php> |
+ | namespace Random; | ||
- | * XorShift128+: | + | abstract class NumberGenerator |
- | * MT19937: 32-bit, reproducible, | + | { |
- | * secure: 64-bit, non-reproducible, | + | abstract public function generate(): int {} |
+ | } | ||
+ | </code> | ||
- | 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. | + | By defining a class that extends Random\NumberGenerator, the user can use their own random number generator. With the introduction of JIT in PHP 8.0, this can generate random numbers |
- | + | ||
- | " | + | |
- | + | ||
- | 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 | + | |
<code php> | <code php> | ||
- | + | class UserDefinedRNG | |
- | class UserDefinedRNG | + | |
{ | { | ||
protected int $current = 0; | protected int $current = 0; | ||
- | | + | |
public function generate(): int | public function generate(): int | ||
{ | { | ||
Line 152: | 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()); |
</ | </ | ||
- | 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\NumberGenerator\MT19937 |
+ | | ||
- | /* similar php_string_shuffle() */ | + | Also, a new internal API will be implemented. |
- | void php_random_class_string_shuffle(php_random_class *random_class, 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 243: | Line 242: | ||
==== New Constants ==== | ==== New Constants ==== | ||
- | * RANDOM_XORSHIFT128PLUS | + | none |
- | * RANDOM_MT19937 | + | |
- | * RANDOM_SECURE | + | |
==== php.ini Defaults ==== | ==== php.ini Defaults ==== | ||
Line 262: | Line 259: | ||
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
- | * https:// | + | * https:// |
rfc/rng_extension.txt · Last modified: 2022/08/01 16:52 by timwolla