<?php
// magpie-look-alike
// Martin Spernau 11-2002 (martin at traumwind.de)
// trying to mimic the PHP native XML functions (expat)
class MagpieRSS {
    
    
# current_item - item currently being parsed
    # items - collection of parsed items
    # channel - hash of channel fields
    #
    
var $current_item    = array();
        var 
$items            = array();
    var 
$channel        = array();
    
    var 
$parent_field    = array('RDF');
    var 
$current_field    '';
    var 
$current_namespace    false;
        
        
// list of common HTML tags that are to be considered 'content'
        
var $ignoreTags '<\/?(br|p|a|img|b|hr|font|i|cite|table|tr|td|ul|ol|dl|dt|dd|li|blockquote|em|strong)(\s+|>|\/)';
    
    function 
MagpieRSS ($source) {
                
// the REX based XML tokenizer comes here:
                
                /*
                * The following code was adopted from
                * (the Javascript and PHP impl of regex being more compatible)
                ' ################
                * REX/Javascript 1.0 
                * Robert D. Cameron "REX: XML Shallow Parsing with Regular Expressions",
                * Technical Report TR 1998-17, School of Computing Science, Simon Fraser 
                * University, November, 1998.
                * Copyright (c) 1998, Robert D. Cameron. 
                * The following code may be freely used and distributed provided that
                * this copyright and citation notice remains intact and that modifications
                * or additions are clearly identified.
                ' ################
                * 'port' to PHP by Martin Spernau 2002
                */
                // define the regex-parts globaly, for later re-use
                
$TextSE "[^<]+";
                
$UntilHyphen "[^-]*-";
                
$Until2Hyphens "$UntilHyphen([^-]$UntilHyphen)*-";
                
$CommentCE "$Until2Hyphens>?";
                
$UntilRSBs "[^]]*]([^]]+])*]+";
                
$CDATA_CE "$UntilRSBs([^]>]$UntilRSBs)*>";
                
$S "[ \\n\\t\\r]+";
                
$NameStrt "[A-Za-z_:]|[^\\x00-\\x7F]";
                
$NameChar "[A-Za-z0-9_:.-]|[^\\x00-\\x7F]";
                
$Name "($NameStrt)($NameChar)*";
                
$QuoteSE "\"[^\"]*\"|'[^']*'";
                
$DT_IdentSE "$S$Name($S($Name|$QuoteSE))*";
                
$MarkupDeclCE "([^]\"'><]+|$QuoteSE)*>";
                
$S1 "[\\n\\r\\t ]";
                
$UntilQMs "[^?]*\\?+";
                
$PI_Tail "\\?>|$S1$UntilQMs([^>?]$UntilQMs)*>";
                
$DT_ItemSE "<(!(--$Until2Hyphens>|[^-]$MarkupDeclCE)|\\?$Name($PI_Tail))|%$Name;|$S";
                
$DocTypeCE "$DT_IdentSE($S)?(\\[($DT_ItemSE)*]($S)?)?>?";
                
$DeclCE "--($CommentCE)?|\\[CDATA\\[($CDATA_CE)?|DOCTYPE($DocTypeCE)?";
                
$PI_CE "$Name($PI_Tail)?";
                
$EndTagCE "$Name($S)?>?";
                
$AttValSE "\"[^<\"]*\"|'[^<']*'";
                
$ElemTagCE "$Name($S$Name($S)?=($S)?($AttValSE))*($S)?\/?>?";
                
$MarkupSPE "<(!($DeclCE)?|\\?($PI_CE)?|\/($EndTagCE)?|($ElemTagCE)?)";
                
                
$XML_SPE "$TextSE|$MarkupSPE";
                
preg_match_all("/$XML_SPE/"$source$tokenList);
                
// done REX tokenizer

                
                // define opening tag and empty tag regexes
                
$OpeningTagRegex "/<($Name)($S$Name($S)?=($S)?($AttValSE))*($S)?>/"
                
$EmptyTagRegex "/<($Name)($S$Name($S)?=($S)?($AttValSE))*($S)?\/>/"
                
$EndTagRegex "/<\/($Name)($S)?>/";
                
// Text regex
                
$TextRegex "/^$TextSE$/";
                
                
// the actual 'parser'
                
while (list($count,$token)=each($tokenList[0])) {
                        if (
preg_match('/'.$this->ignoreTags.'/'$token)) {
                                
// echo "ignoreTag ".htmlspecialchars($token)."<br>\n";
                                
$this->cdata(1$token); // we handle common HTML tags as content
                        
} elseif (preg_match($OpeningTagRegex$token$matches)) {
                                
$this->start_element(1$matches[1], $attr); 
                        } elseif (
preg_match($EndTagRegex$token$matches)) {
                                
$this->end_element(1$matches[1]);
                        } elseif (
preg_match($TextRegex$token)) {
                                
$this->cdata(1$token);
                        }
                }
        
    }
    
        
// the actual Magpie code, unmodified can work with the above
    
function start_element ($p$element, &$attrs) {
        
$element     strtolower$element );
        
# check for a namespace, and split if found
        #
        
$namespace    false;
        if ( 
strpos$element':' ) ) {
            list(
$namespace$element) = split':'$element2); 
        }
        
$this->current_field $element;
        if ( 
$namespace and $namespace != 'rdf' ) {
            
$this->current_namespace $namespace;
        }
        
        if ( 
$element == 'channel' ) {
            
array_unshift$this->parent_field'channel' );
        }
        elseif ( 
$element == 'items' ) {
            
array_unshift$this->parent_field'items' );
        }
        elseif ( 
$element == 'item' ) {
            
array_unshift$this->parent_field'item' );
        }
        
        
    }
    
    function 
end_element ($p$element) {
        
$element strtolower($element);
        if ( 
$element == 'item' ) {
            
$this->items[] = $this->current_item;
            
$this->current_item = array();
            
array_shift$this->parent_field );
        }
        elseif ( 
$element == 'channel' or $element == 'items' ) {
            
array_shift$this->parent_field );
        }
        
        
$this->current_field '';
        
$this->current_namespace false;
    }
    
    function 
cdata ($p$text) {
        
# skip item, channel, items first time we see them
        #
        
if ( $this->parent_field[0] == $this->current_field or
             ! 
$this->current_field ) {
            return;
        }
        elseif ( 
$this->parent_field[0] == 'channel') {
            if ( 
$this->current_namespace ) {
                
$this->channel$this->current_namespace ][ $this->current_field ] .= $text;
            }
            else {
                
$this->channel$this->current_field ] .= $text;
            }
        
        }
        elseif ( 
$this->parent_field[0] == 'item' ) {
            if ( 
$this->current_namespace ) {
                
$this->current_item$this->current_namespace ][ $this->current_field ] .= $text;
            }
            else {
                
$this->current_item$this->current_field ] .= $text;
            }
        }
    }
        
    
# debugging functions
    #
    
function show_list () {
        echo 
"<ol>\n";
        foreach (
$this->items as $item) {
            echo 
"<li>"$this->show_item$item );
        }
        echo 
"</ol>";
    }
    
    function 
show_channel () {
        echo 
"channel:<br>";
        echo 
"<ul>";
        while ( list(
$key$value) = each$this->channel ) ) {
            echo 
"<li> $key$value";
        }
        echo 
"</ul>";
    }
    
    function 
show_item ($item) {
        echo 
"item: $item[title]";
        echo 
"<ul>";
        while ( list(
$key$value) = each($item) ) {
            if ( 
is_array($value) ) {
                echo 
"<br><b>$key</b>";
                echo 
"<ul>";
                while ( list( 
$ns_key$ns_value) = each$value ) ) {
                    echo 
"<li>$ns_key$ns_value";
                }
                echo 
"</ul>";
            }
            else {
                echo 
"<li> $key$value";
            }
        }
        echo 
"</ul>";
    }
    
# end class RSS
?>