0

In PHP I need to search through my $post content and find all the opening <table> tags to add a unique class name based on its index. I know the code below is wrong but hopefully gets the point across.

$content = '<table></table><p></p><table></table><p></p><table></table><p></p>';
preg_match_all('/find all <table> tags/', $content, $matches);
for ($i=0; $i < count($matches); $i++) {
    $new_value = '<table class=""' . $i . ' >'; 
    str_replace( $matches[$i], $new_value, $content);
}
Benjamin
  • 697
  • 1
  • 8
  • 32

3 Answers3

2

The better way is using a DOM parser. With Regular Expressions you are able to do this simple task without a mess but for right tool's sake, do it with a parser:

$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML($content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
libxml_use_internal_errors(false);
$tables = $dom->getElementsByTagName('table');

foreach ($tables as $i => $table) {
    $table->setAttribute('class', "table_$i");
}

echo $dom->saveHTML();

Live demo

RegEx solution, not preferred

$counter = 0;
echo preg_replace_callback('~<table\K>~', function() use (&$counter) {
    return ' class="table_' . $counter++ . '">';
}, $content);

Live demo

revo
  • 47,783
  • 14
  • 74
  • 117
  • I used the Regex solution. I found the DOM solution did work to add the classes to my tables but presented another issue where my html entities were losing their encoding. I had many strange characters in my markup the degree symbol, ° , as an example was effected. – Benjamin Apr 11 '18 at 06:06
  • 1
    Regex seems shorter and handy to achieve same result but better to work around dom. You could consider encoding of html input. I made a live demo here https://3v4l.org/AeXLs – revo Apr 11 '18 at 07:55
  • Thanks Revo...That fixed the encoding issue. mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8') – Benjamin Apr 11 '18 at 19:42
1

I would personally do it without regex.

$content = '<table></table><p></p><table></table><p></p><table></table><p></p>';

function str_replace_count($search, $replace, $subject, $count) {
    return implode($replace, explode($search, $subject, $count + 1));
}

$i = 1;
while (strpos($content, '<table>') !== FALSE) {
    $content = str_replace_count('<table>', '<table class="c_' . $i . '">', $content, 1);
    $i++;
}

Demo http://sandbox.onlinephpfunctions.com/

Remember that HTML tags class values cannot start with a number.

Binar Web
  • 867
  • 1
  • 11
  • 26
  • Throws an error if I keep the last parameter, 1, Fatal error: Only variables can be passed by reference. If I remove the variable or use $count I get the same class name added to ever table "c_1" . Not sure how to fix this – Benjamin Apr 10 '18 at 22:42
  • Yes, now i see my error. I've updated the answer and tested it.Now it is working. – Binar Web Apr 11 '18 at 11:37
0

You use str.replace() and it will replace till no match so it will match against <table> and replace them all , the second loop will not find any match .

There is a workaround from this answer Using str_replace so that it only acts on the first match? so , your code should look like this :

function str_replace_first($from, $to, $content)
{
 $from = '/'.preg_quote($from, '/').'/';

return preg_replace($from, $to, $content, 1);
 }


$content = '<table></table><p></p><table></table><p></p><table></table><p></p>';
preg_match_all('/<table>/', $content, $matches);
$result=$content;
foreach ($matches[0] As $key => $value) {
$new_value = '<table class=' . $key . ' >'; 
$result=str_replace_first($value, $new_value,$result);
}
echo $result; 

Demo https://onlinegdb.com/B1kumZjjf

Mohammed Elhag
  • 4,272
  • 1
  • 10
  • 18