We help IT Professionals succeed at work.

PHP/REGEX: Put each line inside <li> tags

hankknight
hankknight asked
on
Using PHP and REGEX, I want to every item that is on its own line inside <li> tags.

This:
Hello
123
ABC

Open in new window


Should become this:
<li>Hello</li>
<li>123</li>
<li>ABC</li>

Open in new window

Comment
Watch Question

CERTIFIED EXPERT
Most Valuable Expert 2017
Distinguished Expert 2019
Commented:
There are different ways of doing this depending on where the lines come from.
Here is one way but I don't think this is what you were asking
foreach($lines as $l) {
 echo '<li>' . $l . '</li>'
}

Open in new window

Where does the data come from - what form is it in?
Hullo
123
ABC

Open in new window

CERTIFIED EXPERT
Most Valuable Expert 2011
Top Expert 2015
Commented:
Yes, please provide an example of how the data is stored in your code. If it is a string object with embedded newlines, then it seems as though a simple split would suffice (rather than a regex).

e.g.

$input = <<<INPUT
Hello
123
ABC
INPUT;

// tweaking julianH's example
foreach(explode('\n', $input) as $l) {
 echo '<li>' . $l . '</li>'
}

Open in new window

CERTIFIED EXPERT
Most Valuable Expert 2017
Distinguished Expert 2019

Commented:
or
// tweaking kaufmed's example
echo '<li>' . str_replace('\n', '</li><li>', $input) . '</li>;

Open in new window

Author

Commented:
Can this be done with preg_replace?
CERTIFIED EXPERT
Most Valuable Expert 2017
Distinguished Expert 2019

Commented:
yes but why would you want to ? Nothing you have posted indicates the need for a regular expression - hence our questions trying to find out more about your input.

Author

Commented:
My input will be a textarea.  I am worried that depending on their oporating system, using '\n' will not work and that is why I would rather use REGEX.
CERTIFIED EXPERT
Most Valuable Expert 2017
Distinguished Expert 2019

Commented:
REGEX is short regular expression and it would still need something to match on.

The only potential difference in linebreaks is \n or \r\n so you can prefix the above with

$input = str_replace('\r\n','\n', $input)

To standardise (although this is most likely not necessary) and then run the result throug either my or kaufmed's sample above.

I use the above on my CMS for lists - clients don't want to know about <li></li> so I let them put in a \n delimited text area and do the following
  echo '<li>' . str_replace('\n', '</li><li>', $input) . '</li>;

Open in new window

CERTIFIED EXPERT
Most Valuable Expert 2011
Top Expert 2015

Commented:
The only potential difference in linebreaks is \n or \r\n so you can prefix the above with
I take issue with that. One would hope that would be the case, but it is possible that someone (either knowingly or unknowingly) could put simply a "\r". For that reason, I would use a pattern of:

$input = preg_replace('/^(.*)(\r?)$/','<li>$1</li>$2', $input);

Open in new window

CERTIFIED EXPERT
Most Valuable Expert 2017
Distinguished Expert 2019

Commented:
@kaufmed from a purist view I agree with you however consider the code below

If we miss a \r or two - that counts as white space and will be ignored within the list anyway so net effect (in this case) should be nil.
However, if it is really an issue then the solution is just to prefix the conversion with this

$input = str_replace("\r\n","\n", $input);

Which should sort it out
<!doctype html>
<html>
<head>
<title>Title</title>
</head>
<body>
<ul>
<?php

$input = "abc\r\n123\r\nghi";

echo '<li>' . str_replace("\n", '</li><li>', $input) . '</li>';
?>
</ul>
</body>
</html>

Open in new window

Some things to note (and I made this mistake in posts above).

Single quoted strings in PHP do not result in escaped characters being converted double quotes are required.

Also a preg_replace is going to be slightly more expensive than a straight string replace which is also simpler to write / read in this case.

Author

Commented:
This does not work:
<?php

$input ="Hello
ABC
Test
123";

echo '<ul>' . preg_replace('/^(.*)(\r?)$/','<li>$1</li>$2', $input) . '</ul>';

?>

Open in new window

Author

Commented:
Actually, neither of the proposed ideas work for me:

<?php

$input ="Hello
ABC
Test
123";

echo '<ul>' . preg_replace('/^(.*)(\r?)$/','<li>$1</li>$2', $input) . '</ul>';
echo '<hr />';
echo '<ul>' . '<li>' . str_replace('\n', '</li><li>', $input) . '</li>' . '</ul>';

?> 

Open in new window

CERTIFIED EXPERT
Most Valuable Expert 2017
Distinguished Expert 2019

Commented:
@hankknight
Actually, neither of the proposed ideas work for me:

Any particular reason why?

Did you see the comment I made at the bottom of my last post regarding single vs double quotes
<?php

$input ="Hello
ABC
Test
123";

echo '<ul>' . preg_replace('/^(.*)(\r?)$/','<li>$1</li>$2', $input) . '</ul>';
echo '<hr />';
echo '<ul>' . '<li>' . str_replace("\n", '</li><li>', $input) . '</li>' . '</ul>';

?> 

Open in new window


\n and \r\n must be inside "" to work.
CERTIFIED EXPERT
Most Valuable Expert 2011
Top Expert 2015

Commented:
Sorry, I forgot a flag. Add the "m" modifier:

echo '<ul>' . preg_replace('/^(.*)(\r?)$/m','<li>$1</li>$2', $input) . '</ul>';

Open in new window

Terry WoodsWeb Developer, specialising in WordPress
CERTIFIED EXPERT
Most Valuable Expert 2011

Commented:
@kaufmed, you'll need some double quotes, rather than single (-:
echo '<ul>' . preg_replace("/^(.*)(\r?)$/m",'<li>$1</li>$2', $input) . '</ul>';

Open in new window

CERTIFIED EXPERT
Most Valuable Expert 2011
Top Expert 2015

Commented:
Terry,

It seems to work for me:

Screenshot
Terry WoodsWeb Developer, specialising in WordPress
CERTIFIED EXPERT
Most Valuable Expert 2011

Commented:
The \r isn't matching. You'll get the same output with:
echo '<ul>' . preg_replace('/^(.*)$/m','<li>$1</li>', $input) . '</ul>';

Open in new window

CERTIFIED EXPERT
Most Valuable Expert 2011
Top Expert 2015

Commented:
Sorry Terry, but I'm still confused. Even when I use the following data:

Screenshot
I get the desired behavior. (I'm sure we're digressing at this point, but you've piqued my curiosity.) The intent is to match carriage returns, and the only reason I decided to include the carriage return check was to keep it with the newline after the replacement--though in the grand scheme of things it probably doesn't matter. So what am I missing?
Web Developer, specialising in WordPress
CERTIFIED EXPERT
Most Valuable Expert 2011
Commented:
Here's a solution:
preg_replace("/([^\r\n]+)(\r*\n\r*|\r|$)/","<li>$1</li>\n", $input) . '</ul>';

Open in new window


@kaufmed, will elaborate in a moment...
Terry WoodsWeb Developer, specialising in WordPress
CERTIFIED EXPERT
Most Valuable Expert 2011

Commented:
TL;DR

@kaufmed, the (.*) part of your pattern captured any \r characters anyway, so the \r part of the pattern wasn't used. I initially thought the \r would be interpreted literally, but I forgot about the additional layer of parsing that the regex engine does. That's why "\\r" to a regex is the same as "\r" and '\r' I believe.

Also, the multiline mode meant that either an end-of-string or a newline was required for each match, which doesn't work when you just have line feeds.

Anyway, here's some code for testing:
<?php

$inputs = array(
  "Just newline"=>"Hello\nABC\nTest\n123",
  "Just line feed"=>"Hello\rABC\rTest\r123",
  "Line feed and newline"=>"Hello\r\nABC\r\nTest\r\n123"
);

foreach ($inputs as $desc=>$data) {
  print "<b>$desc:</b><br>\n";
  print '@kaufmed\'s pattern result:<ul>' .
    preg_replace('/^(.*)(\r?)$/m','<li>$1</li>$2', $data) . '</ul>';
  print '@TerryAtOpus\' pattern result:<ul>' .
    preg_replace("/([^\r\n]+)(\r*\n\r*|\r|$)/","<li>$1</li>\n", $data) . '</ul>';
}

Open in new window


And the result:
screenshot
CERTIFIED EXPERT
Most Valuable Expert 2011
Top Expert 2015

Commented:
the (.*) part of your pattern captured any \r characters anyway
Agreed (and embarrassed that I forgot about my greedines!).

I reviewed the PHP string documentation and I see that single-quoted strings do not interpret escape characters, so I concur with your assessment.
CERTIFIED EXPERT
Most Valuable Expert 2017
Distinguished Expert 2019

Commented:
Not sure why this is getting so complicated - as discussed above - if a \r or \n is missed it counts as whitespace so you will get the desired result - in terms of how it is used.

Using preg_replace will work but a bit complicated for what should really be a very simple problem which is to put <li></li> tags around each of the items in the list.

Explore More ContentExplore courses, solutions, and other research materials related to this topic.