2010-02-12 76 views
92

生成多維數組的MD5(或任何其他散列)的最佳方式是什麼?PHP最好的方式來MD5多維數組?

我可以很容易地編寫一個遍歷數組每個級別的循環,將每個值連接成一個字符串,並簡單地在字符串上執行MD5。

但是,這似乎很麻煩,我想知道是否有一個時髦的功能,將採取多維數組,並散列它。

回答

208

(複印 - 正糊能夠在底部功能)

正如之前提到的那樣,下面的工作。

md5(serialize($array)); 

然而,值得注意的是,(諷刺)json_encode執行明顯更快:

md5(json_encode($array)); 

事實上,速度增加是在這裏:(1)json_encode單獨執行的速度比兩倍序列化,和(2)json_encode產生一個更小的字符串,因此對於md5來說處理更少。

編輯:這裏是證據來支持這種說法:

<?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是持續超過250%(2.5倍),快(通常超過300%) - 這不是一個微不足道的差異。您可能會看到測試的結果與這個活的腳本在這裏:現在

,有一點要注意的是陣列(1,2,3)將產生不同的MD5作爲數組(3,2,1)。 如果這不是你想要的。試試下面的代碼:

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

編輯:還有的是一些問題,是否扭轉秩序將產生相同的結果。所以,我(正確)來完成,在這裏:

正如你所看到的,結果是完全一樣的。這裏是(修正)測試originally created by someone related to Drupal

和良好的措施,這裏有一個函數/方法,你可以複製和粘貼(在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)); 
} 
+41

哈哈!真?我被投票贊成「過度」優化?實際上,PHP的序列化速度要慢得多。我會用證據更新我的答案... – 2011-10-12 14:03:03

+0

如果任何人對僅測試JSON(不涉及MD5)感興趣,請參閱[看這裏](http://stackoverflow.com/questions/804045/preferred -method-to-store-php-arrays-json-encode-vs-serialize)(具有諷刺意味的是,Shrapnel上校也在這裏進行調試)。 – 2011-10-12 14:40:56

+2

我不是拖釣。 – 2011-10-12 14:49:38

165
md5(serialize($array)); 
+1

輝煌!非常感謝,從來不知道這件事。 – 2010-02-12 19:09:43

+12

如果由於某種原因想要匹配散列(指紋),您可能需要考慮對數組進行排序「sort」或「ksort」,另外還可能需要執行某種類型的清理/清理 – farinspace 2011-05-11 18:15:00

+9

序列化比soooooooo慢得多從第二個答案json_encode。做你的服務器的樂趣,並使用json_encode! :) – s3m3n 2013-05-05 17:19:21

6

除了布魯克的出色答卷(+1),任何像樣的哈希庫允許你更新的增量散列值,所以你應該能夠與每個字符串依次進行更新,而不是建立一個巨大的字符串。

參見:hash_update

+0

值得注意的是,如果您使用小碎片進行更新,則此方法效率低下;儘管如此,它對於大塊文件來說也很好。 – wrygiel 2012-09-15 18:41:59

+0

@wrygiel這是不正確的。對於MD5,壓縮總是以64字節的塊(不管你的「大塊」的大小如何)進行,並且如果你還沒有填滿塊,則在塊填滿之前不進行處理。 (當你完成散列時,最後一個塊被填充到一個完整的塊,作爲最終處理的一部分。)更多背景信息,請閱讀[Merkle-Damgard構造](http://en.wikipedia.org/wiki/Merkle -Damgard_construction)(其中MD5,SHA-1和SHA-2都基於此)。 – 2012-09-16 01:06:21

+0

你說得對。我被其他網站的評論完全誤導了。 – wrygiel 2012-09-20 07:54:35

3
md5(serialize($array)); 

的工作,但散列將取決於陣列的順序改變(可能不雖然重要)。

3

不是e說明serializejson_encode在鍵值不是從0開始的數字數組或關聯數組時表現不同。 json_encode將存儲這樣的數組作爲Object,所以json_decode返回一個Object,其中unserialize將返回一個具有完全相同的密鑰的數組。

2

我認爲這可能是一個很好的提示:

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

重要提示有關serialize()

我不建議使用它作爲散列函數的一部分,因爲它可以返回不同的結果下面的例子。檢查下面的例子:

簡單的例子:

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

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

主要生產

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

但下面的代碼:

<?php 

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

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

輸出:

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

因此,而不是第二個對象php只是創建鏈接「r:2;」到一審。這絕對是序列化數據的好方法,但它可能會導致散列函數的問題。

0

有幾個答案,告訴使用json_code,

但json_encode是有特殊字符不符合ISO-8859-1字符串做工精細,儘快,該字符串被裁剪。

我會建議使用var_export:

md5(var_export($array, true)) 

不如連載慢,而不是如竊聽json_encode

+0

沒那麼快,最好的選擇是使用md4,var_export是也很慢 – user956584 2015-08-10 19:53:48

6

回答是高度依賴於數據類型數組值。 對於大字符串使用:

md5(serialize($array)); 

對於短字符串和整數使用:

md5(json_encode($array)); 

4內置的PHP函數可變換數組字符串: serialize()json_encode()var_export()print_r()

說明:json_encode()功能減慢,同時用字符串作爲值處理關聯數組。在這種情況下請考慮使用serialize()函數。

測試結果爲在鍵和值使用MD5哈希值的多維陣列(32字符):

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% 

爲數字多維數組測試結果:

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% 

聯想數組test source。 數字陣列test source

+0

你能解釋什麼是*大*和*短字符串*? – 2015-10-21 16:25:20

+1

@ A.L *短字符串* - 包含少於25-30個字符的字符串。 *大字符串* - 全部包含超過25-30個字符。 – 2015-10-22 07:39:46

18

我正在通過回答加入非常擁擠的派對,但是有一個重要的考慮因素,那就是現有的答案都不存在。 json_encode()serialize()的值都取決於數組中元素的順序!

這裏是不排序和排序的陣列,上兩個陣列具有相同值,但結果以不同的順序(代碼在柱的底部)加入:

serialize() 
1c4f1064ab79e4722f41ab5a8141b210 
1ad0f2c7e690c8e3cd5c34f7c9b8573a 

    json_encode() 
db7178ba34f9271bfca3a05c5dddf502 
c9661c0852c2bd0e26ef7951b4ca9e6f 

    Sorted serialize() 
1c4f1064ab79e4722f41ab5a8141b210 
1c4f1064ab79e4722f41ab5a8141b210 

    Sorted json_encode() 
db7178ba34f9271bfca3a05c5dddf502 
db7178ba34f9271bfca3a05c5dddf502 

因此,這兩種方法,我會建議散列數組將是:

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

的的選擇或serialize()應該是通過測試確定的數據類型正在使用。通過我自己對純文本和數字數據的測試,如果代碼幾千次沒有運行緊密循環,那麼差異甚至不值得進行基準測試。我個人使用json_encode()作爲這種類型的數據。

這裏是用來生成上述排序測試代碼:

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

我快deep_ksort()的實現,符合這種情況,而是使用在自己的項目前檢查:

/* 
* 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; 
} 
+2

非常好除了上面的答案+1 – Brett 2016-04-28 05:58:46

0

當前最高票數的答案md5(serialize($array));不能很好地與對象配合使用。使用md5(serialize($array));

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

即使陣列是不同的(它們包含不同的對象),它們具有相同的散列:

考慮代碼。所以你的散列沒用!

爲了避免這個問題,您可以在序列化之前用spl_object_hash()替換結果對象。如果你的數組有多個層次,你也應該遞歸地做。

下面的代碼也按鍵排序數組,正如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; 
} 

現在您可以使用md5(serialize(replaceObjectsWithHashes($array)))了。

(請注意,在PHP數組是值類型,所以replaceObjectsWithHashes功能不要改變原來的數組)

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