Got a strange one here.
I'm using a function to generate a random string of chars for a token :
private static function generatePasswordResetCode()
{
   $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-';
   return substr(str_shuffle($chars), 0, RESET_TOKEN_LENGTH);
}
RESET_TOKEN_LENGTH is a constant :
define("RESET_TOKEN_LENGTH", 100);
The code is saved to the table like so :
$code = self::generatePasswordResetCode();
$expiry_timestamp = time() + RESET_CODE_EXPIRY_TIME;
$sql = "INSERT INTO reset_codes (code, user_id, expires_at)
        VALUES (:code, :user_id, :expires_at)";
$db = static::getDB();
$stmt = $db->prepare($sql);
$stmt->bindParam(':user_id', $id, PDO::PARAM_INT);
$stmt->bindParam(':code', $code, PDO::PARAM_STR);
$stmt->bindValue(':expires_at', date('Y-m-d H:i:s', $expiry_timestamp), PDO::PARAM_STR);
$stmt->execute();
In the database the code column is set to varchar(255). I've also tried text and I have the same problem either way.
Whatever I define RESET_TOKEN_LENGTH to be up to and including 63 chars everything works fine, but any number above that and the string is being truncated in the code column :
define("RESET_TOKEN_LENGTH", 63);
// results in
MFYflHL6bVwNEG1DpqRA0ry5TgC9KhmntPUao2x-ujvekX7sZcQizWSd43OBIJ8
Great, but...
define("RESET_TOKEN_LENGTH", 64);
// results in
7fITjSp32gmA0YJCwFhrWvPk4VDQMZonl19btBKs5Ri-zULeXO...
Any ideas what could be causing this?
**** UPDATE ****
So this doesn't appear to be MySQL related but PHP related instead.
It seems that the combination of substr & str_shuffle result in a limit of 63 chars no matter what number above 63 you define.
To test it I changed the function like so :
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-';
$code = substr(str_shuffle($chars), 0, RESET_TOKEN_LENGTH);
$code2 = substr(str_shuffle($chars), 0, RESET_TOKEN_LENGTH);
$code = $code . $code2;
return $code;
Which results in the correct var_dump output :
define("RESET_TOKEN_LENGTH", 50);
//results in
string(100) "fk8DcbusUeVxWOdp1HwNLEy9gzYm6tq0o3PMaZAIh2Sn45F-K7GH9l18hZNk73j6cQpFDfPwxMCe4BTrqiIASdRs5WEnoKgmzyXY" 
Where as doing :
define("RESET_TOKEN_LENGTH", 64);
//results in
string(126) "Q14Hd0WJjVotYRfsaU5pAM7DeZSuvwnCmxqbPgh6839XrEIyBcKTONzG-kil2FL0LUgQG8452WqKEfdmuzHlr9PZevc7VhnNCSjbk-wTyMR1iOspxaXYJDoFtBIA36" 
Showing that $code and $code2 are being truncated to 63 chars.
How very strange!
** UPDATE 2 **
It seems my lack of understanding as to how str_shuffle works was at fault here and nothing else. See the accepted answer below.
 
    