diff --git a/4dev/tests/Create/CoreLibsCreateHashTest.php b/4dev/tests/Create/CoreLibsCreateHashTest.php index 9ce36b35..24863635 100644 --- a/4dev/tests/Create/CoreLibsCreateHashTest.php +++ b/4dev/tests/Create/CoreLibsCreateHashTest.php @@ -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 $input + * @param string $expected + */ + public function testGenerateImmutableHashForArray(array $input, string $expected): void + { + $this->assertEquals( + $expected, + \CoreLibs\Create\Hash::generateImmutableHashForArray($input) + ); + } } // __END__ diff --git a/www/admin/class_test.hash.php b/www/admin/class_test.hash.php index bc4d7853..b721c543 100644 --- a/www/admin/class_test.hash.php +++ b/www/admin/class_test.hash.php @@ -103,6 +103,19 @@ foreach ($hash_types as $hash_type) { // print "UNIQU ID SHORT : " . Hash::__uniqId() . "
"; // print "UNIQU ID LONG : " . Hash::__uniqIdLong() . "
"; +$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) . "
"; + print "SERIALIZED HASH FOR ARRAY: " . hash(Hash::DEFAULT_HASH, serialize($test)) . "
"; +} + print ""; // __END__ diff --git a/www/lib/CoreLibs/Create/Hash.php b/www/lib/CoreLibs/Create/Hash.php index 9ed734db..ac1e29bd 100644 --- a/www/lib/CoreLibs/Create/Hash.php +++ b/www/lib/CoreLibs/Create/Hash.php @@ -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 $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__