Niklas
Niklas

Reputation: 15

PHP preg_replace inside for loop

I'm currently trying out this PHP preg_replace function and I've run into a small problem. I want to replace all the tags with a div with an ID, unique for every div, so I thought I would add it into a for loop. But in some strange way, it only do the first line and gives it an ID of 49, which is the last ID they can get. Here's my code:

$res = mysqli_query($mysqli, "SELECT * FROM song WHERE id = 1");
$row = mysqli_fetch_assoc($res);
mysqli_set_charset("utf8");

$lyric = $row['lyric'];
$lyricHTML = nl2br($lyric);

$lines_arr = preg_split('[<br />]',$lyricHTML);
$lines = count($lines_arr); 

for($i = 0; $i < $lines; $i++) {
    $string = preg_replace(']<br />]', '</h4><h4 id="no'.$i.'">', $lyricHTML, 1);
    echo $i;
}

echo '<h4>';
echo $string;
echo '</h4>';

How it works is that I have a large amount of text in my database, and when I add it into the lyric variable, it's just plain text. But when I nl2br it, it gets after every line, which I use here. I get the number of by using the little "lines_arr" method as you can see, and then basically iterate in a for loop.

The only problem is that it only outputs on the first line and gives that an ID of 49. When I move it outside the for loop and removes the limit, it works and all lines gets an <h4> around them, but then I don't get the unique ID I need.

This is some text I pulled out from the database

Mama called about the paper turns out they wrote about me Now my broken heart´s the only thing that's broke about me So many people should have seen what we got going on I only wanna put my heart and my life in songs Writing about the pain I felt with my daddy gone About the emptiness I felt when I sat alone About the happiness I feel when I sing it loud He should have heard the noise we made with the happy crowd Did my Gran Daddy know he taught me what a poem was How you can use a sentence or just a simple pause What will I say when my kids ask me who my daddy was I thought about it for a while and I'm at a loss Knowing that I´m gonna live my whole life without him I found out a lot of things I never knew about him All I know is that I´ll never really be alone Cause we gotta lot of love and a happy home

And my goal is to give every line an <h4 id="no1">TEXT</h4> for example, and the number after no, like no1 or no4 should be incremented every iteration, that's why I chose a for-loop.

Upvotes: 0

Views: 470

Answers (3)

Casimir et Hippolyte
Casimir et Hippolyte

Reputation: 89584

An other way with preg_replace_callback:

$id = 0;
$lyric = preg_replace_callback('~(^)|$~m',
    function ($m) use (&$id) {
        return (isset($m[1])) ? '<h4 id="no' . ++$id . '">' : '</h4>'; },
    $lyric);

Upvotes: 1

Mulan
Mulan

Reputation: 135367

Looks like you need to escape your regexp

 preg_replace('/\[<br \/\]/', ...);

Really though, this is a classic XY Problem. Instead of asking us how to fix your solution, you should ask us how to solve your problem.

Show us some example text in the database and then show us how you would like it to be formatted. It's very likely there's a better way.


I would use array_walk for this. ideone demo here

$lines = preg_split("/[\r\n]+/", $row['lyric']);

array_walk($lines, function(&$line, $idx) {
  $line = sprintf("<h4 id='no%d'>%s</h4>", $idx+1, $line);
});

echo implode("\n", $lines);

Output

<h4 id="no1">Mama called about the paper turns out they wrote about me</h4>
<h4 id="no2">Now my broken heart's the only thing that's broke about me</h4>
<h4 id="no3">So many people should have seen what we got going on</h4>
...
<h4 id="no16">Cause we gotta lot of love and a happy home</h4>

Explanation of solution

nl2br doesn't really help us here. It converts \n to <br /> but then we'd just end up splitting the string on the br. We might as well split using \n to start with. I'm going to use /[\r\n]+/ because it splits one or more \r, \n, and \r\n.

    $lines = preg_split("/[\r\n]+/", $row['lyric']);

Now we have an array of strings, each containing one line of lyrics. But we want to wrap each string in an <h4 id="noX">...</h4> where X is the number of the line.

Ordinarily we would use array_map for this, but the array_map callback does not receive an index argument. Instead we will use array_walk which does receive the index.

One more note about this line, is the use of &$line as the callback parameter. This allows us to alter the contents of the $line and have it "saved" in our original $lyrics array. (See the Example #1 in the PHP docs to compare the difference).

    array_walk($lines, function(&$line, $idx) {

Here's where the h4 comes in. I use sprintf for formatting HTML strings because I think they are more readable. And it allows you to control how the arguments are output without adding a bunch of view logic in the "template".

Here's the world's tiniest template: '<h4 id="no%d">%s</h4>'. It has two inputs, %d and %s. The first will be output as a number (our line number), and the second will be output as a string (our lyrics).

      $line = sprintf('<h4 id="no%d">%s</h4>', $idx+1, $line);

Close the array_walk callback function

    });

Now $lines is an array of our newly-formatted lyrics. Let's output the lyrics by separating each line with a \n.

    echo implode("\n", $lines);

Done!

Upvotes: 2

PlaviZG
PlaviZG

Reputation: 79

If your text in db is in every line why just not explode it with \n character?

Always try to find a solution without using preg set of functions, because they are heavy memory consumers:

I would go lke this:

$lyric = $row['lyric'];

$lyrics =explode("\n",$lyrics);
$lyricsHtml=null;
$i=0;
foreach($lyrics as $val){
$i++;
$lyricsHtml[] = '<h4 id="no'.$i.'">'.$val.'</h4>';
} 
$lyricsHtml = implode("\n",$lyricsHtml);

Upvotes: 1

Related Questions