I have a PHP script that uses DOMDocument and DOMXPath to find and replace merge codes in an HTML template. A simple example might be:
<html>
<head>
<title>Title</title>
</head>
<body>
<p>Hello {greeting}!</p>
<table><tr>
<td>Details:</td>
<td>{details}</td>
</tr></table>
</body>
</html>
The following code substituted fields based on an associative array where the key matches the merge field:
private function substituteFields (DOMNode $node, $fields)
{
$x = new DOMXPath ($node->ownerDocument);
foreach ($fields as $field => $value)
{
$query = $x->query (".//text()[contains(., '{" . $field . "}')]", $node);
foreach ($query as $subnode)
{
$subnode->nodeValue = str_replace ("{" . $field . "}", $value, $subnode->nodeValue);
}
}
}
This is working well.
However, some merge codes will need HTML substituted into them:
$fields ['greeting'] = "Joe Soap";
$fields ['details'] = "<div class='details'>Details here</div>";
The substitution is happening, but the HTML is being escaped, which is probably a sensible idea in most cases.
Can I work around this?
I got around this a little clumsily, but it works for now. If there are better solutions, I'll happily modify my answer!
Essentially, this looks for an opening tag "<" character within the substitution text. If it finds one, it invokes an HTML substitution method which I modified from this question, answer and comments.
It has a limitation in that it can't substitute HTML mid-node. For example, the following will not work:
<p>Here is a bit of {html_code}</p>
But that can be made to work like this:
<p>Here is a bit of <span>{html_code}</span></p>
Here is the modified code:
private function substituteFields (DOMNode $node, $fields)
{
$x = new DOMXPath ($node->ownerDocument);
foreach ($fields as $field => $value)
{
$query = $x->query (".//text()[contains(., '{" . $field . "}')]", $node);
foreach ($query as $subnode)
{
$replace = str_replace ("{" . $field . "}", $value, $subnode->nodeValue);
if (substr ($replace, 0, 1) != "<")
{
$subnode->nodeValue = $replace;
}
else
{
$this->appendHTML ($subnode, $replace);
}
}
}
}
private function appendHTML (DOMNode $parent, $source)
{
$tmpDoc = new DOMDocument();
$tmpDoc->loadHTML ($source);
foreach ($tmpDoc->getElementsByTagName ('body')->item (0)->childNodes as $node)
{
$importedNode = $parent->ownerDocument->importNode ($node, true);
$parent->parentNode->replaceChild ($importedNode, $parent);
}
}