1

I have PHP function to display all HTML code.

Example :

<?php echo htmlspecialchars("<p>Welcome</p>"); ?>

Then the result will <p>Welcome</p>

Now I need to display image <img src="http://nl3.php.net/images/logo.php"/>, but it will display the code too <img src="http://nl3.php.net/images/logo.php"/>

How can I display the image?

HiDayurie Dave
  • 1,791
  • 2
  • 17
  • 45
  • 5
    Don't escape it. Are you basically saying you have HTML and some of that HTML you want escaped and others you do not? That's quite messy. You'll need a good selective sanitation library for that. Look at http://htmlpurifier.org and customise its rules so it escapes everything except image tags. – deceze Feb 07 '15 at 09:44
  • Check [this][1] post, it might help you. [1]: http://stackoverflow.com/questions/12819804/how-do-i-use-htmlspecialchars-but-allow-only-specific-html-code-to-pass-through – SkyDriver2500 Feb 07 '15 at 12:53
  • @deceze you should write that as an answer, for it is the correct one. – Marijn van Vliet Feb 13 '15 at 09:25

2 Answers2

2

Code desription

Code will find every image tags <img attr1=value1 attr2=value2 ... /> in text, create for every image own image tag with new HtmlElement() - this class is just custom light version of new DOMElement(), replace original image tag with placeholder, after every image is replace with placeholder, in this case I choose this placeholder {#@#IMG_' . $id .'} which is enough original for me type of texts , then will use on whole text htmlspecialchars() function and at the end it will replace placeholders with newly created image tags and return text back.

Code explanation

if (preg_match_all('#<img\s+([^>]+?)\s?(/>|>)#i', $text, $matches)) { Check if text contain any images, if yes, variabale $matches will contain 3 dimensional array of arrays, which will look like this for example:

array (3)
   0 => array (6)
   |  0 => "<img alt="rolleyes" src="//t.domain.com/default/rolleyes.gif" title="rolleyes" />"
   |  1 => "<img alt="rolleyes" src="//t.domain.com/default/rolleyes.gif" title="rolleyes" />"
   |  2 => "<img alt="rolleyes" src="//t.domain.com/default/rolleyes.gif" title="rolleyes" />"
   |  3 => "<img alt="rolleyes" src="//t.domain.com/default/rolleyes.gif" title="rolleyes" />"
   |  4 => "<img alt="rolleyes" src="//t.domain.com/default/rolleyes.gif" title="rolleyes" />"
   |  5 => "<img alt="rolleyes" src="//t.domain.com/default/rolleyes.gif" title="rolleyes" />"
   1 => array (6)
   |  0 => "alt="rolleyes" src="//t.domain.com/default/rolleyes.gif" title="rolleyes"" 
   |  1 => "alt="rolleyes" src="//t.domain.com/default/rolleyes.gif" title="rolleyes"" 
   |  2 => "alt="rolleyes" src="//t.domain.com/default/rolleyes.gif" title="rolleyes"" 
   |  3 => "alt="rolleyes" src="//t.domain.com/default/rolleyes.gif" title="rolleyes"" 
   |  4 => "alt="rolleyes" src="//t.domain.com/default/rolleyes.gif" title="rolleyes"" 
   |  5 => "alt="rolleyes" src="//t.domain.com/default/rolleyes.gif" title="rolleyes"" 
   2 => array (6)
   |  0 => "/>"
   |  1 => "/>"
   |  2 => "/>"
   |  3 => "/>"
   |  4 => "/>"
   |  5 => "/>"

then it create own images based on original <img /> attributes and store them in varibale $images

foreach ($matches[1] as $id => $imgAttributes) {
    $img = new \HtmlElement('img');
    $img->addAttributes($imgAttributes);
    $img->attributes([
        'src' => $img->attr('src'),
        'alt' => escapeHtml($img->attr('alt')),
        'title' => escapeHtml($img->attr('title'))
    ]);
    $images[$id] = $img->render();

in my case i just need attributes alt, title and src from images, also if you don't need use escape HTML in attributes like alt, title or others, you can omit this part of code and witch little alterations continue with next code row:

$text = str_replace($matches[0][$id], '{#@#IMG_' . $id .'}', $text);

which will replace in original text every image tag with placeholder '{#@#IMG_' . $id .'}', so for exmaple first image will be replaced in original text like:

{#@#IMG_0}

Keep in mind placeholder should be enough original for your type of texts and also can't contain any special characters which would be replaced with htmlspecialchars() for HTML entities.

When every image is replaced by own placeholder then we can convert special characters to HTML entities in whole text:

$text = escapeHtml($text);

and as last step we replace placeholders in text with image tags:

foreach ($images as $id => $image) {
    $text = str_replace('{#@#IMG_' . $id . '}', $image, $text);
}

Whole code

function escapeHtml($text)
{
   return htmlspecialchars($text, ENT_QUOTES, 'UTF-8', false);
}
function escapeHtmlKeepImages($text)
{
   if (preg_match_all('#<img\s+([^>]+?)\s?(/>|>)#i', $text, $matches)) {
      $images = [];
      foreach ($matches[1] as $id => $imgAttributes) {
            $img = new \HtmlElement('img');
            $img->addAttributes($imgAttributes);
            $img->attributes([
               'src' => $img->attr('src'),
               'alt' => escapeHtml($img->attr('alt')),
               'title' => escapeHtml($img->attr('title'))
            ]);
            $images[$id] = $img->render();
            $text = str_replace($matches[0][$id], '{#@#IMG_' . $id .'}', $text);
        }
        $text = escapeHtml($text);
        foreach ($images as $id => $image) {
            $text = str_replace('{#@#IMG_' . $id . '}', $image, $text);
        }
    } else {
        $text = escapeHtml($text);
    }
    return $text;
}

Version where you don't need treat attributes alt, title and others, with example


$text = '
<strong>hello</strong>
<img src="image.gif" alt="something" >><3
how are you?
<img src="signature.gif" alt="signature"  title="signature"/>
';

function  escapeHtml($text)
{
    return htmlspecialchars($text, ENT_QUOTES, 'UTF-8', false);
}
function escapeHtmlKeepImages($text)
{
    if (preg_match_all('#<img\s+([^>]+?)\s?(/>|>)#i', $text, $matches)) {
        array_map(
            function ($id, $img) use (&$text) {
                $text = str_replace($img, '{#@#IMG_' . $id . '}', $text);
            },
            array_keys($matches[0]), $matches[0]
        );

        $text = escapeHtml($text);

        array_map(
            function ($id, $img) use (&$text) {
                $text = str_replace('{#@#IMG_' . $id . '}', $img, $text);
            },
            array_keys($matches[0]), $matches[0]
        );
    } else {
        $text = escapeHtml($text);
    }
    return $text;

}

echo escapeHtmlKeepImages($text);

output:

&lt;strong&gt;hello&lt;/strong&gt;
<img src="image.gif" alt="something" >&gt;&lt;3
how are you?
<img src="signature.gif" alt="signature"  title="signature"/>
Hekimen
  • 174
  • 1
  • 2
  • 12
-1

I am a beginner in PHP and html too. Let me try to solve this problem.

As we can see the result of "welcome", you can change to

    <p><?php echo htmlspecialchars("Welcome"); ?></p>

It will shows "Welcome" only. We can use the same method to handle the as

    <img <?php echo htmlspecialchars("src='http://nl3.php.net/images/logo.php");?>/> 

Although it is not a good programming style, you can solve the problem first and study the preg_match() later. Cheers!

Chris.C
  • 297
  • 3
  • 17