
<?php
/*

RSS parser
A RSS document parser which can be also used as a reader.
v1.01

NOTE: 	Make sure you define a URL to a RSS file to parse
		and read.

by Tri Pham (tri[at]tripham.nl)
http://www.tripham.nl/

*/

/************************************************************
*				EDIT RSS_FILE and MAX_HEADLINES				*
************************************************************/
// How many headlines do you want?
define('MAX_HEADLINES', 5);
define('RSS_FILE', 'http://www.nos.nl/export/nosnieuws-rss.xml');
define('OPEN','Error: opening RSS file');


// Class definition
class RSS_Parser {
    
	/*
	Flag tells which tag we enter.
	Allowed values:
	
	$iFlag = 1, we entered <channel>
	$iFlag = 2, we entered <item>
	$iFlag = 3, we entered <image>
	*/
    var $iFlag;
    
    // Which tag did we enter? Ask $sTag!
    var $sTag;
    
    // Incremented each time we leave a tag.
    // How many items have we left?
    // When $iCount equals the maximum headlines, the script exits.
    var $iCount;

    // How many news lines do you want?
    var $iMax_lines;

    // <item> iformation
    var $sTitle;
    var $sDescription;
    var $sLink;
    var $sPubdate;
    // </item>

    // <sChannel> information
    var $sChannel_copyright;
    var $sChannel_managingeditor;
    var $sChannel_language;
    var $sChannel_lastbuilddate;
    // </sChannel>

    
/**********************************************************
*Function: 		set_max_headlines

*Description: 	Initialize $iMax_lines, which tells us how
				many headlines the user wants.

*Parameters:
$iMax			An integer which contains the maximum head-
				lines defined by the user.
***********************************************************/

function set_max_headlines($pMax) {
	// is the parameter an integer?
	if(is_int($pMax) AND $pMax > 0) {
    	$this->iMax_lines = $pMax;
	}
}


/**********************************************************
*Function: 		startElement

*Description: 	Initialize $iFlag which will tell us which
				tag we entered.

*Parameters:
$pParser		The resource identifier used by PHP inter-
				nally.
$pTagname 		This variable will contain the name of the
				closing tag.
$pAttributes	It will contain an array of the attributes,
				which are specified in the document.
				Not every RSS document contains attributes,
				so this feature is a bonus ;)
***********************************************************/

function startElement($pParser, $pTagname, $pAttributes) {
    $this->sTag = $pTagname; // might seem useless at first sight
    
    if($this->sTag == 'CHANNEL') {
        $this->iFlag = 1;
    }
    elseif($this->sTag == 'ITEM') {
        $this->iFlag = 2;
        
    }
    elseif($this->sTag == 'IMAGE'){
        $this->iFlag = 3;
    }
}


/**********************************************************
*Function: endElement

*Description: 	Function to process the information
				collected out of the tags.

*Parameters:
$pParser	The resource identifier used by PHP internally.
$pTagname 	This variable will contain the name of the
			closing tag.
***********************************************************/

function endElement($pParser, $pTagname) {

    // If we enter </item>
    if($pTagname == 'ITEM') {
    	// Check if we have reached the maximum
        if($this->iCount == $this->iMax_lines) {
            exit();
        }
		if($this->sDescription == '') {
        	$this->sDescription = 'unknown';
        }
        if($this->sPubdate == '') {
        	$this->sPubdate = 'unknown';
        }

        // Show the news
        printf('<p><b><a href=\"%s\">%s</a></b><br>', trim($this->sLink), htmlspecialchars(trim($this->sTitle)));
        printf('<b>Description:</b> %s</p>', htmlspecialchars(trim($this->sDescription)));
        

    // Empty the variables for new values
    unset($this->sTitle);
    unset($this->sDescription);
    unset($this->sLink);
    unset($this->sPubdate);
    
    // Update counter asscociated with the maximum headlines
	$this->iCount++;
    }
    // If we leave </channel>
    elseif($pTagname == 'CHANNEL') {
            if($this->sChannel_copyright == '') {
            	$this->sChannel_copyright = 'unknown';
            }
            if($this->sChannel_managingeditor == '') {
            	$this->sChannel_managingeditor = 'unknown';
            }
            if($this->sChannel_language == '') {
            	$this->sChannel_language = 'unknown';
            }
            if($this->sChannel_lastbuilddate == '') {
            	$this->sChannel_lastbuilddate = 'unknown';
            }
    }
}


/**********************************************************
*Function: characterData

*Description: 	The actual feed processing happens here.
				We collect the data and process them.

*Parameters:
$pParser	The resource identifier used by PHP internally.
$pTagname 	This variable will contain the name of the
			closing tag.
***********************************************************/
function characterData($pParser, $pData) {

       if($this->iFlag == 1) { // entering <sChannel>
            // Here is our sTag used for :)
       		switch($this->sTag) {
                case 'LANGUAGE':
                    $this->sChannel_language .= $pData;
                break;
                case 'LASTBUILDDATE':
                    $this->sChannel_lastbuilddate .= $pData;
                break;
                case 'COPYRIGHT':
                    $this->sChannel_copyright .= $pData;
                break;
                case 'MANAGINGEDITOR':
                    $this->sChannel_managingeditor .= $pData;
                break;
            }
        }
       elseif($this->iFlag == 2) {
           switch($this->sTag) {
           // entering <item>
                case 'TITLE':
                    $this->sTitle .= $pData;
                break;
                case 'DESCRIPTION':
                    $this->sDescription .= $pData;
                break;
                case 'LINK':
                    $this->sLink .= $pData;
                break;
                case 'PUBDATE':
                    $this->sPubdate .= $pData;
                break;
           }
       }
    }
} // End of class definition.


// Create XML support and setup xml parser.
$xp = xml_parser_create();
$cRss_parser = new RSS_Parser();
xml_set_object($xp, $cRss_parser);

// Ignore whitespaces, allows the parser to skip "space"
// characters in the xml document.
xml_parser_set_option($xp, XML_OPTION_SKIP_WHITE, TRUE);

// Setup the event handlers.
xml_set_element_handler($xp, 'startElement', 'endElement');
xml_set_character_data_handler($xp, 'characterData');

// Set maximum headlines.
$cRss_parser->set_max_headlines(MAX_HEADLINES);
unset($data);

    // Open xml file which contains the RSS info.
    $fp = fopen(RSS_FILE,'r');
    if($fp) {
    	fflush($fp);

        while(!feof($fp)) {
        	// Read it in chunks of 4 kilobytes.
        	$data = fread($fp, 4096);
            
        	// If the document contains an error,
            // HELP!!!
            // else calm down.
        	if(!(xml_parse($xp, trim($data), feof($fp))))
                die(sprintf("XML error: %s at line %d",
							xml_error_string(xml_get_error_code($xp)),
							xml_get_current_line_number($xp)));
        }
        
        // Close file pointer and free up XML parser.
        fclose($fp);
        xml_parser_free($xp);
    }
    else {
    	// Error opening RSS file.
		die(sprintf("%s %s", OPEN, RSS_FILE));
    }
?>
