Hash: immutable hash over array
with sorted keys, but arrays stay unsorted
This commit is contained in:
@@ -421,6 +421,71 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
'hash hmac valid'
|
||||
);
|
||||
}
|
||||
|
||||
// MARK: generateImmutableHashForArray
|
||||
|
||||
/**
|
||||
* Data provider for testGenerateImmutableHashForArray.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function generateImmutableHashForArrayProvider(): array
|
||||
{
|
||||
return [
|
||||
'Empty array' => [
|
||||
[],
|
||||
'064f01fe',
|
||||
],
|
||||
'Simple array' => [
|
||||
['key' => 'value'],
|
||||
'78f40899',
|
||||
],
|
||||
'flat array a' => [
|
||||
[0, 1, 'b', 'c', 'a'],
|
||||
'ccec0f5b',
|
||||
],
|
||||
'flat array b' => [
|
||||
['b', 'c', 'a', 1, 0],
|
||||
'c6040f5b',
|
||||
],
|
||||
'key array a' => [
|
||||
['A' => 0, 'F' => 1, 'C' => 'b', 'K' => 'c', 'B' => 'a'],
|
||||
'01f11355',
|
||||
],
|
||||
'key array b' => [
|
||||
['A' => 0, 'B' => 'a', 'C' => 'b', 'F' => 1, 'K' => 'c'],
|
||||
'01f11355',
|
||||
],
|
||||
'complex nested array' => [
|
||||
[
|
||||
'level1' => [
|
||||
'level2' => [
|
||||
'level3' => 'value'
|
||||
]
|
||||
]
|
||||
],
|
||||
'd8de1565',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for generating an immutable hash from an array.
|
||||
*
|
||||
* @covers ::generateImmutableHashForArray
|
||||
* @dataProvider generateImmutableHashForArrayProvider
|
||||
* @testdox generate immutable hash for array with result $expected [$_dataName]
|
||||
*
|
||||
* @param array<mixed> $input
|
||||
* @param string $expected
|
||||
*/
|
||||
public function testGenerateImmutableHashForArray(array $input, string $expected): void
|
||||
{
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::generateImmutableHashForArray($input)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -103,6 +103,19 @@ foreach ($hash_types as $hash_type) {
|
||||
// print "UNIQU ID SHORT : " . Hash::__uniqId() . "<br>";
|
||||
// print "UNIQU ID LONG : " . Hash::__uniqIdLong() . "<br>";
|
||||
|
||||
$tests = [
|
||||
// array diff
|
||||
[0, 1, 'b', 'c', 'a'],
|
||||
['b', 'c', 'a', 1, 0],
|
||||
// key set arrays DO NOT diff
|
||||
['A' => 0, 'F' => 1, 'C' => 'b', 'K' => 'c', 'B' => 'a'],
|
||||
['A' => 0, 'B' => 'a', 'C' => 'b', 'F' => 1, 'K' => 'c'],
|
||||
];
|
||||
foreach ($tests as $test) {
|
||||
print "IMMUTABLE HASH FOR ARRAY: " . Hash::generateImmutableHashForArray($test) . "<br>";
|
||||
print "SERIALIZED HASH FOR ARRAY: " . hash(Hash::DEFAULT_HASH, serialize($test)) . "<br>";
|
||||
}
|
||||
|
||||
print "</body></html>";
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -8,6 +8,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\Create;
|
||||
|
||||
use CoreLibs\Combined\ArrayHandler;
|
||||
|
||||
class Hash
|
||||
{
|
||||
/** @var string default short hash -> deprecated use STANDARD_HASH_SHORT */
|
||||
@@ -210,6 +212,22 @@ class Hash
|
||||
{
|
||||
return self::hash($string, self::STANDARD_HASH);
|
||||
}
|
||||
|
||||
/**
|
||||
* generate a hash over any array data
|
||||
*
|
||||
* @param array<mixed> $data
|
||||
* @return string
|
||||
*/
|
||||
public static function generateImmutableHashForArray(array $data): string
|
||||
{
|
||||
// Create a sorted copy to ensure consistent hashing
|
||||
// Generate hash using serialize for better accuracy
|
||||
return hash(
|
||||
self::DEFAULT_HASH,
|
||||
serialize(ArrayHandler::createSortedArrayByKey($data))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
Reference in New Issue
Block a user