rfc:rng_extension

Differences

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

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Next revisionBoth sides next revision
rfc:rng_extension [2021/07/02 13:36] – update stub and more detail serialization failure zeriyoshirfc:rng_extension [2021/09/03 13:53] – fix: missing CSPRNG functions. zeriyoshi
Line 1: Line 1:
-====== PHP RFC: Add Random Extension ====== +====== PHP RFC: Random Extension 3.0 ====== 
-  * Version: 2.1 +  * 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 =====
-Implement and bundled Random extension into PHP. 
  
-The phpstub for the whole extension is as follows:+Clean up the implementation, separate out the random number related functions as Random extension, and add an object scoped API.
  
-<code php> +All of the following functions will be moved to the newly created Random extension.
-<?php+
  
-/** @generate-class-entries */ +  lcg_value() 
-/** @generate-function-entries */+  srand() 
 +  rand() 
 +  mt_srand() 
 +  mt_rand() 
 +  * random_int() 
 +  random_bytes()
  
-namespace Random\NumberGenerator +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.
-+
-    interface RandomNumberGenerator +
-    { +
-        public function generate(): int; +
-    }+
  
-    class XorShift128Plus implements RandomNumberGenerator +  * php_random_int_throw() 
-    { +  * php_random_int_silent() 
-        public function __construct(?int $seed = null{}+  * 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()
  
-        public function generate(): int {}+The following PHP constants will now be provided by the Random extension
  
-        public function __serialize(): array {}+  * MT_RAND_MT19937 
 +  * MT_RAND_PHP
  
-        public function __unserialize(array $data): void {} +To solve the scope problem, the following classes will be added
-    }+
  
-    class MT19937 implements RandomNumberGenerator +  * Random class 
-    { +  * Random\NumberGenrator abstract class 
-        /** @implementation-alias Random\NumberGenerator\XorShift128Plus::__construct */ +  * Random\NumberGenerator\XorShift128Plus class 
-        public function __construct(?int $seed = null) {}+  Random\NumberGenerator\MT19937 class 
 +  * Random\NumberGenerator\Secure class
  
-        /** @implementation-alias Random\NumberGenerator\XorShift128Plus::generate */ +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.
-        public function generate(): int {}+
  
-        /** @implementation-alias Random\NumberGenerator\XorShift128Plus::__serialize */ +  getInt() 
-        public function __serialize(): array {}+  getBytes() 
 +  shuffleArray() 
 +  * shuffleString()
  
-        /** @implementation-alias Random\NumberGenerator\XorShift128Plus::__unserialize */ +The Random class accepts an instance that inherits from Random\NumberGenerator as a constructor argument.
-        public function __unserialize(array $data): void {} +
-    }+
  
-    class Secure implements RandomNumberGenerator +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.
-        public function __construct() {}+
  
-        /** @implementation-alias Random\NumberGenerator\XorShift128Plus::generate */ +The serializability depends on the serializability of the contained $rng.
-        public function generate(): int {} +
-    } +
-}+
  
-namespace+<code php> 
 +final class Random
 { {
-    final class Random +    private Random\NumberGenerator $randomNumberGenerator;
-    { +
-        // FIXME: stub generator (gen_stub.php) does not supported. +
-        // private Random\NumberGenerator\RandomNumberGenerator $rng; +
-        private mixed $rng;+
  
-        public function __construct(?Random\NumberGenerator\RandomNumberGenerator $rng = null) {} +    public function __construct(?Random\NumberGenerator $randomNumberGenerator = null) {} 
-        public function getNumberGenerator(): Random\NumberGenerator\RandomNumberGenerator {} +    public function getNumberGenerator(): Random\NumberGenerator {} 
-        public function nextInt(): int {} +    public function getInt(int $min, int $max): int {} 
-        public function getInt(int $min, int $max): int {} +    public function getBytes(int $length): string {} 
-        public function getBytes(int $length): string {} +    public function shuffleArray(array $array): array {} 
-        public function shuffleArray(array $array): array {} +    public function shuffleString(string $string): string {} 
-        public function shuffleString(string $string): string {} + 
-        public function __serialize(): array {} +    public function __serialize(): array {} 
-        public function __unserialize(array $data): void {+    public function __unserialize(array $data): void {}
-    }+
 } }
 </code> </code>
  
-Each RNG is implemented as a class in the Random\NumberGenerator namespace. They all implement the Random\NumberGenerator\RandomNumberGenerator interface.+The Random\NumberGenerator abstract class has a single abstract method called generate()
  
-The bundled RNGs are as follows:+<code php> 
 +namespace Random;
  
-  * Random\NumberGenerator\XorShift128Plus: 64-bit, reproducible, PRNG. +abstract class NumberGenerator 
-  * Random\NumberGenerator\MT19937: 32-bit, reproducible, PRNG, compatible mt_srand() / mt_rand(). +
-  * Random\NumberGenerator\Secure: 64-bit, non-reproducible, CSPRNG, uses php_random_bytes() internally.+        abstract public function generate(): int {} 
 +
 +</code>
  
-Random class use a XorShift128+ by defaultIt can generate 64-bit values, is used by major browsers, and is fast and reliable.  +By defining a class that extends Random\NumberGenerator, the user can use their own random number generatorWith the introduction of JIT in PHP 8.0, this can generate random numbers at a realistic speed.
-However, when used XorShift128+ in a 32-bit environment, the upper 32 bits are always truncatedThis means that compatibility cannot be maintained between platformsbut this is not a problem since most platforms running PHP today are 64-bit and MT19937 can be used explicitly if compatibility is required. +
- +
-Secure is practically equivalent to random_int() and random_bytes(), This is useful when secure array or string shuffling is required. +
- +
-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 Random\NumberGenerator\RandomNumberGenerator+
 { {
     protected int $current = 0;     protected int $current = 0;
-    +
     public function generate(): int     public function generate(): int
     {     {
Line 143: Line 139:
 } }
  
-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>
  
-Also, as with MT, various alternative APIs using Random class will be 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 generatorRandom\NumberGenerator\XorShift128Plus, if the constructor argument is omitted.
-/* similar php_mt_rand() */ +
-uint64_t php_random_next(php_random *php_randombool shift);+
  
-/* similar php_mt_rand_range() */ +XorShift128Plus is an efficienthigh-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 environmentsbut is acceptable for real-world use.
-zend_long php_random_range(php_random *php_random, zend_long minzend_long max);+
  
-/* similar php_array_data_shuffle() */ +The Random\NumberGenerator\MT19937 class, which implements the MT19937 Mersenne twister, is also provided for backward compatibility or when a higher period is requiredHowevera 1-bit right shift is required to obtain exactly the same result as mt_rand(), as shown below. This is due to historical reasons.
-void php_random_array_data_shuffle(php_random *php_random, zval *array); +
- +
-/* similar php_string_shuffle() */ +
-void php_random_string_shuffle(php_random *php_random, char *str, zend_long len); +
-</code> +
- +
-The Random class can be serialized using the standard PHP serialization mechanismButif the $rng member is not serializable, it will throws Exception.+
  
 <code php> <code php>
-// serialize +$seed 1234;
-$foo new Random(new Random\Numbergenerator\XorShift128Plus()); +
-for ($i = 0; $i < 10; $i++) { $foo->nextInt();+
-var_dump(unserialize(serialize($foo))->nextInt() === $foo->nextInt()); // true+
  
-// can't serialize +$mt = new Random\NumberGenerator\MT19937($seed); 
-$foo new Random(new Random\Numbergenerator\Secure()); +mt_srand($seed); 
-for ($i = 0; $i < 10; $i++) { $foo->nextInt(); } +var_dump(mt_rand() === ($mt->generate() >> 1)); // true
-var_dump(unserialize(serialize($foo))->nextInt() === $foo->nextInt()); // throws Exception:  Serialization of CLASS is not allowed.+
 </code> </code>
  
-It is not possible to clone the Random class. This is because the standard PHP clone method copies the members by reference when cloning. This will be an unintended behavior for most users. Instead, you can use the getNumberGenerator() method to retrieve the internal RNG instance. The RNG instance can be cloned.+The following NumberGenerator class supports serializationSecure is not serializable because it uses random_bytes internally and has no state.
  
-<code php> +  * Random\NumberGenerator\XorShift128Plus 
-$foo = new Random();+  * Random\NumberGenerator\MT19937 
 +  Random\NumberGenerator extends user-defined classes.
  
-// can't direct clone +Also, a new internal API will be implemented.
-// $bar = clone $foo;+
  
-// safe +  * php_random_ng_next() 
-$bar = new Random(clone $foo->getNumberGenerator()); +  * php_random_ng_range(
-</code>+  * php_random_ng_array_data_shuffle() 
 +  * php_random_ng_string_shuffle()
  
-Using this feature, the first example can be rewritten as follows:+A Stub showing these implementations can be found on the Pull-Request. It's probably easier to understand if you look at it.
  
-<code php> +  * [[https://github.com/php/php-src/blob/7a4ef6ccfbf4a2cd48a4f261f2911ebb7b057d46/ext/random/random.stub.php|random.stub.php]]
-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 { +
-    $random = new Random(new Random\NumberGenerator\MT19937($seed)); +
-    $result = $random->nextInt(); +
-    $bar(); +
-    $result += $random->nextInt(); +
-    return $result; +
-+
-</code>+
  
 ===== Future Scope ===== ===== Future Scope =====
  
-This RFC will be the basis for making PHP RNGs safe in the future.+This proposal is just a first step to improve the situation of PHP's random number implementation.
  
-By first accepted this RFCPHP gets a random number in the local scope.+If this proposal is approvedI will then propose the following changes
  
-The Random class can also be used when new features are implemented that use random numbers. This has the effect of discouraging more implementations from using random numbers that depend on the global scope+  * Replace the state of the existing implementation with php_random_ng
- +  * Replace random_bytes() with random_bytes() for random numbers used in shuffle()str_shuffle(), and array_rand(). 
-More in the futurewe can consider doing away with functions such as mt_srand(). These functions are simple and convenientbut they may unintentionally create implementations that depend on global scope.+  * Deprecate srand() and mt_srand() (step by step)
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
-The following class name will no longer be available: 
  
-  "RandomInterface"+The code that includes the following header file needs to be changed to ext/random/random.h 
 + 
 +  ext/standard/lcg.h 
 +  * ext/standard/rand.h 
 +  * ext/standard/mt_rand.h 
 +  * ext/standard/random.h 
 + 
 +The following class names have been reserved and will no longer be available 
   * "Random"   * "Random"
-  * "Random\NumberGenerator\RandomNumberGenerator"+  * "Random\NumberGenerator"
   * "Random\NumberGenerator\XorShift128Plus"   * "Random\NumberGenerator\XorShift128Plus"
   * "Random\NumberGenerator\MT19937"   * "Random\NumberGenerator\MT19937"
Line 230: Line 209:
  
 ===== Proposed PHP Version(s) ===== ===== Proposed PHP Version(s) =====
-8.+8.2
- +
-===== FAQ ===== +
-====  ====+
  
 ===== RFC Impact ===== ===== RFC Impact =====
Line 263: Line 239:
  
 ===== 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/08/01 16:52 by timwolla