PHP лучший способ для многомерного массива MD5?
Каков наилучший способ генерации MD5 (или любого другого хэша) многомерного массива?
Я мог бы легко написать цикл, который проходил бы через каждый уровень массива, объединяя каждое значение в строку и просто выполняя MD5 в строке.
Однако это в лучшем случае кажется громоздким, и я подумал, есть ли функция фанки, которая будет принимать многомерный массив и хеш файл.
Ответы
Ответ 1
(функция Copy-n-paste в нижней части)
Как упоминалось ранее, будет работать следующее.
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 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%) - это не тривиальная разница. Вы можете увидеть результаты теста с помощью этого живого script здесь:
Теперь одно замечание: массив (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));
Изменить:. Был задан вопрос о том, будут ли возвращать ордер на то же результат. Итак, я сделал это (правильно) здесь:
Как вы можете видеть, результаты в точности совпадают. Здесь (исправленный) тест первоначально созданный кем-то, связанным с 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));
}
Ответ 2
md5(serialize($array));
Ответ 3
Я присоединяюсь к очень переполненной партии, отвечая, но есть важное соображение, что ни один из существующих ответов не подходит. Значение 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)) );
Выбор json_encode()
или 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;
}
Ответ 4
Помимо превосходного ответа Брок (+1), любая достойная хеширующая библиотека позволяет вам обновлять хэш с шагом, поэтому вы должны иметь возможность обновлять каждую строку последовательно, вместо этого нужно создать одну гигантскую строку.
Смотрите: hash_update
Ответ 5
Ответ сильно зависит от типов данных значений массива.
Для больших строк используйте:
md5(serialize($array));
Для коротких строк и целых чисел используйте:
md5(json_encode($array));
4 встроенных PHP-функций могут преобразовывать массив в строку:
serialize(), json_encode(), var_export(), print_r().
Примечание: Функция json_encode() замедляется при обработке ассоциативных массивов со строками в качестве значений. В этом случае рассмотрите функцию serialize().
Результаты тестов для многомерного массива с md5-хешами (32 char) в ключах и значениях:
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%
Ассоциативный массив источник тестирования.
Числовой массив тестовый источник.
Ответ 6
md5(serialize($array));
Будет работать, но хэш будет меняться в зависимости от порядка массива (это может быть и неважно).
Ответ 7
Обратите внимание, что serialize
и json_encode
действуют по-разному, когда речь идет о числовых массивах, где ключи не начинаются с 0 или ассоциативных массивов.
json_encode
будет хранить такие массивы как Object
, поэтому json_decode
возвращает Object
, где unserialize
будет возвращать массив с точно такими же ключами.
Ответ 8
Я думаю, что это может быть хорошим советом:
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);
Ответ 9
Важное примечание о 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;" в первую очередь. Это определенно хороший и правильный способ сериализации данных, но это может привести к проблемам с вашей хеширующей функцией.
Ответ 10
есть несколько ответов, говорящих на использование json_code,
но json_encode не работает нормально с строкой iso-8859-1, как только появляется специальный char, строка обрезается.
я бы советовал использовать var_export:
md5(var_export($array, true))
не так медленно, как сериализация, а не как прослушивание json_encode
Ответ 11
В настоящее время наиболее ответивший ответ 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
функция НЕ меняет оригинальный массив.)