From e2773e452491f2664d4ef0d11e2bb557c6d19112 Mon Sep 17 00:00:00 2001 From: Saleh Hashemi <81674631+salehhashemi1992@users.noreply.github.com> Date: Wed, 25 Oct 2023 20:17:03 +0330 Subject: [PATCH] Fix #20032: Added `mask` method for string masking with multibyte support --- framework/CHANGELOG.md | 2 ++ framework/helpers/BaseStringHelper.php | 30 ++++++++++++++++++ tests/framework/helpers/StringHelperTest.php | 32 ++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 69bcec2bd8..ec2923a1e5 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -11,6 +11,8 @@ Yii Framework 2 Change Log - Bug #20002: Fixed superfluous query on HEAD request in serializer (xicond) - Enh #12743: Added new methods `BaseActiveRecord::loadRelations()` and `BaseActiveRecord::loadRelationsFor()` to eager load related models for existing primary model instances (PowerGamer1) - Enh #20030: Improve performance of handling `ErrorHandler::$memoryReserveSize` (antonshevelev, rob006) +- Enh #20032: Added `mask` method for string masking with multibyte support (salehhashemi1992) + 2.0.49.2 October 12, 2023 ------------------------- diff --git a/framework/helpers/BaseStringHelper.php b/framework/helpers/BaseStringHelper.php index 60261f9828..e9c5327b03 100644 --- a/framework/helpers/BaseStringHelper.php +++ b/framework/helpers/BaseStringHelper.php @@ -497,4 +497,34 @@ class BaseStringHelper return implode('', $parts); } + + /** + * Masks a portion of a string with a repeated character. + * This method is multibyte-safe. + * + * @param string $string The input string. + * @param int $start The starting position from where to begin masking. + * This can be a positive or negative integer. + * Positive values count from the beginning, + * negative values count from the end of the string. + * @param int $length The length of the section to be masked. + * The masking will start from the $start position + * and continue for $length characters. + * @param string $mask The character to use for masking. The default is '*'. + * @return string The masked string. + */ + public static function mask($string, $start, $length, $mask = '*') { + $strLength = mb_strlen($string, 'UTF-8'); + + // Return original string if start position is out of bounds + if ($start >= $strLength || $start < -$strLength) { + return $string; + } + + $masked = mb_substr($string, 0, $start, 'UTF-8'); + $masked .= str_repeat($mask, abs($length)); + $masked .= mb_substr($string, $start + abs($length), null, 'UTF-8'); + + return $masked; + } } diff --git a/tests/framework/helpers/StringHelperTest.php b/tests/framework/helpers/StringHelperTest.php index 56acdb0c26..a640e5cdda 100644 --- a/tests/framework/helpers/StringHelperTest.php +++ b/tests/framework/helpers/StringHelperTest.php @@ -474,4 +474,36 @@ class StringHelperTest extends TestCase ['', ''], ]; } + + public function testMask() + { + // Standard masking + $this->assertSame('12******90', StringHelper::mask('1234567890', 2, 6)); + $this->assertSame('a********j', StringHelper::mask('abcdefghij', 1, 8)); + $this->assertSame('*************', StringHelper::mask('Hello, World!', 0, 13)); + $this->assertSame('************!', StringHelper::mask('Hello, World!', 0, 12)); + $this->assertSame('Hello, *orld!', StringHelper::mask('Hello, World!', 7, 1)); + $this->assertSame('Saleh Hashemi', StringHelper::mask('Saleh Hashemi', 0, 0)); + + // Different Mask Character + $this->assertSame('12######90', StringHelper::mask('1234567890', 2, 6, '#')); + + // Positions outside the string + $this->assertSame('1234567890', StringHelper::mask('1234567890', 20, 6)); + $this->assertSame('1234567890', StringHelper::mask('1234567890', -20, 6)); + + // Negative values for start + $this->assertSame('1234****90', StringHelper::mask('1234567890', -6, 4)); + + // type-related edge case + $this->assertSame('1234****90', StringHelper::mask(1234567890, -6, 4)); + + // Multibyte characters + $this->assertSame('你**', StringHelper::mask('你好吗', 1, 2)); + $this->assertSame('你好吗', StringHelper::mask('你好吗', 4, 2)); + + // Special characters + $this->assertSame('em**l@email.com', StringHelper::mask('email@email.com', 2, 2)); + $this->assertSame('******email.com', StringHelper::mask('email@email.com', 0, 6)); + } }