From 1832a68424b4d45dea0686b23d9386575c16711b Mon Sep 17 00:00:00 2001 From: maide Date: Mon, 22 Sep 2014 22:49:36 -0400 Subject: [PATCH] Fix nl2br to allow carriage returns. nl2br() now matches PHP to allow prepending of html new lines to carriage returns. We now also handles special cases when there are carriage returns beside newlines (they count as one linebreak instead of two). --- hphp/runtime/ext/ext_string.cpp | 43 +++++++++++++++++++++++++++----- hphp/test/quick/nl2br.php | 22 ++++++++++++++++ hphp/test/quick/nl2br.php.expect | 16 ++++++++++++ 3 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 hphp/test/quick/nl2br.php create mode 100644 hphp/test/quick/nl2br.php.expect diff --git a/hphp/runtime/ext/ext_string.cpp b/hphp/runtime/ext/ext_string.cpp index 093fff12678cb2..874ac405ff99c7 100644 --- a/hphp/runtime/ext/ext_string.cpp +++ b/hphp/runtime/ext/ext_string.cpp @@ -276,18 +276,49 @@ Variant f_hex2bin(const String& str) { return ret.detach(); } -extern const StaticString s_nl; - const StaticString - s_br("
\n"), - s_non_xhtml_br("
\n"); + s_br("
"), + s_non_xhtml_br("
"); String f_nl2br(const String& str, bool is_xhtml /* = true */) { + if (str.empty()) { + return str; + } + + String htmlType; if (is_xhtml) { - return string_replace(str, s_nl, s_br); + htmlType = s_br; } else { - return string_replace(str, s_nl, s_non_xhtml_br); + htmlType = s_non_xhtml_br; } + + return stringForEachBuffered(str.size(), str, + [&] (StringBuffer& ret, const char*& src, const char* end) { + // PHP treats a carriage return beside a newline as the same break + // no matter what order they're in. Don't do it for two of the same in + // a row, though... + switch (*src) { + case '\n': + ret.append(htmlType); + // skip next if carriage return + if (*(src + 1) == '\r') { + ret.append(*src); + ++src; + } + ret.append(*src); + break; + case '\r': + ret.append(htmlType); + // skip next if newline + if (*(src + 1) == '\n') { + ret.append(*src); + ++src; + } + /* fall through */ + default: + ret.append(*src); + } + }); } String f_quotemeta(const String& str) { diff --git a/hphp/test/quick/nl2br.php b/hphp/test/quick/nl2br.php new file mode 100644 index 00000000000000..50c730fe26b019 --- /dev/null +++ b/hphp/test/quick/nl2br.php @@ -0,0 +1,22 @@ +\nmy
\r\nfriend
\n\r" //case from issue +); +foreach ($stringList as $string) { + var_dump(escapeNewLine(nl2br($string, true))); + var_dump(escapeNewLine(nl2br($string, false))); +} diff --git a/hphp/test/quick/nl2br.php.expect b/hphp/test/quick/nl2br.php.expect new file mode 100644 index 00000000000000..ef07df642d4d84 --- /dev/null +++ b/hphp/test/quick/nl2br.php.expect @@ -0,0 +1,16 @@ +string(18) "Test
\nString" +string(16) "Test
\nString" +string(18) "Test
\rString" +string(16) "Test
\rString" +string(20) "Test
\n\rString" +string(18) "Test
\n\rString" +string(20) "Test
\r\nString" +string(18) "Test
\r\nString" +string(26) "Test
\n
\nString" +string(22) "Test
\n
\nString" +string(26) "Test
\r
\rString" +string(22) "Test
\r
\rString" +string(19) "Test String
\n" +string(17) "Test String
\n" +string(59) "Hello

\nmy

\r\nfriend

\n\r" +string(53) "Hello

\nmy

\r\nfriend

\n\r"