Question

Je souhaite créer un identifiant unique, mais uniqid() donne quelque chose comme '492607b0ee414'. Ce que je voudrais, c'est quelque chose de similaire à ce que tinyurl donne: '64k8ra'. Le plus court, mieux c'est. Les seules exigences sont qu'il ne doit pas y avoir d'ordre clair et qu'il doit paraître plus joli qu'une séquence de nombres apparemment aléatoire. Les lettres sont préférables aux chiffres et, idéalement, il ne s'agirait pas d'un cas mixte. Étant donné que le nombre d'entrées ne sera pas très élevé (jusqu'à 10 000 environ), le risque de collision ne constitue pas un facteur énorme.

Toutes les suggestions ont été appréciées.

Était-ce utile?

La solution

Créez une petite fonction qui renvoie des lettres aléatoires pour une longueur donnée:

<?php
function generate_random_letters($length) {
    $random = '';
    for ($i = 0; $i < $length; $i++) {
        $random .= chr(rand(ord('a'), ord('z')));
    }
    return $random;
}

Ensuite, vous voudrez l'appeler jusqu'à ce que ce soit unique, en pseudo-code en fonction de l'emplacement où vous souhaitez stocker ces informations:

do {
    $unique = generate_random_letters(6);
} while (is_in_table($unique));
add_to_table($unique);

Vous pouvez également vous assurer que les lettres ne forment pas un mot dans un dictionnaire. Qu'il s'agisse du dictionnaire anglais dans son ensemble ou simplement d'un dictionnaire de mauvais mots pour éviter les choses qu'un client pourrait trouver de mauvais goût.

EDIT: J'ajouterais également que cela n'a de sens que si, comme vous avez l'intention de l'utiliser, ce n'est pas pour un grand nombre d'éléments, car cela pourrait être assez lent si vous rencontrez de plus en plus de collisions (obtenir un ID déjà dans le tableau) . Bien sûr, vous voudrez une table indexée et vous aurez besoin de modifier le nombre de lettres dans l'ID pour éviter les collisions. Dans ce cas, avec 6 lettres, vous auriez 26 ^ 6 = 308915776 identifiants uniques possibles (moins les mauvais mots), ce qui devrait suffire pour votre besoin de 10000.

EDIT: Si vous voulez une combinaison de lettres et de chiffres, vous pouvez utiliser le code suivant:

$random .= rand(0, 1) ? rand(0, 9) : chr(rand(ord('a'), ord('z')));

Autres conseils

@gen_uuid () par gord.

preg_replace a eu quelques problèmes désagréables avec utf-8, ce qui fait que les uids peuvent parfois contenir & "; + &"; ou " / " ;. Pour contourner cela, vous devez explicitement créer le motif utf-8

function gen_uuid($len=8) {

    $hex = md5("yourSaltHere" . uniqid("", true));

    $pack = pack('H*', $hex);
    $tmp =  base64_encode($pack);

    $uid = preg_replace("#(*UTF8)[^A-Za-z0-9]#", "", $tmp);

    $len = max(4, min(128, $len));

    while (strlen($uid) < $len)
        $uid .= gen_uuid(22);

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

Il m'a fallu un bon bout de temps pour trouver cela, peut-être que cela sauverait un mal de tête à quelqu'un d'autre

Vous pouvez y parvenir avec moins de code:

function gen_uid($l=10){
    return substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyz"), 0, $l);
}

Résultat (exemples):

  • cjnp56brdy
  • 9d5uv84zfa
  • ih162lryez
  • ri4ocf6tkj
  • xj04s83egi

Il existe deux méthodes pour obtenir un identifiant unique fiable: le rendre tellement long et variable que les risques de collision sont spectaculairement minimes (comme avec un GUID) ou stocker tous les identifiants générés dans une table à des fins de recherche (en mémoire ou en mémoire). dans une base de données ou un fichier) pour vérifier l'unicité de la génération.

Si vous demandez vraiment comment vous pouvez générer une clé aussi courte et garantir son unicité sans contrôle en double, la réponse est non, vous ne pouvez pas.

Voici la routine que j'utilise pour les base62 aléatoires de toute longueur ...

L'appel gen_uuid() retourne des chaînes telles que WJX0u0jV, E9EMaZ3P etc.

Par défaut, cela retourne 8 chiffres, d’où un espace de 64 ^ 8 ou environ 10 ^ 14, c'est souvent suffisant pour faire des collisions assez rares.

Pour une chaîne plus grande ou plus petite, entrez $ len comme vous le souhaitez. Aucune limite de longueur, comme je l’ajoute jusqu’à ce que je sois satisfait [jusqu’à la limite de sécurité de 128 caractères, qui peut être supprimée].

Remarque, utilisez un sel aléatoire à l'intérieur du md5 [ou sha1 si vous préférez], afin qu'il ne puisse pas être facilement désossé.

Je n'ai trouvé aucune conversion base62 fiable sur le Web, d'où cette approche consistant à supprimer les caractères du résultat base64.

Utiliser librement sous licence BSD, profiter,

gord

function gen_uuid($len=8)
{
    $hex = md5("your_random_salt_here_31415" . uniqid("", true));

    $pack = pack('H*', $hex);

    $uid = base64_encode($pack);        // max 22 chars

    $uid = ereg_replace("[^A-Za-z0-9]", "", $uid);    // mixed case
    //$uid = ereg_replace("[^A-Z0-9]", "", strtoupper($uid));    // uppercase only

    if ($len<4)
        $len=4;
    if ($len>128)
        $len=128;                       // prevent silliness, can remove

    while (strlen($uid)<$len)
        $uid = $uid . gen_uuid(22);     // append until length achieved

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

Solution vraiment simple:

Créez l'ID unique avec:

$id = 100;
base_convert($id, 10, 36);

Récupérer la valeur d'origine à nouveau:

intval($str,36);

Je ne peux pas en accepter le crédit car il provient d'une autre page de débordement de pile, mais je pensais que la solution était si élégante et géniale qu'il valait la peine d'être copiée sur ce fil pour les personnes qui y font référence.

Vous pouvez utiliser l'identifiant et le convertir en base 36 si vous souhaitez le convertir en arrière. Peut être utilisé pour n'importe quelle table avec un identifiant entier.

function toUId($baseId, $multiplier = 1) {
    return base_convert($baseId * $multiplier, 10, 36);
}
function fromUId($uid, $multiplier = 1) {
    return (int) base_convert($uid, 36, 10) / $multiplier;
}

echo toUId(10000, 11111);
1u5h0w
echo fromUId('1u5h0w', 11111);
10000

Les gens intelligents peuvent probablement le comprendre avec suffisamment d'exemples d'identifiant. Ne laissez pas cette obscurité remplacer la sécurité.

J'ai trouvé ce que je pense être une solution plutôt cool qui consiste à le faire sans vérification de l'unicité. Je pensais partager avec tous les futurs visiteurs.

Un compteur est un moyen très simple de garantir l'unicité. Si vous utilisez une base de données, une clé primaire garantit également l'unicité. Le problème est que ça a l'air mauvais et peut être vulnérable. Alors j'ai pris la séquence et l'ai mélangée avec un chiffre. Comme le chiffrement peut être inversé, je sais que chaque identifiant est unique tout en apparaissant toujours de manière aléatoire.

C'est python pas php, mais j'ai téléchargé le code ici: https://github.com/adecker89/Tiny-Unique-Identifiers

Les lettres sont jolies, les chiffres sont laids. Vous voulez des chaînes aléatoires, mais vous ne voulez pas & "; Moche &"; chaînes aléatoires?

Créez un nombre aléatoire et imprimez-le en style alpha ( base-26 ), comme pour la réservation & "; nombres &"; que les compagnies aériennes donnent.

Autant que je sache, PHP ne comporte pas de fonctions de conversion de base à usage général intégrées. Vous devez donc le code vous-même.

Autre alternative: utilisez uniqid() et supprimez les chiffres.

function strip_digits_from_string($string) {
    return preg_replace('/[0-9]/', '', $string);
}

Ou remplacez-les par des lettres:

function replace_digits_with_letters($string) {
    return strtr($string, '0123456789', 'abcdefghij');
}

Vous pouvez également le faire comme ceci:

public static function generateCode($length = 6)
    {
        $az = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $azr = rand(0, 51);
        $azs = substr($az, $azr, 10);
        $stamp = hash('sha256', time());
        $mt = hash('sha256', mt_rand(5, 20));
        $alpha = hash('sha256', $azs);
        $hash = str_shuffle($stamp . $mt . $alpha);
        $code = ucfirst(substr($hash, $azr, $length));
        return $code;
    }

Vous pouvez le faire sans moyens impurs et coûteux tels que des boucles, des concaténations de chaînes ou des appels multiples à rand (), de manière propre et lisible. En outre, il vaut mieux utiliser mt_rand():

function createRandomString($length)
{
    $random = mt_rand(0, (1 << ($length << 2)) - 1);
    return dechex($random);
}

Si vous avez besoin que la chaîne ait la longueur exacte dans tous les cas, inscrivez simplement le nombre hexadécimal avec des zéros:

function createRandomString($length)
{
    $random = mt_rand(0, (1 << ($length << 2)) - 1);
    $number = dechex($random);
    return str_pad($number, $length, '0', STR_PAD_LEFT);
}

Le "ient retour théorique " c’est que vous êtes limité aux capacités des PHP - mais c’est plus un problème philosophique dans ce cas;) Passons de toute façon:

  • PHP est limité dans ce qu’il peut représenter sous forme de nombre hexadécimal le faisant ainsi. Ce serait $length <= 8 au moins sur un système 32 bits, où la limitation du nombre de PHP devrait être de 4.294.967.295.
  • Le générateur de nombres aléatoires PHP a également un maximum. Pour do { (generate ID) } while { (id is not uniqe) } (insert id) au moins sur un système 32 bits, il doit correspondre à 2.147.483.647
  • Donc, théoriquement, vous êtes limité à 2.147.483.647 ID.

Pour en revenir au sujet - l’intuitif <=> présente un inconvénient et un défaut possible qui pourrait vous rendre directement dans l’obscurité ...

Inconvénient: la validation est pessimiste. Le faire comme ceci always nécessite une vérification de la base de données. Avoir assez d'espace clé (par exemple, une longueur de 5 pour vos entrées de 10 000 entrées) entraînera très peu de risques de collision aussi souvent, car il pourrait être comparable moins gourmand en ressources de simplement stocker les données et de n'essayer que dans le cas de une erreur UNIQUE KEY.

Flaw: L'utilisateur A récupère un identifiant dont la vérification a été vérifiée. Ensuite, le code va essayer d'insérer les données. Mais entre-temps, l'utilisateur B a entré la même boucle et récupère malheureusement le même nombre aléatoire, car l'utilisateur A n'est pas encore stocké et que cet identifiant était encore libre. Le système stocke maintenant Utilisateur B ou Utilisateur A et, lors de la tentative de stockage du deuxième utilisateur, il y a déjà un autre utilisateur entre-temps: le même ID.

Dans tous les cas, vous devez gérer cette exception et réessayer l'insertion avec un ID nouvellement créé. Ajouter ceci tout en gardant la boucle de vérification pessimiste (que vous auriez besoin de ressaisir) aura pour résultat un code assez moche et difficile à suivre. Heureusement, la solution à ce problème est la même que celle qui présente l'inconvénient: Il suffit de tenter le premier coup et d'essayer de stocker les données. En cas d'erreur UNIQUE KEY, réessayez avec un nouvel identifiant.

Jetez un coup d'œil à cet article

Il explique comment générer des identifiants uniques et courts à partir de vos identifiants bdd, comme le fait YouTube.

En fait, la fonction de l'article est très liée à la fonction php base_convert qui convertit un nombre d’une base à une autre (mais n’est jusqu’à la base 36).

function rand_str($len = 12, $type = '111', $add = null) {
    $rand = ($type[0] == '1'  ? 'abcdefghijklmnpqrstuvwxyz' : '') .
            ($type[1] == '1'  ? 'ABCDEFGHIJKLMNPQRSTUVWXYZ' : '') .
            ($type[2] == '1'  ? '123456789'                 : '') .
            (strlen($add) > 0 ? $add                        : '');

    if(empty($rand)) $rand = sha1( uniqid(mt_rand(), true) . uniqid( uniqid(mt_rand(), true), true) );

    return substr(str_shuffle( str_repeat($rand, 2) ), 0, $len);
}

Si vous préférez une version plus longue d'un identifiant unique, utilisez ceci:
$ uniqueid = sha1 (md5 (time ()));

Meilleure réponse à ce jour: La plus petite unique & "; Hash Like &"; Chaîne avec ID de base de données unique - Solution PHP, aucune bibliothèque tierce n'est requise.

Voici le code:

<?php
/*
THE FOLLOWING CODE WILL PRINT:
A database_id value of 200 maps to 5K
A database_id value of 1 maps to 1
A database_id value of 1987645 maps to 16LOD
*/
$database_id = 200;
$base36value = dec2string($database_id, 36);
echo "A database_id value of 200 maps to $base36value\n";
$database_id = 1;
$base36value = dec2string($database_id, 36);
echo "A database_id value of 1 maps to $base36value\n";
$database_id = 1987645;
$base36value = dec2string($database_id, 36);
echo "A database_id value of 1987645 maps to $base36value\n";

// HERE'S THE FUNCTION THAT DOES THE HEAVY LIFTING...
function dec2string ($decimal, $base)
// convert a decimal number into a string using $base
{
    //DebugBreak();
   global $error;
   $string = null;

   $base = (int)$base;
   if ($base < 2 | $base > 36 | $base == 10) {
      echo 'BASE must be in the range 2-9 or 11-36';
      exit;
   } // if

   // maximum character string is 36 characters
   $charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

   // strip off excess characters (anything beyond $base)
   $charset = substr($charset, 0, $base);

   if (!ereg('(^[0-9]{1,50}$)', trim($decimal))) {
      $error['dec_input'] = 'Value must be a positive integer with < 50 digits';
      return false;
   } // if

   do {
      // get remainder after dividing by BASE
      $remainder = bcmod($decimal, $base);

      $char      = substr($charset, $remainder, 1);   // get CHAR from array
      $string    = "$char$string";                    // prepend to output

      //$decimal   = ($decimal - $remainder) / $base;
      $decimal   = bcdiv(bcsub($decimal, $remainder), $base);

   } while ($decimal > 0);

   return $string;

}

?>
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top