it-swarm.com.ru

PHP лучший способ MD5 многомерного массива?

Каков наилучший способ создания MD5 (или любого другого хеша) из многомерного массива? 

Я мог бы легко написать цикл, который будет проходить через каждый уровень массива, объединяя каждое значение в строку и просто выполняя MD5 в строке. 

Тем не менее, в лучшем случае это кажется громоздким, и я подумал, есть ли какая-нибудь особенная функция, которая бы принимала многомерный массив и хешировала его.

105
Peter John

(Функция копирования-n-вставки внизу)

Как упоминалось ранее, следующее будет работать.

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 раза) быстрее (часто более 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));

Edit: Возник некоторый вопрос относительно того, приведет ли изменение порядка к таким же результатам. Итак, я сделал это (правильно) здесь:

Как видите, результаты точно такие же. Вот (исправлено) тест изначально созданный кем-то, связанным с 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));
}
229
Nathan J. Brauer
md5(serialize($array));
167
Brock Batsell

Я присоединяюсь к очень многолюдной вечеринке, отвечая, но есть важное соображение, что ни один из существующих ответов не обращается. Значение 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() должен быть определяется тестированием типа данных, которые используются 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;
}
21
dotancohen

Ответ сильно зависит от типов данных значений массива . Для больших строк используйте:

md5(serialize($array));

Для коротких строк и целых чисел используйте: 

md5(json_encode($array));

4 встроенные функции PHP могут преобразовывать массив в строку: serialize () , json_encode () , var_export () , print_r () ,.

Примечание:json_encode () функция замедляется при обработке ассоциативных массивов со строками в качестве значений. В этом случае рассмотрите возможность использования serialize () function.

Результаты теста для многомерного массива с 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%

Ассоциативный массив тестовый источник . Числовой массив тестовый источник .

8
Alexander Yancharuk

Помимо превосходного ответа Брока (+1), любая приличная библиотека хеширования позволяет вам обновлять хэш с шагом, поэтому вы должны иметь возможность обновлять каждую строку последовательно, вместо этого нужно создавать одну гигантскую строку.

Смотрите: hash_update

7
Chris Jester-Young

Обратите внимание, что serialize и json_encode действуют по-разному, когда дело доходит до числовых массивов, где ключи не начинаются с 0, или ассоциативных массивов .json_encode будет хранить такие массивы как Object, поэтому json_decode возвращает Object, где unserialize вернет массив с точно такими же ключами.

3
Willem-Jan

Я думаю, что это может быть хорошим советом:

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);
3
Andrej Pandovich
md5(serialize($array));

Будет работать, но хеш будет меняться в зависимости от порядка массива (хотя это может не иметь значения).

3
Max Wheeler

Важное замечание о 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;" в первую очередь. Это определенно хороший и правильный способ сериализации данных, но он может привести к проблемам с вашей функцией хеширования. 

2
TermiT
// 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);
2
ymakux

В настоящее время ответ с наибольшим количеством голосов 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 НЕ меняет исходный массив.)

0
Damian Polac

Я не видел решения так легко выше, поэтому я хотел дать более простой ответ. Я получал один и тот же ключ, пока не использовал 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"
0
Mike Q

есть несколько ответов, говорящих об использовании json_code,

но json_encode не работает нормально со строкой iso-8859-1, как только появляется специальный символ, строка обрезается.

я бы посоветовал использовать var_export:

md5(var_export($array, true))

не так медленно, как сериализация, не так прослушивается как json_encode

0
Bruno