PHP лучший способ для многомерного массива MD5?
каков наилучший способ генерации MD5 (или любого другого хэша) многомерного массива?
Я мог бы легко написать цикл, который будет проходить через каждый уровень массива, объединяя каждое значение в строку и просто выполняя MD5 в строке.
однако, это кажется громоздким в лучшем случае, и я задавался вопросом, есть ли фанки функция, которая будет принимать многомерный массив и хэшировать его.
13 ответов:
(копировать-вставлять-возможность функционировать в самом низу)
Как упоминалось ранее, следующее будет работать.
md5(serialize($array));
однако, стоит отметить, что (по иронии судьбы) json_encode выполняет заметно быстрее:
md5(json_encode($array));
фактически, увеличение скорости здесь в два раза, так как (1) json_encode сам по себе работает быстрее, чем сериализация, и (2) json_encode создает меньшую строку и, следовательно, меньше для md5 ручка.
Edit: вот доказательства в поддержку этого утверждения:
<?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 x) быстрее (часто более 300%) - это не тривиальная разница. Вы можете увидеть результаты теста с помощью этого живого скрипта здесь:
- http://nathanbrauer.com/playground/serialize-vs-json.php
- http://nathanbrauer.com/playground/plain-text/serialize-vs-json.php
теперь следует отметить, что массив (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));
Edit: там был какой-то вопрос о том, является ли изменение порядка приведет к тем же результатам. Итак, я сделал это (правильно) здесь:
- http://nathanbrauer.com/playground/json-vs-serialize.php
- http://nathanbrauer.com/playground/plain-text/json-vs-serialize.php
как вы можете видеть, результаты точно такие же. Вот это (исправлено) тест первоначально созданный кем-то связанным с Друпал:
- http://nathanjbrauer.com/playground/drupal-calculation.php
- http://nathanjbrauer.com/playground/plain-text/drupal-calculation.php
и для хорошей меры, вот функция / метод, который вы можете скопировать и вставить (проверено в 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)); }
Я присоединяюсь к очень многолюдной партии, отвечая, но есть важное соображение, что ни один из существующих ответов не обращается. значение
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; }
ответ сильно зависит от типов данных значений массива. Для больших строк используйте:
md5(serialize($array));
для коротких строк и целых чисел использовать:
md5(json_encode($array));
4 встроенные функции PHP могут преобразовывать массив в строку: serialize (),json_encode (),var_export (), print_r ().
обратите внимание:json_encode () функция замедляется при обработке ассоциативных массивы со строками в качестве значений. В этом случае рассмотрите возможность использования serialize ()
помимо отличного ответа Брока (+1), любая приличная библиотека хэширования позволяет обновлять хэш с шагом, поэтому вы должны иметь возможность обновлять каждую строку последовательно, вместо того, чтобы создавать одну гигантскую строку.
посмотреть:
hash_update
md5(serialize($array));
будет работать, но хэш будет меняться в зависимости от порядка массива (это может не иметь значения, хотя).
отметим, что
serialize
иjson_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()
Я не рекомендую использовать его как часть функции хэширования, потому что он может возвращать разные результаты для следующих примеров. Проверьте пример ниже:
простой пример:
$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;" на первый экземпляр. Это определенно хорошо и правильно способ сериализации данных, но это может привести к проблемам с вашей функцией хэширования.
// 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);
есть несколько ответов, говорящих использовать json_code,
но json_encode не работает нормально со строкой iso-8859-1, Как только появляется специальный символ, строка обрезается.
Я бы посоветовал использовать var_export :
md5(var_export($array, true))
не так медленно, как сериализация, не так прослушивается, как json_encode
в настоящее время наиболее проголосовали ответ
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 является тип значения. Так что
Я не видел решение так легко выше, поэтому я хотел внести свой вклад в более простой ответ. Для меня я получал тот же ключ, пока не использовал ksort (сортировка ключей):
сначала сортируется с помощью Ksort, затем выполняется sha1 на json_encode:
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"