Captain Lightning
Captain Lightning

Reputation: 10753

PHP random string generator

I'm trying to create a randomized string in PHP, and I get absolutely no output with this:

<?php
    function RandomString()
    {
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $randstring = '';
        for ($i = 0; $i < 10; $i++) {
            $randstring = $characters[rand(0, strlen($characters))];
        }
        return $randstring;
    }

    RandomString();
    echo $randstring;

What am I doing wrong?

Upvotes: 1026

Views: 1861959

Answers (30)

Patrick Bergner
Patrick Bergner

Reputation: 623

One-liner for PHP 7+ with $length being the desired result string length

substr(bin2hex(random_bytes(intval(ceil($length / 2.0)))), 0, $length)

Upvotes: 0

Stephen Watkins
Stephen Watkins

Reputation: 25765

To answer this question specifically, two problems:

  1. $randstring is not in scope when you echo it.
  2. The characters are not getting concatenated together in the loop.

Here's a code snippet with the corrections:

function generateRandomString($length = 10) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';

    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[random_int(0, $charactersLength - 1)];
    }

    return $randomString;
}

Output the random string with the call below:

// Echo the random string.
echo generateRandomString();

// Optionally, you can give it a desired string length.
echo generateRandomString(64);

Please note that previous version of this answer used rand() instead of random_int() and therefore generated predictable random strings. So it was changed to be more secure, following advice from this answer.

Upvotes: 1690

rAthus
rAthus

Reputation: 894

Here's my simple one line solution to generate a use friendly random password, excluding the characters that lookalike such as "1" and "l", "O" and "0", "5" and "S", etc... this example generates 5 characters string but you can easily change it by modifyling the last variable:

$user_password = substr(str_shuffle('abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRTUVWXYZ2346789'),0,5);

It's obviously not the most secure solution as it has a limited amount of different characters and each can only be used once in the output, but a 5 character long string will still allow over 311 million combinations, and a 7 character long over 674 billion combinations, etc, so it's still an elegent, user-friendly, super light and easy to use method for non-critical uses.

Upvotes: 19

sensorario
sensorario

Reputation: 21620

Starting from php8.3 you can do this:

$rng = new Random\Randomizer();
$crockfordAlphabet = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
$rng->getBytesFromString($crockfordAlphabet, 10); // "5YH837JSJT"

Upvotes: 8

Alessandro
Alessandro

Reputation: 898

Please, try 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 = random_int(0, strlen($chars) -1);
      $my_string .= substr($chars, $pos, 1);
    }
    return $my_string;
  }
?>

You can adjust the result by passing the length of the string to the function like below:

  $test_with_50_items = random_alphanumeric(50); // 50 characters
  echo $test_with_50_items;

Example (test_with_50_items): Y1FypdjVbFCFK6Gh9FDJpe6dciwJEfV6MQGpJqAfuijaYSZ86

If you need more than 50 chars or less simply call the function how you like:

  $test_with_27_items = random_alphanumeric(27); // 27 characters
  echo $test_with_27_items;

If you need two or more unique strings you can do by loop using while so you are sure to get two unique strings... you can do the same with more strings, the only limit is your fantasy...

  $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: KkvUwia8rbDEV2aChWqm3AgeUZqyrRbUx2AxVhx5s4TSJ2VwA4

$string_2: XraO85YfxBBCInafvwipSOJwLmk6JMWiuWOxYQDnXohcn2D8K6

According to PHP 8.3 random_int() is "secure by default"

Upvotes: 0

fartwhif
fartwhif

Reputation: 359

I wanted pseudorandom strings of specific characters and a preset length. I wanted the highest quality pseudorandomness practically possible for the current version of PHP, at this time it could be v5, v6, v7, or v8, and it could be using default configuration or special. To tame this chaos, I sampled several of the other answers here and included function availability conditionals.

Usage. To use it globally assign $VALID_ID_CHARS with whatever characters you want and then call it:

$VALID_ID_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
$myNewRandomId = makeId(6);
function makeId($desiredLen)
{
    global $VALID_ID_CHARS;
    if ($desiredLen < 1) {
        throw new \RangeException("Length must be a positive integer");
    }
    $vLen = 0;
    if (function_exists('mb_strlen')) {
        $vLen = mb_strlen($VALID_ID_CHARS, '8bit') - 1;
    } else {
        $vLen = strlen($VALID_ID_CHARS) - 1;
    }
    if (function_exists('random_int')) {
        $pieces = [];
        for ($i = 0; $i < $desiredLen; ++$i) {
            $pieces[] = $VALID_ID_CHARS[random_int(0, $vLen)];
        }
        return implode('', $pieces);
    }
    if (function_exists('openssl_random_pseudo_bytes')) {
        $random = openssl_random_pseudo_bytes($desiredLen);
        $id = '';
        for ($i = 0; $i < $desiredLen; ++$i) {
            $id .= $VALID_ID_CHARS[ord($random[$i]) % $vLen];
        }
        return $id;
    }
    http_response_code(500);
    die('random id generation failed. either random_int or openssl_random_pseudo_bytes is needed');
}

Upvotes: 0

Pascual Mu&#241;oz
Pascual Mu&#241;oz

Reputation: 329

recursive solution:

public static function _random(string $set , int $length): string
{
    $setLength = strlen($set);
    $randomKey = random_int(0, $setLength - 1);

    $firstPiece = substr($set, 0, $randomKey);
    $secondPiece = substr($set, $randomKey, $setLength - $randomKey);

    $removedCharacter = $firstPiece[strlen($firstPiece) - 1] ?? null;
    if(null === $removedCharacter || $length === 0) {
        return '';
    }
    $firstPieceWithoutTheLastChar = substr($firstPiece, 0, -1);

    return $removedCharacter . self::_random($firstPieceWithoutTheLastChar . $secondPiece, $length - 1);
}

nice performance , https://3v4l.org/aXaJ6/perf

Upvotes: 0

Amir Khalife Soltani
Amir Khalife Soltani

Reputation: 111

Laravel solution: If you are using Laravel Framework, you can use its StringHelper like this.

use Illuminate\Support\Str;

Str::random($strlentgh = 16)

Upvotes: 0

CodAIK
CodAIK

Reputation: 763

function gen_uid($l=5){
   return substr(str_shuffle("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 10, $l);
}
echo gen_uid();

Default Value[5]: WvPJz

echo gen_uid(30);

Value[30]: cAiGgtf1lDpFWoVwjykNKXxv6SC4Q2

Upvotes: 14

redcenter
redcenter

Reputation: 836

A full solution (class plus test), partly based on some suggestions above...

class TokenFactory
{
    private const LENGTH = 12;
    private const ALLOWED = '123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ~!@#$%^&*{}';
    private const MIN_NUMBER_OF_DIGITS = 1;
    private const MIN_NUMBER_OF_CAPS = 1;
    private const MIN_NUMBER_OF_SPECIALS = 1;
    private const MIN_NUMBER_OF_LETTERS = 1;

    /**
     * @return string
     * @throws \Exception
     */
    public function make(): string
    {
        $pass = $this->generateToken();

        if ($this->isTokenValid($pass)) {
            return $pass;
        } else {
            return $this->make();
        }
    }

    /**
     * @return string
     * @throws \Exception
     */
    private function generateToken(): string
    {
        $allowedCharacters = self::ALLOWED;
        $token              = '';
        $max               = mb_strlen($allowedCharacters, '8bit') - 1;
        for ($i = 0; $i < self::LENGTH; ++$i) {
            $token .= $allowedCharacters[random_int(0, $max)];
        }
        return $token;
    }

    /**
     * @param $token
     * @return bool
     */
    private function isTokenValid($token): bool
    {
        $numberOfDigits   = preg_match_all("/[0-9]/", $token);
        $numberOfCaps     = preg_match_all("/[A-Z]/", $token);
        $numberOfSpecials = preg_match_all("/[~!@#\$%^&*{}]/", $token);
        $numberOfLetters  = preg_match_all("/[a-z]/", $token);

        return
            $numberOfDigits > self::MIN_NUMBER_OF_DIGITS
            && $numberOfCaps > self::MIN_NUMBER_OF_CAPS
            && $numberOfSpecials > self::MIN_NUMBER_OF_SPECIALS
            && $numberOfLetters > self::MIN_NUMBER_OF_LETTERS
            ;
    }
}

class TokenFactoryTest
{
    public function test_correct_syntax()
    {
        /**
         * Arrange
         */
        $length = 12;
        $numberOfChecks = 1000;

        /**
         * Act & Assert
         */
        $class = new TokenFactory();

        $i = 0;
        while ($i < $numberOfChecks) {
            $generatedToken = $class->make();

            $numberOfDigits = preg_match_all( "/[0-9]/", $generatedToken );
            $numberOfCaps = preg_match_all( "/[A-Z]/", $generatedToken );
            $numberOfSpecials   = preg_match_all("/[~!@#\$%^&*{}]/", $generatedToken);
            $numberOfLetters   = preg_match_all("/[a-z]/", $generatedToken);

            Assert::assertEquals($length, strlen($generatedToken));
            Assert::assertTrue($numberOfDigits >= 1, 'Digit error: ' . $generatedToken);
            Assert::assertTrue($numberOfCaps >= 1, 'Caps error: ' . $generatedToken);
            Assert::assertTrue($numberOfSpecials >= 1, 'Specials error: ' . $generatedToken);
            Assert::assertTrue($numberOfLetters >= 1, 'Letters error: ' . $generatedToken);

            $i++;
        }
    }
}

BTW make sure to catch that exception somewhere where it suits your needs!

Upvotes: 0

Ho&#224;ng Vũ Tgtt
Ho&#224;ng Vũ Tgtt

Reputation: 2032

//generateRandomString http://stackoverflow.com/questions/4356289/php-random-string-generator
function randomString($length = 32, $string= "0123456789abcdefghijklmnopqrstuvwxyz" ) {
    //$string can be:
    //0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
    //0123456789abcdefghijklmnopqrstuvwxyz
    return substr(str_shuffle( $string), 0, $length);
}

Upvotes: 3

MRMP
MRMP

Reputation: 313

function generateRandomString($length = 10, $hasNumber = true, $hasLowercase = true, $hasUppercase = true): string
{
    $string = '';
    if ($hasNumber)
        $string .= '0123456789';
    if ($hasLowercase)
        $string .= 'abcdefghijklmnopqrstuvwxyz';
    if ($hasUppercase)
        $string .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    return substr(str_shuffle(str_repeat($x = $string, ceil($length / strlen($x)))), 1, $length);
}

and use:

echo generateRandomString(32);

Upvotes: 4

Reed
Reed

Reputation: 14974

Here's a one-liner. You'll get at least one lower-case, one upper-case, one number, and one symbol. Uses random_int which is supposed to cyrptographically secure. I do not claim this to be secure, though. I am not a security expert.

To copy+Paste: (just change $len to your desired length.)

$len=26;for ($chars = array('0123456789','abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ','!@#$%^&*()_+-='),$randomString="",$i=0;$i<$len;$i++)$randomString .= substr($chars[$i%4], random_int(0,strlen($chars[$i%4])), 1);
echo $randomString;

And a little more broken down:

for (
        $chars = array('0123456789','abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ','!@#$%^&*()_+-='),
        $randomString="",
        $i=0;
        $i<12;$i++)
        $randomString .= 
            substr($chars[$i%4], 
                random_int(0, strlen($chars[$i%4])), 1);

I use $chars[$i%4] in the loop to choose which set of characters to get a random from. It guarantees multiple characters from each set of chars in the array.

It could definitely be improved (randomizing how many of each char set there is), but it's good enough for my purposes.

Upvotes: 2

SXN
SXN

Reputation: 109

from the yii2 framework

/**
 * Generates a random string of specified length.
 * The string generated matches [A-Za-z0-9_-]+ and is transparent to URL-encoding.
 *
 * @param int $length the length of the key in characters
 * @return string the generated random key
 */

function generateRandomString($length = 10) {
    $bytes = random_bytes($length);
    return substr(strtr(base64_encode($bytes), '+/', '-_'), 0, $length);
}

Upvotes: 9

This code will help:

This function will return random string with length between $maxLength and $minLength.

NOTE: Function random_bytes works since PHP 7.

If you need specific length, so $maxLength and $minLength must be same.

function getRandomString($maxLength = 20, $minLength = 10)
{
    $minLength = $maxLength < $minLength ? $maxLength : $minLength;
    $halfMin = ceil($minLength / 2);
    $halfMax = ceil($maxLength / 2);
    $bytes = random_bytes(rand($halfMin, $halfMax));
    $randomString = bin2hex($bytes);
    $randomString = strlen($randomString) > $maxLength ? substr($randomString, 0, -1) : $randomString;
    return $randomString;
}

Upvotes: 0

Madhan
Madhan

Reputation: 107

Try this, it can generate up to length of master. but its simple and fit for small string.

$master="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$master_array = str_split($master);
shuffle($master_array);
print_r(implode("",array_slice($master_array,0,10)));

Upvotes: 1

Vivek Pawar
Vivek Pawar

Reputation: 347

IF YOUR ARE USING PHP 7 +

public function generateRandom(){

$string = bin2hex(openssl_random_pseudo_bytes(10)); // 20 chars

// OR

$string = base64_encode(random_bytes(10)); // ~14 characters, includes /=+

// or

$string = substr(str_replace(['+', '/', '='], '', base64_encode(random_bytes(32))), 0, 32); // 32 characters, without /=+

// or

$string = bin2hex(random_bytes(10)); // 20 characters, only 0-9a-f

}

Upvotes: 0

Madan Sapkota
Madan Sapkota

Reputation: 26071

PHP 7+ Generate cryptographically secure random bytes using random_bytes function.

$bytes = random_bytes(16);
echo bin2hex($bytes);

Possible output

da821217e61e33ed4b2dd96f8439056c


PHP 5.3+ Generate pseudo-random bytes using openssl_random_pseudo_bytes function.

$bytes = openssl_random_pseudo_bytes(16);
echo bin2hex($bytes);

Possible output

e2d1254506fbb6cd842cd640333214ad


The best use case could be

function getRandomBytes($length = 16)
{
    if (function_exists('random_bytes')) {
        $bytes = random_bytes($length / 2);
    } else {
        $bytes = openssl_random_pseudo_bytes($length / 2);
    }
    return bin2hex($bytes);
}
echo getRandomBytes();

Possible output

ba8cc342bdf91143

Upvotes: 42

Umair Khan
Umair Khan

Reputation: 1752

Here is another solution.

function genRandomString($length = 10)
{
    if($length < 1)
        $length = 1;
    return substr(preg_replace("/[^A-Za-z0-9]/", '', base64_encode(openssl_random_pseudo_bytes($length * 2))), 0, $length);
}

PS. I am using PHP 7.2 on Ubuntu.

Upvotes: 2

Vasil Kulakov
Vasil Kulakov

Reputation: 310

Since php7, there is the random_bytes functions. https://www.php.net/manual/ru/function.random-bytes.php So you can generate a random string like that

<?php
$bytes = random_bytes(5);
var_dump(bin2hex($bytes));
?>

Upvotes: 9

Scott Arciszewski
Scott Arciszewski

Reputation: 34093

There are a lot of answers to this question, but none of them leverage a Cryptographically Secure Pseudo-Random Number Generator (CSPRNG).

The simple, secure, and correct answer is to use RandomLib and don't reinvent the wheel.

For those of you who insist on inventing your own solution, PHP 7.0.0 will provide random_int() for this purpose; if you're still on PHP 5.x, we wrote a PHP 5 polyfill for random_int() so you can use the new API even before you upgrade to PHP 7.

Safely generating random integers in PHP isn't a trivial task. You should always check with your resident StackExchange cryptography experts before you deploy a home-grown algorithm in production.

With a secure integer generator in place, generating a random string with a CSPRNG is a walk in the park.

Creating a Secure, Random String

/**
 * Generate a random string, using a cryptographically secure 
 * pseudorandom number generator (random_int)
 *
 * This function uses type hints now (PHP 7+ only), but it was originally
 * written for PHP 5 as well.
 * 
 * For PHP 7, random_int is a PHP core function
 * For PHP 5.x, depends on https://github.com/paragonie/random_compat
 * 
 * @param int $length      How many characters do we want?
 * @param string $keyspace A string of all possible characters
 *                         to select from
 * @return string
 */
function random_str(
    int $length = 64,
    string $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
): string {
    if ($length < 1) {
        throw new \RangeException("Length must be a positive integer");
    }
    $pieces = [];
    $max = mb_strlen($keyspace, '8bit') - 1;
    for ($i = 0; $i < $length; ++$i) {
        $pieces []= $keyspace[random_int(0, $max)];
    }
    return implode('', $pieces);
}

Usage:

$a = random_str(32);
$b = random_str(8, 'abcdefghijklmnopqrstuvwxyz');
$c = random_str();

Demo: https://3v4l.org/IMJGF (Ignore the PHP 5 failures; it needs random_compat)

Upvotes: 436

Aomin&#233;
Aomin&#233;

Reputation: 500

You can try this:

<?php
    function random($len){

        $char = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

        // ----------------------------------------------
        // Number of possible combinations
        // ----------------------------------------------
        $pos = strlen($char);
        $pos = pow($pos, $len);
        echo $pos.'<br>';
        // ----------------------------------------------

        $total = strlen($char)-1;
        $text = "";

        for ($i=0; $i<$len; $i++){
            $text = $text.$char[rand(0, $total)];
        }
        return $text;
    }

    $string = random(15);
    echo $string;
?>

You can also use md5 on time but be careful.

You need to use microtime() and not time() function, because if multiple threads run within the same second, you need to get different string for all of them.

<?php

    $string = md5(microtime());
    echo $string;
?>

Upvotes: 3

dixromos98
dixromos98

Reputation: 754

A class with some of the functions from the discussions above.

$options['numeric'] = true;
$options['uppercase'] = true;
$options['lowercase'] = true;
$new = new RandomString($options);

class RandomString
{
    /**
     * @var array
     */
    private $default = ['numeric' => true, 'uppercase' => true, 'lowercase' => true];

    /**
     * @var array
     */
    private $options;

    /**
     * array
     */
    private $whitelist = ['numeric', 'uppercase', 'lowercase'];

    /**
     * RandomString constructor.
     *
     * @param array $options
     */
    public function __construct(array $options = [])
    {
        $this->options = $this->default;
        if(!empty($options))
        {
            $options = array_intersect_key($options, array_flip($this->whitelist));
            if(empty($options))
            {
                $this->options = $this->default;
            }else
            {
                $this->options = $options;
            }
        }
    }

    /**
     * @return string
     */
    private function returnCharacters(){

        $options = $this->options;

        $numbers = '0123456789';
        $uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $lowercase = "abcdefghijklmnopqrstuvwxyz";

        $characters = '';
        if(isset($options['numeric']) && $options['numeric'] === true){
            $characters .= $numbers;
        }

        if(isset($options['uppercase']) && $options['uppercase'] === true){
            $characters .= $uppercase;
        }

        if(isset($options['lowercase']) && $options['lowercase'] === true){
            $characters .= $lowercase;
        }
        return $characters;
    }

    /**
     * @param $length
     * @param $quantity
     * @return string
     */
    public function randomString($length, $quantity) {

        $string = '';
        $characters = $this->returnCharacters();

        for ($j = 0; $j < $quantity; $j++) {
            for($i = 0; $i < $length; $i++){
                $string .= $characters[mt_rand(0, strlen($characters) - 1)];
            }
            $string .= "\n";
        }
        return $string;
    }

    /**
     * @return array
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * @return mixed
     */
    public function getWhitelist()
    {
        return $this->whitelist;
    }

Upvotes: 1

Junaid
Junaid

Reputation: 610

Finally I have found a solution to get random and unique values.

My solution is:

substr(md5(time()), 0, 12)

time always return a timestamp, and it is always unique. You can use it with MD5 to make it better.

Upvotes: 1

Anjith K P
Anjith K P

Reputation: 2158

function randomString($length = 5) {
    return substr(str_shuffle(implode(array_merge(range('A','Z'), range('a','z'), range(0,9)))), 0, $length);
}

Upvotes: 5

16ctt1x
16ctt1x

Reputation: 331

There is simple code:

echo implode("",array_map(create_function('$s','return substr($s,mt_rand(0,strlen($s)),1);'),array_fill(0,16,"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")));

There is a simple guide:

  • To change the length of string, please change the 16 to another value, only.
  • To select from different characters, please change the character string.

Upvotes: 2

Ruwantha
Ruwantha

Reputation: 2653

To get it in a single line, try this:

$str = substr(str_shuffle(str_repeat("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 5)), 0, $length);

Upvotes: -1

Jacob Smith
Jacob Smith

Reputation: 89

One liner.

It is fast for huge strings with some uniqueness.

function random_string($length){
    return substr(str_repeat(md5(rand()), ceil($length/32)), 0, $length);
}

Upvotes: 5

Rudie
Rudie

Reputation: 53821

This creates a 20 character long hexadecimal string:

$string = bin2hex(openssl_random_pseudo_bytes(10)); // 20 chars

In PHP 7 (random_bytes()):

$string = base64_encode(random_bytes(10)); // ~14 characters, includes /=+
// or
$string = substr(str_replace(['+', '/', '='], '', base64_encode(random_bytes(32))), 0, 32); // 32 characters, without /=+
// or
$string = bin2hex(random_bytes(10)); // 20 characters, only 0-9a-f

Upvotes: 229

kasimir
kasimir

Reputation: 1554

Another one-liner, which generates a random string of 10 characters with letters and numbers. It will create an array with range (adjust the second parameter to set the size), loops over this array and assigns a random ASCII character (range 0-9 or a-z), then implodes the array to get a string.

$str = implode('', array_map(function () { return chr(rand(0, 1) ? rand(48, 57) : rand(97, 122)); }, range(0, 9)));

Note: this only works in PHP 5.3 and later

Upvotes: 5

Related Questions