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/25 15:40] – str_shuffle has drop-in replacement API zeriyoshi | rfc:rng_extension [2021/09/03 13:53] – fix: missing CSPRNG functions. zeriyoshi | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== PHP RFC: Add Random | + | ====== PHP RFC: Random |
- | * Version: | + | * Version: 3.0 |
- | * Date: 2021-05-18 | + | * Date: 2021-09-02 |
* Author: Go Kudo < | * Author: Go Kudo < | ||
- | * Status: | + | * Status: |
+ | * 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 26: | 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 inception | + | 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 |
There is also the problem of functions that implicitly use the state stored in PHP's global state. shuffle(), str_shuffle(), | There is also the problem of functions that implicitly use the state stored in PHP's global state. shuffle(), str_shuffle(), | ||
Line 42: | 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/ |
- | { | + | |
- | public function __construct(string $algo = RANDOM_XORSHIFT128PLUS, ?int $seed = null); | + | |
- | // For user. | + | * 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() | ||
- | // For serialize / unserialize. (but, NOT always available.) | + | The following PHP constants will now be provided by the Random extension |
- | public function __serialize(): | + | |
- | public function __unserialize(array $data): void; | + | |
- | // MUST override in RANDOM_USER. | + | * MT_RAND_MT19937 |
- | | + | * MT_RAND_PHP |
- | } | + | |
- | </ | + | |
- | This single class is used to provide | + | To solve the scope problem, the following classes will be added |
- | This class switches the PRNG implementation to be used by the constructor argument $algo. It is just like the password_hash() function. | + | * Random class |
+ | * Random\NumberGenrator abstract class | ||
+ | * Random\NumberGenerator\XorShift128Plus class | ||
+ | * Random\NumberGenerator\MT19937 class | ||
+ | * Random\NumberGenerator\Secure | ||
- | Also, the static method getNonBiasedMax() allows the user to get the non-biased RNG range. | + | The Random class is a utility class that provides functionality using random numbers. It provides the following methods, but does not provide an alternative |
- | This allows us to rewrite the first example as follows: | + | * getInt() |
+ | * getBytes() | ||
+ | * shuffleArray() | ||
+ | * shuffleString() | ||
- | <code php> | + | The Random class accepts an instance that inherits from Random\NumberGenerator as a constructor argument. |
- | // example 1 | + | |
- | 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 { | + | This class is final and cannot be cloned, but it can be serialized. |
- | $random = new Random(RANDOM_MT19937, | + | This is to prevent |
- | $max = Random:: | + | |
- | $result = $random-> | + | |
- | $bar(); | + | |
- | $result += $random-> | + | |
- | return $result; | + | |
- | } | + | |
- | // example 2 | + | The serializability depends on the serializability of the contained |
- | $random = new Random(RANDOM_MT19937, | + | |
- | $max = Random:: | + | |
- | echo $random-> | + | |
- | $random | + | <code php> |
- | $max = Random::getNonBiasedMax(RANDOM_MT19937); | + | final class Random |
- | str_shuffle(' | + | { |
- | echo $random-> | + | private Random\NumberGenerator |
+ | |||
+ | public function __construct(? | ||
+ | public function getNumberGenerator(): | ||
+ | public function getInt(int $min, int $max): int {} | ||
+ | | ||
+ | public function shuffleArray(array $array): array {} | ||
+ | public function shuffleString(string $string): string {} | ||
+ | |||
+ | | ||
+ | | ||
+ | } | ||
</ | </ | ||
- | Similarly, several C APIs have been added to the PHP core. This can be used to add non-standard PRNGs. | + | The Random\NumberGenerator abstract class has a single abstract method called generate(). |
- | < | + | < |
- | // Note: The detailed implementation is tentative. | + | namespace Random; |
- | typedef struct _php_random_class_algo { | + | |
- | int64_t max; | + | |
- | int64_t (*next)(void *state); | + | |
- | void (*seed)(void *state, const zend_long *seed); // allows NULL. | + | |
- | int (*serialize)(void *state, zval *data); // allows NULL. | + | |
- | int (*unserialize)(void *state, zval *data); // allows NULL. | + | |
- | void *state; | + | |
- | } php_random_class_algo; | + | |
- | int php_random_class_algo_register(const char *ident, const php_random_class_algo *algo); | + | abstract class NumberGenerator |
- | void php_random_class_algo_unregister(const char *ident); | + | { |
+ | abstract public function generate(): int {} | ||
+ | } | ||
</ | </ | ||
- | In php_random_class_algo, the implementation of serialize / unserialize is optional. If the RNG you implement does not support it, you can specify NULL as a member | + | By defining a class that extends Random\NumberGenerator, the user can use their own random number generator. With the introduction |
- | + | ||
- | Also, for RNGs that do not (or cannot) use seed values, the function pointer for seed is optional. If this is passed to a null RNG, an exception will be thrown if a seed value is passed. | + | |
- | + | ||
- | This class also supports the RNG implementation of userland. This is useful for tests that want to fix the expected calculation cost. | + | |
<code php> | <code php> | ||
- | class FixedNumberForTest | + | class UserDefinedRNG |
{ | { | ||
protected int $current = 0; | protected int $current = 0; | ||
- | public function | + | public function |
{ | { | ||
- | | + | |
} | } | ||
+ | } | ||
- | protected | + | function |
- | { | + | |
- | | + | |
} | } | ||
} | } | ||
+ | |||
+ | foobar(new UserDefinedRNG()); | ||
</ | </ | ||
- | ===== Backward Incompatible Changes ===== | + | It is also useful when you want to use a random number sequence with a fixed result, such as in testing. |
- | The class name Random | + | |
- | ===== Proposed PHP Version(s) ===== | + | The Random class creates and uses an instance of the default random number generator, Random\NumberGenerator\XorShift128Plus, |
- | 8.1 | + | |
- | ===== RFC Impact ===== | + | 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, |
- | ==== To SAPIs ==== | + | |
- | none | + | |
- | ==== To Existing Extensions ==== | + | 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. |
- | none | + | |
- | ==== To Opcache ==== | + | <code php> |
- | none | + | $seed = 1234; |
- | ==== New Constants ==== | + | $mt = new Random\NumberGenerator\MT19937($seed); |
- | * RANDOM_XORSHIFT128PLUS | + | mt_srand($seed); |
- | * RANDOM_MT19937 | + | var_dump(mt_rand() |
- | * RANDOM_SECURE | + | </ |
- | * RANDOM_USER | + | |
- | ==== php.ini Defaults ==== | + | The following NumberGenerator class supports serialization. Secure is not serializable because it uses random_bytes internally and has no state. |
- | none | + | |
- | ===== Open Issues ===== | + | * Random\NumberGenerator\XorShift128Plus |
+ | * Random\NumberGenerator\MT19937 | ||
+ | * Random\NumberGenerator extends user-defined classes. | ||
- | === When $seed is null, what is used for the seed value? === | + | Also, a new internal API will be implemented. |
- | Depends on the implementation of algo, but basically it is using internal php_random_int(). | + | |
- | It is similar to mt_srand() from PHP 8.1. | + | |
- | - https:// | + | * php_random_ng_next() |
+ | * php_random_ng_range() | ||
+ | * php_random_ng_array_data_shuffle() | ||
+ | * php_random_ng_string_shuffle() | ||
- | === Why cancelled RNG Extension? === | + | A Stub showing these implementations can be found on the Pull-Request. It's probably easier |
- | As a result of discussions during | + | |
- | The functionality for random numbers is now included in ext/ | + | |
- | === Why not take an object oriented approach? === | + | * [[https:// |
- | This is because it is overly complex and difficult to use, See my previous proposal and the discussion in the internals ML for more details. | + | |
- | * https:// | + | ===== Future Scope ===== |
- | * https:// | + | |
- | * https:// | + | |
- | === Why XorShift128+ as the default algorithm? === | + | This proposal |
- | This algorithm | + | |
- | === Why keep the MT19937 implementation? | + | If this proposal |
- | This is for compatibility. It facilitates quick and easy migration. | + | |
- | === What algorithm does RANDOM_SECURE use exactly? === | + | * Replace the state of the existing implementation with php_random_ng. |
- | It uses php_random_bytes() internally. This API is guaranteed to be a CSPRNG under any circumstances. | + | * Replace random_bytes() with random_bytes() for random numbers used in shuffle(), str_shuffle(), |
+ | * Deprecate srand() and mt_srand() (step by step) | ||
- | === Why support CSPRNG? Isn't random_int() good enough? | + | ===== Backward Incompatible Changes ===== |
- | The goal is to be able to migrate all RNG provided functions to this class in the future. | + | |
- | In other words, to be able to write code without using any of the following functions: | + | |
- | * srand() | + | The code that includes the following header file needs to be changed to ext/ |
- | * rand() | + | |
- | * mt_srand() | + | |
- | * mt_rand() | + | |
- | * shuffle() | + | |
- | * str_shuffle() | + | |
- | * array_rand() | + | |
- | * random_int() | + | |
- | * random_bytes() | + | |
- | In order to use these functions properly, you need to understand PHP core. For many users, this can be difficult. | + | * ext/ |
+ | * ext/ | ||
+ | * ext/ | ||
+ | * ext/ | ||
- | === Why isn't there a drop-in replacement API? === | + | The following |
- | There is no API that can simply replace the following | + | |
- | * shuffle() | + | * " |
- | * array_rand() | + | * " |
+ | * " | ||
+ | * " | ||
+ | * " | ||
- | The approach of these functions is not compatible with recent implementations. | + | ===== Proposed PHP Version(s) ===== |
- | shuffle() uses pass-by-reference, | + | 8.2 |
- | === Why stop deprecation for some functions? | + | ===== RFC Impact ===== |
- | The following functions have been removed from deprecation: | + | ==== To SAPIs ==== |
+ | none | ||
- | * srand() | + | ==== To Existing Extensions ==== |
- | * rand() | + | none |
- | * mt_srand() | + | |
- | * mt_rand() | + | |
- | This is because it is still too early and inappropriate include it in one RFC. | + | ==== To Opcache ==== |
+ | none | ||
- | === What will be the concrete C implementation? | + | ==== New Constants ==== |
- | Please wait. If the discussion in ML is good, I' ll start the implementation. | + | none |
+ | |||
+ | ==== php.ini Defaults ==== | ||
+ | none | ||
+ | |||
+ | ===== Open Issues ===== | ||
+ | none | ||
===== Vote ===== | ===== Vote ===== | ||
Line 247: | Line 239: | ||
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
- | TBD | + | * https:// |
rfc/rng_extension.txt · Last modified: 2022/08/01 16:52 by timwolla