开发者

How to properly replace inline images with styled floating divs via PHP's DOMDocument?

I'm stuck on the following problem and would like to know if you got an advise.

A WYSIWYG editor allows the user to upload and embed images. However, my users are mostly scientists but don't have any knowledge of how to use HTML or even how to re-size images properly for a web page. That's why I am re-sizing the images automatically server-side to a thumbnail and a full view size. Clicking on a thumbnail shall open a lightbox with full image.

The WYSIWYG editor throws images into <p> tags just like this (see last paragraph):

<p>Intro Text</p>
<ul>
   <li>List point 1</li>
   <li>List point 2</li>
</ul>
<p>Some text before an image. 
   <img alt="Slide 1" src="/files/slide1.png" /> 
   Maybe some text in between, nobody knows what the scientists are up to. 
   <img alt="Slide 2" src="/files/slide2.png" /> 
   And even more text right after that.
</p>

What I would like to do is get the images out of the <p> Tags and add them before the respective paragraph within floating <div>s:

<p>Intro Text</p>
<ul>
   <li>List point 1</li>
   <li>List point 2</li>
</ul>
<div class="custom">
   <a href="/files/fullview/slide1.png" rel="lightbox[group][Slide 1]">
      <img src="/files/thumbs/files/slide1.png" />
   </a>
</div>
<div class="custom">
   <a href="/files/fullview/slide2.png" rel="lightbox[group][Slide 2]">
      <img src="/files/thumbs/files/slide2.png" />
   </a>
</div>
<p>Some text before an image. 
   Maybe some text in between, nobody knows what the scientists are up to. 
   And even more text right after that.
</p>

So what I need to do is to get all the image nodes of the html produced by the editor, process them, insert the divs and remove the image nodes. After reading quite a lot of similar questions I'm missing something and can't get it to work. Probably, I am still misunderstanding the whole concept behind DOM manipulation. Here's what I came up with til now:

// create DOMDocument
$doc = new DOMDocument();
// load WYSIWYG html into DOMDocument
$doc->loadHTML($html_from_editor);
// create DOMXpath
$xpath = new DOMXpath($doc);
// create list of all first level DOMNodes (these are p's or ul's in most cases)
$开发者_运维知识库children = $xpath->query("/");
foreach ( $children AS $child ) {
    // now get all images
    $cpath = new DOMXpath($child);
    $images = $cpath->query('//img');
    foreach ( $images AS $img ) {
        // get attributes
        $atts = $img->attributes;
        // create replacement
        $lb_div = $doc->createElement('div');
        $lb_a = $doc->createElement('a');
        $lb_img = $doc->createElement('img');
        $lb_img->setAttribute("src", '/files/thumbs'.$atts->src);
        $lb_a->setAttribute("href", '/files/fullview'.$atts->src);
        $lb_a->setAttribute("rel", "lightbox[slide][".$atts->alt."]");
        $lb_a->appendChild($lb_img);
        $lb_div->setAttribute("class", "custom");
        $lb_div->appendChild($lb_a);
        $child->insertBefore($lb_div);
        // remove original node
        $child->removeChild($img);
    }
}

Problems I ran into:

  1. `$atts` is not populated with values. It does contain the right attribute names, but values are missing.
  2. `insertBefore` should be called on the child's parent node if I understood that right. So, it should rather be `$child->parentNode->insertBefore($lb_div, $child);` but the parent node is not defined.
  3. Removal of original img tag does not work.

I'd be thankful for any advise what I am missing. Am I on the right track or should this be done completely different?

Thans in advance, Paul


This should do it (demo):

$dom = new DOMDocument;
$dom->preserveWhiteSpace = false;
$dom->loadXML("<div>$xhtml</div>"); // we need the div as root element

// find all img elements in paragraphs in the partial body
$xp = new DOMXPath($dom);
foreach ($xp->query('/div/p/img') as $img) {

    $parentNode = $img->parentNode; // store for later
    $parentNode->removeChild($img); // unlink all found img elements

    // create a element
    $a = $dom->createElement('a');
    $a->setAttribute('href', '/files/fullview/' . basename($img->getAttribute('src')));
    $a->setAttribute('rel', sprintf('lightbox[group][%s]', $img->getAttribute('alt')));
    $a->appendChild($img);

    // prepend img src with path to thumbs and remove alt attribute
    $img->setAttribute('href', '/files/thumbs' . $img->getAttribute('src'));
    $img->removeAttribute('alt'); // imo you should keep it for accessibility though

    // create the holding div
    $div = $dom->createElement('div');
    $div->setAttribute('class', 'custom');
    $div->appendChild($a);

    // insert the holding div
    $parentNode->parentNode->insertBefore($div, $parentNode);
}

$dom->formatOutput = true;
echo $dom->saveXml($dom->documentElement);


As I commented, your code had multiple errors which prevented you from getting started. Your concept looks quite well from what I see and the code itself only had minor issues.

  1. You were iterating over the document root element. That's just one element, so picking up all images therein.
  2. The second xpath must be relative to the child, so starting with ..
  3. If you load in a HTML chunk, DomDocument will create the missing elements like body around it. So you need to address that for your xpath queries and the output.
  4. The way you accessed the attributes was wrong. With error reporting on, this would have given you error information about that.

Just take a look through the working code I was able to assemble (Demo). I've left some notes:

$html_from_editor = <<<EOD
<p>Intro Text</p>
<ul>
   <li>List point 1</li>
   <li>List point 2</li>
</ul>
<p>Some text before an image. 
   <img alt="Slide 1" src="/files/slide1.png" /> 
   Maybe some text in between, nobody knows what the scientists are up to. 
   <img alt="Slide 2" src="/files/slide2.png" /> 
   And even more text right after that.
</p>
EOD;

// create DOMDocument
$doc = new DOMDocument();
// load WYSIWYG html into DOMDocument
$doc->loadHTML($html_from_editor);
// create DOMXpath
$xpath = new DOMXpath($doc);

// create list of all first level DOMNodes (these are p's or ul's in most cases)
# NOTE: this is XHTML now
$children = $xpath->query("/html/body/p");

foreach ( $children AS $child ) {
    // now get all images
    $cpath = new DOMXpath($doc);
    $images = $cpath->query('.//img', $child); # NOTE relative to $child, mind the .

    // if no images are found, continue
    if (!$images->length) continue;

    // insert replacement node
    $lb_div = $doc->createElement('div');
    $lb_div->setAttribute("class", "custom");
    $lb_div = $child->parentNode->insertBefore($lb_div, $child);


    foreach ( $images AS $img ) {
        // get attributes
        $atts = $img->attributes;
        $atts = (object) iterator_to_array($atts); // make $atts more accessible    

        // create the new link with lighbox and full view
        $lb_a = $doc->createElement('a');
        $lb_a->setAttribute("href", '/files/fullview'.$atts->src->value);
        $lb_a->setAttribute("rel", "lightbox[slide][".$atts->alt->value."]");

        // create the new image tag for thumbnail
        $lb_img = $img->cloneNode(); # NOTE clone instead of creating new
        $lb_img->setAttribute("src", '/files/thumbs'.$atts->src->value);

        // bring the new nodes together and insert them
        $lb_a->appendChild($lb_img);
        $lb_div->appendChild($lb_a);

        // remove the original image
        $child->removeChild($img);
    }
}

// get body content (original content)
$result = '';
foreach ($xpath->query("/html/body/*") as $child) {
    $result .= $doc->saveXML($child); # NOTE or saveHtml 
}

echo $result;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜