user3716348
user3716348

Reputation: 21

PHP Xpath Trying to get property of non-object

I have a problem with xpath. I'm trying to get data from this:

<div class="clan__table">
      <div class="clan__headers">
        <div class="clan__headerCaption">Rank</div>
        <div class="clan__headerCaption">Name</div>
        <div class="clan__headerCaption">Level</div>
        <div class="clan__headerCaption">League</div>
        <div class="clan__headerCaption">Trophies</div>
        <div class="clan__headerCaption">Donations</div>
        <div class="clan__headerCaption">Role</div>
      </div>    
      <div class="clan__rowContainer">
        <div class="clan__row">
                            #2
                    </div>
        <div class="clan__row">
          <a class="ui__blueLink" href='/profile/8C2PQYYL'>Voodoo</a>
        </div>
        <div class="clan__row">
          <span class="clan__playerLevel">11</span>
        </div>
        <div class="clan__row">
          <div class="clan__leagueContainer">
                            <div class="league__1"></div>
                        </div>
        </div>
        <div class="clan__row">
          <div class="clan__cup">4000</div>
        </div>
        <div class="clan__row">96</div>
        <div class="clan__row">
             Co-Leader
                    </div>
      </div>
....
....

this is my code:

$xpath2 = new DOMXPath($doc2);
$text = "";
$playerlist_ul = $xpath2->query("//div[@class='clan__table']")->item(0);
$playernodes = $playerlist_ul->childNodes;
if (!empty($playernodes))
{
    foreach ($playernodes as $node2)
    {
        if ($node2->hasChildNodes())
        {

            $playerinfo = new DOMDocument();
            libxml_use_internal_errors(true);
            $playerinfo->loadHTML($node2->ownerDocument->saveHTML($node2));
            $xpath3 = new DOMXPath($playerinfo);


            $player['level'] = $xpath3->query("//span[@class='clan__playerLevel']")->item(0)->
                textContent;
            $player['name'] = $xpath3->query("//a[@class='ui__blueLink']")->item(0)->
                textContent;
            $player['id'] = $xpath3->query("//a[@class='ui__blueLink']/@href")->item(0)->
                textContent;
            $player['trophy'] = $xpath3->query("//div[@class='clan__cup']")->item(0)->
                textContent;
            $player['role'] = $xpath3->query("//div[@class='clan__row']")->item(6)->
                textContent;
            $player['donate'] = $xpath3->query("//div[@class='clan__row']")->item(5)->
                textContent;
            $player['clan_position'] = $xpath3->query("//div[@class='clan__row']")->item(0)->
                textContent;


            $players[] = $player;
        }
    }
}

Everything almost working but i get error: Trying to get property of non-object in every line with $player['....'] This is result

array (size=49)
  0 => 
    array (size=6)
      'level' => null
      'name' => null
      'trophy' => null
      'role' => null
      'donate' => null
      'clan_position' => null
  1 => 
    array (size=6)
      'level' => string '11' (length=2)
      'name' => string '/profile/8C2PQYYL' (length=17)
      'trophy' => string '4056' (length=4)
      'role' => string '
             Co-Leader
                    ' (length=44)
      'donate' => string '192' (length=3)
      'clan_position' => string '
                            #1
                    ' (length=52) 

There is something wrong with array[0] So my question is: How to avoid this error?

Upvotes: 0

Views: 612

Answers (1)

ThW
ThW

Reputation: 19492

DOMXpath::query() will always return a DOMNodeList instance. You're fetching an node from that list and access its $textContent property. The error happens if here is no node.

It is easy to solve if you use DOMXpath::evaluate(). Xpath expression allow for typecasts, but only DOMXpath::evaluate() supports scalar return values.

So basically you use foreach() for expressions that return a node list and inside that expressions with typecasts for the details. Here is no need to save a fragment and load it as a separate document, you can provide a context node for the expression.

$document = new DOMDocument();
$document->loadHtml($html);
$xpath = new DOMXpath($document);

$expression = "//div[@class='clan__table']/div[@class='clan__rowContainer']";
foreach ($xpath->evaluate($expression) as $clanNode) {
  $result = [
    'level' => $xpath->evaluate("string(.//span[@class='clan__playerLevel'])", $clanNode),
    'name' => $xpath->evaluate("string(.//a[@class='ui__blueLink'])", $clanNode),
    'id' => $xpath->evaluate("string(.//a[@class='ui__blueLink']/@href)", $clanNode),
    'trophy' => $xpath->evaluate("string(.//div[@class='clan__cup'])", $clanNode),
    'role' => $xpath->evaluate("string(.//div[@class='clan__row'][7])", $clanNode),
    'donate' => $xpath->evaluate("string(.//div[@class='clan__row'][6])", $clanNode),
    'clan_position' => $xpath->evaluate(
       "normalize-space(.//div[@class='clan__row'][1])", $clanNode
    )
  ];
  var_dump($result);
}

Output:

array(7) {
  ["level"]=>
  string(2) "11"
  ["name"]=>
  string(6) "Voodoo"
  ["id"]=>
  string(17) "/profile/8C2PQYYL"
  ["trophy"]=>
  string(4) "4000"
  ["role"]=>
  string(46) "
             Co-Leader
                    "
  ["donate"]=>
  string(2) "96"
  ["clan_position"]=>
  string(2) "#2"
}

A little about the Xpath string() is an explicit typecast. It returns the text content of the first node of the list or an empty string. normalize-space() includes an implicit typecast, but it remove trims the string and replaces all groups of whitespaces with single spaces. [6] is the 6th element in the node list.

Upvotes: 1

Related Questions