diff --git a/ext/random/tests/03_randomizer/methods/shuffleArray_engine_throws_with_holes.phpt b/ext/random/tests/03_randomizer/methods/shuffleArray_engine_throws_with_holes.phpt new file mode 100644 index 000000000000..b7c0a3b416a2 --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/shuffleArray_engine_throws_with_holes.phpt @@ -0,0 +1,61 @@ +--TEST-- +Random: Randomizer: shuffleArray(): Engine throwing on an array with holes +--FILE-- +calls-- <= 0) { + throw new Exception('Error'); + } + + return "\x01\x00\x00\x00\x00\x00\x00\x00"; + } +} + +foreach ([0, 1] as $calls) { + $array = [new stdClass, new stdClass, new stdClass, new stdClass]; + unset($array[1]); + + try { + (new Randomizer(new ThrowingEngine($calls)))->shuffleArray($array); + } catch (Exception $e) { + echo $e->getMessage(), PHP_EOL; + } + + var_dump($array); +} + +?> +--EXPECTF-- +Error +array(3) { + [0]=> + object(stdClass)#%d (0) { + } + [2]=> + object(stdClass)#%d (0) { + } + [3]=> + object(stdClass)#%d (0) { + } +} +Error +array(3) { + [0]=> + object(stdClass)#%d (0) { + } + [2]=> + object(stdClass)#%d (0) { + } + [3]=> + object(stdClass)#%d (0) { + } +} diff --git a/ext/standard/array.c b/ext/standard/array.c index 8454e9616c02..ade0c536677d 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -3302,6 +3302,10 @@ PHPAPI bool php_array_data_shuffle(php_random_algo_with_state engine, zval *arra j++; } } + /* Truncate the stale slots left by compaction before the engine can throw. */ + hash->nNumUsed = n_elems; + hash->nInternalPointer = 0; + hash->nNextFreeElement = n_elems; while (--n_left) { rnd_idx = algo->range(state, 0, n_left); if (EG(exception)) { @@ -3330,6 +3334,9 @@ PHPAPI bool php_array_data_shuffle(php_random_algo_with_state engine, zval *arra j++; } } + hash->nNumUsed = n_elems; + hash->nInternalPointer = 0; + hash->nNextFreeElement = n_elems; while (--n_left) { rnd_idx = algo->range(state, 0, n_left); if (EG(exception)) { @@ -3343,9 +3350,6 @@ PHPAPI bool php_array_data_shuffle(php_random_algo_with_state engine, zval *arra } } } - hash->nNumUsed = n_elems; - hash->nInternalPointer = 0; - hash->nNextFreeElement = n_elems; return true; }