From fae960b892c4ecafac705aa0a1d38f1ba1251498 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 24 Sep 2026 11:57:26 +0900 Subject: [PATCH] Array various functions, DB IO strip assoc, Hash over array immutable type --- src/Check/File.php | 2 +- src/Combined/ArrayHandler.php | 111 ++++- src/Create/Hash.php | 23 ++ src/Create/RandomKey.php | 10 +- src/DB/IO.php | 58 ++- src/DB/Support/ConvertPlaceholder.php | 2 +- .../CoreLibsCombinedArrayHandlerTest.php | 388 +++++++++++++++++- .../phpunit/Create/CoreLibsCreateHashTest.php | 94 +++++ test/phpunit/DB/CoreLibsDBIOTest.php | 53 +++ 9 files changed, 687 insertions(+), 54 deletions(-) diff --git a/src/Check/File.php b/src/Check/File.php index a3f4244..e123374 100644 --- a/src/Check/File.php +++ b/src/Check/File.php @@ -60,7 +60,7 @@ class File * @param string $read_file File to read, relative or absolute path * @return string mime type * @throws \UnexpectedValueException if file cannot be read or is not a file - * @throws \RangeException if we cannot get a mime type and throw exception is on + * @throws \RangeException if we cannot get a mime type */ public static function getMimeType(string $read_file): string { diff --git a/src/Combined/ArrayHandler.php b/src/Combined/ArrayHandler.php index d7e789b..53a8bc2 100644 --- a/src/Combined/ArrayHandler.php +++ b/src/Combined/ArrayHandler.php @@ -248,12 +248,15 @@ class ArrayHandler * If not found return an array with the array block there the required key is missing, * the path as string with seperator block set and the missing key entry * - * @param array $in_array - * @param string|int|float|bool $search_value - * @param string|array $required_key - * @param ?string $search_key [null] - * @param string $path_separator [DATA_SEPARATOR] - * @param string $current_path + * alternate to findArraysWithMissingKey (single missing key) + * + * @param array $in_array Array to search in + * @param string|int|float|bool $search_value Value that must be set in any + * (or with search_key in one entry of the array) + * @param string|array $required_key Key or Keys that must be set + * @param ?string $search_key [null] limit search_value check to this key + * @param string $path_separator [DATA_SEPARATOR] For the path string separator + * @param string $current_path [''] Current path in the array (used for recursion) * @return array,path?:string,missing_key?:array}> */ public static function findArraysMissingKey( @@ -272,14 +275,14 @@ class ArrayHandler // Check if this array contains the search value // either any value match or with key if ($search_key === null) { - $containsValue = in_array($search_value, $value, true); + $contains_value = in_array($search_value, $value, true); } else { - $containsValue = array_key_exists($search_key, $value) && $value[$search_key] === $search_value; + $contains_value = array_key_exists($search_key, $value) && $value[$search_key] === $search_value; } // If it contains the value but doesn't have the required key if ( - $containsValue && + $contains_value && ( ( is_string($required_key) && @@ -475,8 +478,7 @@ class ArrayHandler throw new \ArgumentCountError(__FUNCTION__ . ' needs two or more array arguments'); } $merged = []; - while ($in_arrays) { - $in_array = array_shift($in_arrays); + foreach ($in_arrays as $in_array) { if (!is_array($in_array)) { throw new \TypeError(__FUNCTION__ . ' encountered a non array argument'); } @@ -811,6 +813,93 @@ class ArrayHandler ); return $in_array; } + + /** + * return one random entry from an array (value) + * the return entry is null if the incoming array is empty + * the array can also be an array of arrays + * + * @param array $array + * @return string|int|float|bool|array|null + */ + public static function getRandomEntryFromArray(array $array): string|int|float|bool|array|null + { + if (empty($array)) { + return null; + } + $random_index = array_rand($array); + return $array[$random_index]; + } + + /** + * find and remove one value + * on default does not strict compare + * + * @param array $array + * @param string|int|float|bool|array $value + * @param bool $strict [false] + * @return array + */ + public static function removeArrayEntryByValue( + array $array, + string|int|float|bool|array $value, + bool $strict = false, + ): array { + if (empty($array)) { + return $array; + } + return array_filter($array, function ($element) use ($value, $strict) { + return $strict ? + $element !== $value : + $element != $value; + }); + } + + /** + * add a string to each element in an array, default is at the end of the value, + * can be switched with the prefix flag + * + * @param array $array + * @param string $string_to_add + * @param bool $prefix [false] + * @return array + */ + public static function addStringToEachValueInArray(array $array, string $string_to_add, bool $prefix = false): array + { + // nothing set, skip + if (empty($string_to_add) || empty($array)) { + return $array; + } + return array_map(function ($value) use ($string_to_add, $prefix) { + return $prefix ? + $string_to_add . $value : + $value . $string_to_add; + }, $array); + } + + /** + * sort by key any array, even if it is nested + * + * @param array $data + * @return array + */ + public static function createSortedArrayByKey(array $data): array + { + if (empty($data)) { + return $data; + } + // Sort by keys + ksort($data); + + // Recursively sort nested arrays + foreach ($data as $key => $value) { + if (is_array($value)) { + $data[$key] = self::createSortedArrayByKey($value); + } + } + + return $data; + } } // __END__ diff --git a/src/Create/Hash.php b/src/Create/Hash.php index 9ed734d..5159e5f 100644 --- a/src/Create/Hash.php +++ b/src/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,27 @@ class Hash { return self::hash($string, self::STANDARD_HASH); } + + /** + * generate a hash over any array data + * optional will not sort keys and so create hashes based on the original data. + * for correct array comapre it is recommended to keep sort keys on + * + * @param array $data + * @param bool $sort_keys [true] Whether to sort the array keys before hashing + * @return string + */ + public static function generateImmutableHashForArray(array $data, bool $sort_keys = true): string + { + // Create a sorted copy to ensure consistent hashing + // Generate hash using serialize for better accuracy + return hash( + self::DEFAULT_HASH, + serialize( + $sort_keys ? ArrayHandler::createSortedArrayByKey($data) : $data + ) + ); + } } // __END__ diff --git a/src/Create/RandomKey.php b/src/Create/RandomKey.php index 59b100e..2c14c2e 100644 --- a/src/Create/RandomKey.php +++ b/src/Create/RandomKey.php @@ -154,11 +154,11 @@ class RandomKey * if override key length is set, it will check on valid key and use this * this will not set the class key length variable * - * @param int $key_length [default=-1] key length override, - * if not set use default [LEGACY] - * @param array $key_range a list of key ranges as array, - * if not set use previous set data - * @return string random key + * @param int $key_length [default=-1] key length override, + * if not set use default [LEGACY] + * @param array ...$key_range a list of key ranges as array, + * if not set use previous set data + * @return string random key */ public static function randomKeyGen( int $key_length = self::KEY_LENGTH_DEFAULT, diff --git a/src/DB/IO.php b/src/DB/IO.php index 5c083dc..6f1f598 100644 --- a/src/DB/IO.php +++ b/src/DB/IO.php @@ -520,7 +520,7 @@ class IO } // ************************************************************* - // PRIVATE METHODS + // MARK: PRIVATE METHODS // ************************************************************* /** @@ -1703,11 +1703,11 @@ class IO } // ************************************************************* - // PUBLIC METHODS + // MARK: PUBLIC METHODS // ************************************************************* // *************************** - // CLOSE, STATUS, SETTINGS VARIABLE READ + // MARK: CLOSE, STATUS, SETTINGS VARIABLE READ // *************************** /** @@ -1950,7 +1950,7 @@ class IO } // *************************** - // DEBUG DATA DUMP + // MARK: DEBUG DATA DUMP // *************************** /** @@ -1978,7 +1978,7 @@ class IO } // *************************** - // CHECK QUERY TYPE + // MARK: CHECK QUERY TYPE // *************************** /** @@ -2035,7 +2035,7 @@ class IO } // *************************** - // DATA WRITE CONVERSION + // MARK: DATA WRITE CONVERSION // *************************** /** @@ -2156,7 +2156,7 @@ class IO } // *************************** - // DATA READ/WRITE CONVERSION + // MARK: DATA READ/WRITE CONVERSION // *************************** /** @@ -2194,7 +2194,7 @@ class IO } // *************************** - // DATA READ CONVERSION + // MARK: DATA READ CONVERSION // *************************** /** @@ -2263,8 +2263,22 @@ class IO return is_array($__db_array_parse) ? $__db_array_parse : []; } + /** + * Will strip all single numberic entries from the array. + * This is for database return + * + * @param array $res + * @return array + */ + public function stripAssocOnly(array $res): array + { + return array_filter($res, function ($key) { + return !is_numeric($key); + }, ARRAY_FILTER_USE_KEY); + } + // *************************** - // TABLE META DATA READ + // MARK: TABLE META DATA READ // *************************** /** @@ -2290,7 +2304,7 @@ class IO } // *************************** - // QUERY EXECUSION AND DATA READ + // MARK: QUERY EXECUSION AND DATA READ // *************************** /** @@ -2927,7 +2941,7 @@ class IO } // *************************** - // CURSOR RETURN + // MARK: CURSOR RETURN // *************************** /** @@ -2941,7 +2955,7 @@ class IO } // *************************** - // CURSOR EXT CACHE RESET + // MARK: CURSOR EXT CACHE RESET // *************************** /** @@ -2971,7 +2985,7 @@ class IO } // *************************** - // CURSOR EXT DATA CHECK + // MARK: CURSOR EXT DATA CHECK // *************************** /** @@ -3066,7 +3080,7 @@ class IO } // *************************** - // MAXIMUM QUERY EXECUTION CHECK HELPERS + // MARK: MAXIMUM QUERY EXECUTION CHECK HELPERS // *************************** /** @@ -3102,7 +3116,7 @@ class IO } // *************************** - // PREPARED QUERY WORK + // MARK: PREPARED QUERY WORK // *************************** /** @@ -3379,7 +3393,7 @@ class IO } // *************************** - // ASYNCHRONUS EXECUTION/CHECK + // MARK: ASYNCHRONUS EXECUTION/CHECK // *************************** /** @@ -3539,7 +3553,7 @@ class IO } // *************************** - // COMPLEX WRITE WITH CONFIG ARRAYS + // MARK: COMPLEX WRITE WITH CONFIG ARRAYS // *************************** // ** REMARK ** @@ -3720,7 +3734,7 @@ class IO } // *************************** - // INTERNAL SETTINGS READ/CHANGE + // MARK: INTERNAL SETTINGS READ/CHANGE // *************************** /** @@ -4048,7 +4062,7 @@ class IO } // *************************** - // QUERY DATA AND DB HANDLER + // MARK: QUERY DATA AND DB HANDLER // *************************** /** @@ -4151,7 +4165,7 @@ class IO } // *************************** - // INTERNAL VARIABLES READ POST QUERY RUN + // MARK: INTERNAL VARIABLES READ POST QUERY RUN // *************************** /** @@ -4442,7 +4456,7 @@ class IO } // *************************** - // ERROR AND WARNING DATA + // MARK: ERROR AND WARNING DATA // *************************** /** @@ -4499,7 +4513,7 @@ class IO } // *************************** - // DEPEREACTED CALLS + // MARK: DEPEREACTED CALLS // all call below are no longer in use and throw deprecated errors // *************************** diff --git a/src/DB/Support/ConvertPlaceholder.php b/src/DB/Support/ConvertPlaceholder.php index 9640a5b..5081f01 100644 --- a/src/DB/Support/ConvertPlaceholder.php +++ b/src/DB/Support/ConvertPlaceholder.php @@ -290,7 +290,7 @@ class ConvertPlaceholder } $match = $matches[self::MATCHING_POS]; // only count pos up for actual replacements we will do - if (!empty($match)) { + if (strlen((string)$match) > 0) { $pos++; $params_lookup[] = '$' . $pos; } diff --git a/test/phpunit/Combined/CoreLibsCombinedArrayHandlerTest.php b/test/phpunit/Combined/CoreLibsCombinedArrayHandlerTest.php index a49760a..bf693e0 100644 --- a/test/phpunit/Combined/CoreLibsCombinedArrayHandlerTest.php +++ b/test/phpunit/Combined/CoreLibsCombinedArrayHandlerTest.php @@ -16,7 +16,7 @@ use PHPUnit\Framework\TestCase; */ final class CoreLibsCombinedArrayHandlerTest extends TestCase { - // we use that for all + /** @var array we use that for all */ public static $array = [ 'a' => [ 'b' => 'bar', @@ -51,7 +51,7 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase /** * Undocumented function * - * @return array + * @return array */ public function arraySearchRecursiveProvider(): array { @@ -110,7 +110,7 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase /** * Undocumented function * - * @return array + * @return array */ public function arraySearchRecursiveAllProvider(): array { @@ -182,7 +182,7 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase /** * Undocumented function * - * @return array + * @return array */ public function arraySearchSimpleProvider(): array { @@ -383,7 +383,7 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase /** * Undocumented function * - * @return array + * @return array */ public function arraySearchKeyProvider(): array { @@ -565,7 +565,7 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase /** * provides array listing for the merge test * - * @return array + * @return array */ public function arrayMergeRecursiveProvider(): array { @@ -602,7 +602,7 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase /** * for warning checks * - * @return array + * @return array */ public function arrayMergeRecursiveProviderWarning(): array { @@ -634,7 +634,7 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase /** * Undocumented function * - * @return array + * @return array */ public function arrayCompareProvider(): array { @@ -665,7 +665,7 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase /** * Undocumented function * - * @return array + * @return array */ public function inArrayAnyProvider(): array { @@ -688,6 +688,11 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ]; } + /** + * Undocumented function + * + * @return array + */ public function genAssocArrayProvider(): array { return [ @@ -764,7 +769,7 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase /** * Undocumented function * - * @return array + * @return array */ public function flattenArrayProvider(): array { @@ -808,7 +813,7 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase /** * use the flattenArrayProvider and replace 1 with 2 array pos * - * @return array + * @return array */ public function flattenArrayKeyProvider(): array { @@ -825,7 +830,7 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase /** * use the flattenArrayProvider and replace 1 with 3 array pos * - * @return array + * @return array */ public function flattenArrayKeyLeavesOnlyProvider(): array { @@ -842,7 +847,7 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase /** * Undocumented function * - * @return array + * @return array */ public function arrayFlatForKeyProvider(): array { @@ -906,6 +911,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ]; } + // MARK: arraySearchRecursive + /** * Undocumented function * @@ -926,6 +933,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: arraySearchRecursiveAll + /** * Undocumented function * @@ -947,6 +956,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: arraySearchSimple + /** * Undocumented function * @@ -969,6 +980,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: arraySearchKey + /** * Undocumented function * @@ -1007,6 +1020,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: arrayMergeRecursive + /** * Undocumented function * @@ -1031,6 +1046,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: arrayMergeRecursive + /** * Undocumented function * @@ -1063,6 +1080,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase restore_error_handler(); } + // MARK: arrayDiff + /** * Undocumented function * @@ -1083,6 +1102,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: inArrayAny + /** * Undocumented function * @@ -1103,6 +1124,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: genAssocArray + /** * Undocumented function * @@ -1125,6 +1148,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: flattenArray + /** * Undocumented function * @@ -1144,6 +1169,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: flattenArrayKey + /** * Undocumented function * @@ -1163,6 +1190,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: flattenArrayKeyLeavesOnly + /** * Undocumented function * @@ -1182,6 +1211,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: arrayFlatForKey + /** * Undocumented function * @@ -1202,6 +1233,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: arrayGetPrevKey, arrayGetNextKey + /** * Undocumented function * @@ -1295,6 +1328,13 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: arrayReturnMatchingKeyOnly + + /** + * Undocumented function + * + * @return array + */ public function providerReturnMatchingKeyOnley(): array { return [ @@ -1380,6 +1420,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: arrayModifyKey + /** * provider for arrayModifyKey * @@ -1492,6 +1534,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: sortArray + /** * sort * @@ -1711,7 +1755,9 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase } } -/** + // MARK: ksortArray + + /** * sort * * @return array @@ -1867,6 +1913,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase } } + // MARK: findArraysMissingKey + /** * Undocumented function * @@ -2034,6 +2082,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase ); } + // MARK: selectArrayFromOption + /** * Undocumented function * @@ -2379,6 +2429,316 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase $result ); } + + // MARK: getRandomEntryFromArrayProvider + + /** + * Get random entry from array provider + * + * @return array + */ + public function getRandomEntryFromArrayProvider(): array + { + return [ + 'empty array' => [ + [], + ], + 'single element array' => [ + ['only'], + ], + 'multiple elements array' => [ + ['first', 'second', 'third'], + ], + ]; + } + + /** + * Undocumented function + * + * @covers ::getRandomEntryFromArray + * @dataProvider getRandomEntryFromArrayProvider + * @testdox getRandomEntryFromArray get random value from array [$_dataName] + * + * @param array $input + * @return void + */ + public function testGetRandomEntryFromArray(array $input): void + { + $result = \CoreLibs\Combined\ArrayHandler::getRandomEntryFromArray($input); + if (empty($input)) { + $this->assertNull($result); + } else { + $this->assertContains($result, $input); + } + } + + // MARK: removeArrayEntryByValue + + /** + * Remove array entry by value provider + * + * @return array + */ + public function removeArrayEntryByValueProvider(): array + { + return [ + 'empty array' => [ + [], + '', + null, + [], + ], + 'single element array' => [ + ['only'], + 'only', + null, + [] + ], + 'single element array, not found' => [ + ['only'], + 'foo', + null, + ['only'] + ], + 'multiple elements array' => [ + ['first', 'second', 'third'], + 'second', + null, + ['first', 'third'], + ], + 'array with multiple identical elements' => [ + ['first', 'second', 'first', 'third'], + 'first', + null, + ['second', 'third'], + ], + 'array with multiple identical elements, other remove' => [ + ['first', 'second', 'first', 'third'], + 'second', + null, + ['first', 'first', 'third'], + ], + 'array with key value entries' => [ + [ + 'key1' => 'value1', + 'key2' => 'value2', + 'key3' => 'value3', + ], + 'value2', + null, + [ + 'key1' => 'value1', + 'key3' => 'value3', + ] + ], + 'nested array, array match' => [ + [ + ['first', 'second'], + ['third', 'fourth'], + ], + ['third', 'fourth'], + null, + [ + ['first', 'second'], + ], + ], + 'strict test, off' => [ + ['3', 3,], + '3', + null, + [], + ], + 'strict test, on' => [ + ['3', 3,], + '3', + true, + [3,], + ], + 'strict test bool, off' => [ + ['false', false, 0], + false, + null, + ['false'], + ], + 'strict test bool, on' => [ + ['false', false, 0], + false, + true, + ['false', 0], + ], + 'strict test float, off' => [ + ['3.14', 3.14], + 3.14, + null, + [], + ], + 'strict test float, on' => [ + ['3.14', 3.14], + 3.14, + true, + ['3.14'], + ], + 'stric test float and int, off' => [ + ['1.0', '1', 1.0, 1], + 1, + null, + [], + ], + 'stric test float and int, on' => [ + ['1.0', '1', 1.0, 1], + 1, + true, + ['1.0', '1', 1.0], + ], + ]; + } + + /** + * Test for removeArrayEntryByValue function + * + * @covers ::removeArrayEntryByValue + * @dataProvider removeArrayEntryByValueProvider + * @testdox removeArrayEntryByValue Remove $value from array $input, with $strict [$_dataName] + * + * @param array $input + * @param string|int|float|bool|array $value + * @param bool|null $strict + * @param array $expected + * @return void + */ + public function testRemoveArrayEntryByValue( + array $input, + string|int|float|bool|array $value, + ?bool $strict, + array $expected + ): void { + if ($strict !== null) { + $result = \CoreLibs\Combined\ArrayHandler::removeArrayEntryByValue($input, $value, $strict); + } else { + $result = \CoreLibs\Combined\ArrayHandler::removeArrayEntryByValue($input, $value); + } + $this->assertEquals(array_values($expected), array_values($result)); + } + + // MARK: addStringToEachValueInArray + + /** + * Data provider for addStringToEachValueInArray tests. + * + * @return array + */ + public function addStringToEachValueInArrayProvider(): array + { + return [ + 'empty array' => [ + [], + 'string', + null, + [], + ], + 'empty string to add' => [ + ['a', 'b'], + '', + null, + ['a', 'b'], + ], + 'prefix true' => [ + ['a', 'b'], + 'pre_', + true, + ['pre_a', 'pre_b'], + ], + 'prefix false' => [ + ['a', 'b'], + '_suf', + false, + ['a_suf', 'b_suf'], + ], + 'mixed values' => [ + ['a', 'b', 'c', 5, 6], + 'X', + true, + ['Xa', 'Xb', 'Xc', 'X5', 'X6'], + ] + ]; + } + + /** + * Test for addStringToEachValueInArray function + * + * @covers ::addStringToEachValueInArray + * @dataProvider addStringToEachValueInArrayProvider + * @testdox addStringToEachValueInArray Add $string_to_add to each value in $array, with $prefix [$_dataName] + * + * @param array $array + * @param string $string_to_add + * @param bool|null $prefix + * @param array|null $expected + * @return void + */ + public function testAddStringToEachValueInArray( + array $array, + string $string_to_add, + ?bool $prefix, + ?array $expected + ) { + if ($prefix == null) { + $result = \CoreLibs\Combined\ArrayHandler::addStringToEachValueInArray($array, $string_to_add); + } else { + $result = \CoreLibs\Combined\ArrayHandler::addStringToEachValueInArray($array, $string_to_add, $prefix); + } + $this->assertEquals($expected, $result); + } + + // MARK: createSortedArrayByKey + + /** + * Data provider for createSortedArrayByKey tests. + * + * @return array + */ + public function createSortedArrayByKeyProvider(): array + { + return [ + 'empty' => [ + [], + [], + ], + 'flat array' => [ + ['b' => 2, 'a' => 1, 'c' => 3], + ['a' => 1, 'b' => 2, 'c' => 3], + ], + 'nested array' => [ + ['b' => 2, 'a' => 1, 'c' => ['d' => 4, 'b' => 2]], + ['a' => 1, 'b' => 2, 'c' => ['b' => 2, 'd' => 4]], + ], + 'deeply nested array' => [ + ['b' => 2, 'a' => 1, 'c' => ['d' => 4, 'b' => 2, 'e' => ['g' => 7, 'f' => 6]]], + ['a' => 1, 'b' => 2, 'c' => ['b' => 2, 'd' => 4, 'e' => ['f' => 6, 'g' => 7]]], + ], + 'mixed key type' => [ + ['b' => 2, 1 => 'one', 'a' => 1], + [1 => 'one', 'a' => 1, 'b' => 2], + ], + ]; + } + + /** + * Test for createSortedArrayByKey function + * + * @covers ::createSortedArrayByKey + * @dataProvider createSortedArrayByKeyProvider + * @testdox createSortedArrayByKey Sort the array by keys recursively [$_dataName] + * + * @param array $input + * @param array $expected + * @return void + */ + public function testCreateSortedArrayByKey(array $input, array $expected): void + { + $result = \CoreLibs\Combined\ArrayHandler::createSortedArrayByKey($input); + $this->assertEquals($expected, $result); + } } // __END__ diff --git a/test/phpunit/Create/CoreLibsCreateHashTest.php b/test/phpunit/Create/CoreLibsCreateHashTest.php index 9ce36b3..c95f23c 100644 --- a/test/phpunit/Create/CoreLibsCreateHashTest.php +++ b/test/phpunit/Create/CoreLibsCreateHashTest.php @@ -421,6 +421,100 @@ final class CoreLibsCreateHashTest extends TestCase 'hash hmac valid' ); } + + // MARK: generateImmutableHashForArray + + /** + * Data provider for testGenerateImmutableHashForArray. + * + * @return array + */ + public function generateImmutableHashForArrayProvider(): array + { + return [ + 'Empty array' => [ + [], + null, + '064f01fe', + ], + 'Simple array' => [ + ['key' => 'value'], + null, + '78f40899', + ], + 'flat array a' => [ + [0, 1, 'b', 'c', 'a'], + null, + 'ccec0f5b', + ], + 'flat array b' => [ + ['b', 'c', 'a', 1, 0], + null, + 'c6040f5b', + ], + 'flat array b, not sorted' => [ + ['b', 'c', 'a', 1, 0], + false, + 'c6040f5b', + ], + 'key array a' => [ + ['A' => 0, 'F' => 1, 'C' => 'b', 'K' => 'c', 'B' => 'a'], + null, + '01f11355', + ], + 'key array a, not sorted' => [ + ['A' => 0, 'F' => 1, 'C' => 'b', 'K' => 'c', 'B' => 'a'], + false, + '04911355', + ], + 'key array b' => [ + ['A' => 0, 'B' => 'a', 'C' => 'b', 'F' => 1, 'K' => 'c'], + null, + '01f11355', + ], + 'key array b, not sorted' => [ + ['A' => 0, 'B' => 'a', 'C' => 'b', 'F' => 1, 'K' => 'c'], + false, + '01f11355', + ], + 'complex nested array' => [ + [ + 'level1' => [ + 'level2' => [ + 'level3' => 'value' + ] + ] + ], + null, + '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, ?bool $sort_keys, string $expected): void + { + if ($sort_keys === null) { + $this->assertEquals( + $expected, + \CoreLibs\Create\Hash::generateImmutableHashForArray($input) + ); + } else { + $this->assertEquals( + $expected, + \CoreLibs\Create\Hash::generateImmutableHashForArray($input, $sort_keys) + ); + } + } } // __END__ diff --git a/test/phpunit/DB/CoreLibsDBIOTest.php b/test/phpunit/DB/CoreLibsDBIOTest.php index fdbd45d..f521ba5 100644 --- a/test/phpunit/DB/CoreLibsDBIOTest.php +++ b/test/phpunit/DB/CoreLibsDBIOTest.php @@ -1007,6 +1007,59 @@ final class CoreLibsDBIOTest extends TestCase $db->dbClose(); } + // MARK: stripAssocOnly + + /** + * Data provider for stripAssocOnly tests + * + * @return array + */ + public function stripAssocOnlyProvider(): array + { + return [ + 'empty' => [ + [], + [] + ], + 'int only' => [ + [0 => 'foo', 1 => 'bar'], + [], + ], + 'mixed' => [ + [0 => 'foo', 'col_a' => 'foo', 1 => 'bar', 'col_b' => 'bar'], + ['col_a' => 'foo', 'col_b' => 'bar'], + ], + 'key only' => [ + ['col_a' => 'foo', 'col_b' => 'bar'], + ['col_a' => 'foo', 'col_b' => 'bar'], + ] + ]; + } + + /** + * Test for stripAssocOnly function + * + * @covers ::stripAssocOnly + * @dataProvider stripAssocOnlyProvider + * @testdox strip assoc non string entries from return [$_dataName] + * + * @param array $input + * @param array $expected + * @return void + */ + public function testStripAssocOnly(array $input, array $expected): void + { + $db = new \CoreLibs\DB\IO( + self::$db_config['valid'], + self::$log + ); + $this->assertEquals( + $expected, + $db->stripAssocOnly($input) + ); + $db->dbClose(); + } + // - string escape tests // dbEscapeString, dbEscapeLiteral, dbEscapeIdentifier,