it-swarm.com.de

PHP bester Weg zu mehrdimensionalen MD5-Arrays?

Was ist der beste Weg, um ein MD5 (oder einen anderen Hash) eines mehrdimensionalen Arrays zu generieren? 

Ich könnte leicht eine Schleife schreiben, die jede Ebene des Arrays durchquert, jeden Wert zu einem String verkettet und einfach den MD5 für den String ausführt. 

Dies scheint jedoch bestenfalls umständlich zu sein, und ich fragte mich, ob es eine funky Funktion gab, die ein mehrdimensionales Array brauchte und es hash.

105
Peter John

(Copy-n-Paste-fähige Funktion unten)

Wie bereits erwähnt, wird Folgendes funktionieren.

md5(serialize($array));

Es ist jedoch erwähnenswert, dass (ironischerweise) json_encode merklich schneller ist:

md5(json_encode($array));

Tatsächlich ist die Geschwindigkeitssteigerung hier doppelt so hoch, da (1) json_encode alleine schneller als serialisiert ist und (2) json_encode eine kleinere Zeichenfolge erzeugt und daher weniger für md5 verarbeitet werden muss.

Edit: Hier sind Beweise für diese Behauptung:

<?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 ist durchgängig über 250% (2,5x) schneller (häufig über 300%) - dies ist kein unbedeutender Unterschied. Sie können die Ergebnisse des Tests mit diesem Live-Skript hier sehen:

Zu beachten ist, dass array (1,2,3) ein anderes MD5 als array (3,2,1) erzeugt. Wenn ist das NICHT, was Sie wollen. Versuchen Sie den folgenden Code:

//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: Es wurde die Frage gestellt, ob das Umkehren der Reihenfolge zu den gleichen Ergebnissen führt. Also habe ich das (richtig) hier gemacht:

Wie Sie sehen, sind die Ergebnisse genau die gleichen. Hier ist der (korrigierte) Test ursprünglich von jemandem erstellt, der mit Drupal verwandt ist :

Und für ein gutes Maß ist hier eine Funktion/Methode, die Sie kopieren und einfügen können (getestet in 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
md5(serialize($array));
167
Brock Batsell

Ich schließe mich einer überfüllten Gruppe an, indem ich antworte, aber es gibt eine wichtige Überlegung, die in keiner der vorhandenen Antworten angesprochen wird. Der Wert von json_encode() und serialize() hängt beide von der Reihenfolge der Elemente im Array ab !

Hier sind die Ergebnisse, wenn Sie die Arrays nicht sortieren und sortieren, auf zwei Arrays mit identischen Werten, die jedoch in einer anderen Reihenfolge hinzugefügt wurden (Code am Ende des Beitrags) :

    serialize()
1c4f1064ab79e4722f41ab5a8141b210
1ad0f2c7e690c8e3cd5c34f7c9b8573a

    json_encode()
db7178ba34f9271bfca3a05c5dddf502
c9661c0852c2bd0e26ef7951b4ca9e6f

    Sorted serialize()
1c4f1064ab79e4722f41ab5a8141b210
1c4f1064ab79e4722f41ab5a8141b210

    Sorted json_encode()
db7178ba34f9271bfca3a05c5dddf502
db7178ba34f9271bfca3a05c5dddf502

Daher wären die beiden Methoden, die ich empfehlen würde, ein Array zu hashen:

// 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)) );

Die Wahl von json_encode() oder serialize() sollte durch Testen des Datentyps bestimmt werden, den Sie verwenden. Nach meinen eigenen Tests mit rein textuellen und numerischen Daten lohnt es sich nicht einmal, den Unterschied zu vergleichen, wenn der Code nicht tausendmal eine enge Schleife durchläuft. Ich persönlich benutze json_encode() für diese Art von Daten.

Hier ist der Code, der zum Generieren des obigen Sortiertests verwendet wird:

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

Meine schnelle deep_ksort () -Implementierung passt in diesen Fall, aber prüfen Sie sie, bevor Sie sie für Ihre eigenen Projekte verwenden:

/*
* 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

Die Antwort hängt stark von den Datentypen der Array-Werte ab. Für große Zeichenfolgen verwenden Sie Folgendes:

md5(serialize($array));

Für kurze Zeichenfolgen und Ganzzahlen verwenden Sie: 

md5(json_encode($array));

4 integrierte PHP - Funktionen können ein Array in einen String umwandeln: serialize () , json_encode () , var_export () , print_r () .

Notice: Die Funktionjson_encode () verlangsamt sich bei der Verarbeitung assoziativer Arrays mit Strings als Werten. Verwenden Sie in diesem Fall die Funktion serialize () .

Testergebnisse für ein mehrdimensionales Array mit md5-Hash (32 Zeichen) in Schlüsseln und Werten:

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%

Testergebnis für numerische mehrdimensionale Felder:

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%

Assoziatives Array Testquelle . Numerisches Array Testquelle .

8

Abgesehen von Brocks hervorragender Antwort (+1) können Sie mit jeder anständigen Hash-Bibliothek den Hash in Schritten aktualisieren. Sie sollten also in der Lage sein, mit jedem String nacheinander zu aktualisieren, anstatt einen riesigen String aufzubauen.

Siehe: hash_update

7

Beachten Sie, dass serialize und json_encode sich unterschiedlich verhalten, wenn es um numerische Arrays geht, deren Schlüssel nicht bei 0 beginnen, oder um assoziative Arrays. json_encode speichert solche Arrays als Object, daher gibt json_decode ein Object zurück, wobei unserialize ein Array mit genau den gleichen Schlüsseln zurückgibt.

3
Willem-Jan

Ich denke, das könnte ein guter Tipp sein:

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
md5(serialize($array));

Funktioniert, aber der Hash ändert sich abhängig von der Reihenfolge des Arrays (das spielt jedoch keine Rolle).

3
Max Wheeler

Wichtiger Hinweis zu serialize()

Ich empfehle nicht, es als Teil der Hash-Funktion zu verwenden, da dies für die folgenden Beispiele zu unterschiedlichen Ergebnissen führen kann. Überprüfen Sie das Beispiel unten:

Einfaches Beispiel:

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

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

Produziert

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

Aber der folgende Code:

<?php

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

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

Ausgabe:

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

Anstelle des zweiten Objekts erstellen Sie einfach den Link "r: 2;". zur ersten Instanz. Es ist definitiv eine gute und korrekte Art, Daten zu serialisieren, kann jedoch zu Problemen mit Ihrer Hash-Funktion führen. 

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

Derzeit funktioniert die am meisten gewählte Antwort md5(serialize($array)); nicht gut mit Objekten.

Code berücksichtigen:

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

Obwohl Arrays unterschiedlich sind (sie enthalten unterschiedliche Objekte), haben sie bei Verwendung von md5(serialize($array)); den gleichen Hashwert. Ihr Hash ist also nutzlos!

Um dieses Problem zu vermeiden, können Sie vor dem Serialisieren Objekte mit dem Ergebnis von spl_object_hash() ersetzen. Sie sollten dies auch rekursiv durchführen, wenn Ihr Array über mehrere Ebenen verfügt. 

Der folgende Code sortiert Arrays auch nach Schlüsseln, wie dotancohen vorgeschlagen hat.

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

Jetzt können Sie md5(serialize(replaceObjectsWithHashes($array))) verwenden.

(Beachten Sie, dass das Array in PHP vom Werttyp ist. Die replaceObjectsWithHashes-Funktion ändert also NICHT das ursprüngliche Array.)

0
Damian Polac

Ich habe die Lösung oben nicht so leicht gesehen, also wollte ich eine einfachere Antwort einbringen. Für mich bekam ich den gleichen Schlüssel, bis ich ksort (Schlüsselsortierung) verwendete:

Zuerst mit Ksort sortiert, dann sha1 mit einem json_encode durchgeführt:

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

beispiel:

$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)));

Ausgabe geänderter Arrays und Hashes:

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

es gibt mehrere Antworten, die auf die Verwendung von json_code hinweisen.

json_encode funktioniert jedoch nicht mit iso-8859-1-Zeichenfolge, sobald ein spezielles Zeichen vorhanden ist, wird die Zeichenfolge abgeschnitten.

ich würde raten, var_export zu verwenden:

md5(var_export($array, true))

nicht so langsam wie serialize, nicht so fehlerbehaftet wie json_encode

0
Bruno