2

I have an XML file which has the following structure:

<item>
  <title></title>
  <link></link>
  <description></description>
  <content:encode></content:encode>
  <pubDate></pubDate>
  <author></author>
  <guid></guid>
  <dc:date></dc:date>
</item>

I am displaying specific data from the above.

<?php
$importPathHref="test.xml";
$importBuffer = implode("",file($importPathHref));
$dom = new DomDocument();
$dom->loadXML($importBuffer);
$xpath = new DOMXpath($dom);
$items = $xpath->query("//item");
$results = parse($items);

$col = $dom->getElementsByTagName('item');

$i = 0;
foreach( $col as $item ){
    $blogTitle=$item->childNodes[1]->nodeValue;
    $blogImage=$item->childNodes[5]->nodeValue;
    $blogDate=$item->childNodes[9]->nodeValue; 
    $blogAuthor=$item->childNodes[11]->nodeValue;
        
        
    if(++$i > 4) break;
        
?>

<?php if ($i == 1) { ?>
  <div class="item__card">
    <div class="item__cardImg">
      <?php echo $blogImage; ?>
    </div>
    <div class="item__cardContent">
      <span class="author"><?php echo $blogAuthor; ?></span>
      <span class="title"><?php echo $blogTitle; ?></span>
      <span class="date"><?php echo $blogDate; ?></span>
    </div>
  </div>
<?php } ?>

<?php } ?>
<!-- end for loop -->

I'm trying to run the for to get results (notice if(++$i > 4) break;). Everything else works fine, however, the $blogAuthor, only shows for the first item. Author is empty for the last three results. Unsure why?

1
  • This does not seem to be your real code. The condition <?php if ($i == 1) { ?> will result in displaying just one item. Commented Jun 3, 2019 at 7:05

2 Answers 2

3

Your access to the childNodes of $item is dangerous.

It will work for something like this.

<item>
  <title>Title</title>
  <link>Link</link>
  <description>Desc</description>
  <content:encode>Encode</content:encode>
  <pubDate>Date</pubDate>
  <author>Author</author>
  <guid>Guid</guid>
  <dc:date>Date</dc:date>
</item>

but will fail for this

<item>
  <title>Title</title>
  <link>Link</link>
  <description>Desc</description>
  <content:encode>Encode</content:encode>
  <pubDate>Date</pubDate><author>Author</author>
  <guid>Guid</guid>
  <dc:date>Date</dc:date>
</item>

This is because childnodes is matching the whitespace between the elements. If one is missing (like having no whitespace between <pubDate></pubDate> and <author></author>), your counting will fail.

You'd be better off by using children

    $blogTitle=$item->children[0]->nodeValue;
    $blogImage=$item->children[2]->nodeValue;
    $blogDate=$item->children[4]->nodeValue; 
    $blogAuthor=$item->children[5]->nodeValue;

or even better with a dom query

    $blogTitle=$item->getElementsByTagName('title')->item(0)->nodeValue;
    $blogImage=$item->getElementsByTagName('description')->item(0)->nodeValue;
    $blogDate=$item->getElementsByTagName('pubDate')->item(0)->nodeValue; 
    $blogAuthor=$item->getElementsByTagName('author')->item(0)->nodeValue;
Sign up to request clarification or add additional context in comments.

4 Comments

This querySelector ?? where does this come from?
Thank you for explaining what's causing the issue. But as RamRaider mentioned, where is querySelector coming from? It's not supported by PHP's DOMDocument?
@Freddy I'm sorry. Been in JS mode. I changed my code using getElementsByTagName now. This is untested
@yunzen - No worries and thank you, it's working now (and is a much more concrete approach). Thanks again :)
0

The index of item child nodes might not be the same. They can have a different order, additional nodes (including comments) or just some line break.

By default the whitespaces between nodes will be parsed into text nodes. This allows the library to save the same XML. You can set the DOMDocument::preserveWhiteSpace property to avoid it.

You started using Xpath, but later ignored the results. Xpath expressions allow you a more targeted access to the XML nodes.

$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
// fetch all item nodes and limit to the first 4
$items = $xpath->evaluate("(//item)[position() < 5]");

foreach ($items as $index => $item) {
    // cast the first title child node to string - empty if not found
    $blogTitle = $xpath->evaluate('string(title)', $item);

    // ...   
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.