PHP and XML finding a node based on an attribute to find another attribute or the index of the node

StackOverflow https://stackoverflow.com/questions/21293712

Question

I'm trying to optimize a little php app I wrote for parsing YouTube profiles. Enter a YouTube user name and the app returns a simple listing of the number of videos uploaded, favorites, subscribers and so on by parsing the XML returned by a gdata query for the account profile. For example this XML:

<?xml version="1.0" encoding="UTF-8" ?> 
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:yt="http://gdata.youtube.com/schemas/2007" gd:etag="W/"C0EHSX47eCp7I2A9Wh5aEU4.""> 
<id>tag:youtube.com,2008:user:H5m_qmnr3dHOO8x7m7dtvw</id> 
<published>2006-06-15T22:59:11.000Z</published> 
<updated>2014-01-07T21:53:58.000Z</updated> 
<category scheme="http://schemas.google.com/g/2005#kind" term="http://gdata.youtube.com/schemas/2007#userProfile" /> 
<category scheme="http://gdata.youtube.com/schemas/2007/channeltypes.cat" term="DIRECTOR" /> 
<title>epontius</title> 
<summary>channel page of epontius</summary> 
<link rel="alternate" type="text/html" href="https://www.youtube.com/channel/UCH5m_qmnr3dHOO8x7m7dtvw" /> 
<link rel="self" type="application/atom+xml" href="https://gdata.youtube.com/feeds/api/users/H5m_qmnr3dHOO8x7m7dtvw?v=2" /> 
 <author> 
<name>epontius</name> 
<uri>https://gdata.youtube.com/feeds/api/users/epontius</uri> 
<yt:userId>H5m_qmnr3dHOO8x7m7dtvw</yt:userId> 
</author> 
<yt:channelId>UCH5m_qmnr3dHOO8x7m7dtvw</yt:channelId> 
<gd:feedLink rel="http://gdata.youtube.com/schemas/2007#user.subscriptions" href="https://gdata.youtube.com/feeds/api/users/epontius/subscriptions?v=2" countHint="161" /> 
<gd:feedLink rel="http://gdata.youtube.com/schemas/2007#user.liveevent" href="https://gdata.youtube.com/feeds/api/users/epontius/live/events?v=2" countHint="0" /> 
<gd:feedLink rel="http://gdata.youtube.com/schemas/2007#user.favorites" href="https://gdata.youtube.com/feeds/api/users/epontius/favorites?v=2" countHint="73" /> 
<gd:feedLink rel="http://gdata.youtube.com/schemas/2007#user.contacts" href="https://gdata.youtube.com/feeds/api/users/epontius/contacts?v=2" countHint="184" /> 
<gd:feedLink rel="http://gdata.youtube.com/schemas/2007#user.inbox" href="https://gdata.youtube.com/feeds/api/users/epontius/inbox?v=2" /> 
<gd:feedLink rel="http://gdata.youtube.com/schemas/2007#user.playlists" href="https://gdata.youtube.com/feeds/api/users/epontius/playlists?v=2" /> 
<gd:feedLink rel="http://gdata.youtube.com/schemas/2007#user.uploads" href="https://gdata.youtube.com/feeds/api/users/epontius/uploads?v=2" countHint="26" /> 
<gd:feedLink rel="http://gdata.youtube.com/schemas/2007#user.newsubscriptionvideos" href="https://gdata.youtube.com/feeds/api/users/epontius/newsubscriptionvideos?v=2" /> 
<gd:feedLink rel="http://gdata.youtube.com/schemas/2007#user.recentactivity" href="https://gdata.youtube.com/feeds/api/users/epontius/events?v=2" /> 
<yt:googlePlusUserId>105085469892080308187</yt:googlePlusUserId> 
<yt:location>US</yt:location> 
<yt:statistics lastWebAccess="1970-01-01T00:00:00.000Z" subscriberCount="245" videoWatchCount="0" viewCount="0" totalUploadViews="68815" /> 
<media:thumbnail url="https://yt3.ggpht.com/-N_Et9Qg1APc/AAAAAAAAAAI/AAAAAAAAAAA/VIuQ_GuzA0Q/s88-c-k-no/photo.jpg" /> 
<yt:userId>H5m_qmnr3dHOO8x7m7dtvw</yt:userId> 
<yt:username display="epontius">epontius</yt:username> 
</entry>

The app as it stands works fine, but every so often YouTube changes the order of some elements or adds/removes items in the lines, so the app returns incorrect data as I am accessing the 'countHint' attribute. For example:

$uploadscount = $gd->feedLink[6]->attributes();
$uploads = $uploadscount['countHint'];
echo 'Number of uploads: ' . '<span class="datatext">' . $uploads . '</span>' . '<br />';

Which would return 26 in this case. But if the number or order of the feedLink lines changes, I'd get incorrect information or an error since the index number of the feedLink is hard coded. Each feedLink seems to have a unique rel= attribute and I was hoping to be able to use xpath and some sort of loop like a foreach to search for a specific rel value (ie. rel = "http://gdata.youtube.com/schemas/2007#user.uploads" ) and then be able to grab its countHint attribute value to assign it to a variable or at least grab its node index number (ie 6 in the case of the uploads) to then access the appropriate countHint attribute. And then repeat this for each of the feedLink lines and attributes for the data I want to grab. That way it will be more accurate and dynamic in the event these feedLink lines are modified. I just can't get my head around how to do it. The feedLink elements are empty elements in a different namespace (gd) and there are multiples which makes using xpath kind of confusing for me. I keep returning empty values and getting lost. Any suggestions would be appreciated.

Ok. Think I'm getting somewhere thanks to suggestions.

  foreach ($gd->feedLink as $feedLink) {
           $attributes = $feedLink->attributes();
            if (strpos($attributes['rel'], '#user.uploads')) {
             $uploads = $attributes['countHint'];
             }

            elseif (strpos($attributes['rel'], '#user.favorites')) {
             $favs = $attributes['countHint'];
              }

            elseif (strpos($attributes['rel'], '#user.subscriptions')) {
             $subscriptions = $attributes['countHint'];
              }

            elseif (strpos($attributes['rel'], '#user.liveevent')) {
             $liveevents = $attributes['countHint'];
             }

           elseif (strpos($attributes['rel'], '#user.contacts')) {
            $friends = $attributes['countHint'];
             }
          }

That will return the proper values I'm looking for, but I'm worried now that I'm doing extra processing doing the loop, since I would assume each loop tests each line regardless of whether it has already found that value in a previous loop?

Was it helpful?

Solution

You're on the right path with using foreach to parse through the XML data. I would just do a strpos() on each feedlink until I found the uploads element. Then would I set $uploadscount.

Something like this, maybe:

foreach ($gd->feedLink as $feedLink) {
  $attributes = $feedlink->attributes();
  if (strpos($attributes['rel'], '#user.uploads')) {
    $uploadscount = $attributes;
    break;
  }
  continue; 
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top