0

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.

1
  • The <br /> tag comes from when I use nl2br on the lyrics, it automatically adds <br /> after every line :) A bit of a risky way, but I didn't find anything better to use... Commented Apr 13, 2014 at 17:51

3 Answers 3

2

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!

Sign up to request clarification or add additional context in comments.

7 Comments

You need to wrap the regular expression in valid delimiters.
Sorry, I've added some examples now! :)
@naomik, would you mind explaining that code, just a little bit? It looks good, although it makes my website go blank after I initialized it...
Nevermind, saw that you changed it around a bit :) Thank you so much! Always greatful for explanations, but you've helped me enough! Thanks!
@Niklas, I added a demo too ^.^
|
1

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);

1 Comment

Right, thanks for the tip! Went with Naomik's answer, but thanks anyway :)
1

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);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.