[php-src] PHP-8.4: Fix wordwrap() ignoring an existing break at the end of the string (#24116)
| From: | Nicolas Grekas via GitHub | Date: | Mon, 05 Oct 2026 17:44:39 +0000 |
| Subject: | [php-src] PHP-8.4: Fix wordwrap() ignoring an existing break at the end of the string (#24116) | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-141971@lists.php.net to get a copy of this message | ||
Author: Nicolas Grekas (nicolas-grekas)
Committer: GitHub (web-flow)
Pusher: arnaud-lb
Date: 2026-10-05T19:44:36+02:00
Commit: https://github.com/php/php-src/commit/f817996280d7dc184a27b93dca29a2e4a8633090
Raw diff: https://github.com/php/php-src/commit/f817996280d7dc184a27b93dca29a2e4a8633090.diff
Fix wordwrap() ignoring an existing break at the end of the string (#24116)
When a multi-character break or $cut_long_words is used, an existing
break was only recognized when at least one character followed it. A
trailing break was handled as regular text, so wordwrap("abc\n", 3,
"\n", true) returned "abc\n\n" and wordwrap("ab cd\n", 5,
"\n", true)
returned "ab\ncd\n". The single-character fast path was not affected.
Changed paths:
A ext/standard/tests/strings/wordwrap_trailing_break.phpt
M NEWS
M ext/standard/string.c
Diff:
diff --git a/NEWS b/NEWS
index 6134971c6ba6..14b0d0b08db1 100644
--- a/NEWS
+++ b/NEWS
@@ -219,6 +219,8 @@ PHP NEWS
PROCESS_INFORMATION, an indeterminate comspec pointer after a failed
lookup, and an unchecked CreateFileA() failure. (Ilia Alshanetsky)
. Fix persistent stream context lifetime during shutdown (Levi Morrison)
+ . Fix wordwrap() ignoring an existing break at the end of the string
+ (Nicolas Grekas)
- Tidy:
. Fixed a use-after-free when a tidyNode is used after its document is
diff --git a/ext/standard/string.c b/ext/standard/string.c
index 34444d80d185..40b05135a8e8 100644
--- a/ext/standard/string.c
+++ b/ext/standard/string.c
@@ -779,7 +779,7 @@ PHP_FUNCTION(wordwrap)
/* when we hit an existing break, copy to new buffer, and
* fix up laststart and lastspace */
if (ZSTR_VAL(text)[current] == breakchar[0]
- && current + breakchar_len < ZSTR_LEN(text)
+ && current + breakchar_len <= ZSTR_LEN(text)
&& !strncmp(ZSTR_VAL(text) + current, breakchar, breakchar_len)) {
memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart +
breakchar_len);
newtextlen += current - laststart + breakchar_len;
diff --git a/ext/standard/tests/strings/wordwrap_trailing_break.phpt
b/ext/standard/tests/strings/wordwrap_trailing_break.phpt
new file mode 100644
index 000000000000..2e57ce176665
--- /dev/null
+++ b/ext/standard/tests/strings/wordwrap_trailing_break.phpt
@@ -0,0 +1,28 @@
+--TEST--
+wordwrap() recognizes an existing break at the end of the string
+--FILE--
+<?php
+$tests = [
+ ["abc\n", 3, "\n", true],
+ ["ab cd\n", 5, "\n", true],
+ ["abcd\n", 3, "\n", true],
+ ["abc\nabc\n", 3, "\n", true],
+ ["abc-", 3, "-", true],
+ ["abc\r\n", 3, "\r\n", true],
+ ["\r\n", 1, "\r\n", true],
+ ["ab cd\r\n", 5, "\r\n", false],
+];
+
+foreach ($tests as [$text, $width, $break, $cut]) {
+ echo json_encode(wordwrap($text, $width, $break, $cut)), "\n";
+}
+?>
+--EXPECT--
+"abc\n"
+"ab cd\n"
+"abc\nd\n"
+"abc\nabc\n"
+"abc-"
+"abc\r\n"
+"\r\n"
+"ab cd\r\n"