PHP 最好的方式来 MD5多维数组?

生成多维数组的 MD5(或任何其他散列)的最佳方法是什么?

我可以很容易地编写一个循环,遍历数组的每个级别,将每个值连接到一个字符串中,并简单地对字符串执行 MD5。

然而,这似乎最多只能算是繁琐的,我想知道是否有一个时髦的函数将采用一个多维数组,并散列它。

70539 次浏览
md5(serialize($array));

除了 Brock 的出色回答(+ 1)之外,任何像样的哈希库都允许您以增量的方式更新哈希,因此您应该能够按顺序更新每个字符串,而不是必须构建一个巨大的字符串。

见: hash_update

md5(serialize($array));

将工作,但散列将根据数组的顺序而改变(但这可能无关紧要)。

(复制-n-粘贴函数在底部)

正如前面提到的,以下方法可行。

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%)——这不是一个微不足道的差异。您可以在这里通过这个实时脚本看到测试结果:

现在,需要注意的是 array (1,2,3)将生成一个不同的 MD5作为 array (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));

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

正如你所看到的,结果是完全一样的。下面是(纠正)测试 最初是由与 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));
}

请注意,对于键不从0开始的数字数组或关联数组,serializejson_encode的作用是不同的。 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()的重要说明

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

举个简单的例子:

$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;”,而不是第二个对象。这绝对是一种很好的正确的序列化数据的方法,但是它会导致散列函数出现问题。

有几个答案告诉我们使用 json _ code,

但是 json _ encode 不能很好地使用 iso-8859-1字符串,只要有一个特殊的字符,字符串就会被裁剪掉。

我建议使用 var _ export:

md5(var_export($array, true))

没有序列化那么慢,没有 json _ encode 那么漏洞百出

答案在很大程度上取决于数组值的数据类型。 对于大字符串使用:

md5(serialize($array));

对于短字符串和整数,请使用:

md5(json_encode($array));

4个内置的 PHP 函数可以将数组转换为字符串: 序列化() Json _ encode ()Var _ export ()Print _ r ()

注意: Json _ encode ()函数在处理以字符串作为值的关联数组时会减慢速度。在这种情况下,考虑使用 序列化()函数。

以键和值为 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%

关联数组。 数字数组 测试来源

我加入了一个非常拥挤的派对回答,但有一个重要的考虑,没有一个现存的答案地址。ABC0和 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)) );

json_encode()serialize()的选择应该是 通过测试 < em > you 使用的数据类型来确定。通过我自己对纯文本和数字数据的测试,如果代码没有运行数千次紧密循环,那么这种差异甚至不值得进行基准测试。我个人使用 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;
}

目前最受欢迎的答案是 md5(serialize($array));不能很好地处理对象。

考虑代码:

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

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

为了避免这个问题,您可以在序列化之前用 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函数不要更改原始数组。)

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

我没有那么容易看到上面的解决方案,所以我想贡献一个更简单的答案。对我来说,在使用 ksort (key sort)之前,我得到的是相同的密钥:

首先使用 Ksort 进行排序,然后在 json _ encode 上执行 sha1:

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

例如:

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

更改后的数组和散列的输出:

string(40) "502c2cbfbe62e47eb0fe96306ecb2e6c7e6d014c"
string(40) "b3319c58edadab3513832ceeb5d68bfce2fb3983"

在某些情况下,最好使用 Http _ build _ query将数组转换为字符串:

md5( http_build_query( $array ) );