rfc:rng_extension

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
Next revision Both sides next revision
rfc:rng_extension [2021/06/02 11:49]
zeriyoshi fix typo
rfc:rng_extension [2021/09/03 14:28]
zeriyoshi
Line 1: Line 1:
-====== PHP RFC: Add Random class and RandomNumberGenerator intreface ====== +====== PHP RFC: Random Extension 3.0 ====== 
-  * Version: 1.11 +  * Version: 3.0 
-  * Date: 2021-05-18+  * Date: 2021-09-02
   * Author: Go Kudo <zeriyoshi@gmail.com>   * Author: Go Kudo <zeriyoshi@gmail.com>
   * Status: Under Discussion   * Status: Under Discussion
-  * Implementation: https://github.com/php/php-src/pull/7079+  * Implementation: https://github.com/php/php-src/pull/7453
   * First Published at: http://wiki.php.net/rfc/object_scope_prng   * First Published at: http://wiki.php.net/rfc/object_scope_prng
  
 ===== Introduction ===== ===== Introduction =====
-PHP is currently having problems with RNG reproducibility. 
  
-PHP'RNG has been unified into an implementation using the Mersenne twister, with the rand() and srand() functions becoming aliases for mt_rand() and mt_srand() respectively in PHP 7.1.+Currently, PHP'random number implementation suffers from several problems.
  
-Butthese 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, and the header file dependencies are complex. 
 + 
 +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 may be lost. Look at the following example.
  
 <code php> <code php>
Line 27: Line 28:
 </code> </code>
  
-As mentioned above, the reproducibility of random numbers can easily be lost if additional processing is added later.+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. 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/standardalways bundled PHP core.+Clean up the implementationseparate out the random number related functions as Random extension, and add an object scoped API.
  
-The PHP code that represents the implementation is as follows:+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/random/random.h. 
 + 
 +  * 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 PHP constants will now be provided by the Random extension 
 + 
 +  * 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 the following methods, but does not provide an alternative to array_rand because it is too complex. 
 + 
 +  * getInt() 
 +  * getBytes() 
 +  * shuffleArray() 
 +  * shuffleString() 
 + 
 +This class can be used in the following way.
  
 <code php> <code php>
-const RANDOM_XORSHIFT128PLUS = 'xorshift128plus'; // 64-bit, fastdefault +// functions 
-const RANDOM_MT19937 = 'mt19937'; // 32-bit, for backward compatibility +mt_srand(1234); 
-const RANDOM_SECURE 'secure'; // Cryptographically-Secure PRNG+mt_rand(); // generate random number 
 +mt_rand(110); // generate random number in range 
 +str_shuffle("foobar"); // shuffle string 
 +$arr range(1, 10); 
 +shuffle($arr); // shuffle array items (pass by reference)
  
-interface RandomNumberGenerator +// object 
-{ +$mt = new Random\NumberGenerator\MT19937(1234); 
-    /** +$mt->generate(); // generate random number  
-      * Generate a number. +$random = new Random($mt); 
-      *+$random->getInt(1, 10); // generate random number in range 
-    public function generate(): int+$random->shuffleString("foobar"); // shuffle string 
-}+$random->shuffleArray(range(1, 10)); // shuffle array items (pass by value) 
 +</code>
  
-final class Random +The Random class accepts an instance that inherits from Random\NumberGenerator as a constructor argument.
-+
-    private ?RandomNumberGenerator $rng;+
  
-    /** +This class is final and cannot be clonedbut it can be serialized
-     * Get registered algorithm string in array. +This is to prevent $rng from being copied by reference to a property and causing unintended behavior.
-     */ +
-    public static function getAlgos(): array; +
-     +
-    /** +
-     * Get algorithm information in array. +
-     * if not registeredreturns null+
-     */ +
-    public static function getAlgoInfo(string $algo): ?array;+
  
-    /** +The serializability depends on the serializability of the contained $rng.
-     * Constructor. +
-     * if $seed is null, generating seed by php_random_bytes(), but whether to use depends to algo. +
-     */ +
-    public function __construct(string|RandomNumberGenerator $algo = RANDOM_XORSHIFT128PLUS, ?int $seed = null) {}+
  
-    /** +<code php> 
-     * Returns raw generated number by RNG. +final class Random 
-     * if on the 32-bit machine, least 32-bit will be truncated. +{ 
-     */ +    private Random\NumberGenerator $randomNumberGenerator;
-    public function nextInt(): int {}+
  
-    /** +    public function __construct(?Random\NumberGenerator $randomNumberGenerator = null{} 
-     * Generates a number within a given range. +    public function getNumberGenerator(): Random\NumberGenerator {}
-     * 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(): array {}     public function __serialize(): array {}
- 
-    /** 
-     * Unserialize state from string if algo's supported. 
-     */ 
     public function __unserialize(array $data): void {}     public function __unserialize(array $data): void {}
 } }
- 
 </code> </code>
  
-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 class has a single abstract method called generate()
  
-The bundled RNGs are as follows:+<code php> 
 +namespace Random;
  
-  * XorShift128+: 64-bit, reproducible, PRNG. +abstract class NumberGenerator 
-  * MT19937: 32-bit, reproducible, PRNG, compatible mt_srand() / mt_rand(). +
-  * secure: 64-bit, non-reproducible, CSPRNG, uses php_random_bytes() internally.+        abstract public function generate(): int {} 
 +
 +</code>
  
-By defaultXorShift128+ is used. It can generate 64-bit values, is used by major browsers, and is fast and reliableOn the other hand, MT19937 is retained for compatibility. +By defining a class that extends Random\NumberGeneratorthe user can use their own random number generatorWith the introduction of JIT in PHP 8.0this can generate random numbers at a realistic speed.
- +
-"secure" is practically equivalent to random_int()but can be used to shuffle arrays and strings. Also, since it is an object, it is easy to exchange implementations. +
- +
-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 fixed number.+
  
 <code php> <code php>
- +class UserDefinedRNG extends Random\NumberGenerator
-class UserDefinedRNG implements RandomNumberGenerator+
 { {
     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 $numberGenerator): void {
     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()); // Results: 123456789
 </code> </code>
  
-Algorithms can be registered and unregistered using PHP's C APIand new implementations can be added via ExtensionThe following APIs are provided:+It is also useful when you want to use a random number sequence with a fixed resultsuch as in testing.
  
-<code c> +The Random class creates and uses an instance of the default random number generator, Random\NumberGenerator\XorShift128Plus, if the constructor argument is omitted.
-/* 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, the range beyond zend_long will simply be truncated. This indicates incompatibility between environments, but is acceptable for real-world use.
-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)+
-</code>+
  
-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->generate() >> 1)); // true 
 +</code>
  
-/* 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, zend_long min, zend_long max);+
  
-/similar php_array_data_shuffle() */ +  Random\NumberGenerator\XorShift128Plus 
-void php_random_class_array_data_shuffle(php_random_class *random_class, zval *array);+  Random\NumberGenerator\MT19937 
 +  Random\NumberGenerator extends user-defined classes.
  
-/* similar php_string_shuffle() */ +Alsoa new internal API will be implemented.
-void php_random_class_string_shuffle(php_random_class *random_classchar *str, zend_long len); +
-</code>+
  
-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->nextInt();+
-var_dump(unserialize(serialize($foo))->nextInt() === $foo->nextInt()); // true+
  
-// clone +  * [[https://github.com/php/php-src/blob/7a4ef6ccfbf4a2cd48a4f261f2911ebb7b057d46/ext/random/random.stub.php|random.stub.php]]
-$bar = new Random(); +
-for ($i = 0; $i < 10; $i++) { $bar->nextInt();+
-$baz = clone $bar; +
-var_dump($baz->nextInt() === $bar->nextInt()); // true +
-</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 $seedcallable $bar): int { +If this proposal is approvedI will then propose the following changes 
-    $random = new Random(RANDOM_MT19937$seed)+ 
-    $result = $random->nextInt(); +  * Replace the state of the existing implementation with php_random_ng. 
-    $bar()+  * Changes random source to php_random_int() a shuffle()str_shuffle(), and array_rand() . 
-    $result += $random->nextInt()+  * Deprecate srand() and mt_srand() (step by step)
-    return $result; +
-+
-</code>+
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
-The following items will no longer be available: 
  
-  - "Random" class name +The code that includes the following header file needs to be changed to ext/random/random.h 
-  "RandomNumberGeneratorclass (interface) name + 
-  "RANDOM_XORSHIFT128PLUSconstant +  * ext/standard/lcg.h 
-  "RANDOM_MT19937constant +  * ext/standard/rand.h 
-  "RANDOM_SECUREconstant+  * ext/standard/mt_rand.h 
 +  * ext/standard/random.h 
 + 
 +The following class names have been reserved and will no longer be available 
 + 
 +  * "Random" 
 +  "Random\NumberGenerator
 +  "Random\NumberGenerator\XorShift128Plus
 +  "Random\NumberGenerator\MT19937
 +  "Random\NumberGenerator\Secure"
  
 ===== 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://github.com/php/php-src/pull/7079+  * https://github.com/php/php-src/pull/7453
rfc/rng_extension.txt · Last modified: 2022/06/28 02:36 by zeriyoshi