<?PHP
// My XML / RDF Parser for PHP

/*
    This file was written by Thorsten Gunkel <tgunkel@gmx.de> to parse
    xml/rdf newsticker for <url:http://www.tgunkel.de>.
    Copyright (C) 2004 Thorsten Gunkel

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

/*
 My XML Parser
 - Splits string into tags attributes and values (e.g. <item attribute="xy">Value</item>
 - Builds a parse tree e.g.
   <item>
    <title>
    </title>
   </item>
 - Collects data that is stored in special places in that tree (e.g. value of all titles that are childs of an item)
*/

class rdf_rss_xml_parser
{
  var 
$parser;                // needed by the internal php xml parser
  
var $parse_tree=array();    // needed to detect the markup
  
var $xml_errorfree=true;    // is set to false if we detect problems
  
var $xml_errormsgs=array(); // list of all problems
  
var $items=array();         // this array contains the results
  
  // init function
  
function xml_parser()
    {
    }
  
  
// reads a xml source into a one line string with maxsize (kb) (negative value means no additional limit)
  
function xml_doimport($source,$maxsize,$cache_file,$cache_maxage)
    {
      
// check if cache for source is uptodate or re-read source
      
if (!(file_exists($cache_file)) || (filemtime($cache_file)+($cache_maxage*60) < time()))
        {
          
// test if remote file can be openend within seconds (why has php's fopen no timeout?)
          
$tmp_urlparts=parse_url($source);
          
$tmp_old_error_level=error_reporting(0);
          
$tmp_rmfile fsockopen ($tmp_urlparts['host'], 80$tmp_errnr$tmp_errtxt8);
          
error_reporting($tmp_old_error_level);
          
// remote file OK?
          
if (!$tmp_rmfile)
            {
              
$this->xml_errormsgs[]="Error while waiting for remote host.";
              
$this->xml_errorfree=false;
            }
          else
            {
              
// close test-connection
              
fclose($tmp_rmfile);
              
              
// open (remote) file
              
$tmp_old_error_level=error_reporting(0);
              if (
$fp fopen($source"r"))
                {
                  
error_reporting($tmp_old_error_level);
                  
// if exclusive open succeeds, we will be the process that will update the cache
                  
if ($fp2 fopen($cache_file'w'))
                    {
                      
// read data
                      
$c1=0;
                      
$data="";
                      while ((
$line fread($fp1024)) && (($c1<=$maxsize) || ($maxsize<=0)))
                        {
                          
$data.=$line;
                          
$c1++;
                        }
                      
// remove line breaks
                      //$data=preg_replace('/(\r\n|\r|\n)/', '', $data);
                      
fwrite($fp2$data);
                      
fclose($fp); fclose($fp2);
                      return 
$data;
                    }
                  else
                    {
                      
// this process failed to update cache file. sleep and hope another one had more luck
                      
$this->xml_errormsgs[]="Could not write data to cache.";
                      
$this->xml_errorfree=false;
                      
sleep(rand(5,9));
                    }
                }
              else
                {
                  
error_reporting($tmp_old_error_level);
                  
// this process failed to read news source
                  
$this->xml_errormsgs[]="Could not read xml source.";
                  
$xml_errorfree=false;
                  
//return false;
                
}
              
// --------------------  
              
            
}
        }
      
// if we reach this line we accept the cached data
      
$data='';
      
$fp3 fopen($cache_file'rb');
      if (
$fp3)
        {
          while (!
feof($fp3)) $data .= fread($fp31024);
          
fclose($fp3);
          return 
$data;
        }
      else
        {
          
$this->xml_errormsgs[]="Could not read cached XML data.";
          
$xml_errorfree=false;
          return 
false;
        }
    }
  
  
// this uses php functions to extract tags + content
  
function xml_doparse($xmldata)
    {
      
$this->parser xml_parser_create();
      
xml_set_object($this->parser, &$this);
      
$tmp_errtag=xml_set_element_handler($this->parser"parser_tagopen""parser_tagclose");
      
$tmp_errdat=xml_set_character_data_handler($this->parser"parser_data");
      
$this->xml_errorfree=($this->xml_errorfree && $tmp_errtag && $tmp_errdat);
      if (!
$this->xml_errorfree$this->xml_errormsgs[]="Internal problems with XML parser.";
      if (!
xml_parse($this->parser$xmldata))
        {
          
$this->xml_errorfree=false;
          
$this->xml_errormsgs[]="Parse error detected by internal XML parser.";
        }
      
xml_parser_free($this->parser);
    }
  
  
// what to do when a new tag is openend
  
function parser_tagopen($parser$tag$attributes)
    {
      
// Put the new tag on the stack
      
$tmparray=array(); $tmparray['tag']=$tag$tmparray['channel']=""$tmparray['title']=""$tmparray['url']=""
      
array_push($this->parse_tree,$tmparray);
    }

  
// what to do with data
  
function parser_data($parser$data)
      {
        
$stack_size=count($this->parse_tree)-1;
        
        
// CHANNEL -> TITLE || ARTIKEL -> RUBRIK
        
if ($stack_size>=1)
          {
            if (((
$this->parse_tree[(int)($stack_size-1)]['tag']=='CHANNEL') && ($this->parse_tree[$stack_size]['tag']=='TITLE')) || (($this->parse_tree[$stack_size-1]['tag']=='ARTIKEL') && ($this->parse_tree[$stack_size]['tag']=='URL')))
          
$this->parse_tree[$stack_size-1]['channel']=$data;
          }

        
// ITEM -> TITLE || ARTIKEL -> HEADLINE || STORY -> TITLE
        
if ($stack_size>=1)
          {
            if (((
$this->parse_tree[$stack_size-1]['tag']=='ITEM')    && ($this->parse_tree[$stack_size]['tag']=='TITLE'))    ||
                ((
$this->parse_tree[$stack_size-1]['tag']=='ARTIKEL') && ($this->parse_tree[$stack_size]['tag']=='HEADLINE')) ||
                ((
$this->parse_tree[$stack_size-1]['tag']=='STORY')   && ($this->parse_tree[$stack_size]['tag']=='TITLE'))
                )
              
$this->parse_tree[$stack_size-1]['title'].=$data;
          }
        
        
// ITEM -> LINK || STORY -> URL
        
if ($stack_size>=1)
          {
            if (((
$this->parse_tree[($stack_size-1)]['tag']=='ITEM')    && ($this->parse_tree[$stack_size]['tag']=='LINK')) ||
                ((
$this->parse_tree[($stack_size-1)]['tag']=='ARTIKEL') && ($this->parse_tree[$stack_size]['tag']=='URL'))  ||
                ((
$this->parse_tree[($stack_size-1)]['tag']=='STORY')   && ($this->parse_tree[$stack_size]['tag']=='URL'))
                )
              
$this->parse_tree[$stack_size-1]['url'].=$data;
          }
      }

  
// what to do when a tag is closed
  
function parser_tagclose($parser$tag)
    {
      
$stack_size=count($this->parse_tree)-1;
      
// Try to remove the tag from the stack
      
if (($stack_size>=0) && ($this->parse_tree[$stack_size]['tag']==$tag))
        {
          
/*
            remove tag and store informations that where collected for this tag (and sub tags)
            Interesting tags are ITEM ARTIKEL STORY
          */
          
if ($tag=='ITEM' || $tag=='ARTIKEL'|| $tag=='STORY')
            {
              
$tmparray=array();
              
$tmparray['title']=$this->parse_tree[$stack_size]['title'];
              
$tmparray['url']=$this->parse_tree[$stack_size]['url'];
              
$this->items[]=$tmparray;
            }
          
array_pop($this->parse_tree);
        }
      else
        {
          
$this->xml_errormsgs[]="A xml markup is broken.";
          
$xml_errorfree=false;
          return 
false;
        }
    }
}
?>