I have some <img>
tags like these:
<img alt="" src="{assets_8170:{filedir_14}test.png}" style="width: 700px; height: 181px;" />
<img src="{filedir_14}test.png" alt="" />
And I need to update the src value, extracting the filename and adding it inside a WordPress shortcode:
<img src="[my-shortcode file='test.png']" ... />
The regex to extract the filename is this one: [a-zA-Z_0-9-()]+\.[a-zA-Z]{2,4}
, but I am not able to create the complete regex, considering that the image tag attributes do not follow the same order in all instances.
<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 their src
attribute changed? $dom->saveHTML()
return the whole HTML. How can I return only the body content? body
element (since the DOMDocument object will create an html frame to wrap your input code) The answer grew bigger during its lifecycle trying to address the issue.
Several attempts were made but the latest one (loadXML/saveXML) nailed it.
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:
<?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;
}
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.
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.
Now to put a fix on that all I could come out with so far was this:
//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.
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
%5B
, %22
). How can I avoid that? urldecode($bodyContent)
. %7B
and %7D
. I didn't cite it because I trusted even less if I can't control what you are going to replace. It would be effective if it could be applied ONLY to the src attribute value.. but by design you can't control the raw value of a single attribute/element from the DOMDocument object until the moment you have the final entire html string