From 1a665f86db2da543922e00ca957bec24004813a1 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 31 Oct 2023 11:58:03 +0900 Subject: [PATCH 01/10] refactor: move ArrayHelper to Helpers/Array/ArrayHelper.php --- system/Commands/Translation/LocalizationFinder.php | 2 +- .../LocalizationFinder => Helpers/Array}/ArrayHelper.php | 4 ++-- .../LocalizationFinder => Helpers/Array}/ArrayHelperTest.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename system/{Commands/Translation/LocalizationFinder => Helpers/Array}/ArrayHelper.php (93%) rename tests/system/{Commands/Translation/LocalizationFinder => Helpers/Array}/ArrayHelperTest.php (98%) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index e5147093d2dc..d8fbbf1aad50 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -13,7 +13,7 @@ use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; -use CodeIgniter\Commands\Translation\LocalizationFinder\ArrayHelper; +use CodeIgniter\Helpers\Array\ArrayHelper; use Config\App; use Locale; use RecursiveDirectoryIterator; diff --git a/system/Commands/Translation/LocalizationFinder/ArrayHelper.php b/system/Helpers/Array/ArrayHelper.php similarity index 93% rename from system/Commands/Translation/LocalizationFinder/ArrayHelper.php rename to system/Helpers/Array/ArrayHelper.php index c3cf4b9c53d9..4b4b99ae63a9 100644 --- a/system/Commands/Translation/LocalizationFinder/ArrayHelper.php +++ b/system/Helpers/Array/ArrayHelper.php @@ -9,10 +9,10 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Commands\Translation\LocalizationFinder; +namespace CodeIgniter\Helpers\Array; /** - * @see \CodeIgniter\Commands\Translation\LocalizationFinder\ArrayHelperTest + * @see \CodeIgniter\Helpers\Array\ArrayHelperTest */ class ArrayHelper { diff --git a/tests/system/Commands/Translation/LocalizationFinder/ArrayHelperTest.php b/tests/system/Helpers/Array/ArrayHelperTest.php similarity index 98% rename from tests/system/Commands/Translation/LocalizationFinder/ArrayHelperTest.php rename to tests/system/Helpers/Array/ArrayHelperTest.php index cc64f3cd3cbf..ac94b20fb2da 100644 --- a/tests/system/Commands/Translation/LocalizationFinder/ArrayHelperTest.php +++ b/tests/system/Helpers/Array/ArrayHelperTest.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Commands\Translation\LocalizationFinder; +namespace CodeIgniter\Helpers\Array; use CodeIgniter\Test\CIUnitTestCase; From 514fe62f81cf8d165077aaaaa2ff49f0db75e74a Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 31 Oct 2023 12:08:50 +0900 Subject: [PATCH 02/10] refactor: make ArrayHelper final --- system/Helpers/Array/ArrayHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Helpers/Array/ArrayHelper.php b/system/Helpers/Array/ArrayHelper.php index 4b4b99ae63a9..8a74b23bc09b 100644 --- a/system/Helpers/Array/ArrayHelper.php +++ b/system/Helpers/Array/ArrayHelper.php @@ -14,7 +14,7 @@ /** * @see \CodeIgniter\Helpers\Array\ArrayHelperTest */ -class ArrayHelper +final class ArrayHelper { /** * Compare recursively two associative arrays and return difference as new array. From bc4f308b47a52ac56ac909907223cf9a278972a6 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 31 Oct 2023 12:09:11 +0900 Subject: [PATCH 03/10] refactor: move code from array_helper.php to ArrayHelper.php --- system/Helpers/Array/ArrayHelper.php | 143 +++++++++++++++++++++++++++ system/Helpers/array_helper.php | 130 +----------------------- 2 files changed, 147 insertions(+), 126 deletions(-) diff --git a/system/Helpers/Array/ArrayHelper.php b/system/Helpers/Array/ArrayHelper.php index 8a74b23bc09b..222896f73468 100644 --- a/system/Helpers/Array/ArrayHelper.php +++ b/system/Helpers/Array/ArrayHelper.php @@ -16,6 +16,149 @@ */ final class ArrayHelper { + /** + * Searches an array through dot syntax. Supports + * wildcard searches, like foo.*.bar + * + * @return array|bool|int|object|string|null + */ + public static function dotArraySearch(string $index, array $array) + { + // See https://regex101.com/r/44Ipql/1 + $segments = preg_split( + '/(? str_replace('\.', '.', $key), $segments); + + return self::arraySearchDot($segments, $array); + } + + /** + * Used by `dotArraySearch()` to recursively search the + * array with wildcards. + * + * @return array|bool|float|int|object|string|null + */ + private static function arraySearchDot(array $indexes, array $array) + { + // If index is empty, returns null. + if ($indexes === []) { + return null; + } + + // Grab the current index + $currentIndex = array_shift($indexes); + + if (! isset($array[$currentIndex]) && $currentIndex !== '*') { + return null; + } + + // Handle Wildcard (*) + if ($currentIndex === '*') { + $answer = []; + + foreach ($array as $value) { + if (! is_array($value)) { + return null; + } + + $answer[] = self::arraySearchDot($indexes, $value); + } + + $answer = array_filter($answer, static fn ($value) => $value !== null); + + if ($answer !== []) { + if (count($answer) === 1) { + // If array only has one element, we return that element for BC. + return current($answer); + } + + return $answer; + } + + return null; + } + + // If this is the last index, make sure to return it now, + // and not try to recurse through things. + if (empty($indexes)) { + return $array[$currentIndex]; + } + + // Do we need to recursively search this value? + if (is_array($array[$currentIndex]) && $array[$currentIndex] !== []) { + return self::arraySearchDot($indexes, $array[$currentIndex]); + } + + // Otherwise, not found. + return null; + } + + /** + * Groups all rows by their index values. Result's depth equals number of indexes + * + * @param array $array Data array (i.e. from query result) + * @param array $indexes Indexes to group by. Dot syntax used. Returns $array if empty + * @param bool $includeEmpty If true, null and '' are also added as valid keys to group + * + * @return array Result array where rows are grouped together by indexes values. + */ + public static function arrayGroupBy(array $array, array $indexes, bool $includeEmpty = false): array + { + if ($indexes === []) { + return $array; + } + + $result = []; + + foreach ($array as $row) { + $result = self::arrayAttachIndexedValue($result, $row, $indexes, $includeEmpty); + } + + return $result; + } + + /** + * Used by `arrayGroupBy()` to recursively attach $row to the $indexes path of values found by + * `dot_array_search()` + * + * @internal This should not be used on its own + */ + private static function arrayAttachIndexedValue(array $result, array $row, array $indexes, bool $includeEmpty): array + { + if (($index = array_shift($indexes)) === null) { + $result[] = $row; + + return $result; + } + + $value = dot_array_search($index, $row); + + if (! is_scalar($value)) { + $value = ''; + } + + if (is_bool($value)) { + $value = (int) $value; + } + + if (! $includeEmpty && $value === '') { + return $result; + } + + if (! array_key_exists($value, $result)) { + $result[$value] = []; + } + + $result[$value] = self::arrayAttachIndexedValue($result[$value], $row, $indexes, $includeEmpty); + + return $result; + } + /** * Compare recursively two associative arrays and return difference as new array. * Returns keys that exist in `$original` but not in `$compareWith`. diff --git a/system/Helpers/array_helper.php b/system/Helpers/array_helper.php index 65645c3cc316..75b345c831bc 100644 --- a/system/Helpers/array_helper.php +++ b/system/Helpers/array_helper.php @@ -9,6 +9,8 @@ * the LICENSE file that was distributed with this source code. */ +use CodeIgniter\Helpers\Array\ArrayHelper; + // CodeIgniter Array Helpers if (! function_exists('dot_array_search')) { @@ -20,82 +22,7 @@ */ function dot_array_search(string $index, array $array) { - // See https://regex101.com/r/44Ipql/1 - $segments = preg_split( - '/(? str_replace('\.', '.', $key), $segments); - - return _array_search_dot($segments, $array); - } -} - -if (! function_exists('_array_search_dot')) { - /** - * Used by `dot_array_search` to recursively search the - * array with wildcards. - * - * @internal This should not be used on its own. - * - * @return array|bool|float|int|object|string|null - */ - function _array_search_dot(array $indexes, array $array) - { - // If index is empty, returns null. - if ($indexes === []) { - return null; - } - - // Grab the current index - $currentIndex = array_shift($indexes); - - if (! isset($array[$currentIndex]) && $currentIndex !== '*') { - return null; - } - - // Handle Wildcard (*) - if ($currentIndex === '*') { - $answer = []; - - foreach ($array as $value) { - if (! is_array($value)) { - return null; - } - - $answer[] = _array_search_dot($indexes, $value); - } - - $answer = array_filter($answer, static fn ($value) => $value !== null); - - if ($answer !== []) { - if (count($answer) === 1) { - // If array only has one element, we return that element for BC. - return current($answer); - } - - return $answer; - } - - return null; - } - - // If this is the last index, make sure to return it now, - // and not try to recurse through things. - if (empty($indexes)) { - return $array[$currentIndex]; - } - - // Do we need to recursively search this value? - if (is_array($array[$currentIndex]) && $array[$currentIndex] !== []) { - return _array_search_dot($indexes, $array[$currentIndex]); - } - - // Otherwise, not found. - return null; + return ArrayHelper::dotArraySearch($index, $array); } } @@ -231,55 +158,6 @@ function array_flatten_with_dots(iterable $array, string $id = ''): array */ function array_group_by(array $array, array $indexes, bool $includeEmpty = false): array { - if ($indexes === []) { - return $array; - } - - $result = []; - - foreach ($array as $row) { - $result = _array_attach_indexed_value($result, $row, $indexes, $includeEmpty); - } - - return $result; - } -} - -if (! function_exists('_array_attach_indexed_value')) { - /** - * Used by `array_group_by` to recursively attach $row to the $indexes path of values found by - * `dot_array_search` - * - * @internal This should not be used on its own - */ - function _array_attach_indexed_value(array $result, array $row, array $indexes, bool $includeEmpty): array - { - if (($index = array_shift($indexes)) === null) { - $result[] = $row; - - return $result; - } - - $value = dot_array_search($index, $row); - - if (! is_scalar($value)) { - $value = ''; - } - - if (is_bool($value)) { - $value = (int) $value; - } - - if (! $includeEmpty && $value === '') { - return $result; - } - - if (! array_key_exists($value, $result)) { - $result[$value] = []; - } - - $result[$value] = _array_attach_indexed_value($result[$value], $row, $indexes, $includeEmpty); - - return $result; + return ArrayHelper::arrayGroupBy($array, $indexes, $includeEmpty); } } From d1f1ec4b1a265326bf77128c691c8254b36e7183 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 31 Oct 2023 12:55:26 +0900 Subject: [PATCH 04/10] refactor: replace empty() --- system/Helpers/Array/ArrayHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Helpers/Array/ArrayHelper.php b/system/Helpers/Array/ArrayHelper.php index 222896f73468..e4be38293683 100644 --- a/system/Helpers/Array/ArrayHelper.php +++ b/system/Helpers/Array/ArrayHelper.php @@ -85,7 +85,7 @@ private static function arraySearchDot(array $indexes, array $array) // If this is the last index, make sure to return it now, // and not try to recurse through things. - if (empty($indexes)) { + if ($indexes === []) { return $array[$currentIndex]; } From 775ded877d7532727ff4a0f29edbfbc5d7a22369 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 31 Oct 2023 12:55:44 +0900 Subject: [PATCH 05/10] chore: update phpstan-baseline.php --- phpstan-baseline.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpstan-baseline.php b/phpstan-baseline.php index 894db3a12616..3a9e665be916 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -2293,7 +2293,7 @@ ]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', - 'count' => 3, + 'count' => 2, 'path' => __DIR__ . '/system/Helpers/array_helper.php', ]; $ignoreErrors[] = [ From abb664ec04febcdf38fb2395dc63fd881056ce83 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 31 Oct 2023 13:05:03 +0900 Subject: [PATCH 06/10] refactor: make method names shorter --- system/Helpers/Array/ArrayHelper.php | 4 ++-- system/Helpers/array_helper.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/system/Helpers/Array/ArrayHelper.php b/system/Helpers/Array/ArrayHelper.php index e4be38293683..ab6755d03daf 100644 --- a/system/Helpers/Array/ArrayHelper.php +++ b/system/Helpers/Array/ArrayHelper.php @@ -22,7 +22,7 @@ final class ArrayHelper * * @return array|bool|int|object|string|null */ - public static function dotArraySearch(string $index, array $array) + public static function dotSearch(string $index, array $array) { // See https://regex101.com/r/44Ipql/1 $segments = preg_split( @@ -107,7 +107,7 @@ private static function arraySearchDot(array $indexes, array $array) * * @return array Result array where rows are grouped together by indexes values. */ - public static function arrayGroupBy(array $array, array $indexes, bool $includeEmpty = false): array + public static function groupBy(array $array, array $indexes, bool $includeEmpty = false): array { if ($indexes === []) { return $array; diff --git a/system/Helpers/array_helper.php b/system/Helpers/array_helper.php index 75b345c831bc..6d501e273202 100644 --- a/system/Helpers/array_helper.php +++ b/system/Helpers/array_helper.php @@ -22,7 +22,7 @@ */ function dot_array_search(string $index, array $array) { - return ArrayHelper::dotArraySearch($index, $array); + return ArrayHelper::dotSearch($index, $array); } } @@ -158,6 +158,6 @@ function array_flatten_with_dots(iterable $array, string $id = ''): array */ function array_group_by(array $array, array $indexes, bool $includeEmpty = false): array { - return ArrayHelper::arrayGroupBy($array, $indexes, $includeEmpty); + return ArrayHelper::groupBy($array, $indexes, $includeEmpty); } } From 629a1c1ab8dbe38c2139aca96805ba096c111a42 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 31 Oct 2023 13:08:52 +0900 Subject: [PATCH 07/10] test: rename test classname --- system/Helpers/Array/ArrayHelper.php | 2 +- .../{ArrayHelperTest.php => ArrayHelperRecursiveDiffTest.php} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename tests/system/Helpers/Array/{ArrayHelperTest.php => ArrayHelperRecursiveDiffTest.php} (98%) diff --git a/system/Helpers/Array/ArrayHelper.php b/system/Helpers/Array/ArrayHelper.php index ab6755d03daf..2a1a46385723 100644 --- a/system/Helpers/Array/ArrayHelper.php +++ b/system/Helpers/Array/ArrayHelper.php @@ -12,7 +12,7 @@ namespace CodeIgniter\Helpers\Array; /** - * @see \CodeIgniter\Helpers\Array\ArrayHelperTest + * @see \CodeIgniter\Helpers\Array\ArrayHelperRecursiveDiffTest */ final class ArrayHelper { diff --git a/tests/system/Helpers/Array/ArrayHelperTest.php b/tests/system/Helpers/Array/ArrayHelperRecursiveDiffTest.php similarity index 98% rename from tests/system/Helpers/Array/ArrayHelperTest.php rename to tests/system/Helpers/Array/ArrayHelperRecursiveDiffTest.php index ac94b20fb2da..81cf87ecfc46 100644 --- a/tests/system/Helpers/Array/ArrayHelperTest.php +++ b/tests/system/Helpers/Array/ArrayHelperRecursiveDiffTest.php @@ -18,7 +18,7 @@ * * @internal */ -final class ArrayHelperTest extends CIUnitTestCase +final class ArrayHelperRecursiveDiffTest extends CIUnitTestCase { private array $compareWith; From b225032b4fce9d95e61404fcd181adeff0ac8791 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 31 Oct 2023 13:22:48 +0900 Subject: [PATCH 08/10] docs: add @used-by --- system/Helpers/Array/ArrayHelper.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/system/Helpers/Array/ArrayHelper.php b/system/Helpers/Array/ArrayHelper.php index 2a1a46385723..d60c94291258 100644 --- a/system/Helpers/Array/ArrayHelper.php +++ b/system/Helpers/Array/ArrayHelper.php @@ -17,8 +17,10 @@ final class ArrayHelper { /** - * Searches an array through dot syntax. Supports - * wildcard searches, like foo.*.bar + * Searches an array through dot syntax. Supports wildcard searches, + * like `foo.*.bar`. + * + * @used-by dot_array_search() * * @return array|bool|int|object|string|null */ @@ -38,8 +40,9 @@ public static function dotSearch(string $index, array $array) } /** - * Used by `dotArraySearch()` to recursively search the - * array with wildcards. + * Recursively search the array with wildcards. + * + * @used-by dotSearch() * * @return array|bool|float|int|object|string|null */ @@ -101,6 +104,8 @@ private static function arraySearchDot(array $indexes, array $array) /** * Groups all rows by their index values. Result's depth equals number of indexes * + * @used-by array_group_by() + * * @param array $array Data array (i.e. from query result) * @param array $indexes Indexes to group by. Dot syntax used. Returns $array if empty * @param bool $includeEmpty If true, null and '' are also added as valid keys to group @@ -123,10 +128,10 @@ public static function groupBy(array $array, array $indexes, bool $includeEmpty } /** - * Used by `arrayGroupBy()` to recursively attach $row to the $indexes path of values found by - * `dot_array_search()` + * Recursively attach $row to the $indexes path of values found by + * `dot_array_search()`. * - * @internal This should not be used on its own + * @used-by groupBy() */ private static function arrayAttachIndexedValue(array $result, array $row, array $indexes, bool $includeEmpty): array { From 3d167be52c9d56d6ab5a22a25e47c87acdd3d552 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 31 Oct 2023 15:18:58 +0900 Subject: [PATCH 09/10] style: break long line --- system/Helpers/Array/ArrayHelper.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/system/Helpers/Array/ArrayHelper.php b/system/Helpers/Array/ArrayHelper.php index d60c94291258..8195a2121506 100644 --- a/system/Helpers/Array/ArrayHelper.php +++ b/system/Helpers/Array/ArrayHelper.php @@ -133,8 +133,12 @@ public static function groupBy(array $array, array $indexes, bool $includeEmpty * * @used-by groupBy() */ - private static function arrayAttachIndexedValue(array $result, array $row, array $indexes, bool $includeEmpty): array - { + private static function arrayAttachIndexedValue( + array $result, + array $row, + array $indexes, + bool $includeEmpty + ): array { if (($index = array_shift($indexes)) === null) { $result[] = $row; From 112fcea91523552561315ebbcc6ad699b90e1dff Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 31 Oct 2023 20:25:31 +0900 Subject: [PATCH 10/10] docs: add @internal I don't want to provide pubic static method. --- system/Helpers/Array/ArrayHelper.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/system/Helpers/Array/ArrayHelper.php b/system/Helpers/Array/ArrayHelper.php index 8195a2121506..37382af4d012 100644 --- a/system/Helpers/Array/ArrayHelper.php +++ b/system/Helpers/Array/ArrayHelper.php @@ -12,6 +12,11 @@ namespace CodeIgniter\Helpers\Array; /** + * @interal This is internal implementation for the framework. + * + * If there are any methods that should be provided, make them + * public APIs via helper functions. + * * @see \CodeIgniter\Helpers\Array\ArrayHelperRecursiveDiffTest */ final class ArrayHelper