From 1fcadcd28e8439a7b0de9735884bfaeec8b904b2 Mon Sep 17 00:00:00 2001 From: Chris Reichel Date: Fri, 18 Oct 2024 09:04:47 +0200 Subject: [PATCH] fix ArrayHelper::map() for path strings ! don't use array_column if ArrayHelper strings are paths + add according tests + add BaseStringHelper::contains() helper function and according tests --- framework/CHANGELOG.md | 1 + framework/helpers/BaseArrayHelper.php | 2 +- framework/helpers/BaseStringHelper.php | 56 +++++++++++++++----- tests/framework/helpers/ArrayHelperTest.php | 13 +++++ tests/framework/helpers/StringHelperTest.php | 9 ++++ 5 files changed, 66 insertions(+), 15 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 1935d097811..dd4ff449450 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -10,6 +10,7 @@ Yii Framework 2 Change Log - Bug #20256: Add support for dropping views in MSSQL server when running migrate/fresh (ambrozt) - Enh #20248: Add support for attaching behaviors in configurations with Closure (timkelty) - Enh #20268: Minor optimisation in `\yii\helpers\BaseArrayHelper::map` (chriscpty) +- Enh #20268: Add `\yii\helpers\BaseStringHelper::contains()` as a polyfill for `str_contains` (chriscpty) 2.0.51 July 18, 2024 -------------------- diff --git a/framework/helpers/BaseArrayHelper.php b/framework/helpers/BaseArrayHelper.php index a92aadd9915..334f55112e0 100644 --- a/framework/helpers/BaseArrayHelper.php +++ b/framework/helpers/BaseArrayHelper.php @@ -595,7 +595,7 @@ public static function getColumn($array, $name, $keepKeys = true) */ public static function map($array, $from, $to, $group = null) { - if (is_string($from) && is_string($to) && $group === null) { + if (is_string($from) && is_string($to) && $group === null && !\yii\helpers\StringHelper::contains($from, '.') && !\yii\helpers\StringHelper::contains($to, '.')) { return array_column($array, $to, $from); } $result = []; diff --git a/framework/helpers/BaseStringHelper.php b/framework/helpers/BaseStringHelper.php index ec9252aa4c4..48f23c93ab7 100644 --- a/framework/helpers/BaseStringHelper.php +++ b/framework/helpers/BaseStringHelper.php @@ -226,6 +226,29 @@ protected static function truncateHtml($string, $count, $suffix, $encoding = fal return $generator->generateFromTokens($truncated) . ($totalCount >= $count ? $suffix : ''); } + /** + * @param string $string The input string. + * @param string $needle Part to search inside $string + * @param bool $caseSensitive Case sensitive search. Default is true. When case sensitive is enabled, `$with` must + * exactly match the starting of the string in order to get a true value. + * @return bool + */ + public static function contains($string, $needle, $caseSensitive = true) + { + $string = (string)$string; + $needle = (string)$needle; + + if ($caseSensitive) { + if (function_exists('str_contains')) { + return str_contains($string, $needle); + } + $encoding = Yii::$app ? Yii::$app->charset : 'UTF-8'; + return mb_strpos($string, $needle, 0, $encoding) !== false; + } + $encoding = Yii::$app ? Yii::$app->charset : 'UTF-8'; + return mb_stripos($string, $needle, 0, $encoding) !== false; + } + /** * Check if given string starts with specified substring. Binary and multibyte safe. * @@ -313,9 +336,14 @@ public static function explode($string, $delimiter = ',', $trim = true, $skipEmp } if ($skipEmpty) { // Wrapped with array_values to make array keys sequential after empty values removing - $result = array_values(array_filter($result, function ($value) { - return $value !== ''; - })); + $result = array_values( + array_filter( + $result, + function ($value) { + return $value !== ''; + } + ) + ); } return $result; @@ -343,7 +371,7 @@ public static function countWords($string) */ public static function normalizeNumber($value) { - $value = (string) $value; + $value = (string)$value; $localeInfo = localeconv(); $decimalSeparator = isset($localeInfo['decimal_point']) ? $localeInfo['decimal_point'] : null; @@ -396,7 +424,7 @@ public static function floatToString($number) { // . and , are the only decimal separators known in ICU data, // so its safe to call str_replace here - return str_replace(',', '.', (string) $number); + return str_replace(',', '.', (string)$number); } /** @@ -422,14 +450,14 @@ public static function matchWildcard($pattern, $string, $options = []) $replacements = [ '\\\\\\\\' => '\\\\', - '\\\\\\*' => '[*]', - '\\\\\\?' => '[?]', - '\*' => '.*', - '\?' => '.', - '\[\!' => '[^', - '\[' => '[', - '\]' => ']', - '\-' => '-', + '\\\\\\*' => '[*]', + '\\\\\\?' => '[?]', + '\*' => '.*', + '\?' => '.', + '\[\!' => '[^', + '\[' => '[', + '\]' => ']', + '\-' => '-', ]; if (isset($options['escape']) && !$options['escape']) { @@ -483,7 +511,7 @@ public static function mb_ucfirst($string, $encoding = 'UTF-8') */ public static function mb_ucwords($string, $encoding = 'UTF-8') { - $string = (string) $string; + $string = (string)$string; if (empty($string)) { return $string; } diff --git a/tests/framework/helpers/ArrayHelperTest.php b/tests/framework/helpers/ArrayHelperTest.php index a8afe19f6a6..a086503006b 100644 --- a/tests/framework/helpers/ArrayHelperTest.php +++ b/tests/framework/helpers/ArrayHelperTest.php @@ -772,6 +772,19 @@ static function (array $group) { ], ], $result); + $array = [ + ['id' => '123', 'name' => 'aaa', 'class' => 'x', 'map' => ['a' => '11', 'b' => '22']], + ['id' => '124', 'name' => 'bbb', 'class' => 'x', 'map' => ['a' => '33', 'b' => '44']], + ['id' => '345', 'name' => 'ccc', 'class' => 'y', 'map' => ['a' => '55', 'b' => '66']], + ]; + + $result = ArrayHelper::map($array, 'map.a', 'map.b'); + + $this->assertEquals([ + '11' => '22', + '33' => '44', + '55' => '66' + ], $result); } public function testKeyExists() diff --git a/tests/framework/helpers/StringHelperTest.php b/tests/framework/helpers/StringHelperTest.php index 5f222ec4e7a..49ffee0d876 100644 --- a/tests/framework/helpers/StringHelperTest.php +++ b/tests/framework/helpers/StringHelperTest.php @@ -150,6 +150,15 @@ public function testTruncateWords() $this->assertEquals('This is a test for...', StringHelper::truncateWords('This is a test for a sentance', 5, '...', true)); } + public function testContains() + { + $this->assertEquals(true, StringHelper::contains('Test', 'st')); + $this->assertEquals(false, StringHelper::contains('Test', 'ts')); + $this->assertEquals(false, StringHelper::contains('Test', 'St')); + $this->assertEquals(true, StringHelper::contains('Test', 'St', false)); + $this->assertEquals(false, StringHelper::contains('Test', 'Ste', false)); + } + /** * @dataProvider providerStartsWith * @param bool $result