From d69c3c0dcad12a70a2f0beae5e4e63cf52ae427f Mon Sep 17 00:00:00 2001 From: joshuaruesweg Date: Mon, 26 Sep 2022 21:54:00 +0100 Subject: [PATCH 1/3] Add `Randomizer::getBytesFromAlphabet()` method --- ext/random/php_random.h | 2 + ext/random/random.c | 10 +- ext/random/random.stub.php | 2 + ext/random/random_arginfo.h | 9 +- ext/random/randomizer.c | 98 ++++++++++++++++++- .../03_randomizer/engine_unsafe_biased.phpt | 14 +++ .../engine_unsafe_empty_string.phpt | 14 +++ .../methods/getBytesFromAlphabet.phpt | 63 ++++++++++++ .../methods/getBytesFromAlphabet_error.phpt | 28 ++++++ 9 files changed, 232 insertions(+), 8 deletions(-) create mode 100644 ext/random/tests/03_randomizer/methods/getBytesFromAlphabet.phpt create mode 100644 ext/random/tests/03_randomizer/methods/getBytesFromAlphabet_error.phpt diff --git a/ext/random/php_random.h b/ext/random/php_random.h index 6a8b0eb84a704..a4665b5d10aca 100644 --- a/ext/random/php_random.h +++ b/ext/random/php_random.h @@ -74,6 +74,8 @@ PHPAPI double php_combined_lcg(void); # define MT_N (624) +#define PHP_RANDOM_RANGE_ATTEMPTS (50) + PHPAPI void php_mt_srand(uint32_t seed); PHPAPI uint32_t php_mt_rand(void); PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max); diff --git a/ext/random/random.c b/ext/random/random.c index a1a1702e16789..5f6ae0c720681 100644 --- a/ext/random/random.c +++ b/ext/random/random.c @@ -86,8 +86,6 @@ static zend_object_handlers random_engine_xoshiro256starstar_object_handlers; static zend_object_handlers random_engine_secure_object_handlers; static zend_object_handlers random_randomizer_object_handlers; -#define RANDOM_RANGE_ATTEMPTS (50) - static inline uint32_t rand_range32(const php_random_algo *algo, php_random_status *status, uint32_t umax) { uint32_t result, limit; @@ -124,8 +122,8 @@ static inline uint32_t rand_range32(const php_random_algo *algo, php_random_stat /* Discard numbers over the limit to avoid modulo bias */ while (UNEXPECTED(result > limit)) { /* If the requirements cannot be met in a cycles, return fail */ - if (++count > RANDOM_RANGE_ATTEMPTS) { - zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", RANDOM_RANGE_ATTEMPTS); + if (++count > PHP_RANDOM_RANGE_ATTEMPTS) { + zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS); return 0; } @@ -180,8 +178,8 @@ static inline uint64_t rand_range64(const php_random_algo *algo, php_random_stat /* Discard numbers over the limit to avoid modulo bias */ while (UNEXPECTED(result > limit)) { /* If the requirements cannot be met in a cycles, return fail */ - if (++count > RANDOM_RANGE_ATTEMPTS) { - zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", RANDOM_RANGE_ATTEMPTS); + if (++count > PHP_RANDOM_RANGE_ATTEMPTS) { + zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS); return 0; } diff --git a/ext/random/random.stub.php b/ext/random/random.stub.php index 0a178f2657dc2..aba8f034fdfa6 100644 --- a/ext/random/random.stub.php +++ b/ext/random/random.stub.php @@ -137,6 +137,8 @@ public function getInt(int $min, int $max): int {} public function getBytes(int $length): string {} + public function getBytesFromAlphabet(string $alphabet, int $length): string {} + public function shuffleArray(array $array): array {} public function shuffleBytes(string $bytes): string {} diff --git a/ext/random/random_arginfo.h b/ext/random/random_arginfo.h index fc272607360d0..8bb2ce2fb6786 100644 --- a/ext/random/random_arginfo.h +++ b/ext/random/random_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6cc9022516ce23c2e95af30606db43e9fc28e38a */ + * Stub hash: 6c7a4e0100a1c53553293fdb68619a43ab2047d1 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_lcg_value, 0, 0, IS_DOUBLE, 0) ZEND_END_ARG_INFO() @@ -94,6 +94,11 @@ ZEND_END_ARG_INFO() #define arginfo_class_Random_Randomizer_getBytes arginfo_random_bytes +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Random_Randomizer_getBytesFromAlphabet, 0, 2, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, alphabet, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Random_Randomizer_shuffleArray, 0, 1, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -133,6 +138,7 @@ ZEND_METHOD(Random_Randomizer, __construct); ZEND_METHOD(Random_Randomizer, nextInt); ZEND_METHOD(Random_Randomizer, getInt); ZEND_METHOD(Random_Randomizer, getBytes); +ZEND_METHOD(Random_Randomizer, getBytesFromAlphabet); ZEND_METHOD(Random_Randomizer, shuffleArray); ZEND_METHOD(Random_Randomizer, shuffleBytes); ZEND_METHOD(Random_Randomizer, pickArrayKeys); @@ -209,6 +215,7 @@ static const zend_function_entry class_Random_Randomizer_methods[] = { ZEND_ME(Random_Randomizer, nextInt, arginfo_class_Random_Randomizer_nextInt, ZEND_ACC_PUBLIC) ZEND_ME(Random_Randomizer, getInt, arginfo_class_Random_Randomizer_getInt, ZEND_ACC_PUBLIC) ZEND_ME(Random_Randomizer, getBytes, arginfo_class_Random_Randomizer_getBytes, ZEND_ACC_PUBLIC) + ZEND_ME(Random_Randomizer, getBytesFromAlphabet, arginfo_class_Random_Randomizer_getBytesFromAlphabet, ZEND_ACC_PUBLIC) ZEND_ME(Random_Randomizer, shuffleArray, arginfo_class_Random_Randomizer_shuffleArray, ZEND_ACC_PUBLIC) ZEND_ME(Random_Randomizer, shuffleBytes, arginfo_class_Random_Randomizer_shuffleBytes, ZEND_ACC_PUBLIC) ZEND_ME(Random_Randomizer, pickArrayKeys, arginfo_class_Random_Randomizer_pickArrayKeys, ZEND_ACC_PUBLIC) diff --git a/ext/random/randomizer.c b/ext/random/randomizer.c index 391cc16cc74db..9c362541f498d 100644 --- a/ext/random/randomizer.c +++ b/ext/random/randomizer.c @@ -131,7 +131,7 @@ PHP_METHOD(Random_Randomizer, getInt) && ((php_random_status_state_mt19937 *) randomizer->status->state)->mode != MT_RAND_MT19937 )) { uint64_t r = php_random_algo_mt19937.generate(randomizer->status) >> 1; - + /* This is an inlined version of the RAND_RANGE_BADSCALING macro that does not invoke UB when encountering * (max - min) > ZEND_LONG_MAX. */ @@ -258,6 +258,102 @@ PHP_METHOD(Random_Randomizer, pickArrayKeys) } /* }}} */ +/* {{{ Get Random Bytes for Alphabet */ +PHP_METHOD(Random_Randomizer, getBytesFromAlphabet) +{ + php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); + zend_long length; + zend_string *alphabet, *retval; + size_t total_size = 0; + + ZEND_PARSE_PARAMETERS_START(2, 2); + Z_PARAM_STR(alphabet) + Z_PARAM_LONG(length) + ZEND_PARSE_PARAMETERS_END(); + + const size_t alphabet_length = ZSTR_LEN(alphabet); + + if (alphabet_length < 1) { + zend_argument_value_error(1, "cannot be empty"); + RETURN_THROWS(); + } + + if (length < 1) { + zend_argument_value_error(2, "must be greater than 0"); + RETURN_THROWS(); + } + + retval = zend_string_alloc(length, 0); + + if (alphabet_length > 0xFF) { + while (total_size < length) { + uint64_t offset = randomizer->algo->range(randomizer->status, 0, alphabet_length - 1); + + if (EG(exception)) { + zend_string_free(retval); + RETURN_THROWS(); + } + + ZSTR_VAL(retval)[total_size++] = ZSTR_VAL(alphabet)[offset]; + } + } else { + uint64_t mask; + if (alphabet_length <= 0x1) { + mask = 0x0; + } else if (alphabet_length <= 0x2) { + mask = 0x1; + } else if (alphabet_length <= 0x4) { + mask = 0x3; + } else if (alphabet_length <= 0x8) { + mask = 0x7; + } else if (alphabet_length <= 0x10) { + mask = 0xF; + } else if (alphabet_length <= 0x20) { + mask = 0x1F; + } else if (alphabet_length <= 0x40) { + mask = 0x3F; + } else if (alphabet_length <= 0x80) { + mask = 0x7F; + } else { + mask = 0xFF; + } + + int failures = 0; + while (total_size < length) { + uint64_t result = randomizer->algo->generate(randomizer->status); + if (EG(exception)) { + zend_string_free(retval); + RETURN_THROWS(); + } + + for (size_t i = 0; i < randomizer->status->last_generated_size; i++) { + uint64_t offset = (result >> (i * 8)) & mask; + + if (offset >= alphabet_length) { + if (++failures > PHP_RANDOM_RANGE_ATTEMPTS) { + zend_string_free(retval); + zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS); + RETURN_THROWS(); + } + + continue; + } + + failures = 0; + + ZSTR_VAL(retval)[total_size++] = ZSTR_VAL(alphabet)[offset]; + if (total_size >= length) { + break; + } + } + } + } + + ZSTR_VAL(retval)[length] = '\0'; + RETURN_STR(retval); +} +/* }}} */ + /* {{{ Random\Randomizer::__serialize() */ PHP_METHOD(Random_Randomizer, __serialize) { diff --git a/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt b/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt index 09fbd85b54eb0..6c89adf8cb21a 100644 --- a/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt +++ b/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt @@ -49,6 +49,18 @@ try { echo $e->getMessage(), PHP_EOL; } +try { + var_dump(randomizer()->getBytesFromAlphabet('123', 10)); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->getBytesFromAlphabet(str_repeat('a', 500), 10)); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + ?> --EXPECTF-- Failed to generate an acceptable random number in 50 attempts @@ -56,3 +68,5 @@ int(%d) string(2) "ff" Failed to generate an acceptable random number in 50 attempts Failed to generate an acceptable random number in 50 attempts +Failed to generate an acceptable random number in 50 attempts +Failed to generate an acceptable random number in 50 attempts diff --git a/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt b/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt index 01bd293bc0508..aee30d6a5f977 100644 --- a/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt +++ b/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt @@ -49,6 +49,18 @@ try { echo $e->getMessage(), PHP_EOL; } +try { + var_dump(randomizer()->getBytesFromAlphabet('123', 10)); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->getBytesFromAlphabet(str_repeat('a', 500), 10)); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + ?> --EXPECT-- A random engine must return a non-empty string @@ -56,3 +68,5 @@ A random engine must return a non-empty string A random engine must return a non-empty string A random engine must return a non-empty string A random engine must return a non-empty string +A random engine must return a non-empty string +A random engine must return a non-empty string diff --git a/ext/random/tests/03_randomizer/methods/getBytesFromAlphabet.phpt b/ext/random/tests/03_randomizer/methods/getBytesFromAlphabet.phpt new file mode 100644 index 0000000000000..255f51472dfac --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/getBytesFromAlphabet.phpt @@ -0,0 +1,63 @@ +--TEST-- +Random: Randomizer: getBytesFromAlphabet(): Basic functionality +--FILE-- +getBytesFromAlphabet('a', 10)); + var_dump($randomizer->getBytesFromAlphabet(str_repeat('a', 256), 5)); + + for ($i = 1; $i < 250; $i++) { + $output = $randomizer->getBytesFromAlphabet(str_repeat('ab', $i), 500); + + // This check can theoretically fail with a chance of 0.5**500. + if (!str_contains($output, 'a') || !str_contains($output, 'b')) { + die("failure: didn't see both a and b at {$i}"); + } + } +} + +die('success'); + +?> +--EXPECT-- +Random\Engine\Mt19937 +string(10) "aaaaaaaaaa" +string(5) "aaaaa" +Random\Engine\Mt19937 +string(10) "aaaaaaaaaa" +string(5) "aaaaa" +Random\Engine\PcgOneseq128XslRr64 +string(10) "aaaaaaaaaa" +string(5) "aaaaa" +Random\Engine\Xoshiro256StarStar +string(10) "aaaaaaaaaa" +string(5) "aaaaa" +Random\Engine\Secure +string(10) "aaaaaaaaaa" +string(5) "aaaaa" +Random\Engine\Test\TestShaEngine +string(10) "aaaaaaaaaa" +string(5) "aaaaa" +success diff --git a/ext/random/tests/03_randomizer/methods/getBytesFromAlphabet_error.phpt b/ext/random/tests/03_randomizer/methods/getBytesFromAlphabet_error.phpt new file mode 100644 index 0000000000000..d159a456ab868 --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/getBytesFromAlphabet_error.phpt @@ -0,0 +1,28 @@ +--TEST-- +Random: Randomizer: getBytesFromAlphabet(): Parameters are correctly validated +--FILE-- +getBytesFromAlphabet("", 2)); +} catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->getBytesFromAlphabet("abc", 0)); +} catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +Random\Randomizer::getBytesFromAlphabet(): Argument #1 ($alphabet) cannot be empty +Random\Randomizer::getBytesFromAlphabet(): Argument #2 ($length) must be greater than 0 From 0baf64647d65114f06807c25bd79577c36048333 Mon Sep 17 00:00:00 2001 From: joshuaruesweg Date: Mon, 7 Nov 2022 21:31:28 +0100 Subject: [PATCH 2/3] Rename `getBytesFromAlphabet` to `getBytesFromString` --- ext/random/random.stub.php | 2 +- ext/random/random_arginfo.h | 10 ++--- ext/random/randomizer.c | 38 +++++++++---------- .../03_randomizer/engine_unsafe_biased.phpt | 4 +- .../engine_unsafe_empty_string.phpt | 4 +- .../methods/getBytesFromAlphabet_error.phpt | 28 -------------- ...mAlphabet.phpt => getBytesFromString.phpt} | 8 ++-- .../methods/getBytesFromString_error.phpt | 28 ++++++++++++++ 8 files changed, 61 insertions(+), 61 deletions(-) delete mode 100644 ext/random/tests/03_randomizer/methods/getBytesFromAlphabet_error.phpt rename ext/random/tests/03_randomizer/methods/{getBytesFromAlphabet.phpt => getBytesFromString.phpt} (83%) create mode 100644 ext/random/tests/03_randomizer/methods/getBytesFromString_error.phpt diff --git a/ext/random/random.stub.php b/ext/random/random.stub.php index aba8f034fdfa6..69049a837b2c2 100644 --- a/ext/random/random.stub.php +++ b/ext/random/random.stub.php @@ -137,7 +137,7 @@ public function getInt(int $min, int $max): int {} public function getBytes(int $length): string {} - public function getBytesFromAlphabet(string $alphabet, int $length): string {} + public function getBytesFromString(string $string, int $length): string {} public function shuffleArray(array $array): array {} diff --git a/ext/random/random_arginfo.h b/ext/random/random_arginfo.h index 8bb2ce2fb6786..1da1b8576b196 100644 --- a/ext/random/random_arginfo.h +++ b/ext/random/random_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6c7a4e0100a1c53553293fdb68619a43ab2047d1 */ + * Stub hash: a4226bc7838eba98c5a935b279f681a7d083c0b2 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_lcg_value, 0, 0, IS_DOUBLE, 0) ZEND_END_ARG_INFO() @@ -94,8 +94,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_Random_Randomizer_getBytes arginfo_random_bytes -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Random_Randomizer_getBytesFromAlphabet, 0, 2, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, alphabet, IS_STRING, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Random_Randomizer_getBytesFromString, 0, 2, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -138,7 +138,7 @@ ZEND_METHOD(Random_Randomizer, __construct); ZEND_METHOD(Random_Randomizer, nextInt); ZEND_METHOD(Random_Randomizer, getInt); ZEND_METHOD(Random_Randomizer, getBytes); -ZEND_METHOD(Random_Randomizer, getBytesFromAlphabet); +ZEND_METHOD(Random_Randomizer, getBytesFromString); ZEND_METHOD(Random_Randomizer, shuffleArray); ZEND_METHOD(Random_Randomizer, shuffleBytes); ZEND_METHOD(Random_Randomizer, pickArrayKeys); @@ -215,7 +215,7 @@ static const zend_function_entry class_Random_Randomizer_methods[] = { ZEND_ME(Random_Randomizer, nextInt, arginfo_class_Random_Randomizer_nextInt, ZEND_ACC_PUBLIC) ZEND_ME(Random_Randomizer, getInt, arginfo_class_Random_Randomizer_getInt, ZEND_ACC_PUBLIC) ZEND_ME(Random_Randomizer, getBytes, arginfo_class_Random_Randomizer_getBytes, ZEND_ACC_PUBLIC) - ZEND_ME(Random_Randomizer, getBytesFromAlphabet, arginfo_class_Random_Randomizer_getBytesFromAlphabet, ZEND_ACC_PUBLIC) + ZEND_ME(Random_Randomizer, getBytesFromString, arginfo_class_Random_Randomizer_getBytesFromString, ZEND_ACC_PUBLIC) ZEND_ME(Random_Randomizer, shuffleArray, arginfo_class_Random_Randomizer_shuffleArray, ZEND_ACC_PUBLIC) ZEND_ME(Random_Randomizer, shuffleBytes, arginfo_class_Random_Randomizer_shuffleBytes, ZEND_ACC_PUBLIC) ZEND_ME(Random_Randomizer, pickArrayKeys, arginfo_class_Random_Randomizer_pickArrayKeys, ZEND_ACC_PUBLIC) diff --git a/ext/random/randomizer.c b/ext/random/randomizer.c index 9c362541f498d..a95e6b0fdd8a8 100644 --- a/ext/random/randomizer.c +++ b/ext/random/randomizer.c @@ -258,22 +258,22 @@ PHP_METHOD(Random_Randomizer, pickArrayKeys) } /* }}} */ -/* {{{ Get Random Bytes for Alphabet */ -PHP_METHOD(Random_Randomizer, getBytesFromAlphabet) +/* {{{ Get Random Bytes for String */ +PHP_METHOD(Random_Randomizer, getBytesFromString) { php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); zend_long length; - zend_string *alphabet, *retval; + zend_string *source, *retval; size_t total_size = 0; ZEND_PARSE_PARAMETERS_START(2, 2); - Z_PARAM_STR(alphabet) + Z_PARAM_STR(source) Z_PARAM_LONG(length) ZEND_PARSE_PARAMETERS_END(); - const size_t alphabet_length = ZSTR_LEN(alphabet); + const size_t source_length = ZSTR_LEN(source); - if (alphabet_length < 1) { + if (source_length < 1) { zend_argument_value_error(1, "cannot be empty"); RETURN_THROWS(); } @@ -285,34 +285,34 @@ PHP_METHOD(Random_Randomizer, getBytesFromAlphabet) retval = zend_string_alloc(length, 0); - if (alphabet_length > 0xFF) { + if (source_length > 0xFF) { while (total_size < length) { - uint64_t offset = randomizer->algo->range(randomizer->status, 0, alphabet_length - 1); + uint64_t offset = randomizer->algo->range(randomizer->status, 0, source_length - 1); if (EG(exception)) { zend_string_free(retval); RETURN_THROWS(); } - ZSTR_VAL(retval)[total_size++] = ZSTR_VAL(alphabet)[offset]; + ZSTR_VAL(retval)[total_size++] = ZSTR_VAL(source)[offset]; } } else { uint64_t mask; - if (alphabet_length <= 0x1) { + if (source_length <= 0x1) { mask = 0x0; - } else if (alphabet_length <= 0x2) { + } else if (source_length <= 0x2) { mask = 0x1; - } else if (alphabet_length <= 0x4) { + } else if (source_length <= 0x4) { mask = 0x3; - } else if (alphabet_length <= 0x8) { + } else if (source_length <= 0x8) { mask = 0x7; - } else if (alphabet_length <= 0x10) { + } else if (source_length <= 0x10) { mask = 0xF; - } else if (alphabet_length <= 0x20) { + } else if (source_length <= 0x20) { mask = 0x1F; - } else if (alphabet_length <= 0x40) { + } else if (source_length <= 0x40) { mask = 0x3F; - } else if (alphabet_length <= 0x80) { + } else if (source_length <= 0x80) { mask = 0x7F; } else { mask = 0xFF; @@ -329,7 +329,7 @@ PHP_METHOD(Random_Randomizer, getBytesFromAlphabet) for (size_t i = 0; i < randomizer->status->last_generated_size; i++) { uint64_t offset = (result >> (i * 8)) & mask; - if (offset >= alphabet_length) { + if (offset >= source_length) { if (++failures > PHP_RANDOM_RANGE_ATTEMPTS) { zend_string_free(retval); zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS); @@ -341,7 +341,7 @@ PHP_METHOD(Random_Randomizer, getBytesFromAlphabet) failures = 0; - ZSTR_VAL(retval)[total_size++] = ZSTR_VAL(alphabet)[offset]; + ZSTR_VAL(retval)[total_size++] = ZSTR_VAL(source)[offset]; if (total_size >= length) { break; } diff --git a/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt b/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt index 6c89adf8cb21a..e6fd2d7672343 100644 --- a/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt +++ b/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt @@ -50,13 +50,13 @@ try { } try { - var_dump(randomizer()->getBytesFromAlphabet('123', 10)); + var_dump(randomizer()->getBytesFromString('123', 10)); } catch (Random\BrokenRandomEngineError $e) { echo $e->getMessage(), PHP_EOL; } try { - var_dump(randomizer()->getBytesFromAlphabet(str_repeat('a', 500), 10)); + var_dump(randomizer()->getBytesFromString(str_repeat('a', 500), 10)); } catch (Random\BrokenRandomEngineError $e) { echo $e->getMessage(), PHP_EOL; } diff --git a/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt b/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt index aee30d6a5f977..13ad0637fc33d 100644 --- a/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt +++ b/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt @@ -50,13 +50,13 @@ try { } try { - var_dump(randomizer()->getBytesFromAlphabet('123', 10)); + var_dump(randomizer()->getBytesFromString('123', 10)); } catch (Random\BrokenRandomEngineError $e) { echo $e->getMessage(), PHP_EOL; } try { - var_dump(randomizer()->getBytesFromAlphabet(str_repeat('a', 500), 10)); + var_dump(randomizer()->getBytesFromString(str_repeat('a', 500), 10)); } catch (Random\BrokenRandomEngineError $e) { echo $e->getMessage(), PHP_EOL; } diff --git a/ext/random/tests/03_randomizer/methods/getBytesFromAlphabet_error.phpt b/ext/random/tests/03_randomizer/methods/getBytesFromAlphabet_error.phpt deleted file mode 100644 index d159a456ab868..0000000000000 --- a/ext/random/tests/03_randomizer/methods/getBytesFromAlphabet_error.phpt +++ /dev/null @@ -1,28 +0,0 @@ ---TEST-- -Random: Randomizer: getBytesFromAlphabet(): Parameters are correctly validated ---FILE-- -getBytesFromAlphabet("", 2)); -} catch (ValueError $e) { - echo $e->getMessage(), PHP_EOL; -} - -try { - var_dump(randomizer()->getBytesFromAlphabet("abc", 0)); -} catch (ValueError $e) { - echo $e->getMessage(), PHP_EOL; -} - -?> ---EXPECTF-- -Random\Randomizer::getBytesFromAlphabet(): Argument #1 ($alphabet) cannot be empty -Random\Randomizer::getBytesFromAlphabet(): Argument #2 ($length) must be greater than 0 diff --git a/ext/random/tests/03_randomizer/methods/getBytesFromAlphabet.phpt b/ext/random/tests/03_randomizer/methods/getBytesFromString.phpt similarity index 83% rename from ext/random/tests/03_randomizer/methods/getBytesFromAlphabet.phpt rename to ext/random/tests/03_randomizer/methods/getBytesFromString.phpt index 255f51472dfac..7ee7d990adff7 100644 --- a/ext/random/tests/03_randomizer/methods/getBytesFromAlphabet.phpt +++ b/ext/random/tests/03_randomizer/methods/getBytesFromString.phpt @@ -1,5 +1,5 @@ --TEST-- -Random: Randomizer: getBytesFromAlphabet(): Basic functionality +Random: Randomizer: getBytesFromString(): Basic functionality --FILE-- getBytesFromAlphabet('a', 10)); - var_dump($randomizer->getBytesFromAlphabet(str_repeat('a', 256), 5)); + var_dump($randomizer->getBytesFromString('a', 10)); + var_dump($randomizer->getBytesFromString(str_repeat('a', 256), 5)); for ($i = 1; $i < 250; $i++) { - $output = $randomizer->getBytesFromAlphabet(str_repeat('ab', $i), 500); + $output = $randomizer->getBytesFromString(str_repeat('ab', $i), 500); // This check can theoretically fail with a chance of 0.5**500. if (!str_contains($output, 'a') || !str_contains($output, 'b')) { diff --git a/ext/random/tests/03_randomizer/methods/getBytesFromString_error.phpt b/ext/random/tests/03_randomizer/methods/getBytesFromString_error.phpt new file mode 100644 index 0000000000000..7280949d647e8 --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/getBytesFromString_error.phpt @@ -0,0 +1,28 @@ +--TEST-- +Random: Randomizer: getBytesFromString(): Parameters are correctly validated +--FILE-- +getBytesFromString("", 2)); +} catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->getBytesFromString("abc", 0)); +} catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +Random\Randomizer::getBytesFromString(): Argument #1 ($string) cannot be empty +Random\Randomizer::getBytesFromString(): Argument #2 ($length) must be greater than 0 From 96ad925f84e2ae57ae93c305bc4708d52a901e48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Fri, 9 Dec 2022 17:33:42 +0100 Subject: [PATCH 3/3] [ci skip] Add NEWS/UPGRADING for Randomizer::getBytesFromString() --- NEWS | 3 +++ UPGRADING | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/NEWS b/NEWS index 070a225fcffd2..196ad576f5b31 100644 --- a/NEWS +++ b/NEWS @@ -55,6 +55,9 @@ PHP NEWS - Posix: . Added posix_sysconf. (David Carlier) +- Random: + . Added Randomizer::getBytesFromString(). (Joshua Rüsweg) + - Reflection: . Fix GH-9470 (ReflectionMethod constructor should not find private parent method). (ilutov) diff --git a/UPGRADING b/UPGRADING index 16f2de9eff26b..0956b914b93c7 100644 --- a/UPGRADING +++ b/UPGRADING @@ -62,6 +62,10 @@ PHP 8.3 UPGRADE NOTES - Posix: . Added posix_sysconf call to get runtime informations. +- Random: + . Added Randomizer::getBytesFromString(). + RFC: https://wiki.php.net/rfc/randomizer_additions + - Sockets: . Added socket_atmark to checks if the socket is OOB marked.