diff --git a/system/Helpers/text_helper.php b/system/Helpers/text_helper.php index cf9459100dfb..09d14abbe2ee 100644 --- a/system/Helpers/text_helper.php +++ b/system/Helpers/text_helper.php @@ -383,7 +383,9 @@ function word_wrap(string $str, int $charlim = 76): string $temp = ''; - while (mb_strlen($line) > $charlim) { + // A limit of 1 can't be used to split a word (each piece needs room for + // the continuation), so the word is kept whole, like a long URL. + while ($charlim > 1 && mb_strlen($line) > $charlim) { // If the over-length word is a URL we won't wrap it if (preg_match('!\[url.+\]|://|www\.!', $line)) { break; diff --git a/tests/system/Helpers/TextHelperTest.php b/tests/system/Helpers/TextHelperTest.php index a4702db4b604..88878bcf8294 100644 --- a/tests/system/Helpers/TextHelperTest.php +++ b/tests/system/Helpers/TextHelperTest.php @@ -358,6 +358,27 @@ public function testEllipsize(): void } } + /** + * A character limit below 2 used to make word_wrap() loop forever. + */ + #[DataProvider('provideWordWrapSmallCharLimit')] + public function testWordWrapSmallCharLimit(int $charlim): void + { + $this->assertSame("aaaa\nbbbb", word_wrap('aaaa bbbb', $charlim)); + } + + /** + * @return iterable + */ + public static function provideWordWrapSmallCharLimit(): iterable + { + yield 'one' => [1]; + + yield 'zero' => [0]; + + yield 'negative' => [-5]; + } + public function testWordWrap(): void { $string = 'Here is a simple string of text that will help us demonstrate this function.'; diff --git a/user_guide_src/source/changelogs/v4.7.5.rst b/user_guide_src/source/changelogs/v4.7.5.rst index deabaf4b7ab4..492db76c20c7 100644 --- a/user_guide_src/source/changelogs/v4.7.5.rst +++ b/user_guide_src/source/changelogs/v4.7.5.rst @@ -57,6 +57,7 @@ Bugs Fixed - **Debug:** Fixed a bug where ``Timer::start()`` treated ``0.0`` as an empty value and substituted the current time. - **Files:** Fixed a bug where ``File::move()`` and ``UploadedFile::move()`` set executable and overly permissive file permissions (``0777 & ~umask()`` instead of ``0666 & ~umask()``), and ``UploadedFile::move()`` targeted the parent directory instead of the destination file for ``chmod()``. - **Helpers:** Fixed a bug where ``get_dir_file_info()`` returned incomplete entries for subdirectories and missing files instead of omitting them. +- **Helpers:** Fixed a bug where ``word_wrap()`` entered an infinite loop when the character limit was below 2. Over-length words are now kept whole in that case. - **Honeypot:** Fixed a bug where bot detection returned an HTTP 500 response instead of 403 (Forbidden). - **I18n:** Fixed a bug where ``Time::today()``, ``Time::yesterday()``, and ``Time::tomorrow()`` ignored the specified ``$timezone`` and ``setTestNow()`` when calculating the day. - **Logger:** Fixed a bug where interpolating a log message with array or non-stringable context values could raise PHP warnings or errors.