From 3d96a45f6b2ded171c607e20b9f3e39a618ab9cb Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Tue, 8 May 2018 16:39:55 +0300 Subject: [PATCH] Fixed BC breaking change in Inflector Fixes #16239 --- framework/helpers/BaseInflector.php | 24 +++-------- framework/helpers/BaseStringHelper.php | 36 ++++++++++++++++ tests/framework/helpers/InflectorTest.php | 6 ++- tests/framework/helpers/StringHelperTest.php | 45 ++++++++++++++++++++ 4 files changed, 90 insertions(+), 21 deletions(-) diff --git a/framework/helpers/BaseInflector.php b/framework/helpers/BaseInflector.php index 5b640a6206..282f6e9e73 100644 --- a/framework/helpers/BaseInflector.php +++ b/framework/helpers/BaseInflector.php @@ -342,7 +342,7 @@ class BaseInflector { $words = static::humanize(static::underscore($words), $ucAll); - return $ucAll ? mb_convert_case($words, MB_CASE_TITLE, self::encoding()) : self::mb_ucfirst($words, self::encoding()); + return $ucAll ? StringHelper::mb_ucwords($words, self::encoding()) : StringHelper::mb_ucfirst($words, self::encoding()); } /** @@ -357,7 +357,7 @@ class BaseInflector */ public static function camelize($word) { - return str_replace(' ', '', mb_convert_case(preg_replace('/[^\pL\pN]+/u', ' ', $word), MB_CASE_TITLE, self::encoding())); + return str_replace(' ', '', StringHelper::mb_ucwords(preg_replace('/[^\pL\pN]+/u', ' ', $word), self::encoding())); } /** @@ -375,7 +375,7 @@ class BaseInflector '.', ], ' ', preg_replace('/(\p{Lu})/u', ' \0', $name))), self::encoding()); - return $ucwords ? mb_convert_case($label, MB_CASE_TITLE, self::encoding()) : $label; + return $ucwords ? StringHelper::mb_ucwords($label, self::encoding()) : $label; } /** @@ -407,7 +407,7 @@ class BaseInflector */ public static function id2camel($id, $separator = '-') { - return str_replace(' ', '', mb_convert_case(str_replace($separator, ' ', $id), MB_CASE_TITLE, self::encoding())); + return str_replace(' ', '', StringHelper::mb_ucwords(str_replace($separator, ' ', $id), self::encoding())); } /** @@ -431,7 +431,7 @@ class BaseInflector $word = str_replace('_', ' ', preg_replace('/_id$/', '', $word)); $encoding = self::encoding(); - return $ucAll ? mb_convert_case($word, MB_CASE_TITLE, $encoding) : self::mb_ucfirst($word, $encoding); + return $ucAll ? StringHelper::mb_ucwords($word, $encoding) : StringHelper::mb_ucfirst($word, $encoding); } /** @@ -609,18 +609,4 @@ class BaseInflector return isset(Yii::$app) ? Yii::$app->charset : 'UTF-8'; } - /** - * The same as built-in `ucfirst`, but unicode-safe - * - * @param string $string - * @param string $encoding - * @return string - */ - private static function mb_ucfirst($string, $encoding) - { - $firstChar = mb_substr($string, 0, 1, $encoding); - $rest = mb_substr($string, 1, null, $encoding); - - return mb_strtoupper($firstChar, $encoding) . $rest; - } } diff --git a/framework/helpers/BaseStringHelper.php b/framework/helpers/BaseStringHelper.php index 524f1d729f..e3a304ac04 100644 --- a/framework/helpers/BaseStringHelper.php +++ b/framework/helpers/BaseStringHelper.php @@ -418,4 +418,40 @@ class BaseStringHelper return preg_match($pattern, $string) === 1; } + + /** + * This method provides a unicode-safe implementation of built-in PHP function `ucfirst()`. + * + * @param string $string the string to be proceeded + * @param string $encoding Optional, defaults to "UTF-8" + * @return string + * @see http://php.net/manual/en/function.ucfirst.php + * @since 2.0.16 + */ + public static function mb_ucfirst($string, $encoding = 'UTF-8') + { + $firstChar = mb_substr($string, 0, 1, $encoding); + $rest = mb_substr($string, 1, null, $encoding); + + return mb_strtoupper($firstChar, $encoding) . $rest; + } + + /** + * This method provides a unicode-safe implementation of built-in PHP function `ucwords()`. + * + * @param string $string the string to be proceeded + * @param string $encoding Optional, defaults to "UTF-8" + * @see http://php.net/manual/en/function.ucwords.php + * @return string + */ + public static function mb_ucwords($string, $encoding = 'UTF-8') + { + $words = preg_split("/\s/u", $string, -1, PREG_SPLIT_NO_EMPTY); + + $titelized = array_map(function ($word) use ($encoding) { + return static::mb_ucfirst($word, $encoding); + }, $words); + + return implode(' ', $titelized); + } } diff --git a/tests/framework/helpers/InflectorTest.php b/tests/framework/helpers/InflectorTest.php index 728b92119a..6b921df452 100644 --- a/tests/framework/helpers/InflectorTest.php +++ b/tests/framework/helpers/InflectorTest.php @@ -89,9 +89,9 @@ class InflectorTest extends TestCase public function testCamelize() { - $this->assertEquals('MeMySelfAndi', Inflector::camelize('me my_self-andI')); + $this->assertEquals('MeMySelfAndI', Inflector::camelize('me my_self-andI')); $this->assertEquals('QweQweEwq', Inflector::camelize('qwe qwe^ewq')); - $this->assertEquals('ВідомоЩоТестиЗберігатьНашіНерви', Inflector::camelize('Відомо, що тести зберігать наші НЕРВИ! 🙃')); + $this->assertEquals('ВідомоЩоТестиЗберігатьНашіНЕРВИ', Inflector::camelize('Відомо, що тести зберігать наші НЕРВИ! 🙃')); } public function testUnderscore() @@ -138,6 +138,8 @@ class InflectorTest extends TestCase $this->assertEquals('PostTag', Inflector::id2camel('post_tag', '_')); $this->assertEquals('НевжеІЦеПрацює', Inflector::id2camel('невже_і_це_працює', '_')); + $this->assertEquals('ShouldNotBecomeLowercased', Inflector::id2camel('ShouldNotBecomeLowercased', '_')); + $this->assertEquals('FooYBar', Inflector::id2camel('foo-y-bar')); $this->assertEquals('FooYBar', Inflector::id2camel('foo_y_bar', '_')); } diff --git a/tests/framework/helpers/StringHelperTest.php b/tests/framework/helpers/StringHelperTest.php index 419c1e0b85..5f6416e6a4 100644 --- a/tests/framework/helpers/StringHelperTest.php +++ b/tests/framework/helpers/StringHelperTest.php @@ -16,6 +16,7 @@ use yiiunit\TestCase; */ class StringHelperTest extends TestCase { + protected function setUp() { parent::setUp(); @@ -399,4 +400,48 @@ class StringHelperTest extends TestCase { $this->assertSame($expectedResult, StringHelper::matchWildcard($pattern, $string, $options)); } + + public function dataProviderMb_ucfirst() + { + return [ + ['foo', 'Foo'], + ['foo bar', 'Foo bar'], + ['👍🏻 foo bar', '👍🏻 foo bar'], + ['', ''], + [null, ''], + ['здесь我 multibyte我 строка', 'Здесь我 multibyte我 строка'], + ]; + } + + /** + * @param string $string + * @param string $expectedResult + * @dataProvider dataProviderMb_ucfirst + */ + public function testMb_ucfirst($string, $expectedResult) + { + $this->assertSame($expectedResult, StringHelper::mb_ucfirst($string)); + } + + public function dataProviderMb_ucwords() + { + return [ + ['foo', 'Foo'], + ['foo bar', 'Foo Bar'], + ['👍🏻 foo bar', '👍🏻 Foo Bar'], + ['', ''], + [null, ''], + ['здесь我 multibyte我 строка', 'Здесь我 Multibyte我 Строка'], + ]; + } + + /** + * @param string $string + * @param string $expectedResult + * @dataProvider dataProviderMb_ucwords + */ + public function testMb_ucwords($string, $expectedResult) + { + $this->assertSame($expectedResult, StringHelper::mb_ucwords($string)); + } }