Il modo migliore di PHP per l'arrays multidimensionale MD5?

Qual è il modo migliore per generare un MD5 (o qualsiasi altro hash) di un arrays multidimensionale?

Potrei facilmente scrivere un ciclo che attraverserebbe each livello dell'arrays, concatenando each valore in una string e semplicemente eseguendo l'MD5 sulla string.

Tuttavia, ciò sembra alquanto scomodo e mi chiedevo se esistesse una function funky che avrebbe richiesto un arrays multidimensionale e lo avrebbe cancellato.

(Funzione copia-n-incolla in basso)

Come menzionato prima, functionrà il seguente.

md5(serialize($arrays)); 

Tuttavia, vale la pena notare che (ironicamente) json_encode si comport in modo notevolmente più veloce:

 md5(json_encode($arrays)); 

In effetti, l'aumento di velocità è duplice in quanto (1) json_encode da solo esegue più velocemente della serializzazione, e (2) json_encode produce una string più piccola e quindi less da gestire per md5.

Modifica: ecco le prove per supportre questa affermazione:

 <?php //this is the arrays I'm using -- it's multidimensional. $arrays = 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($arrays)); } 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($arrays)); } 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 è costantemente oltre il 250% (2,5 volte) più veloce (spesso oltre il 300%): questa non è una differenza banale. Puoi vedere i risultati del test con questo script live qui:

Ora, una cosa da notare è che l'arrays (1,2,3) produrrà un MD5 diverso come arrays (3,2,1). Se questo NON è quello che vuoi. Prova il seguente codice:

 //Optionally make a copy of the arrays (if you want to preserve the original order) $original = $arrays; arrays_multisort($arrays); $hash = md5(json_encode($arrays)); 

Modifica: C'è stato qualche dubbio sul fatto che invertire l'ordine avrebbe prodotto gli stessi risultati. Quindi, l'ho fatto ( correttamente ) qui:

Come puoi vedere, i risultati sono esattamente gli stessi. Ecco il test ( corretto ) originariamente creato da qualcuno correlato a Drupal :

E per buona misura, ecco una function / metodo che puoi copiare e incollare (testato in 5.3.3-1ubuntu9.5):

 function arrays_md5(Array $arrays) { //since we're inside a function (which uses a copied arrays, not //a referenced arrays), you shouldn't need to copy the arrays arrays_multisort($arrays); return md5(json_encode($arrays)); } 
 md5(serialize($arrays)); 

Mi unisco a una festa molto affollata rispondendo, ma c'è un'importnte considerazione che nessuna delle risposte esistenti risponde. Il valore di json_encode() e serialize() dipendono entrambi dall'ordine degli elementi nella matrix!

Ecco i risultati di non ordinare e ordinare gli arrays, su due arrays con valori identici ma aggiunti in un ordine diverso (codice in fondo al post) :

  serialize() 1c4f1064ab79e4722f41ab5a8141b210 1ad0f2c7e690c8e3cd5c34f7c9b8573a json_encode() db7178ba34f9271bfca3a05c5dddf502 c9661c0852c2bd0e26ef7951b4ca9e6f Sorted serialize() 1c4f1064ab79e4722f41ab5a8141b210 1c4f1064ab79e4722f41ab5a8141b210 Sorted json_encode() db7178ba34f9271bfca3a05c5dddf502 db7178ba34f9271bfca3a05c5dddf502 

Di conseguenza, i due methods che consiglierei per l' hash di un arrays sarebbero:

 // You will need to write your own deep_ksort(), or see // my example below md5( serialize(deep_ksort($arrays)) ); md5( json_encode(deep_ksort($arrays)) ); 

La scelta di json_encode() o serialize() dovrebbe essere determinata testando il tipo di dati che stai utilizzando . Tramite il mio test su dati puramente testuali e numbersci, se il codice non esegue un ciclo chiuso migliaia di volte, allora la differenza non vale nemless il benchmarking. Personalmente uso json_encode() per quel tipo di dati.

Ecco il codice utilizzato per generare il test di sorting sopra:

 $a = arrays(); $a['aa'] = arrays( 'aaa'=>'AAA', 'bbb'=>'ooo', 'qqq'=>'fff',); $a['bb'] = arrays( 'aaa'=>'BBBB', 'iii'=>'dd',); $b = arrays(); $b['aa'] = arrays( 'aaa'=>'AAA', 'qqq'=>'fff', 'bbb'=>'ooo',); $b['bb'] = arrays( '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"; 

La mia implementazione rapida deep_ksort (), si adatta a questo caso ma controlla prima di utilizzare i tuoi progetti:

 /* * Sort an arrays by keys, and additionall sort its arrays values by keys * * Does not try to sort an object, but does iterate its properties to * sort arrayss in properties */ function deep_ksort($input) { if ( !is_object($input) && !is_arrays($input) ) { return $input; } foreach ( $input as $k=>$v ) { if ( is_object($v) || is_arrays($v) ) { $input[$k] = deep_ksort($v); } } if ( is_arrays($input) ) { ksort($input); } // Do not sort objects return $input; } 

La risposta dipende molto dai tipi di dati dei valori dell'arrays. Per le stringhe grandi usare:

 md5(serialize($arrays)); 

Per stringhe brevi e numbers interi utilizzare:

 md5(json_encode($arrays)); 

4 funzioni PHP integrate possono trasformare arrays in string: serialize () , json_encode () , var_export () , print_r () .

Nota: la function json_encode () rallenta durante l'elaborazione di arrays associativi con stringhe come valori. In questo caso, prendere in considerazione l'uso della function serialize () .

Risultati dei test per arrays multidimensionale con md5-hashes (32 caratteri) in chiavi e valori:

 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% 

Risultato del test per l'arrays multidimensionale numbersco:

 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% 

Sorgente di prova dell'arrays associativo. Sorgente test di arrays numbersco.

A parte l'eccellente risposta di Brock (+1), qualsiasi libreria di hashing decente ti permette di aggiornare l'hash in incrementi, quindi dovresti essere in grado di aggiornare con each string in sequenza, invece di wherer build una string gigante.

Vedi: hash_update

 md5(serialize($arrays)); 

Funzionerà, ma l'hash cambierà in base all'ordine dell'arrays (che potrebbe non avere importnza).

Si noti che serialize e json_encode comportno diversamente quando si tratta di arrays numbersci in cui le chiavi non iniziano a 0 o arrays associativi. json_encode memorizzerà tali matrici come un Object , quindi json_decode restituisce un Object , where unserialize restituirà un arrays con le stesse chiavi esatte.

Penso che questo potrebbe essere un buon consiglio:

 Class hasharrays { public function arrays_flat($in,$keys=arrays(),$out=arrays()){ foreach($in as $k => $v){ $keys[] = $k; if(is_arrays($v)){ $out = $this->arrays_flat($v,$keys,$out); }else{ $out[implode("/",$keys)] = $v; } arrays_pop($keys); } return $out; } public function arrays_hash($in){ $a = $this->arrays_flat($in); ksort($a); return md5(json_encode($a)); } } $h = new hasharrays; echo $h->arrays_hash($multi_dimensional_arrays); 

Nota importnte su serialize()

Non è consigliabile utilizzarlo come parte della function di hashing perché può restituire risultati diversi per i seguenti esempi. Controlla l'esempio qui sotto:

Semplice esempio:

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

produce

 "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";}}" 

Ma il seguente codice:

 <?php $a = new \stdClass; $a->test = 'sample'; $b = new \stdClass; $b->one = $a; $b->two = $a; 

Produzione:

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

Quindi, invece del secondo object php, basta creare il link "r: 2;" alla prima istanza. È sicuramente un modo valido e corretto per serializzare i dati, ma può portre ai problemi con la tua function di hashing.

ci sono diverse risposte che dicono di usare json_code,

ma json_encode non funziona bene con la string iso-8859-1, non appena c'è un carattere speciale, la string viene ritagliata.

consiglierei di usare var_export:

 md5(var_export($arrays, true)) 

non così lento come serializzare, non come buggato come json_encode

Attualmente la risposta più votata md5(serialize($arrays)); non funziona bene con gli oggetti.

Considera il codice:

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

Anche se gli arrays sono diversi (contengono oggetti diversi), hanno lo stesso hash quando si utilizza md5(serialize($arrays)); . Quindi il tuo hash è inutile!

Per evitare questo problema, è ansible sostituire gli oggetti con il risultato di spl_object_hash() prima della serializzazione. Dovresti anche farlo in modo ricorsivo se l'arrays ha più livelli.

Il codice seguente ordina anche gli arrays con le chiavi, come suggerito da punti.

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

Ora puoi usare md5(serialize(replaceObjectsWithHashes($arrays))) .

(Si noti che l'arrays in PHP è un tipo di valore. Quindi, replaceObjectsWithHashes function replaceObjectsWithHashes NON modificare l'arrays originale.)