program tip

MD5 다차원 배열에 PHP 가장 좋은 방법?

radiobox 2020. 8. 2. 18:04
반응형

MD5 다차원 배열에 PHP 가장 좋은 방법?


다차원 배열의 MD5 (또는 다른 해시)를 생성하는 가장 좋은 방법은 무엇입니까?

배열의 각 레벨을 순회하면서 각 값을 문자열로 연결하고 단순히 문자열에서 MD5를 수행하는 루프를 쉽게 작성할 수 있습니다.

그러나 이것은 가장 번거로운 것처럼 보이며 다차원 배열을 사용하여 해시하는 펑키 함수가 있는지 궁금했습니다.


(바닥에 복사-붙여 넣기 가능 기능)

앞에서 언급했듯이 다음이 작동합니다.

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.5x) 이상 빠르며 (종종 300 % 이상) 사소한 차이가 아닙니다. 이 라이브 스크립트로 테스트 결과를 볼 수 있습니다.

주목해야 할 것은 array (1,2,3)은 array (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과 관련된 누군가가 처음에 만든 ( 수정 된 ) 테스트 는 다음과 같습니다 .

그리고 좋은 측정을 위해 복사하여 붙여 넣을 수있는 기능 / 방법이 있습니다 (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));
}

md5(serialize($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 built-in PHP functions can transform array to string: serialize(), json_encode(), var_export(), print_r().

Notice: json_encode() function slows down while processing associative arrays with strings as values. In this case consider to use serialize() function.

Test results for multi-dimensional array with md5-hashes (32 char) in keys and values:

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 result for numeric multi-dimensional array:

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%

Associative array test source. Numeric array test source.


Aside from Brock's excellent answer (+1), any decent hashing library allows you to update the hash in increments, so you should be able to update with each string sequentially, instead having to build up one giant string.

See: hash_update


md5(serialize($array));

Will work, but the hash will change depending on the order of the array (that might not matter though).


Note that serialize and json_encode act differently when it comes to numeric arrays where the keys don't start at 0, or associative arrays. json_encode will store such arrays as an Object, so json_decode returns an Object, where unserialize will return an array with exact the same keys.


I think that this could be a good tip:

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

Important note about serialize()

I don't recommend to use it as part of hashing function because it can return different result for the following examples. Check the example below:

Simple example:

$a = new \stdClass;
$a->test = 'sample';

$b = new \stdClass;
$b->one = $a;
$b->two = clone $a;

Produces

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

But the following code:

<?php

$a = new \stdClass;
$a->test = 'sample';

$b = new \stdClass;
$b->one = $a;
$b->two = $a;

Output:

"O:8:"stdClass":2:{s:3:"one";O:8:"stdClass":1:{s:4:"test";s:6:"sample";}s:3:"two";r:2;}"

So instead of second object php just create link "r:2;" to the first instance. It's definitely good and correct way to serialize data, but it can lead to the issues with your hashing function.


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

there are several answers telling to use json_code,

but json_encode don't work fine with iso-8859-1 string, as soon as there is a special char, the string is cropped.

i would advice to use var_export :

md5(var_export($array, true))

not as slow as serialize, not as bugged as json_encode


Currently the most up-voted answer md5(serialize($array)); doesn't work well with objects.

Consider code:

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

Even though arrays are different (they contain different objects), they have same hash when using md5(serialize($array));. So your hash is useless!

To avoid that problem, you can replace objects with result of spl_object_hash() before serializing. You also should do it recursively if your array has multiple levels.

Code below also sorts arrays by keys, as dotancohen have suggested.

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

Now you can use md5(serialize(replaceObjectsWithHashes($array))).

(Note that the array in PHP is value type. So replaceObjectsWithHashes function DO NOT change original array.)


I didn't see the solution so easily above so I wanted to contribute a simpler answer. For me, I was getting the same key until I used ksort (key sort):

Sorted first with Ksort, then performed sha1 on a json_encode:

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

example:

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

Output of altered arrays and hashes:

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

참고URL : https://stackoverflow.com/questions/2254220/php-best-way-to-md5-multi-dimensional-array

반응형