Fala, meu povo!
Tudo sossegado?
Essa semana estava trabalhando em um sistema que precisava gerar IDs únicos para acesso e fui dar uma pesquisada pra ver o que encontrava. E não é que, para a minha surpresa, o PHP possui um gerador de IDs únicos?
A função chama-se uniqid() e funciona da seguinte maneira:
string uniqid ([ string $prefix = "" [, bool $more_entropy = false ]] )
Cada ID é gerado de acordo com os microsegundos, ou seja, se você estiver gerando IDs únicos em hosts diferentes é interessante utilizar o parâmetro $prefix para estipular um prefixo para cada ID gerado e evitar que sejam gerados hashs iguais (no mesmo microsegundo).
Modo simples
Chamar a função sem nenhum parâmetro irá gerar um ID único de 13 caracteres.
Eu utilizei este código e obtive os seguintes resultados:
<?php
for ($i = 0; $i < 20; $i++)
{
echo uniqid() , '<br />';
}
4e7a2a9eda4f4 4e7a2a9eda513 4e7a2a9eda520 4e7a2a9eda52c 4e7a2a9eda538 4e7a2a9eda53e 4e7a2a9eda545 4e7a2a9eda54a 4e7a2a9eda54f 4e7a2a9eda553 4e7a2a9eda558 4e7a2a9eda55d 4e7a2a9eda562 4e7a2a9eda567 4e7a2a9eda56c 4e7a2a9eda571 4e7a2a9eda576 4e7a2a9eda57b 4e7a2a9eda580 4e7a2a9eda585
Nessa amostra é possível perceber que um padrão é seguido, porém a variação sempre está presente.
Aumentando a entropia
É possível aumentar a entropia e bagunçar ainda mais o número, diminuindo a previsibilidade do identificador gerado.
Alterando para true o segundo parâmetro da função, aumentaremos de 13 para 23 o número de caracteres gerados.
<?php
for ($i = 0; $i < 20; $i++)
{
echo uniqid(NULL, true) , '<br />';
}
4e7a289bd01f45.79477223 4e7a289bd02032.89581934 4e7a289bd02052.11471119 4e7a289bd02075.00423065 4e7a289bd02085.08264238 4e7a289bd020a0.54923203 4e7a289bd020b3.38239852 4e7a289bd020c5.41874042 4e7a289bd020e7.13109847 4e7a289bd020f8.07879585 4e7a289bd02105.23156388 4e7a289bd02121.78128696 4e7a289bd02133.03380035 4e7a289bd02142.46928362 4e7a289bd02158.14421079 4e7a289bd02168.85897068 4e7a289bd02186.39675466 4e7a289bd02192.69187621 4e7a289bd021a1.41820411 4e7a289bd021b4.97244295
Identificadores extremamente imprevisíveis
Pra finalizar, uma paulada! Vamos preencher o primeiro parâmetro (prefixo) utilizando a função rand(). Então teremos um identificador de entropia elevadíssima, sendo um ID aleatório de 23 posições precedido por um número aleatório.
<?php
for ($i = 0; $i < 20; $i++)
{
echo uniqid(rand(), true) , '<br />';
}
19060798504e7a29e9c9c059.93729338 13329376364e7a29e9c9c121.60865627 11839762204e7a29e9c9c151.33271890 15126155264e7a29e9c9c173.28119226 3205916434e7a29e9c9c196.14831352 6879387264e7a29e9c9c1a1.12625014 12097042174e7a29e9c9c1c4.76932924 5578020564e7a29e9c9c1e8.49604578 8800718324e7a29e9c9c1f7.42284494 3516524794e7a29e9c9c210.74379494 13719049204e7a29e9c9c232.53688755 13615040334e7a29e9c9c258.57255460 9026903494e7a29e9c9c262.94629146 12770888504e7a29e9c9c283.76903832 2071162574e7a29e9c9c2a1.35952502 7032290634e7a29e9c9c2b9.36462979 3520522964e7a29e9c9c2d3.39877178 4841746524e7a29e9c9c2e4.67328983 15352161784e7a29e9c9c301.78593394 16981154524e7a29e9c9c318.06476122
Wow! :D
Considerações finais
O sistema mostrou-se extremamente eficiente para realizar a tarefa a que se propõe e eu fiquei bastante satisfeito com os resultados obtidos.
Espero que tenha sido uma dica útil e gostaria de lembra-los que o blog está sempre aberto a comentários, sendo de críticas, elogios ou sugestões!
Um abraço a todos!
Fiquem com Deus.
Rafael Jaques
se quiser algo mais complexo, podemos brincar assim também
md5(uniqid(time()));
FikDik ! :)
E vai cada vez mais aumentando! Hehehehe!
Boa dica :)
MD5 tem problemas de colisão, não recomendo.
usa assim: mais entropia, menos caracteres e sem a chance de colisão que o MD5 tem:
echo base_convert(hexdec(uniqid(rand(), true)) + microtime(true), 10, 36);
[...] lendo: phpit.com.br Delicious Digg Facebook Reddit Stumblers [...]
Boa Dica! Será de grande utilizadade.
Boas dicas, to usando :)
Show de bola! Muito melhor do que usar time() * rand() :P hehehehe
Quero gerar um id de 5 caracteres onde que eu defino no código ?
Teoricamente não existe como gerar um ID de apenas 5 caracteres.
Tu pode pegar os últimos 5 caracteres do ID gerado:
<?php
echo substr(uniqid(), -5);
?>
Rafa, essa solução não aparenta ser muito segura, por que o uniqid só será mesmo único se for o número todo. Como você mesmo colocou, ele é baseado nos microsegundos do servidor, e também segue um padrão. Se pegar os últimos 5 caracteres mais cedo ou mais tarde vai haver uma duplicata. Alguma outra sugestão talvez? Tentei procurar um pouco e não encontrei nada tão simples e eficiente. Valeu.
Com certeza. Embora vá demorar bastante, certamente um dia vai acabar dando colisão.
Pra resolver esse tipo de problema, poderíamos tentar duas abordagens que irão variar de acordo com a finalidade dos IDs gerados:
1. Criar um algoritmo próprio de geração de IDs que terá como base o ID anterior, aumentando apenas o valor de um ou mais caracteres em relação ao anterior. Nesse caso, considerando [A-Za-z0-9] em uma string de 5 caracteres case sensitive, temos a possibilidade de 62^5 combinações diferentes (utilizando passos de 1). Cedo ou tarde também haverá colisão, o que parece ser o mesmo caso do uniqid() que aumenta no mesmo passo, variando os caracteres da direita para esquerda. O que nos leva à proposta 2.
2. Trabalhar com IDs expiráveis. Depois de um certo tempo você pode extinguir determinado ID e deixa-lo disponível para outro usuário (o que ocorrerá numa eventual "virada dos números").
De qualquer maneira, não existe nenhum modo possível de criar IDs únicos para um número indeterminado de usuários limitando os caracteres. Com um teto sempre haverá um limite que não poderá ser ultrapassado sem que se acrescente um caractere. Por isso os números de telefone estão cada vez maiores.
Enfim, ficam aí minhas sugestões e espero que sejam úteis! :)
Valeu Rafa, gostei das tuas sugestões, bem interessantes. Acho que vai ficar esclarecido pra quem mais passar por aqui.
De qualquer forma, a minha duvida quando cheguei ao artigo era gerar IDs como "92384", não importando que tenha um teto, desde que ele fosse grande. E também não seguir uma lógica de + 1, mas sim algo randômico. Por quê? Bem, seria para dar IDs a coisas de forma que não deixasse claro quantidade ou sequência. Conversando com o Nando, ficamos falando de duas sugestões:
1) Algo como esta: http://stackoverflow.com/a/1467620 – daí só gerando os números ao invés de string;
2) Ou então criar uma tabela e inserir as linhas para todos os IDs disponíveis, setando um campo como indisponível quando a ID for utilizada e pegando a linha aleatoriamente. É outra opção.
Espero ter contribuído também. ;) Valeu!
Vai em ajudar muito no meu trabalho de faculdade.
Ai carlos andré eu testei o seu código mas o resultado foi a colisão de ids, ou seja vários id iguais
Gostaria de saber se existe alguma função para criar 'ids únicos', ou seja um ID que não se repita com números e caracteres ASCII?
uso as funçoes
function GerarSeed(){
list($usec, $sec) = explode(' ', microtime());
return (float) $sec + ((float) $usec * 10000);
}
function Protocolo(){
date_default_timezone_set('America/Sao_Paulo');
$data = date('d').date('m').date('Y').'.';
mt_srand(GerarSeed());
$randval = mt_rand(111,999);
$randval2 = mt_rand(111,999);
$hr = date('s');
$cod = "$data"."$randval"."$randval2"."$hr";
return $cod;
}