Get XML Attribute with SimpleXML - php

I'm trying to get the $xml->entry->yt:statistics->attributes()->viewCount attribute, and I've tried some stuff with SimpleXML, and I can't really get it working!
Attempt #1
<?php
$xml = simplexml_load_file("http://gdata.youtube.com/feeds/api/videos?author=Google");
echo $xml->entry[0]->yt:statistics['viewCount'];
?>
Attempt #2
<?php
$xml = simplexml_load_file("http://gdata.youtube.com/feeds/api/videos?author=Google");
echo $xml->entry[0]->yt:statistics->attributes()->viewCount;
?>
Both of which return blank, though SimpleXML is working, I tried to get the feed's title, which worked!
Any ideas?
I've looked at loads of other examples on SO and other sites, but somehow this isn't working? does PHP recognize the ':' to be a cut-off, or am I just doing something stupid?
Thank you, any responses greatly appreciated!

If you just want to get the viewcount of a youtube video then you have to specify the video ID. The youtube ID is found in each video url. For example "http://www.youtube.com/watch?v=ccI-MugndOU" so the id is ccI-MugndOU. In order to get the viewcount then try the code below
$sample_video_ID = "ccI-MugndOU";
$JSON = file_get_contents("http://gdata.youtube.com/feeds/api/videos?q={$sample_video_ID}&alt=json");
$JSON_Data = json_decode($JSON);
$views = $JSON_Data->{'feed'}->{'entry'}[0]->{'yt$statistics'}->{'viewCount'};
echo $views;

I would use the gdata component from the zend framework. Is also available as a separate module, so you don't need to use the whole zend.

The yt: prefix marks that element as being in a different "XML namespace" from the rest of the document. You have to tell SimpleXML to switch to that namespace using the ->children() method.
The line you were attempting should actually look like this:
echo (string)$xml->entry[0]->children('yt', true)->statistics->attributes(NULL)->viewCount;
To break this down:
(string) - this is just a good habit: you want the string contents of the attribute, not a SimpleXML object representing it
$xml->entry[0] - as expected
->children('yt', true) - switch to the namespace with the local alias 'yt'
->statistics - as expected
->attributes(NULL) - technically, the attribute "viewCount" is back in the default namespace, because it is not prefixed with "yt:", so we have to switch back in order to see it
->viewCount - running ->attributes() gives us nothing but attributes, which are accessed with ->foo not ['foo']

Related

PHP xpath on simplexml_load_file doesn't work

I want to read some data of the latest video on a youtube channel.
So i load the feed with simplexml_load_file and after that I use XPath for navigate to nodes.
$xmlFeed = simplexml_load_file("https://www.youtube.com/feeds/videos.xml?channel_id=UCo0bvu1jzU4WpHS3FglLU8g");
echo $xmlFeed->xpath("//entry[1]/title")[0];
echo $xmlFeed->xpath("//entry[1]/link")[0];
echo $xmlFeed->xpath("//entry[1]/id")[0];
I tried multiple style of XPath and it never work, I also tried to use DOMDocument and DOMXPath classes and it didn't work.
I use similar code for a wordpress rss and all works fine.
What am I wrong?
As per SimpleXMLElement::xpath's doc page's first comment:
To run an xpath query on an XML document that has a namespace, the
namespace must be registered with
SimpleXMLElement::registerXPathNamespace() before running the query.
If the XML document namespace does not include a prefix, you must make
up an arbitrary one, and then use it in your query.
You should therefore do this:
$xmlFeed = simplexml_load_file('https://www.youtube.com/feeds/videos.xml?channel_id=UCo0bvu1jzU4WpHS3FglLU8g');
foreach ($xmlFeed->getDocNamespaces() as $prefix => $namespace) {
$xmlFeed->registerXPathNamespace($prefix ?: 'default', $namespace);
}
echo $xmlFeed->xpath('//default:entry[1]/default:title')[0];
echo $xmlFeed->xpath('//default:entry[1]/default:link')[0];
echo $xmlFeed->xpath('//default:entry[1]/default:id')[0];
Note: feel free to use something shorter than default if it's inconvenient.

How to change specific xml tag value, where tag has an id. Using php

I am trying to change a value in an xml file using php. I am loading the xml file using php into an object like this..
if(file_exists('../XML/example.xml')) {
$example = simplexml_load_file('../XML/example.xml');
}
else {
exit ("can't load the file");
}
Then once it is loaded I am changing values within tags, by assigning them the contents of another variable, like this...
$example->first_section->second_section->third_section->title = $var['data'];
Then once I've made the necessary changes the file is saved. So far this process is working well, but have now hit a stumbling block.
I want to change a value within a particular tag in my xml file, which has an id. In the XML file it looks like this.
<first_section>
<second_section>
<third_section id="2">
<title>Mrs</title>
</third_section>
</second_section>
</first_section>
How can I change this value using similar syntax to what I've been using?
doing..
$example->first_section->second_section->third_section id="2" ->title = $var['data']
doesn't work as the syntax is wrong.
I've been scanning through stack overflow, and all over the net for an example of doing it this way but come up empty.
Is it possible to target and change a value in an xml like this, or do I need to change the way I am amending this file?
Thanks.
Some dummy code as your provided XML is surely not the original one.
$xml = simplexml_load_file('../XML/example.xml');
$section = $xml->xpath("//third_section[#id='2']")[0];
// runs a query on the xml tree
// gives always back an array, so pick the first one directly
$section["id"] = "3";
// check if it has indeed changed
echo $xml->asXML();
As #Muhammed M. already said, check the SimpleXML documentation for more information. Check the corresponding demo on ideone.com.
Figured it our after much messing around. Thanks to your contributions I indeed needed to use Xpath. However the reason it wasn't working for me was because I wasn't specifying the entire path for the node I wanted to edit.
For example, after loading the xml file into an object ($xml):
foreach($xml->xpath("/first_section/second_section/third_section[#id='2']") as $entry ) {
$entry->title = "mr";
}
This will work, because the whole path to the node is included in the parenthesis.
But in our above examples eg:
foreach($xml->xpath("//third_section[#id='2']" as $entry ) {
$entry->title = "mr";
}
This wouldn't work, even though it was my understanding that the double // will make it drill down, and I assumed that xpath will search the whole xml structure and return where id=2. It appears after spending hours testing this isn't the case. You must include the entire path to the node. As soon as I did that it worked.
Also on a side note. $section = $xml->xpath("//third_section[#id='2']")[0];
IS incorrect syntax. You don't need to specify the index "[0]" at the end. Including it flags up Dreamweavers syntax checker. And ignoring Dreamweaver and uploading anyway breaks the code. All you need is..
$section = $xml->xpath(" entire path to node in here [#id='2']");
Thanks for helping and suggesting xpath. It works very well... once you know how to use it.

Can't access XML node via xpath() (YT channel feed)

Very stumped by this one. In PHP, I'm fetching a YouTube user's vids feed and trying to access the nodes, like so:
$url = 'http://gdata.youtube.com/feeds/api/users/HCAFCOfficial/uploads';
$xml = simplexml_load_file($url);
So far, so fine. Really basic stuff. I can see the data comes back by running:
echo '<p>Found '.count($xml->xpath('*')).' nodes.</p>'; //41
echo '<textarea>';print_r($xml);echo '</textarea>';
Both print what I would expect, and the print_r replicates the XML structure.
However, I have no idea why this is returning zero:
echo '<p>Found '.count($xml->xpath('entry')).'"entry" nodes.</p>';
There blatantly are entry nodes in the XML. This is confirmed by running:
foreach($xml->xpath('*') as $node) echo '<p>['.$node->getName().']</p>';
...which duly outputs "[entry]" 25 times. So perhaps this is a bug in SimpleXML? This is part of a wider feed caching system and I'm not having any trouble with other, non-YT feeds, only YT ones.
[UPDATE]
This question shows that it works if you do
count($xml->entry)
But I'm curious as to why count($xml->xpath('entry')) doesn't also work...
[Update 2]
I can happily traverse YT's anternate feed format just fine:
http://gdata.youtube.com/feeds/base/users/{user id}/uploads?alt=rss&v=2
This is happening because the feed is an Atom document with a defined default namespace.
<feed xmlns="http://www.w3.org/2005/Atom" ...
Since a namespace is defined, you have to define it for your xpath call too. Doing something like this works:
$url = 'http://gdata.youtube.com/feeds/api/users/HCAFCOfficial/uploads';
$xml = simplexml_load_file($url);
$xml->registerXPathNamespace('ns', 'http://www.w3.org/2005/Atom');
$results = $xml->xpath('ns:entry');
echo count($results);
The main thing to know here is that SimpleXML respects any and all defined namespaces and you need to handle them accordingly, including the default namespace. You'll notice that the second feed you listed does not define a default namespace and so the xpath call works fine as is.

PHP SimpleXML issue

I'm trying to get an xml stream by using curl. I've recieved the string with curl but I'm having troubles parsing the xmlstream with SimpleXML. The url im using is http://www.google.com/books/feeds/volumes/fR4vqfywNlgC
and it seems to be ignoring the parts containing "dc". Why?
The dublin core data (at least, I'm assuming that's what the DC prefix means in this case) uses its own namespace. You need to refer to that namespace when retrieving these elements. This can be done using the 'children' method.
Example:
$sxml = simplexml_load_string($xml);
$dcData = $sxml->children('dc', TRUE);
echo (string)$dcData->creator;
An article/posting detailing the problem and solution can be found here.
http://blogs.sitepoint.com/simplexml-and-namespaces/

Parsing XML (PHP)

I'm using SimpleXML . I want to get this node's text attribute.
<yweather:condition text="Mostly Cloudy" ......
I'm using this it's not working :
$xml->children("yweather", TRUE)->condition->attributes()->text;
Do a print_r() on $xml to see how the structure looks. From there you should be able to see how to access the information.
It looks like you are trying to access an attribute, which is stored in an array in $xml->yweather->attributes() so:
$attributes = $xml->condition->attributes();
$weather = $attributes['text'];
To deal with the namespace, you need to use children() to get the members of that namespace.
$weather_items = $xml->channel->item->children("http://xml.weather.yahoo.com/ns/rss/1.0");
It might help to mention that the string you showed is part of a feed, specifically the RSS formatted Yahoo Weather feed.
You would probably use $xml->condition but there may be branches before that.

Categories