web-dev-qa-db-fra.com

PHP meilleur moyen de MD5 tableau multidimensionnel?

Quel est le meilleur moyen de générer un MD5 (ou tout autre hachage) d'un tableau multidimensionnel? 

Je pourrais facilement écrire une boucle qui traverserait chaque niveau du tableau, en concaténant chaque valeur dans une chaîne et en effectuant simplement le MD5 sur la chaîne. 

Cependant, cela semble au mieux encombrant et je me suis demandé s'il existait une fonction géniale qui prendrait un tableau multidimensionnel et le hachait.

105
Peter John

(Fonction copier-coller en bas)}

Comme mentionné précédemment, ce qui suit fonctionnera.

md5(serialize($array));

Cependant, il est intéressant de noter que (ironiquement) json_encode exécute visiblement plus rapidement:

md5(json_encode($array));

En fait, l’augmentation de la vitesse est double ici (1) json_encode seul est plus rapide que la sérialisation, et (2) json_encode produit une chaîne plus petite et donc moins facile à gérer pour md5.

Edit: Voici des preuves à l'appui de cette affirmation:

<?php //this is the array I'm using -- it's multidimensional.
$array = unserialize('a:6:{i:0;a:0:{}i:1;a:3:{i:0;a:0:{}i:1;a:0:{}i:2;a:3:{i:0;a:0:{}i:1;a:0:{}i:2;a:0:{}}}i:2;s:5:"hello";i:3;a:2:{i:0;a:0:{}i:1;a:0:{}}i:4;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:0:{}}}}}}}i:5;a:5:{i:0;a:0:{}i:1;a:4:{i:0;a:0:{}i:1;a:0:{}i:2;a:3:{i:0;a:0:{}i:1;a:0:{}i:2;a:0:{}}i:3;a:6:{i:0;a:0:{}i:1;a:3:{i:0;a:0:{}i:1;a:0:{}i:2;a:3:{i:0;a:0:{}i:1;a:0:{}i:2;a:0:{}}}i:2;s:5:"hello";i:3;a:2:{i:0;a:0:{}i:1;a:0:{}}i:4;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:0:{}}}}}}}i:5;a:5:{i:0;a:0:{}i:1;a:3:{i:0;a:0:{}i:1;a:0:{}i:2;a:3:{i:0;a:0:{}i:1;a:0:{}i:2;a:0:{}}}i:2;s:5:"hello";i:3;a:2:{i:0;a:0:{}i:1;a:0:{}}i:4;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:0:{}}}}}}}}}}i:2;s:5:"hello";i:3;a:2:{i:0;a:0:{}i:1;a:0:{}}i:4;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:0:{}}}}}}}}}');

//The serialize test
$b4_s = microtime(1);
for ($i=0;$i<10000;$i++) {
    $serial = md5(serialize($array));
}
echo 'serialize() w/ md5() took: '.($sTime = microtime(1)-$b4_s).' sec<br/>';

//The json test
$b4_j = microtime(1);
for ($i=0;$i<10000;$i++) {
    $serial = md5(json_encode($array));
}
echo 'json_encode() w/ md5() took: '.($jTime = microtime(1)-$b4_j).' sec<br/><br/>';
echo 'json_encode is <strong>'.( round(($sTime/$jTime)*100,1) ).'%</strong> faster with a difference of <strong>'.($sTime-$jTime).' seconds</strong>';

JSON_ENCODE dépasse régulièrement 250% (2,5 fois) plus vite (souvent plus de 300%) - ce n'est pas une différence triviale. Vous pouvez voir les résultats du test avec ce script en direct ici:

Maintenant, une chose à noter est tableau (1,2,3) produira un MD5 différent en tant que tableau (3,2,1). Si ce n'est pas ce que vous voulez. Essayez le code suivant:

//Optionally make a copy of the array (if you want to preserve the original order)
$original = $array;

array_multisort($array);
$hash = md5(json_encode($array));

Edit: On s'est demandé si une inversion de l'ordre produirait les mêmes résultats. Donc, j'ai fait ça (correctement _) ici:

Comme vous pouvez le constater, les résultats sont exactement les mêmes. Voici le (corrigé} _) test créé à l'origine par une personne liée à Drupal :

Et pour faire bonne mesure, voici une fonction/méthode que vous pouvez copier et coller (testée en 5.3.3-1ubuntu9.5):

function array_md5(Array $array) {
    //since we're inside a function (which uses a copied array, not 
    //a referenced array), you shouldn't need to copy the array
    array_multisort($array);
    return md5(json_encode($array));
}
229
Nathan J. Brauer
md5(serialize($array));
167
Brock Batsell

Je rejoins une partie très encombrée en répondant, mais il y a une considération importante qu'aucune des réponses existantes ne répond à. Les valeurs de json_encode() et serialize() dépendent toutes deux de l'ordre des éléments dans le tableau!

Voici les résultats de ne pas trier et trier les tableaux, sur deux tableaux avec des valeurs identiques mais ajoutés dans un ordre différent (code en bas de post):

    serialize()
1c4f1064ab79e4722f41ab5a8141b210
1ad0f2c7e690c8e3cd5c34f7c9b8573a

    json_encode()
db7178ba34f9271bfca3a05c5dddf502
c9661c0852c2bd0e26ef7951b4ca9e6f

    Sorted serialize()
1c4f1064ab79e4722f41ab5a8141b210
1c4f1064ab79e4722f41ab5a8141b210

    Sorted json_encode()
db7178ba34f9271bfca3a05c5dddf502
db7178ba34f9271bfca3a05c5dddf502

Par conséquent, les deux méthodes que je recommanderais pour hacher un tableau seraient:

// You will need to write your own deep_ksort(), or see
// my example below

md5(   serialize(deep_ksort($array)) );

md5( json_encode(deep_ksort($array)) );

Le choix de json_encode() ou serialize() doit être déterminé par en testant le type de données que vous utilisez . D'après mes propres tests sur des données purement textuelles et numériques, si le code n'exécute pas une boucle serrée des milliers de fois, la différence ne vaut même pas la peine d'être comparée. J'utilise personnellement json_encode() pour ce type de données.

Voici le code utilisé pour générer le test de tri ci-dessus:

$a = array();
$a['aa'] = array( 'aaa'=>'AAA', 'bbb'=>'ooo', 'qqq'=>'fff',);
$a['bb'] = array( 'aaa'=>'BBBB', 'iii'=>'dd',);

$b = array();
$b['aa'] = array( 'aaa'=>'AAA', 'qqq'=>'fff', 'bbb'=>'ooo',);
$b['bb'] = array( 'iii'=>'dd', 'aaa'=>'BBBB',);

echo "    serialize()\n";
echo md5(serialize($a))."\n";
echo md5(serialize($b))."\n";

echo "\n    json_encode()\n";
echo md5(json_encode($a))."\n";
echo md5(json_encode($b))."\n";



$a = deep_ksort($a);
$b = deep_ksort($b);

echo "\n    Sorted serialize()\n";
echo md5(serialize($a))."\n";
echo md5(serialize($b))."\n";

echo "\n    Sorted json_encode()\n";
echo md5(json_encode($a))."\n";
echo md5(json_encode($b))."\n";

Ma mise en œuvre rapide deep_ksort (), convient à cette situation, mais vérifiez-la avant de l'utiliser sur vos propres projets:

/*
* Sort an array by keys, and additionall sort its array values by keys
*
* Does not try to sort an object, but does iterate its properties to
* sort arrays in properties
*/
function deep_ksort($input)
{
    if ( !is_object($input) && !is_array($input) ) {
        return $input;
    }

    foreach ( $input as $k=>$v ) {
        if ( is_object($v) || is_array($v) ) {
            $input[$k] = deep_ksort($v);
        }
    }

    if ( is_array($input) ) {
        ksort($input);
    }

    // Do not sort objects

    return $input;
}
21
dotancohen

La réponse dépend fortement des types de données des valeurs de tableau . Pour les grandes chaînes, utilisez:

md5(serialize($array));

Pour les chaînes courtes et les entiers, utilisez: 

md5(json_encode($array));

4 fonctions PHP intégrées peuvent transformer un tableau en chaîne: serialize () , json_encode () , var_export () , print_r () .

Remarque:json_encode () function ralentit lors du traitement des tableaux associatifs avec des chaînes comme valeurs. Dans ce cas, envisagez d'utiliser serialize () function.

Résultats de test pour un tableau multidimensionnel avec hachage md5 (32 caractères) dans les clés et les valeurs:

Test name       Repeats         Result          Performance     
serialize       10000           0.761195 sec    +0.00%
print_r         10000           1.669689 sec    -119.35%
json_encode     10000           1.712214 sec    -124.94%
var_export      10000           1.735023 sec    -127.93%

Résultat du test pour un tableau multidimensionnel numérique:

Test name       Repeats         Result          Performance     
json_encode     10000           1.040612 sec    +0.00%
var_export      10000           1.753170 sec    -68.47%
serialize       10000           1.947791 sec    -87.18%
print_r         10000           9.084989 sec    -773.04%

Tableau associatif source de test . Tableau numérique source de test .

8
Alexander Yancharuk

Mis à part l'excellente réponse de Brock (+1), toute bibliothèque de hachage décente vous permet de mettre à jour le hachage par incréments. Vous devriez donc pouvoir mettre à jour chaque chaîne séquentiellement, sans avoir à créer une chaîne géante.

Voir: hash_update

7
Chris Jester-Young

Notez que serialize et json_encode agissent différemment en ce qui concerne les tableaux numériques où les clés ne commencent pas à 0, ou les tableaux associatifs .json_encode stockera ces tableaux sous la forme Object, donc json_decode renvoie un Object, où unserialize retournera un tableau avec exactement les mêmes clés.

3
Willem-Jan

Je pense que cela pourrait être un bon conseil:

Class hasharray {

    public function array_flat($in,$keys=array(),$out=array()){
        foreach($in as $k => $v){
            $keys[] = $k; 
            if(is_array($v)){
                $out = $this->array_flat($v,$keys,$out);
            }else{
                $out[implode("/",$keys)] = $v;
            }
            array_pop($keys);
        }
        return $out;  
    }

    public function array_hash($in){
        $a = $this->array_flat($in);
        ksort($a);
        return md5(json_encode($a));
    }

}

$h = new hasharray;
echo $h->array_hash($multi_dimensional_array);
3
Andrej Pandovich
md5(serialize($array));

Cela fonctionnera, mais le hachage changera en fonction de l'ordre du tableau (cela n'a pas d'importance cependant).

3
Max Wheeler

Note importante à propos de serialize()

Je ne recommande pas de l'utiliser dans le cadre de la fonction de hachage, car il peut renvoyer un résultat différent pour les exemples suivants. Vérifiez l'exemple ci-dessous:

Exemple simple:

$a = new \stdClass;
$a->test = 'sample';

$b = new \stdClass;
$b->one = $a;
$b->two = clone $a;

Produit

"O:8:"stdClass":2:{s:3:"one";O:8:"stdClass":1:{s:4:"test";s:6:"sample";}s:3:"two";O:8:"stdClass":1:{s:4:"test";s:6:"sample";}}"

Mais le code suivant:

<?php

$a = new \stdClass;
$a->test = 'sample';

$b = new \stdClass;
$b->one = $a;
$b->two = $a;

Sortie:

"O:8:"stdClass":2:{s:3:"one";O:8:"stdClass":1:{s:4:"test";s:6:"sample";}s:3:"two";r:2;}"

Donc, au lieu du second objet php, créez simplement le lien "r: 2;" à la première instance. C'est certainement un bon moyen de sérialiser les données, mais cela peut entraîner des problèmes avec votre fonction de hachage. 

2
TermiT
// Convert nested arrays to a simple array
$array = array();
array_walk_recursive($input, function ($a) use (&$array) {
    $array[] = $a;
});

sort($array);

$hash = md5(json_encode($array));

----

These arrays have the same hash:
$arr1 = array(0 => array(1, 2, 3), 1, 2);
$arr2 = array(0 => array(1, 3, 2), 1, 2);
2
ymakux

Actuellement, la réponse la plus votée md5(serialize($array)); ne fonctionne pas bien avec les objets.

Considérons le code:

 $a = array(new \stdClass());
 $b = array(new \stdClass());

Même si les tableaux sont différents (ils contiennent des objets différents), ils utilisent le même hash lorsqu'ils utilisent md5(serialize($array));. Donc, votre hash est inutile!

Pour éviter ce problème, vous pouvez remplacer les objets par le résultat de spl_object_hash() avant la sérialisation. Vous devriez également le faire de manière récursive si votre tableau a plusieurs niveaux. 

Le code ci-dessous trie également les tableaux par clé, comme suggéré par dotancohen.

function replaceObjectsWithHashes(array $array)
{
    foreach ($array as &$value) {
        if (is_array($value)) {
            $value = $this->replaceObjectsInArrayWithHashes($value);
        } elseif (is_object($value)) {
            $value = spl_object_hash($value);
        }
    }
    ksort($array);
    return $array;
}

Vous pouvez maintenant utiliser md5(serialize(replaceObjectsWithHashes($array))).

(Notez que le tableau dans PHP est un type valeur. Par conséquent, la fonction replaceObjectsWithHashes NE modifie PAS le tableau d'origine.)

0
Damian Polac

Je n'ai pas vu la solution si facilement ci-dessus, alors je voulais apporter une réponse plus simple. Pour moi, je recevais la même clé jusqu'à ce que j'utilise ksort (type de clé):

Trié d'abord avec Ksort, puis exécuté sha1 sur un json_encode:

ksort($array)
$hash = sha1(json_encode($array) //be mindful of UTF8

exemple:

$arr1 = array( 'dealer' => '100', 'direction' => 'ASC', 'dist' => '500', 'limit' => '1', 'Zip' => '10601');
ksort($arr1);

$arr2 = array( 'direction' => 'ASC', 'limit' => '1', 'Zip' => '10601', 'dealer' => '100', 'dist' => '5000');
ksort($arr2);

var_dump(sha1(json_encode($arr1)));
var_dump(sha1(json_encode($arr2)));

Sortie de tableaux et de hachages modifiés:

string(40) "502c2cbfbe62e47eb0fe96306ecb2e6c7e6d014c"
string(40) "b3319c58edadab3513832ceeb5d68bfce2fb3983"
0
Mike Q

il y a plusieurs réponses disant d'utiliser json_code,

mais json_encode ne fonctionne pas bien avec la chaîne iso-8859-1. Dès qu’un caractère spécial est créé, la chaîne est tronquée.

je conseillerais d'utiliser var_export:

md5(var_export($array, true))

pas aussi lent que sérialiser, pas aussi bogué que json_encode

0
Bruno