MD5 다차원 배열에 대한 PHP 최고의 방법?


120

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

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

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

답변:


260

(하단에 복사 및 붙여 넣기 기능)

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

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

47
LOL! 정말? "오버"최적화에 대한 투표를 받았습니까? 실제로 PHP의 직렬화는 상당히 느립니다. 내 대답을 증거로 업데이트하겠습니다 ...
Nathan JB

19
Nathan이 여기서 한 일은 그 가치를 알 수 없더라도 가치가 있습니다. 우리의 맥락을 벗어난 상황에서 가치있는 최적화가 될 수 있습니다. 마이크로 최적화는 일부가 아닌 모든 상황에서 가난한 결정이다
SeanDowney

13
나는 그것을 위해 마이크로 최적화를위한 사람은 아니지만 추가 작업없이 문서화 된 성능 향상이있는 경우에는 사용하지 않는 것이 좋습니다.
bumperbox

2
실제로 어레이의 깊이에 따라 다릅니다. 나는 내 사용 사례에 코드에서 $ 배열 변수를 변경할 때 요구,로 json_encode가 () ~ 300 % 빠르다는 것을 가능한 한 당신의 POC 쇼 동안 빠른 속도로 실행하는 것이 필요 뭔가 일어날, 그것은 반환 serialize() w/ md5() took: 0.27773594856262 sec json_encode() w/ md5() took: 0.34809803962708 sec json_encode is (79.8%) faster with a difference of (-0.070362091064453 seconds)합니다 (퍼센트 인 계산 분명히 부정확 함). 내 배열은 최대 2 단계 깊이이므로 (평소처럼) 마일리지가 다를 수 있음을 명심하십시오.
samitny

3
좋아, Nathan의 대답이 왜 최고 대답이 아닌지 모르겠습니다. 심각하게 직렬화를 사용하고 엄청나게 느린 사이트로 사용자를 성가 시게하십시오. 에픽 +1 @ NathanJ.Brauer!
ReSpawN 2014 년

168
md5(serialize($array));

13
어떤 이유로 해시 (지문)를 일치 시키려면 배열 "정렬"또는 "크 소트"정렬을 ​​고려할 수 있습니다. 추가로 일종의 스크러빙 / 정리를 구현해야 할 수도 있습니다
farinspace

9
Serialize는 두 번째 답변의 json_encode보다 훨씬 느립니다. 서버를 즐겁게 사용하고 json_encode를 사용하십시오! :)
s3m3n 2013

3
json_encode를 사용해야하는지 직렬화해야하는지 알아 내기 위해 자체 배열을 벤치마킹해야하는 것 같습니다. 어레이에 따라 다릅니다.
Ligemer 2014 년

잘못된 방법이라고 생각합니다. 아래 설명을 확인하십시오.
TermiT 2014 년

1
@joelpittet-아니요. 그 drupal 링크의 두 예 모두 버그가 있습니다. 아래 내 대답의 주석을 참조하십시오. ;) 예 dl.dropboxusercontent.com/u/4115701/Screenshots/...
나단 JB

26

나는 응답으로 매우 붐비는 파티에 참여하고 있지만 현존하는 응답 중 어느 것도 언급하지 않는다는 중요한 고려 사항이 있습니다. 의 값 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;
}

11

대답은 배열 값의 데이터 유형에 따라 크게 달라집니다. 큰 문자열의 경우 다음을 사용하십시오.

md5(serialize($array));

짧은 문자열 및 정수의 경우 다음을 사용하십시오.

md5(json_encode($array));

4 개의 내장 PHP 함수가 배열을 문자열로 변환 할 수 있습니다 : serialize () , json_encode () , var_export () , print_r () .

주의 : json_encode () 함수는 문자열이있는 연관 배열을 값으로 처리하는 동안 속도가 느려집니다. 이 경우 serialize () 함수 사용을 고려하십시오 .

키와 값에 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%

연관 어레이 테스트 소스 . 숫자 배열 테스트 소스 .


당신은 무엇을 설명해 주시겠습니까 짧은 문자열은 ?
AL

1
@AL 짧은 문자열 -25-30 자 미만의 문자를 포함하는 문자열입니다. 큰 문자열 -모두 25-30 자 이상을 포함합니다.
Alexander Yancharuk

7

Brock의 훌륭한 답변 (+1) 외에도 괜찮은 해싱 라이브러리를 사용하면 해시를 증분 업데이트 할 수 있으므로 하나의 거대한 문자열을 구축하는 대신 각 문자열을 순차적으로 업데이트 할 수 있어야합니다.

보다: hash_update


작은 조각으로 업데이트하는 경우이 방법이 비효율적이라는 점은 주목할 가치가 있습니다. 그래도 큰 파일의 큰 덩어리에는 좋습니다.
wrygiel

@wrygiel 사실이 아닙니다. MD5의 경우 압축은 항상 64 바이트 블록에서 수행되며 ( "큰 청크"의 크기에 관계없이) 아직 블록을 채우지 않은 경우 블록이 채워질 때까지 처리가 수행되지 않습니다. (해시를 마무리 할 때 최종 처리의 일부로 마지막 블록이 전체 블록으로 채워집니다.) 자세한 배경 정보는 Merkle-Damgard 구성 (MD5, SHA-1 및 SHA-2가 모두 기반으로 ).
크리스 광대 - 젊은

네가 옳아. 나는 다른 사이트에 대한 의견에 완전히 오도되었습니다.
wrygiel 2012 년

@wrygiel 이것이 "인터넷에서 발견 된"아이디어를 따를 때 자신의 연구를 수행하는 데 도움이되는 이유입니다. ;-) 그렇게 말하면서, 그 마지막 코멘트는 제가 작성하기 쉬웠습니다. 왜냐하면 저는 실제로 몇 년 전 (내 Scheme 프로그래밍 기술을 연습하기 위해) 처음부터 MD5를 구현했기 때문에 그 작동 방식을 아주 잘 알고 있습니다.
Chris Jester-Young

이것이 바로 내가 원하는 것입니다. 메모리에서 대용량 데이터를 이동 및 복사하는 것은 때때로 허용되지 않습니다. 따라서 serialize ()를 사용하는 다른 답변과 마찬가지로 성능 측면에서 매우 나쁜 생각입니다. 그러나 특정 오프셋에서 문자열의 일부만 해시하려는 경우이 API가 여전히 누락되었습니다.
Jianwu Chen

4
md5(serialize($array));

작동하지만 해시는 배열의 순서에 따라 변경됩니다 (중요하지 않을 수도 있음).


3

주의 serializejson_encode그 키가 0에서 시작하지 않는 숫자 배열, 또는 연관 배열에 올 때 다르게 행동한다. json_encode이러한 배열을로 저장 Object하므로를 json_decode반환합니다 Object. 여기서는 unserialize정확히 동일한 키를 가진 배열을 반환합니다.


3

이것이 좋은 팁이 될 수 있다고 생각합니다.

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

2

에 대한 중요 참고 사항 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
// 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);

1

json_code를 사용하라는 몇 가지 답변이 있습니다.

그러나 json_encode는 iso-8859-1 문자열에서 제대로 작동하지 않습니다. 특수 문자가 있으면 문자열이 잘립니다.

var_export를 사용하는 것이 좋습니다.

md5(var_export($array, true))

직렬화만큼 느리지 않고 json_encode만큼 버그가 없습니다.


아니 너무 빨리, 가장 좋은 옵션은 var_export 느린 또한 사용 MD4이다
user956584은

0

현재 가장 많이 득표 한 답변 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

나는 위의 해결책을 그렇게 쉽게 보지 못했기 때문에 더 간단한 대답을 제공하고 싶었습니다. 나를 위해 ksort (키 정렬)를 사용할 때까지 동일한 키를 얻었습니다.

먼저 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"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.