So decided to use SimpleXMLElement to create simple RSS feeds for Lafango. However there seems to be no easy way to create a simple file like this:
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
<channel>
<title>Lafango - Videos</title>
<link>http://lafango.com/</link>
<description>Directory of Videos on Lafango</description>
<language>en-us</language>
<pubDate>Tue, 16 Nov 2010 15:29:19 EST</pubDate>
<lastBuildDate>Tue, 16 Nov 2010 15:29:19 EST</lastBuildDate>
<managingEditor>support@lafango.com (Admin)</managingEditor>
<webMaster>webmaster@lafango.com (Admin)</webMaster>
<ttl>5</ttl>
<item>
<link>http://lafango.com/btfffilms/media/12228-pennysavers-trailer</link>
<pubDate>Tue, 13 Jan 2009 15:29:19 EST</pubDate>
<title>PennySavers Trailer</title>
<description>The Theatrical Trailer to this summer's breakout hit...</description>
<media:title>PennySavers Trailer</media:title>
<media:description>The Theatrical Trailer to this summer's breakout hit...</media:description>
</item>
</channel>
</rss>
Which would think was simple enough to accomplish, yet to get the 'media' namespace was a pain in the tush. With the built in addAttribute('media:title','PennySavers Trailer','http://search.yahoo.com/mrss/'); it was adding the namespace alright, just the problem being it was being added to the nodes themselves and there was no way to just append it to the main RSS node.
So, without adding the namespace to the RSS node as in the example, the readers these were being sent to just laughed at me. That usually means it's time to rummage through php.net's documentation and comments and see if someone else already solved this tiny problem. Unfortunately nothing good came out of it other then having to write my own basic extended SimpleXMLElement. So here it is:
<?php
class Extended_SimpleXMLElement extends SimpleXMLElement {
# Source: http://phy.im/m/blog_31/extended_simplexmlelement.php
# Add a namespace to the main node.
public function addNamespace($namespace=false,$url=false) {
# Make sure we have a namespace to add.
if(!$namespace||!$url) return false;
# Add the attribute to the beginning. This will later be
# stripped out, we just want SimpleXMLElement's auto attribute.
$this->addAttribute($namespace.':mullanaphy','',$url);
return $url;
}
# We need to preg_replace the added $ns:mullanaphy="" out.
public function asXML() { return preg_replace('# \w+\:mullanaphy=""#','',parent::asXML()); }
# This I actually got from 'Yuri Vecchi' via php.net.
# liked the idea so added it in. It allows you to chain
# addAttribute() on the same node.
# e.g. $node = $XML->addChild('node','')->addAttribute('a',1)->addAttribute('b',2);
public function addAttribute($name,$value,$namespace=NULL) {
parent::addAttribute($name,$value,$namespace);
return $this;
}
}
?>
The extension add the addNamespace to the class which allowed me to do what was needed. On top of that added something that seemed pretty cool that was done by a Yuri on php.net's comment section. Lastly asXML needed to be overwritten just for the simple clean up preg_replace.
Here's an example of using the extended class:
<?php
class Example {
public function __construct() {
# Require the class file if you don't have an __autoload() set.
require_once 'extended_simplexmlelement.php';
# Define the class and provide the default RSS.
$XML = new Extended_SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"></rss>');
# Add a namespace.
$media = $XML->addNamespace('media','http://search.yahoo.com/mrss/');
# Add a channel node and fill it up.
$channel->addChild('title','Lafango - Videos');
$channel->addChild('link','http://lafango.com/');
$channel->addChild('description','Directory of Videos on Lafango');
$channel->addChild('language','en-us');
$channel->addChild('pubDate',date('D, d M Y H:i:s T');
$channel->addChild('lastBuildDate',date('D, d M Y H:i:s T'));
$channel->addChild('managingEditor','support@lafango.com (Admin)');
$channel->addChild('webMaster','webmaster@lafango.com (Admin)');
$channel->addChild('ttl',5);
# Iterate through an array and append item nodes.
foreach($this->fetch() as $row):
$item = $channel->addChild('item');
$item->addChild('title',$row->title);
$item->addChild('link',$row->url);
$item->addChild('description',$row->description);
$item->addChild('media:title',$row->title,$media);
$item->addChild('media:description',$row->description,$media);
endforeach;
echo $XML->asXML();
}
private function fetch() { /* gets some array data from someplace, don't worry about it. */ }
}
new Example;
?>
Granted in this example we could have easily included the namespace inside the rss node during the __construct(), however in my situation the RSS attributes needed to be dynamic as RSS was defined on an abstract class and those extending it all had different needs.
This solution is probably not that relevant yet if it saves someone time then so be it. Please use and abuse this freely.
Hi, John! Don't know if u still remmember me, haha. We were penpals about two years ago. Good to see that u r still updating ur blog. I'm now studying in the US. Just want to say hi and wish u and ur family a happy Thanksgiving!
Too add, someone could get crafty with the addNamespace. Store locally inside the class the namespace, something like:
$this->namespaces[$name] = $url;
Then rewrite addChild to preg_match the child name for "#(\w+):\w+#". Then if isset($this->namespaces[$matches[1]]) to auto append $this->namespace[$matches[1]] as the namespace on the outside. So in essence, addChild doesn't need the third parameter if you've already used addNamespace();
Think I'll update the linked Extended_SimpleXMLElement to accommodate that.