PHP最好的方式来MD5multidimensional array?

生成multidimensional array的MD5(或其他散列)的最佳方法是什么?

我可以很容易地编写一个遍历数组的每一级的循环,将每个值连接成一个string,并简单地在string上执行MD5。

然而,这似乎很麻烦,我想知道是否有一个时髦的function,将采取multidimensional array,并散列它。

(底部的复制粘贴function)

如前所述,以下将起作用。

md5(serialize($array)); 

但值得注意的是(具有讽刺意味的是)json_encode的执行速度明显加快了:

 md5(json_encode($array)); 

实际上,速度增加是双重的,因为(1)单独执行json_encode的速度比序列化快,(2)json_encode产生一个较小的string,因此对于md5来说处理更less。

编辑:这是支持这一说法的证据:

 <?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%) – 这不是一个微不足道的差异。 您可以在这里看到这个活脚本的testing结果:

现在需要注意的一点是,数组(1,2,3)将产生与数组(3,2,1)不同的MD5。 如果这不是你想要的。 试试下面的代码:

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

编辑:关于反转顺序是否会产生相同的结果存在一些问题。 所以,我在这里做了( 正确的 ):

正如你所看到的,结果是完全一样的。 以下是由Drupal相关人员创build的( 更正的 )testing:

为了好的措施,下面是一个可以复制和粘贴的函数/方法(在5.3.3-1ubuntu9.5中testing过):

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

我正在通过回答参加一个非常拥挤的聚会,但有一个重要的考虑,现有的答案都没有解决。 json_encode()serialize()都取决于数组中元素的顺序!

下面是不对数组进行sorting和sorting的结果: 两个数组具有相同的值,但以不同的顺序添加 (代码在底部)

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

因此,我build议散列数组的两种方法是:

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

json_encode()或者serialize()应该通过testing正在使用的数据types确定 。 通过我自己对纯文本和数字数据的testing,如果代码几千次运行不紧密,那么差异甚至不值得基准testing。 我个人使用json_encode()为这种types的数据。

以下是用于生成上述sortingtesting的代码:

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

除了Brock的优秀答案(+1)之外,任何像样的哈希库都允许您以增量方式更新哈希值,所以您应该能够按顺序更新每个string,而不必构build一个巨大的string。

请参阅: hash_update

答案在很大程度上取决于数组值的数据types。 对于大string使用:

 md5(serialize($array)); 

对于短string和整数使用:

 md5(json_encode($array)); 

4个内置PHP函数可以将数组转换为string: serialize() , json_encode() , var_export() , print_r() 。

注意: json_encode()函数在处理以string作为值的关联数组时会变慢。 在这种情况下,考虑使用serialize()函数。

在键和值中使用md5-hashes(32个字符)的multidimensional array的testing结果:

 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% 

数值multidimensional array的testing结果:

 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% 

关联数组testing源 。 数字数组testing源 。

 md5(serialize($array)); 

将工作,但散列会根据数组的顺序(这可能并不重要)改变。

请注意, serializejson_encode在键值不是从0开始的数字数组或关联数组的作用是不同的。 json_encode将这样的数组存储为一个Object ,所以json_decode返回一个Object ,其中反unserialize将返回一个具有完全相同键的数组。

我认为这可能是一个好的提示:

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

有关serialize()重要说明

我不build议将它用作哈希函数的一部分,因为它可以为以下示例返回不同的结果。 检查下面的例子:

简单的例子:

 $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只是创build链接“R:2;” 初审。 这绝对是序列化数据的好方法,但它可能会导致你的哈希函数的问题。

有几个答案告诉使用json_code,

但json_encode不能正常工作与iso-8859-1string,只要有一个特殊的字符,string被裁剪。

我会build议使用var_export:

 md5(var_export($array, true)) 

不像序列化那么慢,不像json_encode那样错误

目前最有投票的答案md5(serialize($array)); 对物体运作不好。

考虑一下代码:

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

即使数组是不同的(它们包含不同的对象),当使用md5(serialize($array));时,它们具有相同的散列md5(serialize($array)); 。 所以你的散列是没用的!

为了避免这个问题,可以在序列化前用spl_object_hash()结果replace对象。 如果你的数组有多个级别,你也应该recursion地做。

下面的代码也按键sorting数组,正如dotancohen所build议的。

 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中的数组是值types,因此replaceObjectsWithHashes函数不要更改原始数组。