Hello,

I am trying to manipulate a HTML content stored in a php variable.
The logic is as follows: The variable should be checked if it contains an <img ... /> tag and if a tag is found it should be wraped in an <a> tag and some parametres of the a tag should be read from the img tag.
The code in the end enables the fancybox plugin to all images, this can be done even via JS, but in my case I would like to have the content ready as it would be served to the visitor.

Example:
The initial content.

$content = 'This content has two images
<img src="/images/img1.jpg" alt="Image 1" />
<img src="/images/img2.jpg" alt="Image 2" />';

The final content

$content = 'This content has two images
    <a href="/images/img1.jpg" data-fancybox="image-popup" data-caption="Image 1">
        <img src="/images/img1.jpg" alt="Image 1" />
    </a>
    <a href="/images/img2.jpg" data-fancybox="image-popup" data-caption="Image 2">
         <img src="/images/img2.jpg" alt="Image 2" />
    </a>';

Thank you!

Dani AI

Generated

Short summary and recommendation: ’s regex is fine for very simple, consistently formatted img tags and ’s later regex improves on that, but regular expressions remain brittle when editors add attributes in different orders, when images use lazy-loading (data-src/srcset), or when the HTML is a fragment. For reliable, long-term results do the transformation with PHP’s DOM (DOMDocument + DOMXPath): it understands real HTML, preserves other attributes, and can skip images already inside links.

Example recipe (robust, handles encoded fragments, data-src and srcset, and avoids double-wrapping):

<?php
libxml_use_internal_errors(true);

$html = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8');

// load as fragment (avoid added <html>/<body> when supported)
$doc = new DOMDocument();
$doc->loadHTML('<?xml encoding="utf-8"?>' . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

$xpath = new DOMXPath($doc);
$imgs = $xpath->query('//img[not(ancestor::a)]'); // skip already-linked images

foreach ($imgs as $img) {
    $src = $img->getAttribute('src') ?: $img->getAttribute('data-src');
    if (!$src) {
        $srcset = $img->getAttribute('srcset');
        if ($srcset) {
            $first = preg_split('/\s*,\s*/', $srcset)[0];
            $src = preg_split('/\s+/', $first)[0];
        }
    }
    if (!$src) continue;

    $caption = $img->getAttribute('data-caption') ?: $img->getAttribute('alt');

    $a = $doc->createElement('a');
    $a->setAttribute('href', $src);
    $a->setAttribute('data-fancybox', 'image-popup');
    if ($caption !== '') $a->setAttribute('data-caption', $caption);

    $img->parentNode->replaceChild($a, $img);
    $a->appendChild($img);
}

$result = $doc->saveHTML();
?>

Troubleshooting & cautions:

  • If your content is stored escaped (you see &lt;img in DB), decode before parsing and re-save unescaped HTML if that’s desired.
  • Use libxml_use_internal_errors(true) to suppress warnings and LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD to avoid added wrapper tags (older PHP versions may not support those flags; in that case wrap in a temporary <div>).
  • Don’t forget sanitization (HTMLPurifier or similar) if content comes from untrusted editors to avoid XSS via attributes.
  • For responsive markup (<picture>) adapt the logic to wrap the picture/figure block rather than the bare img.

This approach keeps the transformation correct across attribute order, extra attributes (width/height/id/class), lazy-loading patterns, and nested markup — and it’s easier to extend than fragile regexes.

Recommended Answers

All 4 Replies

<?php
$content = 'This content has two images
<img src="/images/img1.jpg" alt="Image 1" />
<img src="/images/img2.jpg" alt="Image 2" />';

$content = preg_replace('/\<img src="([^\"]+)" alt="([^\"]+)" \/>/',
    '<a href="\\1" data-fancybox="image-popup" data-caption="\\2">
        <img src="\\1" alt="\\2" />
    </a>', $content);

header("Content-type:text/plain;charset=utf-8");
print_r($content);
?>

Thank you
This code works well, but it may needs some improvements.

In some cases the editor of the website puts some other parameters to the image (width, height)
on this cases you code does not work.

Could the above suggestion be improved?!

Another way - create document object and manipulate any element and its attributes e.g.

<?php

$content = '<!DOCTYPE html>
<html>
<head>
    <title>PAGE TITLE</title>
</head>
<body>
    <p>This content has two images</p>
    <div>
        <img src="/images/img1.jpg" alt="Image 1" />
        <img src="/images/img2.jpg" alt="Image 2" />
    </div>
</body>
</html>';

$doc = new DOMDocument();
$doc->loadHTML($content);

// prepare document object ...

echo $doc->saveHTML();

?>

Someone else helped me to fond a solution.
Here is all the logic.

$source = '
  <img src="1.jpg"/>' . "\n" . '
  <img src="2.jpg" alt="2" />' . "\n" . '
  <img name="omg" src="3" asd="sdfgf" alt="2.jpg">' . "\n" . '
  <img src="4.jpg" alt="4" width="1107" height="1626"/>' . "\n" . '
  <img src="5.jpg" id="zz" alt="5" >' . "\n" . '
  <img src="6.jpg" alt="6" width="1229" height="922" />' . "\n" . '
  <img src="7.jpg" alt="7"/>' . "\n" . '
  <img src="8.jpg" alt="8" width="807" height="1117">
';

$pattern = '~(<img .*?src="([^"]+)" ?.*?(alt="([^"]+)")?.*?>)~';
$replacement = '<a href="$2" data-fancybox="image-popup" data-caption="$4">$1</a>';

$result = preg_replace($pattern, $replacement, $source);

echo 'SOURCE:<br />' . nl2br(htmlspecialchars($source)) . '<br /><br /><br />';
echo 'RESULT:<br />' . nl2br(htmlspecialchars($result));
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.