Strings7 min readExamples run on PHP 8.5.10

str_shuffle()

Learn how PHP 8.4 str_shuffle() randomly reorders the bytes of a string, what it guarantees, where it is unsafe, and how to test it deterministically.

Signature

str_shuffle(string $string): string
Available since PHP 4.3.07.48.08.18.28.38.48.5All versions →

Purpose

str_shuffle() takes a string and returns a new string containing the same bytes in a random order. Of all the possible permutations of the input, one is produced. It is handy for low-stakes jobs such as scrambling a word for a puzzle, mixing the letters on a game rack, or generating throwaway test data.

It is not a security tool. The function does not produce cryptographically secure results, so never use it for passwords, tokens, invitation codes or anything that must be unguessable.

Signature

Parameters and defaults

  • $string: the input string. It is the only parameter and has no default value. The original string is not modified, because PHP strings are passed by value.

Return value and failure cases

The function returns the shuffled string. The result always has the same length as the input and contains exactly the same bytes, only in a possibly different order. The manual documents no special failure return value. Passing a value that cannot be used as a string, such as an array, raises a TypeError.

Because the order is random, the result may occasionally equal the input. For a short string like 'ab', that happens about half the time.

Example 1: what is guaranteed

You cannot predict the exact output, but you can test the properties that are guaranteed: same length and same collection of characters.

Example
<?php
$original = 'abcdef';
$shuffled = str_shuffle($original);

$a = str_split($original);
$b = str_split($shuffled);
sort($a);
sort($b);

echo json_encode([
    'same_length' => strlen($original) === strlen($shuffled),
    'same_characters' => $a === $b,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"same_length":true,"same_characters":true}

Sorting both character lists removes the effect of the order, so the comparison checks only the contents.

Example 2: edge cases

Strings with fewer than two distinct bytes have only one possible permutation, so the result is always identical to the input.

Example
<?php
echo json_encode([
    str_shuffle(''),
    str_shuffle('Z'),
    str_shuffle('aaaa'),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
["","Z","aaaa"]

An empty string returns an empty string, and no error is raised.

Example 3: seeding and shared state

str_shuffle() uses the global Mersenne Twister generator. That means it shares state with other functions that use it, such as mt_rand() and shuffle(). Seeding with mt_srand() makes the sequence repeatable, which is useful when you need reproducible tests or demos.

Example
<?php
mt_srand(42);
$first = str_shuffle('abcdefghij');

mt_srand(42);
$second = str_shuffle('abcdefghij');

echo json_encode([
    'repeatable' => $first === $second,
    'length' => strlen($first),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"repeatable":true,"length":10}

The example compares the two results rather than printing them, so it does not depend on the exact permutation.

Edge cases

  • Byte-based behaviour: the function works on bytes, not characters. With ASCII text this is the same thing. With UTF-8 text, a character such as é takes more than one byte, and shuffling can separate those bytes and produce an invalid string. For multibyte text, split into characters with mb_str_split() or preg_split('//u', ...), shuffle the array, then join it again.
  • Repeated characters: duplicates are kept. Shuffling 'aab' can only yield 'aab', 'aba' or 'baa'.
  • Identical output: a shuffle is allowed to return the original order. Do not write code that assumes the result differs from the input.
  • Global state: any call advances the global generator, so adding an unrelated shuffle call can change later seeded results.

Common mistakes

  1. Using it for secrets. The manual says the result must not be used where values need to be unguessable. For secure randomness, use random_int(), random_bytes(), or Random\Randomizer with Random\Engine\Secure.
  2. Expecting a unique result. Shuffling does not guarantee a different order, and it does not create unique values across calls.
  3. Testing exact output without seeding. A test that compares against a fixed shuffled string will fail intermittently. Test length and character counts, or seed the generator and compare two runs.
  4. Shuffling multibyte text directly. This can corrupt UTF-8 sequences. Split by character first.
  5. Forgetting to use the return value. str_shuffle($text); on its own does nothing useful, because the input is not changed in place.
  6. Assuming isolated randomness. The shared global generator means seeding in one place can affect shuffles elsewhere in the same request.

The manual also recommends preferring Random\Randomizer methods in newly written code. For this task, Random\Randomizer::shuffleBytes() provides the byte-wise permutation, and you can choose the engine explicitly.

  • shuffle(): shuffles an array in place.
  • Random\Randomizer::shuffleBytes(): byte-wise permutation of a string with a selectable engine.
  • Random\Randomizer::shuffleArray(): permutation of an array.
  • str_split() and mb_str_split(): split a string into pieces before shuffling.
  • mt_srand(): seeds the global Mersenne Twister generator.
  • random_int() and random_bytes(): secure random values.

Recap

str_shuffle() returns a randomly permuted copy of a string with the same length and bytes. It is simple and convenient for games and test data, but it works on bytes, shares global generator state, and is not secure. Test its guaranteed properties rather than exact output, and reach for Random\Randomizer or the secure random functions when quality of randomness matters.

Tested examples

Shuffling keeps length and characters
<?php
$original = 'abcdef';
$shuffled = str_shuffle($original);

$a = str_split($original);
$b = str_split($shuffled);
sort($a);
sort($b);

echo json_encode([
    'same_length' => strlen($original) === strlen($shuffled),
    'same_characters' => $a === $b,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"same_length":true,"same_characters":true}
Edge cases with one possible permutation
<?php
echo json_encode([
    str_shuffle(''),
    str_shuffle('Z'),
    str_shuffle('aaaa'),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
["","Z","aaaa"]
Seeding makes the shuffle repeatable
<?php
mt_srand(42);
$first = str_shuffle('abcdefghij');

mt_srand(42);
$second = str_shuffle('abcdefghij');

echo json_encode([
    'repeatable' => $first === $second,
    'length' => strlen($first),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"repeatable":true,"length":10}