Je recherche une fonction PHP qui crée un hash court à partir d'une chaîne de caractères ou d'un fichier, similaire à ces sites web de raccourcissement d'URL tels que tinyurl.com
Le hachage ne doit pas comporter plus de 8 caractères.
Je recherche une fonction PHP qui crée un hash court à partir d'une chaîne de caractères ou d'un fichier, similaire à ces sites web de raccourcissement d'URL tels que tinyurl.com
Le hachage ne doit pas comporter plus de 8 caractères.
TinyURL ne hash rien, il utilise des entiers en base 36 (ou même en base 62, en utilisant des lettres minuscules et majuscules) pour indiquer quel enregistrement visiter.
De la base 36 au nombre entier :
intval($str, 36);
Entier en base 36 :
base_convert($val, 10, 36);
Ainsi, au lieu de rediriger vers une route telle que /url/1234
il devient /url/ax
à la place. Cela vous donne beaucoup plus d'utilité qu'un hachage, car il n'y aura pas de collisions. Avec cela, vous pouvez facilement vérifier si une url existe et renvoyer l'ID approprié, existant, en base 36 sans que l'utilisateur sache qu'il était déjà dans la base de données.
Ne pas hacher, utiliser d'autres bases pour ce genre de choses. (C'est plus rapide et peut être rendu résistant aux collisions).
Bonjour @RobertK, comment serait le PHP pour convertir des chaînes de 6 chiffres qui incluent à la fois des chiffres et des lettres ?
@timpeterson, il suffit d'appeler intval et de passer dans la base donnée (voir mon premier bloc de code).
@RobertK, mais intval()
transforme tout en un nombre. Je suppose que je suis confus sur la façon dont intval()
se connecte avec les autres étapes nécessaires pour faire la redirection comme le rôle de la base de données.
J'ai écrit une petite librairie pour générer des hachages obfusqués à partir d'entiers.
http://web.archive.org/web/20130727034425/http://blog.kevburnsjr.com/php-unique-hash
$ids = range(1,10);
foreach($ids as $id) {
echo PseudoCrypt::unhash($id) . "\n";
}
m8z2p
8hy5e
uqx83
gzwas
38vdh
phug6
bqtiv
xzslk
k8ro9
6hqqy
7/14/2015 : Ajout du code réel ci-dessous, car il est devenu difficile à trouver :
<?php
/**
* PseudoCrypt by KevBurns (http://blog.kevburnsjr.com/php-unique-hash)
* Reference/source: http://stackoverflow.com/a/1464155/933782
*
* I want a short alphanumeric hash that’s unique and who’s sequence is difficult to deduce.
* I could run it out to md5 and trim the first n chars but that’s not going to be very unique.
* Storing a truncated checksum in a unique field means that the frequency of collisions will increase
* geometrically as the number of unique keys for a base 62 encoded integer approaches 62^n.
* I’d rather do it right than code myself a timebomb. So I came up with this.
*
* Sample Code:
*
* echo "<pre>";
* foreach(range(1, 10) as $n) {
* echo $n." - ";
* $hash = PseudoCrypt::hash($n, 6);
* echo $hash." - ";
* echo PseudoCrypt::unhash($hash)."<br/>";
* }
*
* Sample Results:
* 1 - cJinsP - 1
* 2 - EdRbko - 2
* 3 - qxAPdD - 3
* 4 - TGtDVc - 4
* 5 - 5ac1O1 - 5
* 6 - huKpGQ - 6
* 7 - KE3d8p - 7
* 8 - wXmR1E - 8
* 9 - YrVEtd - 9
* 10 - BBE2m2 - 10
*/
class PseudoCrypt {
/* Key: Next prime greater than 62 ^ n / 1.618033988749894848 */
/* Value: modular multiplicative inverse */
private static $golden_primes = array(
'1' => '1',
'41' => '59',
'2377' => '1677',
'147299' => '187507',
'9132313' => '5952585',
'566201239' => '643566407',
'35104476161' => '22071637057',
'2176477521929' => '294289236153',
'134941606358731' => '88879354792675',
'8366379594239857' => '7275288500431249',
'518715534842869223' => '280042546585394647'
);
/* Ascii : 0 9, A Z, a z */
/* $chars = array_merge(range(48,57), range(65,90), range(97,122)) */
private static $chars62 = array(
0=>48,1=>49,2=>50,3=>51,4=>52,5=>53,6=>54,7=>55,8=>56,9=>57,10=>65,
11=>66,12=>67,13=>68,14=>69,15=>70,16=>71,17=>72,18=>73,19=>74,20=>75,
21=>76,22=>77,23=>78,24=>79,25=>80,26=>81,27=>82,28=>83,29=>84,30=>85,
31=>86,32=>87,33=>88,34=>89,35=>90,36=>97,37=>98,38=>99,39=>100,40=>101,
41=>102,42=>103,43=>104,44=>105,45=>106,46=>107,47=>108,48=>109,49=>110,
50=>111,51=>112,52=>113,53=>114,54=>115,55=>116,56=>117,57=>118,58=>119,
59=>120,60=>121,61=>122
);
public static function base62($int) {
$key = "";
while(bccomp($int, 0) > 0) {
$mod = bcmod($int, 62);
$key .= chr(self::$chars62[$mod]);
$int = bcdiv($int, 62);
}
return strrev($key);
}
public static function hash($num, $len = 5) {
$ceil = bcpow(62, $len);
$primes = array_keys(self::$golden_primes);
$prime = $primes[$len];
$dec = bcmod(bcmul($num, $prime), $ceil);
$hash = self::base62($dec);
return str_pad($hash, $len, "0", STR_PAD_LEFT);
}
public static function unbase62($key) {
$int = 0;
foreach(str_split(strrev($key)) as $i => $char) {
$dec = array_search(ord($char), self::$chars62);
$int = bcadd(bcmul($dec, bcpow(62, $i)), $int);
}
return $int;
}
public static function unhash($hash) {
$len = strlen($hash);
$ceil = bcpow(62, $len);
$mmiprimes = array_values(self::$golden_primes);
$mmi = $mmiprimes[$len];
$num = self::unbase62($hash);
$dec = bcmod(bcmul($num, $mmi), $ceil);
return $dec;
}
}
Je sais que je commente un article plus ancien. J'ai pensé que je mentionnerais que le code de KevBurnsJr fonctionne bien. Cependant, je suis récemment passé d'un serveur Windows 2003 32 bits à un serveur Windows 2008 R2 x64 et je constate que je duplique les hashs uniques. Je dois maintenant trouver une autre méthode pour créer des codes de confirmation.
L'article a été mis à jour pour utiliser bcmath avec l'aide de certains commentateurs, il devrait donc être solide maintenant. Quelqu'un a également trouvé un moyen de le rendre réversible, ce qui est totalement génial.
Les services de raccourcissement d'URL utilisent plutôt une valeur entière auto incrémentée (comme un ID de base de données supplémentaire) et l'encodent avec Base64 ou d'autres codages pour avoir plus d'informations par caractère (64 au lieu de seulement 10 comme les chiffres).
@ravisoni Si vous utilisez les chiffres décimaux 0
- 9
pour représenter un nombre, vous avez 10 valeurs possibles par caractère encodé (ld(10) 3,32 bits/caractère). Cependant, si vous représentez le même nombre avec des caractères Base64, vous disposez de 64 valeurs possibles par caractère codé (ld(64) = 6 bits/caractère). Avec Base64, il y a donc plus d'informations stockées dans chaque caractère codé, c'est-à-dire 6 bits d'informations au lieu de 3,32 bits.
Si vous utilisez la base64, rien n'empêche un script de dire for($i=0;$i<999999;$i++) { $pageContent=fread(fopen(' votreurl.com/'.base64_encode($i) ) ; } et maintenant j'ai accès à chaque URL de votre base de données.
Le hachage le plus court a une longueur de 32 caractères, mais vous pouvez utiliser les 8 premiers caractères du hachage md5.
echo substr(md5('http://www.google.com'), 0, 8);
Mise à jour : voici une autre classe trouvée ici écrit par Travell Perkins qui prend le numéro d'enregistrement et crée un hash court pour celui-ci. Un numéro de 14 chiffres produit une chaîne de 8 chiffres. À la date où vous atteignez ce nombre, vous devenez plus populaire que tinyurl ;)
class BaseIntEncoder {
//const $codeset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
//readable character set excluded (0,O,1,l)
const codeset = "23456789abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ";
static function encode($n){
$base = strlen(self::codeset);
$converted = '';
while ($n > 0) {
$converted = substr(self::codeset, bcmod($n,$base), 1) . $converted;
$n = self::bcFloor(bcdiv($n, $base));
}
return $converted ;
}
static function decode($code){
$base = strlen(self::codeset);
$c = '0';
for ($i = strlen($code); $i; $i--) {
$c = bcadd($c,bcmul(strpos(self::codeset, substr($code, (-1 * ( $i - strlen($code) )),1))
,bcpow($base,$i-1)));
}
return bcmul($c, 1, 0);
}
static private function bcFloor($x)
{
return bcmul($x, '1', 0);
}
static private function bcCeil($x)
{
$floor = bcFloor($x);
return bcadd($floor, ceil(bcsub($x, $floor)));
}
static private function bcRound($x)
{
$floor = bcFloor($x);
return bcadd($floor, round(bcsub($x, $floor)));
}
}
Voici un exemple de son utilisation :
BaseIntEncoder::encode('1122344523');//result:3IcjVE
BaseIntEncoder::decode('3IcjVE');//result:1122344523
En utilisant les 8 premiers caractères de md5, il y a probablement une chance raisonnable que deux URLs aient le même hash.
Oui, une telle collision peut se produire, mais la probabilité est très faible pour une chaîne aléatoire, elle est d'environ un à quatre milliards, mais si vous voulez avoir un hachage 100% unique que vous pouvez utiliser comme référence à un enregistrement de base de données, utilisez la classe incluse.
Meilleure réponse encore : La plus petite chaîne unique de type "Hash Like" est donnée comme identifiant unique de base de données - 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 $database_id maps to $base36value\n";
$database_id = 1;
$base36value = dec2string($database_id, 36);
echo "A database_id value of $database_id maps to $base36value\n";
$database_id = 1987645;
$base36value = dec2string($database_id, 36);
echo "A database_id value of $database_id 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;
}
?>
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
2 votes
Je sais que c'est une vieille question mais regardez : hashids.org . Fonctionne avec la plupart des langages de programmation
0 votes
Vérifiez ShortCode bibliothèque. Elle fait exactement ce que vous voulez. Basé sur la conversion de base.
1 votes
À part l'utilisation de l'Adler-32 ou du CRC32, vous ne pouvez pas raccourcir les hachages modernes (résistants aux collisions). que beaucoup (c'est-à-dire jusqu'à 8 caractères). Pas avec SHA-2, pas avec SHA-1, et même pas avec MD5. Avec
Alphabet::convert($hash, Alphabet::HEX, Alphabet::ALPHANUMERIC)
vous pouvez réduire le MD5 à 22 (au lieu de 32) caractères. Ce que vous voulez, c'est plutôt coder les ID entiers des fichiers (par exemple, à partir de votre base de données) avec(new Id())->encode($id)
.