Search code examples
phprssrss-reader

How to display multiple RSS feeds using PHP?


I am making a personal feed to run on an unused monitor using PHP, and I want to display multiple RSS feeds simultaneously. Is that possible? This is my current code:

 <?php
class rss 
{
    var $feed;
    function rss($feed) 
    {
        $this->feed = $feed;
    }
    function parse() 
    {
        $rss = simplexml_load_file($this->feed);
    $rss_split = array();
    foreach ($rss->channel->item as $item) {
            $title = (string) $item->title;
                $link   = (string) $item->link;
            $description = (string) $item->description;
                $rss_split[] = '<div class="item">
                                <a href="'.$link.'" target="_blank" title="">'.$title.'</a>
                                <hr>
                                </div>';
        }
        return $rss_split;
    }
    function display($numrows,$head) 
    {
        $rss_split = $this->parse();
        $i = 0;
        $rss_data = '<div class="container">
                     <div class="title">'.$head.'</div>
                     <div class="links">';

        while ( $i < $numrows ) 
    {
                $rss_data .= $rss_split[$i];
                $i++;
        }
        $trim = str_replace('', '',$this->feed);
        $user = str_replace('&lang=en-us&format=rss_200','',$trim);
    $rss_data.='</div></div>';
        return $rss_data;
    }
}

$feedlist = new rss("https://rss.app/feeds/OzfHGFFGdG4RG4LV.xml");

echo $feedlist->display(1000,"");
?>

The only answer I could find on here was using SimplePie, but I would rather make it pure vanilla PHP even if it may be harder. Thanks.


Solution

  • I would suggest first separating the display of the data from the retrieval of the data. Eg:

    // this is important later
    class RssInterface {
        public function generateItems();
    }
    
    class Rss implements RssInterface {
        protected $feed;
        
        public function __construct($feed) {
            // load the feed in the constructor so that relevant errors are raised immediately
            $this->feed = simplexml_load_file($feed);
        }
        
        public function generateItems() {
            foreach ($this->feed->channel->item as $item) {
                yield $item;
            }
        }
    }
    
    class DisplayMyRss {
        protected $rss;
        
        public function __construct(RssInterface $rss) {
            $this->rss = $rss;
        }
        
        public function asHtml() {
            $out = '<div class="container" etc...>';
            foreach( $rss->generateItems() as $item ) {
                $out .= '<div class="item" etc...>';
                // ...
                $out .= '</div>';
            }
            $out .= '</div>';
        }
    }
    

    So now you could do something like:

    $feed = new Rss('https://example.com/rss1.xml');
    $display = new DisplayMyRss($feed);
    echo $display->asHtml();
    

    Then we can wrap multiple RSS iterator classes in something like:

    class MultiRss implements RssInterface {
        protected $feeds = [];
        
        public function __construct(array $feeds) {
            foreach($feeds as $feed) {
                $this->addFeed($feed);
            }
        }
        
        // simply enforce that the items adhere to the interface
        public function addFeed(RssInterface $feed) {
            $this->feeds[] = $feed;
        }
        
        // adhere to the interface spec
        public function generateItems() {
            while( $this->hasValidIterators() ) {
                yield $this->getNext();
            }
        }
        
        // check if all the iterators have finished
        protected function hasValidIterators() {
            return ! empty(array_filter(array_map(function($f){return $f->valid;}, $this->feeds)));
        }
        
        // assumes lists are ordered ascending by $item->date, picks the earliest one and advances its iterator
        protected function getNext() {
            $next = NULL;
            $feed_index = -1;
            foreach( $this->feeds as $index => $feed ) {
                if( ! $feed->valid ) { continue; } // skip finished iterators
                $cur = $feed->cur;
                if( $feed_index == -1 || $cur->date < $next->date ) {
                    $feed_index = $index;
                }
            }
            $this->feeds[$next_index]->next();
            return $next;
        }
    }
    

    and still use the same classes and display logic:

    $feeds = [
        new Rss('https://example.com/rss1.xml'),
        new Rss('https://example.com/rss2.xml')
    ];
    $feed = new MultiRss($feeds);
    $display = new DisplayMyRss($feed);
    echo $display->asHtml();
    

    Note that none of this code has actually been run or tested, it is straight off the top of my head and likely contains some bugs.

    References: