PHP - Parsing html contents, making transforms and returning the resulting html
The answer grew bigger during its lifecycle trying to address the issue.
Several attempts were made but the latest one (loadXML/saveXML) nailed it.
DOMDocument - loadHTML and saveHTML
If you need to parse an html string in php so that you can later fetch and modify its content in a structured and safe manner without breaking the encoding, you can use DOMDocument::loadHTML():
https://www.php.net/manual/en/domdocument.loadhtml.php
Here I show how to parse your html string, fetch all its <img> elements and for each of them how to retrieve their src attribute and set it with an arbitrary value.
At the end to return the html string of the transformed document, you can use DOMDocument::saveHTML:
https://www.php.net/manual/en/domdocument.savehtml
Taking into account the fact that by default the document will contain the basic html frame wrapping your original content. So to be sure the resulting html will be limited to that part only, here I show how to fetch the body content and loop through its children to return the final composition:
https://onlinephp.io/c/157de
<?php
$html = "
<img alt=\"\" src=\"{assets_8170:{filedir_14}test.png}\" style=\"width: 700px; height: 181px;\" />
<img src=\"{filedir_14}test.png\" alt=\"\" />
";
$transformed = processImages($html);
echo $transformed;
function processImages($html){
//parse the html fragment
$dom = new DOMDocument();
$dom->loadHTML($html);
//fetch the <img> elements
$images = $dom->getElementsByTagName('img');
//for each <img>
foreach ($images as $img) {
//get the src attribute
$src = $img->getAttribute('src');
//set the src attribute
$img->setAttribute('src', 'bogus');
}
//return the html modified so far (body content only)
$body = $dom->getElementsByTagName('body')->item(0);
$bodyChildren = $body->childNodes;
$bodyContent = '';
foreach ($bodyChildren as $child) {
$bodyContent .= $dom->saveHTML($child);
}
return $bodyContent;
}
Problems with src attribute value restrictions
After reading on comments you pointed out that saveHTML was returning an html where the image src attribute value had its special characters escaped I made some more research...
The reason why that happens it's because DOMDocument wants to make sure that the src attribute contains a valid url and {,} are not valid characters.
Evidence that it doesn't happen with custom data attributes
For example if I added an attribute like data-test="mycustomcontent: {wildlyusingwhatever}" that one was going to be returned untouched because it didn't require strict rules to adhere to.
Quick fix to make it work (defeating the parser as a whole)
Now to put a fix on that all I could come out with so far was this:
https://onlinephp.io/c/0e334
//VERY UNSAFE -- replace the in $bodyContent %7B as { and %7D as }
$bodyContent = str_replace("%7B", "{", $bodyContent);
$bodyContent = str_replace("%7D", "}", $bodyContent);
return $bodyContent;
But of course it's nor safe nor smart and neither a very good solution. First of all because it defeats the whole purpose of using a parser instead of regex and secondly because it could seriously damage the result.
A better approach using loadXML and saveXML
To prevent the html rules to kick in, it could be attempted the route of parsing the text as XML instead of HTML so that it will still adhere to the nested markdown syntax (difficult/impossible to deal with using regex) but it won't apply all the restrictions about contents.
I modified the core logic by doing this:
//loads the html content as xml wrapping it with a root element
$dom->loadXml("<root>${html}</root>");
//...
//returns the xml content of each children in <root> as processed so far
$rootNode = $dom->childNodes[0];
$children = $rootNode->childNodes;
$content = '';
foreach ($children as $child) {
$content .= $dom->saveXML($child);
}
return $content;
And this is the working demo: https://onlinephp.io/c/f9de1
<img>tags as plain text and you wish to produce another text content having the processed text. Or if you are aiming at a process happening at runtime on some platform. Because if the question is limited to regex is not clear what's the subject string. Is it a text containing any html including<img>that you need to process and transform so that the<img>are replaced with theirsrcattribute changed?$dom->saveHTML()return the whole HTML. How can I return only the body content?bodyelement (since the DOMDocument object will create an html frame to wrap your input code)