<?php

// Prototype for feeds as classes in WordPress
// Jamie Talbot, 2006
// http://jamietalbot.com/

$defined_feeds = array('rss2''rss''atom03''atom10''rdf');
$default_feed 'rss2';

class 
WPFeed
{
    var 
feed_data;
    var 
feed_type;
    var 
mime_type;
    var 
item_count;
    var 
use_excerpt;
    var 
head;
    var 
body;
    var 
footer;

    function 
WPFeed()
    {
        
// First choice, retrieve this data from a cache here. Otherwise...

        
global $posts;

        
$this->item_count get_settings('posts_per_rss');
        
$this->use_excerpt get_settings('rss_use_excerpt');

        
// Get meta information for this feed.
        
$this->feed_data['charset'] = get_settings('blog_charset');
        
$this->feed_data['xml_header'] = '<?xml version="1.0" encoding="' get_settings('blog_charset'). '?' '>';
        
$this->feed_data['generator'] = 'wordpress/' bloginfo_rss('version');
        
$this->feed_data['generator_url'] = "http://wordpress.org/?v=" bloginfo_rss('version');
        
$this->feed_data['title'] = bloginfo_rss('name');
        
$this->feed_data['url'] = bloginfo_rss('url');
        
$this->feed_data['description'] = bloginfo_rss('description');
        
$this->feed_data['published_date'] = get_lastpostmodified('GMT');
        
$this->feed_data['language'] = get_option('rss_language');
        
$this->feed_data['copyright'] = 'Copyright ' mysql2date('Y'get_lastpostdate('blog'), 0);
        
$this->feed_data['items'] = array();    
        
        if (
$posts
        { 
            
$post_count 0;
            foreach (
$posts as $post
            {
                
start_wp();
                
// These would have to return instead of echo.
                
$this->feed_data['items'][]['title'] = the_title_rss();
                
$this->feed_data['items'][]['url'] = permalink_single_rss();
                
$this->feed_data['items'][]['comment_link'] = comments_link();
                
$this->feed_data['items'][]['published_date'] = get_post_time('Y-m-d H:i:s'true);
                
$this->feed_data['items'][]['author'] = the_author();
                
$this->feed_data['items'][]['categories'] = the_category_rss();
                
$this->feed_data['items'][]['guid'] = the_guid();
                
$this->feed_data['items'][]['excerpt'] = the_excerpt_rss();
                
$this->feed_data['items'][]['content'] = the_content(''0'');
                
$this->feed_data['items'][]['comments_feed'] = comments_rss();
                
$this->feed_data['items'][]['enclosures'] = $this->enclosures();
                if (++
$post_count == $this->item_count) break;
            }    
        }
        
        
// Allow plugins to modify this source data.
        
$this->feed_data apply_filters('feed_data'$this->feed_data);
    }
        
    function 
feed_head()
    {
        
// Defining a filter in this way allows us to create plugins that can act on
        // the head only of specific feed types or of any feed type.
        
echo apply_filters('feed_head'$this->head$this->feed_type);    
    }

    function 
feed_body()
    {
        
// Defining a filter in this way allows us to create plugins that can act on
        // the body only of specific feed types or of any feed type.
        
echo apply_filters('feed_body'$this->body$this->feed_type);    
    }
    
    function 
feed_footer()
    {
        
// For completeness, but is probably unnecessary.
        
echo apply_filters('feed_footer'$this->footer$this->feed_type);        
    }

    function 
enclosures()
    {
        
// This is just a copy of rss_enclosures() that returns an array instead of printing it.
        // Or returns an empty array.
    
}
    
    
// Output the file using feed-specific subclassed functions.    
    
function output()
    {
        
header("Content-type: $this->mime_type; charset=$this->feed_data[charset]"true);
        
$this->feed_head();
        
$this->feed_body();
        
$this->feed_footer();        
    }
}

class 
WPrss2 extends WPFeed
{
    function 
WPrss2()
    {
        
$this->mime_type 'text/xml';
        
$this->feed_type 'rss2';
    }
    
    function 
feed_head()
    {
        
$published_date mysql2date('D, d M Y H:i:s +0000'$this->feed_data['published_date'], false);
        
        
// Can do output buffering here around old hooks to maintain backwards compatibility.
        
ob_start();
        
do_action('rss2_ns');
        
$rss2_ns ob_get_clean();

        
ob_start();
        
do_action('rss2_head');
        
$rss2_head ob_get_clean();
        
        
// Build the head of the RSS2 feed.
        
$this->head = <<<FEED_HEAD
        $this->feed_data[xml_header]
        <!-- generator="$this
->feed_data[generator]" -->
        <rss version="2.0"
        xmlns:content="http://purl.org/rss/1.0/modules/content/"        
        xmlns:wfw="http://wellformedweb.org/CommentAPI/"
      xmlns:dc="http://purl.org/dc/elements/1.1/"
      $rss2_ns
        >
      <channel>
          <title>$this
->feed_data[title]</title>
          <link>$this
->feed_data[url]</link>
          <description>$this
->feed_data[description]</description>
          <pubDate>$published_date</pubDate>
          <generator>$this
->feed_data[generator_url]</generator>
          <language>$this
->feed_data[language]</language>
            $rss2_head
FEED_HEAD;

        
$parent::feed_head();
    }

    function 
feed_body()
    {
        foreach (
$feed_data['items'] as $item)
        {
            
// More backwards compatibility
            
ob_start();
            
do_action('rss2_item');
            
$rss2_item ob_get_clean();

            
$published_date mysql2date('D, d M Y H:i:s +0000'$item['published_date'], false);
            
            if (!
$this->use_excerpt)
            {
                
$content = (strlen($item['content'])) ? 
                    
"<content:encoded><![CDATA[{$item['content']}]]></content:encoded>" 
                    
"<content:encoded><![CDATA[{$item['excerpt']}]]></content:encoded>";
            }
            
            
$enclosures implode(''$item['enclosures']);
            
            
$this->body .= <<<FEED_BODY
            <item>
              <title>$item
[title]</title>
              <link>$item
[url]</link>
              <comments>$item
[comment_link]</comments>
              <pubDate>$item
[published_date]</pubDate>
              <dc:creator>$item
[author]</dc:creator>
              $item
[categories]
              <guid isPermaLink="false">$item
[guid]</guid>
              <description><![CDATA[$item
[excerpt]]]></description>
              $content
                <wfw:commentRSS>$item
[comments_feed]</wfw:commentRSS>
                $enclosures
              $rss2_item
          </item>
FEED_BODY;
        }
        
        
$this->enclosure()

        
$parent::feed_body();
    }

    function 
feed_footer()
    {
        
$this->footer = <<<FEED_FOOTER
        </channel>
        </rss>        
FEED_FOOTER;
        
$parent::feed_footer();
    }

}

// This function checks to see if the given feed type is defined for WordPress.
function is_defined_feed($feed_type)
{    
    global 
$defined_feeds;
    
    foreach (
$defined_feeds as $defined) if ($feed_type == $defined) return true;
    return 
false;
}

// Feeds can then be outputted with a simple call:
$feed_class is_defined_feed($requested_feed) ? 'WP' $requested_feed 'WP' $default_feed;
$wpfeed = new $feed_class ();

// If separation of feed classes into different files was wanted, you could make 
// the defined_feeds array an associative array of feed_type->filename and include
// on the fly here.

// Generating the feed is the same way for every feed, even plugin added ones, through
// dynamic binding of feed class.
$wpfeed->output();

// We can also enumerate feeds in rewrite rules easily:
$feed_regex '(' implode('|'$defined_feeds) . ')';

// Plugins can add entirely new feed types before WordPress supports it.
// This would avoid moaning such as 'WordPress should do "x"'
// This also allows us to offload feeds that people feel are archaic 
// into plugins, if desired.
$defined_feeds[] = 'hyper_mega_feed_10'
class WPhyper_mega_feed_10
{
    
// Define my latest and greatest feed type here...
}


?>