我试图在PHP中创建一个随机字符串,我得到绝对没有输出:

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

    RandomString();
    echo $randstring;

我做错了什么?


当前回答

我喜欢使用openssl_random_pseudo_bytes的最后一个注释,但这对我来说不是一个解决方案,因为我仍然必须删除我不想要的字符,而且我无法获得一个设置长度的字符串。这是我的解决方案……

function rndStr($len = 20) {
    $rnd='';
    for($i=0;$i<$len;$i++) {
        do {
            $byte = openssl_random_pseudo_bytes(1);
            $asc = chr(base_convert(substr(bin2hex($byte),0,2),16,10));
        } while(!ctype_alnum($asc));
        $rnd .= $asc;
    }
    return $rnd;
}

其他回答

$pass = "";
for ($i=0;$i<12;++$i)
  $pass.= ($r=mt_rand(0,35))<26?chr(ord('a')+$r):chr(ord('0')+$r-26);

这个问题有很多答案,但没有一个是利用加密安全伪随机数生成器(CSPRNG)的。

简单、安全、正确的答案是使用RandomLib,不要白费力气。

对于那些坚持发明自己的解决方案的人,PHP 7.0.0将为此目的提供random_int();如果你还在使用PHP 5。x,我们为random_int()写了一个PHP 5的polyfill,这样你甚至可以在升级到PHP 7之前使用新的API。

在PHP中安全地生成随机整数并不是一项简单的任务。在生产环境中部署自己开发的算法之前,您应该始终与常驻StackExchange密码学专家进行检查。

有了安全的整数生成器,使用CSPRNG生成随机字符串就像在公园里散步一样简单。

创建安全的随机字符串

/**
 * 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);
}

用法:

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

演示:https://3v4l.org/IMJGF(忽略PHP 5失败;它需要random_compat)

这段代码将帮助:

此函数将返回长度在$maxLength和$minLength之间的随机字符串。

注意:函数random_bytes从PHP 7开始工作。

如果你需要特定的长度,那么$maxLength和$minLength必须相同。

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;
}

一个完整的解决方案(课程加测试),部分基于上面的一些建议…

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++;
        }
    }
}

顺便说一句,请确保在适合您需要的地方捕获该异常!

这是另一个解决方案。

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.我在Ubuntu上使用PHP 7.2。