diff --git a/UPGRADE-7.2.md b/UPGRADE-7.2.md index 20a062663a6fd..53a2752c956be 100644 --- a/UPGRADE-7.2.md +++ b/UPGRADE-7.2.md @@ -17,3 +17,11 @@ Yaml ---- * Deprecate parsing duplicate mapping keys whose value is `null` + +String +------ + + * `truncate` method now also accept `TruncateMode` enum instead of a boolean: + * `TruncateMode::Char` is equivalent to `true` value ; + * `TruncateMode::WordAfter` is equivalent to `true` value ; + * `TruncateMode::Word` is a new mode that will cut the sentence on the last word before the limit is reached. diff --git a/src/Symfony/Component/String/AbstractString.php b/src/Symfony/Component/String/AbstractString.php index d68f334095fd5..176a7ae1d1ac4 100644 --- a/src/Symfony/Component/String/AbstractString.php +++ b/src/Symfony/Component/String/AbstractString.php @@ -605,7 +605,7 @@ public function trimSuffix($suffix): static return $str; } - public function truncate(int $length, string $ellipsis = '', bool $cut = true): static + public function truncate(int $length, string $ellipsis = '', bool|TruncateMode $cut = TruncateMode::Char): static { $stringLength = $this->length(); @@ -619,7 +619,8 @@ public function truncate(int $length, string $ellipsis = '', bool $cut = true): $ellipsisLength = 0; } - if (!$cut) { + $desiredLength = $length; + if (TruncateMode::WordAfter === $cut || TruncateMode::WordBefore === $cut || !$cut) { if (null === $length = $this->indexOf([' ', "\r", "\n", "\t"], ($length ?: 1) - 1)) { return clone $this; } @@ -629,6 +630,14 @@ public function truncate(int $length, string $ellipsis = '', bool $cut = true): $str = $this->slice(0, $length - $ellipsisLength); + if (TruncateMode::WordBefore === $cut) { + if (0 === $ellipsisLength && $desiredLength === $this->indexOf([' ', "\r", "\n", "\t"], $length)) { + return $str; + } + + $str = $str->beforeLast([' ', "\r", "\n", "\t"]); + } + return $ellipsisLength ? $str->trimEnd()->append($ellipsis) : $str; } diff --git a/src/Symfony/Component/String/CHANGELOG.md b/src/Symfony/Component/String/CHANGELOG.md index 621cedfcddedf..f1f7204784f7d 100644 --- a/src/Symfony/Component/String/CHANGELOG.md +++ b/src/Symfony/Component/String/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.2 +--- + + * Add `TruncateMode` enum to handle more truncate methods + 7.1 --- diff --git a/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php b/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php index f8b0509ffa185..3cbcd0837a32e 100644 --- a/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php +++ b/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php @@ -16,6 +16,7 @@ use Symfony\Component\String\ByteString; use Symfony\Component\String\CodePointString; use Symfony\Component\String\Exception\InvalidArgumentException; +use Symfony\Component\String\TruncateMode; use Symfony\Component\String\UnicodeString; abstract class AbstractAsciiTestCase extends TestCase @@ -1500,22 +1501,24 @@ public static function providePadStart() /** * @dataProvider provideTruncate */ - public function testTruncate(string $expected, string $origin, int $length, string $ellipsis, bool $cut = true) + public function testTruncate(string $expected, string $origin, int $length, string $ellipsis, bool|TruncateMode $cut = TruncateMode::Char) { $instance = static::createFromString($origin)->truncate($length, $ellipsis, $cut); $this->assertEquals(static::createFromString($expected), $instance); } - public static function provideTruncate() + public static function provideTruncate(): array { return [ ['', '', 3, ''], ['', 'foo', 0, '...'], ['foo', 'foo', 0, '...', false], + ['foo', 'foo', 0, '...', TruncateMode::WordAfter], ['fo', 'foobar', 2, ''], ['foobar', 'foobar', 10, ''], ['foobar', 'foobar', 10, '...', false], + ['foobar', 'foobar', 10, '...', TruncateMode::WordAfter], ['foo', 'foo', 3, '...'], ['fo', 'foobar', 2, '...'], ['...', 'foobar', 3, '...'], @@ -1524,6 +1527,14 @@ public static function provideTruncate() ['foobar...', 'foobar foo', 7, '...', false], ['foobar foo...', 'foobar foo a', 10, '...', false], ['foobar foo aar', 'foobar foo aar', 12, '...', false], + ['foobar...', 'foobar foo', 6, '...', TruncateMode::WordAfter], + ['foobar...', 'foobar foo', 7, '...', TruncateMode::WordAfter], + ['foobar foo...', 'foobar foo a', 10, '...', TruncateMode::WordAfter], + ['foobar foo aar', 'foobar foo aar', 12, '...', TruncateMode::WordAfter], + ['foobar foo', 'foobar foo aar', 10, '', TruncateMode::WordBefore], + ['foobar...', 'foobar foo aar', 10, '...', TruncateMode::WordBefore], + ['Lorem ipsum', 'Lorem ipsum dolor sit amet', 14, '', TruncateMode::WordBefore], + ['Lorem...', 'Lorem ipsum dolor sit amet', 10, '...', TruncateMode::WordBefore], ]; } diff --git a/src/Symfony/Component/String/TruncateMode.php b/src/Symfony/Component/String/TruncateMode.php new file mode 100644 index 0000000000000..12568cd5b2fa5 --- /dev/null +++ b/src/Symfony/Component/String/TruncateMode.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String; + +enum TruncateMode +{ + /** + * Will cut exactly at given length. + * + * Length: 14 + * Source: Lorem ipsum dolor sit amet + * Output: Lorem ipsum do + */ + case Char; + + /** + * Returns the string up to the last complete word containing the specified length. + * + * Length: 14 + * Source: Lorem ipsum dolor sit amet + * Output: Lorem ipsum + */ + case WordBefore; + + /** + * Returns the string up to the complete word after or at the given length. + * + * Length: 14 + * Source: Lorem ipsum dolor sit amet + * Output: Lorem ipsum dolor + */ + case WordAfter; +}