I want to replace element content with content which is definded in $tagsReplace array but have problem with preg_replace, my current ode:
$tagsReplace = array(
'header' => 'header',
'tag1' => 'this is tag1',
'tag2' => 'this is tag2',
'tag3' => 'this is tag3',
'footer' => 'footer',
);
$content = '
<!DOCTYPE html>
<hthml>
<header data-edit="true" data-tag-id="header"></header>
<div data-edit="true" data-tag-id="tag1"></div>
<div data-edit="true" data-tag-id="tag2"></div>
<div data-edit="true" data-tag-id="tag3"></div>
<footer data-edit="true" data-tag-id="footer"></footer>
</html>
';
$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML($content);
$xpath = new DomXpath($dom);
foreach ($xpath->query('//*[@data-edit="true"]') as $rowNode) {
$tagID = $rowNode->getAttribute('data-tag-id');
$content = preg_replace('/(<div.*?data-edit="true"[^>]*>)(.*?)(<\/div>)/i', '$1'. $tagsReplace[$tagID] . '$3', $content);
}
echo $content;
What i want is to get output like that:
<!DOCTYPE html>
<hthml>
<header data-edit="true" data-tag-id="header">header</header>
<div data-edit="true" data-tag-id="tag1">this is tag1</div>
<div data-edit="true" data-tag-id="tag2">this is tag2</div>
<div data-edit="true" data-tag-id="tag3">this is tag3</div>
<footer data-edit="true" data-tag-id="footer">footer</footer>
</html>
Current output is:
<!DOCTYPE html>
<hthml>
<header data-edit="true" data-tag-id="header"></header>
<div data-edit="true" data-tag-id="tag1">footer</div>
<div data-edit="true" data-tag-id="tag2">footer</div>
<div data-edit="true" data-tag-id="tag3">footer</div>
<footer data-edit="true" data-tag-id="footer"></footer>
</html>
Soo all elements which contain atribute data-edit="true" and data-tag-id has to get content inside them replaced by tagid value which is definded in arra $tagsReplace.
Because you are specifying div in your preg_match pattern and we know that the first element starts with header so it won't be matched so simply replace the "div" at the beginning of the preg_match pattern with (.*?) to match header and even other words. this :
$content = preg_replace('/(<div.?data-edit="true"[^>]>)(.*?)(</div>)/i', '$1'. $tagsReplace[$tagID] . '$3', $content);
becomes:
$content = preg_replace('/(<(.*?) data-edit="true"[^>]>)(.?)(</div>)/i', '$1'. $tagsReplace[$tagID] . '$3', $content);
Another option is available you can use preg_replace_callback(pattern,function,subject) to do what you want magically, here is a solution that worked for me :
$content = '
<!DOCTYPE html>
<hthml>
<header data-edit="true" data-tag-id="header"></header>
<div data-edit="true" data-tag-id="tag1"></div>
<div data-edit="true" data-tag-id="tag2"></div>
<div data-edit="true" data-tag-id="tag3"></div>
<footer data-edit="true" data-tag-id="footer"></footer>
</html>
';
$pattern = '/<(.+) data-edit="true" data-tag-id="(.*)"(.*?)>(.*?)<\/(.+)>/';
$another = preg_replace_callback($pattern, function($matches){
$tagsReplace = array(
'header' => 'header1',
'tag1' => 'this is tag1',
'tag2' => 'this is tag2',
'tag3' => 'this is tag3',
'footer' => 'footer',
);
return '<'.$matches[1].' data-edit="true" data-tag-id="'.$matches[2].'"'.$matches[3].'>'.$tagsReplace[$matches[2]].'<\/'.$matches[5].'>/';
}, $content);
# before the replacement
print_r($content);
echo '
';
// another holds the content after replacement
print_r($another);
You can't replace always with the same patron because you have 3 differents tags.
Take a look to this code:
<?php
$tagsReplace = array(
'header' => 'header',
'tag1' => 'this is tag1',
'tag2' => 'this is tag2',
'tag3' => 'this is tag3',
'footer' => 'footer',
);
$content = '
<!DOCTYPE html>
<html>
<header data-edit="true" data-tag-id="header"></header>
<div data-edit="true" data-tag-id="tag1"></div>
<div data-edit="true" data-tag-id="tag2"></div>
<div data-edit="true" data-tag-id="tag3"></div>
<footer data-edit="true" data-tag-id="footer"></footer>
</html>
';
$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML($content);
$xpath = new DomXpath($dom);
$html = explode("
", trim($content));
$i = 2;
foreach ($xpath->query('//*[@data-edit="true"]') as $rowNode) {
$tagID = $rowNode->getAttribute('data-tag-id');
switch ($rowNode->nodeName) {
case 'header':
$html[$i] = preg_replace('/(<header.*?data-edit="true"[^>]*>)(.*?)(<\/header>)/i', '$1'. $tagsReplace[$tagID] . '$3', $html[$i]);
break;
case 'div':
$html[$i] = preg_replace('/(<div.*?data-edit="true"[^>]*>)(.*?)(<\/div>)/i', '$1'. $tagsReplace[$tagID] . '$3', $html[$i]);
break;
case 'footer':
$html[$i] = preg_replace('/(<footer.*?data-edit="true"[^>]*>)(.*?)(<\/footer>)/i', '$1'. $tagsReplace[$tagID] . '$3', $html[$i]);
break;
}
$i++;
}
$content = implode("
", $html);
echo $content;
?>
To get the result you want you could loop through your replacement array and search for both data-edit = true
and data-tag-id = <key>
in the same XPath query, and then replace the node value. That way you do not have to use preg_replace
at all.
$content = '
<!DOCTYPE html>
<html>
<header data-edit="true" data-tag-id="header"></header>
<div data-edit="true" data-tag-id="tag1"></div>
<div data-edit="true" data-tag-id="tag2"></div>
<div data-edit="true" data-tag-id="tag3"></div>
<footer data-edit="true" data-tag-id="footer"></footer>
</html>
';
$tagsReplace = array(
'header' => 'header',
'tag1' => 'this is tag1',
'tag2' => 'this is tag2',
'tag3' => 'this is tag3',
'footer' => 'footer',
);
$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML($content);
$xpath = new DomXpath($dom);
foreach ($tagsReplace as $key => $value) {
$nodes = $xpath->query(
'//*[@data-edit="true" and @data-tag-id="' . $key . '"]'
);
if ($nodes->length) {
$nodes->item(0)->nodeValue = $value;
}
}
$dom->formatOutput = true;
echo $dom->saveHTML();
Output:
<!DOCTYPE html>
<html><body>
<header data-edit="true" data-tag-id="header">header</header>
<div data-edit="true" data-tag-id="tag1">this is tag1</div>
<div data-edit="true" data-tag-id="tag2">this is tag2</div>
<div data-edit="true" data-tag-id="tag3">this is tag3</div>
<footer data-edit="true" data-tag-id="footer">footer</footer>
</body></html>
Try this, it accepts any type of tag
$tagsReplace = array(
'header' => 'header',
'tag1' => 'this is tag1',
'tag2' => 'this is tag2',
'tag3' => 'this is tag3',
'footer' => 'footer',
);
$content = '
<!DOCTYPE html>
<html>
<header data-edit="true" data-tag-id="header"></header>
<div data-edit="true" data-tag-id="tag1"></div>
<div data-edit="true" data-tag-id="tag2"></div>
<div data-edit="true" data-tag-id="tag3"></div>
<footer data-edit="true" data-tag-id="footer"></footer>
</html>
';
$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML($content);
$xpath = new DomXpath($dom);
foreach ($xpath->query('//*[@data-edit="true"]') as $rowNode) {
$tagID = $rowNode->getAttribute('data-tag-id');
$content = preg_replace('/(<(.*?) [^>]*?data-tag-id="'.$tagID.'"[^>]*>)(.*?)(<\/\2>)/i', '$1'. $tagsReplace[$tagID] . '$4', $content);
}
echo $content;
foreach ($xpath->query('//*[@data-edit="true"]') as $rowNode) {
$rowNode->nodeValue = $tagsReplace[$rowNode->getAttribute('data-tag-id')];
}
echo $dom->saveHTML();
Do not use RegEx to parse/change HTML or XML. You already use the DOM, so why don't use it to change the document, too?
$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML($content);
$xpath = new DOMXPath($dom);
foreach ($xpath->evaluate('//*[@data-edit="true"]') as $rowNode) {
// read id attribute
$tagId = $rowNode->getAttribute('data-tag-id');
// remove all child nodes
$rowNode->nodeValue = '';
// if here is a new content available
if (isset($tagsReplace[$tagId])) {
// create a text node and append it
$rowNode->appendChild(
$dom->createTextNode($tagsReplace[$tagId])
);
}
}
echo $dom->saveHtml();
Output (formatted):
<!DOCTYPE html>
<html>
<body>
<header data-edit="true" data-tag-id="header">header</header>
<div data-edit="true" data-tag-id="tag1">this is tag1</div>
<div data-edit="true" data-tag-id="tag2">this is tag2</div>
<div data-edit="true" data-tag-id="tag3">this is tag3</div>
<footer data-edit="true" data-tag-id="footer">footer</footer>
</body>
</html>
Warning!: You HTML looks like HTML5 and that is not fully compatible to the PHP DOM implementation. You might need a library to import/export the HTML5 to the DOMDocument as XHTML.