Keith
Keith

Reputation: 26489

How to create a random string using PHP?

I know that the rand function in PHP generates random integers, but what is the best way to generate a random string such as:

Original string, 9 chars

$string = 'abcdefghi';

Example random string limiting to 6 chars

$string = 'ibfeca';

UPDATE: I have found tons of these types of functions, basically I'm trying to understand the logic behind each step.

UPDATE: The function should generate any amount of chars as required.

Please comment the parts if you reply.

Upvotes: 57

Views: 99647

Answers (23)

miken32
miken32

Reputation: 42694

Maybe the last thing this question needs is another answer, but I got tired of seeing every one of these answers with an ABCDEFGHI... string in them. Just use ASCII! (Also, we need more answers that aren't using rand() and mt_rand().)

It's pretty straightforward, just generating a random ASCII code in a loop, optionally filtering for punctuation, and appending the character to a string. Implemented as a standalone class, this will only work in modern versions of PHP.

class RandomString implements Stringable {
    const PRINTABLE_START = 0x33;
    const PRINTABLE_END = 0x7E;

    public function __construct(
        private int $length = 12,
        private bool $alphanumeric = true
    ){}

    public function __toString(): string
    {
        $ret = "";
        do {
            $cp = random_int(self::PRINTABLE_START, self::PRINTABLE_END);
            $ret .= ($this->alphanumeric && self::isPunctuation($cp)) ? "" : chr($cp);
        } while (strlen($ret) < $this->length);

        return $ret;
    }

    private static function isPunctuation(int $code): bool
    {
        return ($code < 0x30)
            || ($code > 0x39 && $code < 0x41)
            || ($code > 0x5A && $code < 0x61)
            || ($code > 0x7A);
    }
}

echo new RandomString();
echo new RandomString(alphanumeric: false);
echo new RandomString(16);

Output:

62jnmI2f8OhJ
qRXIjngt>cKO
c8k2znh0XN12aj9a

Upvotes: 0

betrice mpalanzi
betrice mpalanzi

Reputation: 1117

One approach is to generate an md5 from a random number and extract the number of characters you want:

<?php 
    $random = substr(md5(mt_rand()), 0, 7);
    echo $random;
?>

mt_rand will generate a random number, md5 will create a 32 character string (containing both letters and numbers) and, in this example, we're extracting the first 7 characters of text.

Upvotes: -1

Toskan
Toskan

Reputation: 14931

built on top of https://stackoverflow.com/a/853898/533426

but with php 7 cryptographically secure random function and lower AND upper case alphabet

function random($length = 8){
            $valid_chars ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

            // start with an empty random string
            $random_string = "";

            // count the number of chars in the valid chars string so we know how many choices we have
            $num_valid_chars = strlen($valid_chars);

            // repeat the steps until we've created a string of the right length
            for ($i = 0; $i < $length; $i++)
            {
                // pick a random number from 1 up to the number of valid chars
                $random_pick = random_int(1, $num_valid_chars);

                // take the random character out of the string of valid chars
                // subtract 1 from $random_pick because strings are indexed starting at 0, and we started picking at 1
                $random_char = $valid_chars[$random_pick-1];

                // add the randomly-chosen char onto the end of our string so far
                $random_string .= $random_char;
            }

            // return our finished random string
            return $random_string;
    }

//example output XjdXHakZ, yBG8hpZG, L6jg4FpK

Upvotes: 3

Alpha
Alpha

Reputation: 329

Try this

Simple enough!

function RandomFromCharset($charset,$length) 
{

$characters = $charset; // your existing charset / defined string
    $charactersLength = strlen($characters);
    $random_from_charset = '';
    for ($i = 0; $i < $length; $i++) 
    {
        $random_from_charset.= $characters[rand(0, $charactersLength - 1)];
    }

return random_from_charset;
}

Call the function as follows

RandomFromCharset($charset,$length);

where $length will be length of random string you want (this can be predefined also in the function as RandomFromCharset(charset,$length=10) ) to generate and $charset will be your existing string to which you want to restrict the characters.

Upvotes: -1

Oscar Garcia
Oscar Garcia

Reputation: 81

echo substr(bin2hex(random_bytes(14)), 0, $length);

this code gets a random bytes, that are converted from binary to hexadecimal, and then takes a substring of this hexadecimal string, as long you puts in $length variable

Upvotes: 0

Alessandro
Alessandro

Reputation: 898

This is an old question but I want try to post my solution... I always use this my function to generate a custom random alphanumeric string...

<?php
  function random_alphanumeric($length) {
    $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345689';
    $my_string = '';
    for ($i = 0; $i < $length; $i++) {
      $pos = mt_rand(0, strlen($chars) -1);
      $my_string .= substr($chars, $pos, 1);
    }
    return $my_string;
  }
  $test = random_alphanumeric(50); // 50 characters
  echo $test;
?>

test: UFOruSSTCPIqxTRIIMTRkqjOGidcVlhYaS9gtwttxglheVugFM

if you need two or more unique strings you can use this trick...

$string_1 = random_alphanumeric(50);
$string_2 = random_alphanumeric(50);
while ($string_1 == $string_2) {
  $string_1 = random_alphanumeric(50);
  $string_2 = random_alphanumeric(50);
  if ($string_1 != $string_2) {
     break;
  }
}
echo $string_1;
echo "<br>\n";
echo $string_2;

$string_1: tMYicqLCHEvENwYbMUUVGTfkROxKIekEB2YXx5FHyVByp3mlJO

$string_2: XdMNJYpMlFRKFDlF6GhVn6jsBVNQ1BCCevj8yK2niFOgpDI2MU

I hope this help.

Upvotes: -1

nameht
nameht

Reputation:

function generate_random_string($name_length = 8) {
    $alpha_numeric = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    return substr(str_shuffle(str_repeat($alpha_numeric, $name_length)), 0, $name_length);
}

Updated the code as per mzhang's great suggestion in the comments below.

Upvotes: 18

Scott Arciszewski
Scott Arciszewski

Reputation: 34093

What do you need a random string for?

Is this going to be used for anything remotely analogous to a password?

If your random string requires any security properties at all, you should use PHP 7's random_int() function instead of all the insecure mt_rand() answers in this thread.

/**
 * Generate a random string
 * 
 * @link https://paragonie.com/b/JvICXzh_jhLyt4y3
 *
 * @param int $length - How long should our random string be?
 * @param string $charset - A string of all possible characters to choose from
 * @return string
 */
function random_str($length = 32, $charset = 'abcdefghijklmnopqrstuvwxyz')
{
    // Type checks:
    if (!is_numeric($length)) {
        throw new InvalidArgumentException(
            'random_str - Argument 1 - expected an integer'
        );
    }
    if (!is_string($charset)) {
        throw new InvalidArgumentException(
            'random_str - Argument 2 - expected a string'
        );
    }

    if ($length < 1) {
        // Just return an empty string. Any value < 1 is meaningless.
        return '';
    }
    // This is the maximum index for all of the characters in the string $charset
    $charset_max = strlen($charset) - 1;
    if ($charset_max < 1) {
        // Avoid letting users do: random_str($int, 'a'); -> 'aaaaa...'
        throw new LogicException(
            'random_str - Argument 2 - expected a string at least 2 characters long'
        );
    }
    // Now that we have good data, this is the meat of our function:
    $random_str = '';
    for ($i = 0; $i < $length; ++$i) {
        $r = random_int(0, $charset_max);
        $random_str .= $charset[$r];
    }
    return $random_str;
}

If you aren't on PHP 7 yet (which is probably the case, as it hasn't been released as of this writing), then you'll want paragonie/random_compat, which is a userland implementation of random_bytes() and random_int() for PHP 5 projects.

For security contexts, always use random_int(), not rand(), mt_rand(), etc. See ircmaxell's answer as well.

Upvotes: 3

ircmaxell
ircmaxell

Reputation: 165193

So, let me start off by saying USE A LIBRARY. Many exist:

The core of the problem is almost every answer in this page is susceptible to attack. mt_rand(), rand(), lcg_value() and uniqid() are all vulnerable to attack.

A good system will use /dev/urandom from the filesystem, or mcrypt_create_iv() (with MCRYPT_DEV_URANDOM) or openssl_pseudo_random_bytes(). Which all of the above do. PHP 7 will come with two new functions random_bytes($len) and random_int($min, $max) that are also safe.

Be aware that most of those functions (except random_int()) return "raw strings" meaning they can contain any ASCII character from 0 - 255. If you want a printable string, I'd suggest running the result through base64_encode().

Upvotes: 24

CoderOfTheNight
CoderOfTheNight

Reputation: 1122

Most aspects of this have already been discussed, but i'd recommend a slight update: If you are using this for retail usage, I would avoid the domain ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789

and instead use: ABCDEFGHJKMNPQRSTUVWXY3456789

Granted, you end up with far fewer characters, but it saves a great deal of hassle, as customers cannot mistake 0 for O, or 1 for l or 2 for Z. Also, you can do an UPPER on the input and customers can then enter upper or lower case letters -- that is also sometimes confusing since they can look similar.

Upvotes: 2

rodrigo-silveira
rodrigo-silveira

Reputation: 13068

If you're not concerned about time, memory, or cpu efficiency, and if your system can handle it, why not give this algorithm a try?!

function randStr($len, $charset = 'abcdABCD0123') {
    $out = '';
    $str = array();

    for ($i = 0; $i < PHP_INT_MAX; $i++) {
        $str[$i] = $charset;

        shuffle($str);
        $charset .= implode($charset, $str);
        $charset = str_shuffle($charset);
    }

    $str = array_flip($str);
    $str = array_keys($str);

    for ($i = 0; $i < PHP_INT_MAX; $i++) {
        shuffle($str);
    }

    $str = implode('', $str);

    for ($i = 0; $i < strlen($str); $i++) {
        $index = mt_rand(1, strlen($str));
        $out .= $str[$index - 1];
    }

    for ($i = 0; $i < PHP_INT_MAX; $i++) {
        $out = str_shuffle($out);
    }

    return substr($out, 0, $len);
}

Maybe this will read better if it uses recursion, but I'm not sure if PHP uses tail recursion or not...

Upvotes: -1

zeeks
zeeks

Reputation: 794

I think I will add my contribution here as well.

function random_string($length) {
    $bytes_1 = openssl_random_pseudo_bytes($length);
    $hex_1 = bin2hex($bytes_1);
    $random_numbers = substr(sha1(rand()), 0, $length);
    $bytes_2 = openssl_random_pseudo_bytes($length);
    $hex_2 = bin2hex($bytes_2);
    $combined_chars = $hex_1 . $random_numbers . $hex_2;
    $chars_crypted = hash('sha512', $combined_chars);

    return $chars_crypted;
}

Thanks

Upvotes: 4

Sliq
Sliq

Reputation: 16494

A better and updated version of @taskamiski's excellent answer:

Better version, using mt_rand() instead of rand():

echo md5(mt_rand()); // 32 char string = 128bit

Even better, for longer strings, using the hash() function that allows to select hashing algorithmns:

echo hash('sha256', mt_rand()); // 64 char string
echo hash('sha512', mt_rand()); // 128 char string

If you want to cut the result down to let's say 50 chars, do it like this:

echo substr(hash('sha256', mt_rand()), 0, 50); // 50 char string

Upvotes: 9

tfrascaroli
tfrascaroli

Reputation: 1219

well, I was looking for a solution, and I kindda used @Chad Birch's solution merged with @Gumbo's one. This is what I came up with:

function get_random_string($length, $valid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456790!·$%&/()=?¿¡',.-;:+*`+´ç")
{
    $random_string = "";
    $num_valid_chars = strlen($valid_chars);
    for ($i = 0; $i < $length; $i++, $random_string .= $valid_chars[mt_rand(1, $num_valid_chars)-1]);
    return $random_string;
}

I think comments are pretty much unnecesary since the answers I used to build up this one are already thoroughly commented. Cheers!

Upvotes: -1

Gajus
Gajus

Reputation: 73738

// @author http://codeascraft.etsy.com/2012/07/19/better-random-numbers-in-php-using-devurandom/                                                
function devurandom_rand($min = 0, $max = 0x7FFFFFFF)
{
    $diff = $max - $min;
    if ($diff < 0 || $diff > 0x7FFFFFFF) {
        throw new RuntimeException('Bad range');
    }
    $bytes = mcrypt_create_iv(4, MCRYPT_DEV_URANDOM);
    if ($bytes === false || strlen($bytes) != 4) {
        throw new RuntimeException('Unable to get 4 bytes');
    }
    $ary = unpack('Nint', $bytes);
    $val = $ary['int'] & 0x7FFFFFFF; // 32-bit safe                           
    $fp = (float) $val / 2147483647.0; // convert to [0,1]                          
    return round($fp * $diff) + $min;
}

function build_token($length = 60, $characters_map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') {
    $map_length = mb_strlen($characters_map)-1;
    $token = '';
    while ($length--) {
        $token .= mb_substr($characters_map, devurandom_rand(0,$map_length),1);
    }
    return $token;
}

This will work only in UNIX environment where PHP is compiled with mcrypt.

Upvotes: 1

Akhilraj N S
Akhilraj N S

Reputation: 9457

this will generate random string

function generateRandomString($length=10) {
    $original_string = array_merge(range(0,9), range('a','z'), range('A', 'Z'));
    $original_string = implode("", $original_string);
    return substr(str_shuffle($original_string), 0, $length);
}
echo generateRandomString(6);

Upvotes: 4

tasmaniski
tasmaniski

Reputation: 4898

My favorite:

 echo substr(md5(rand()), 0, 7);

Upvotes: 53

steampowered
steampowered

Reputation: 12062

This builds on Gumbo's solution by adding functionality to list a set of characters to be skipped in the base character set. The random string selects characters from $base_charset which do not also appear in $skip_charset.

/* Make a random string of length using characters from $charset, excluding $skip_chars.
 * @param length (integer) length of return value
 * @param skip_chars (string) characters to be excluded from $charset
 * @param charset (string) characters of posibilities for characters in return val
 * @return (string) random string of length $length    */
function rand_string(
        $length, 
        $skip_charset = '', 
        $base_charset='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'){
  $skip_len = strlen($skip_charset);
  for ($i = 0; $i<$skip_len; $i++){
    $base_charset = str_replace($skip_charset[$i], '', $base_charset);
  }
  cvar_dump($base_charset, '$base_charset after replace');
  $str = '';
  $count = strlen($base_charset);
  while ($length--) {
    $str .= $base_charset[mt_rand(0, $count - 1)];
  }
  return $str;
}

Here are some usage examples. The first two examples use the default value for $base_charset. The last example explicitly defines $base_charset.

echo rand_string(15, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
//  470620078953298
echo rand_string(8, 'abcdefghijklmnopqrstuvwxyz0123456789');
//  UKLIHOTFSUZMFPU
echo rand_string(15, 'def', 'abcdef');
//  cbcbbccbabccaba

Upvotes: -1

Josh Curren
Josh Curren

Reputation: 10226

you could make an array of characters then use rand() to pick a letter from the array and added it to a string.

$letters = array( [0] => 'a' [1] => 'b' [2] => 'c' [3] => 'd' ... [25] = 'z');

$lengthOfString = 10;
$str = '';

while( $lengthOfString-- )
{
   $str .= $letters[rand(0,25)];
}
echo $str;

*note that this does allow repeat characters

Upvotes: -1

Imran
Imran

Reputation: 90999

Joining characters at the end should be more efficient that repeated string concatenation.

Edit #1: Added option to avoid character repetition.

Edit #2: Throws exception to avoid getting into infinite loop if $norepeat is selected and $len is greater than the charset to pick from.

Edit #3: Uses array keys to store picked random characters when $norepeat is selected, as associative array key lookup is faster than linearly searching the array.

function rand_str($len, $norepeat = true)
{
    $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    $max = strlen($chars) - 1;

    if ($norepeat && len > $max + 1) {
        throw new Exception("Non repetitive random string can't be longer than charset");
    }

    $rand_chars = array();

    while ($len) {
        $picked = $chars[mt_rand(0, $max)];

        if ($norepeat) {
            if (!array_key_exists($picked, $rand_chars)) {
                $rand_chars[$picked] = true;
                $len--;
            }
        }
        else {
            $rand_chars[] = $picked;
            $len--;
        }
    }

    return implode('', $norepeat ? array_keys($rand_chars) : $rand_chars);   
}

Upvotes: 6

Gumbo
Gumbo

Reputation: 655129

If you want to allow repetitive occurences of characters, you can use this function:

function randString($length, $charset='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')
{
    $str = '';
    $count = strlen($charset);
    while ($length--) {
        $str .= $charset[mt_rand(0, $count-1)];
    }
    return $str;
}

The basic algorithm is to generate <length> times a random number between 0 and <number of characters> − 1 we use as index to pick a character from our set and concatenate those characters. The 0 and <number of characters> − 1 bounds represent the bounds of the $charset string as the first character is addressed with $charset[0] and the last with $charset[count($charset) - 1].

Upvotes: 98

Chad Birch
Chad Birch

Reputation: 74518

Well, you didn't clarify all the questions I asked in my comment, but I'll assume that you want a function that can take a string of "possible" characters and a length of string to return. Commented thoroughly as requested, using more variables than I would normally, for clarity:

function get_random_string($valid_chars, $length)
{
    // start with an empty random string
    $random_string = "";

    // count the number of chars in the valid chars string so we know how many choices we have
    $num_valid_chars = strlen($valid_chars);

    // repeat the steps until we've created a string of the right length
    for ($i = 0; $i < $length; $i++)
    {
        // pick a random number from 1 up to the number of valid chars
        $random_pick = mt_rand(1, $num_valid_chars);

        // take the random character out of the string of valid chars
        // subtract 1 from $random_pick because strings are indexed starting at 0, and we started picking at 1
        $random_char = $valid_chars[$random_pick-1];

        // add the randomly-chosen char onto the end of our string so far
        $random_string .= $random_char;
    }

    // return our finished random string
    return $random_string;
}

To call this function with your example data, you'd call it something like:

$original_string = 'abcdefghi';
$random_string = get_random_string($original_string, 6);

Note that this function doesn't check for uniqueness in the valid chars passed to it. For example, if you called it with a valid chars string of 'AAAB', it would be three times more likely to choose an A for each letter as a B. That could be considered a bug or a feature, depending on your needs.

Upvotes: 92

Dario
Dario

Reputation: 49208

Do you want to create your password by a random permutation of the original letters? Should it just contain unique characters?

Use rand to choose random letters by index.

Upvotes: -1

Related Questions