XML_Feed_Parser
[ class tree: XML_Feed_Parser ] [ index: XML_Feed_Parser ] [ all elements ]

Source for file RSS2.php

Documentation is available at RSS2.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3.  
  4. /**
  5.  * Class representing feed-level data for an RSS2 feed
  6.  *
  7.  * PHP versions 5
  8.  *
  9.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  10.  * that is available through the world-wide-web at the following URI:
  11.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  12.  * the PHP License and are unable to obtain it through the web, please
  13.  * send a note to license@php.net so we can mail you a copy immediately.
  14.  *
  15.  * @category   XML
  16.  * @package    XML_Feed_Parser
  17.  * @author     James Stewart <james@jystewart.net>
  18.  * @copyright  2005 James Stewart <james@jystewart.net>
  19.  * @license    http://www.gnu.org/copyleft/lesser.html  GNU LGPL 2.1
  20.  * @version    CVS: $Id: RSS2.php,v 1.11 2006/07/27 13:52:05 jystewart Exp $
  21.  * @link       http://pear.php.net/package/XML_Feed_Parser/
  22.  */
  23.  
  24. /**
  25.  * This class handles RSS2 feeds.
  26.  * 
  27.  * @author    James Stewart <james@jystewart.net>
  28.  * @version    Release: 1.0.2
  29.  * @package XML_Feed_Parser
  30.  */
  31. {
  32.     /**
  33.      * The URI of the RelaxNG schema used to (optionally) validate the feed
  34.      * @var string 
  35.      */
  36.     private $relax 'rss20.rnc';
  37.  
  38.     /**
  39.      * We're likely to use XPath, so let's keep it global
  40.      * @var DOMXPath 
  41.      */
  42.     protected $xpath;
  43.  
  44.     /**
  45.      * The feed type we are parsing
  46.      * @var string 
  47.      */
  48.     public $version = 'RSS 2.0';
  49.  
  50.     /**
  51.      * The class used to represent individual items
  52.      * @var string 
  53.      */     
  54.     protected $itemClass = 'XML_Feed_Parser_RSS2Element';
  55.     
  56.     /**
  57.      * The element containing entries
  58.      * @var string 
  59.      */
  60.     protected $itemElement = 'item';
  61.  
  62.     /**
  63.      * Here we map those elements we're not going to handle individually
  64.      * to the constructs they are. The optional second parameter in the array
  65.      * tells the parser whether to 'fall back' (not apt. at the feed level) or
  66.      * fail if the element is missing. If the parameter is not set, the function
  67.      * will simply return false and leave it to the client to decide what to do.
  68.      * @var array 
  69.      */
  70.     protected $map = array(
  71.         'ttl' => array('Text'),
  72.         'pubDate' => array('Date'),
  73.         'lastBuildDate' => array('Date'),
  74.         'title' => array('Text'),
  75.         'link' => array('Link'),
  76.         'description' => array('Text'),
  77.         'language' => array('Text'),
  78.         'copyright' => array('Text'),
  79.         'managingEditor' => array('Text'),
  80.         'webMaster' => array('Text'),
  81.         'category' => array('Text'),
  82.         'generator' => array('Text'),
  83.         'docs' => array('Text'),
  84.         'ttl' => array('Text'),
  85.         'image' => array('Image'),
  86.         'skipDays' => array('skipDays'),
  87.         'skipHours' => array('skipHours'));
  88.  
  89.     /**
  90.      * Here we map some elements to their atom equivalents. This is going to be
  91.      * quite tricky to pull off effectively (and some users' methods may vary)
  92.      * but is worth trying. The key is the atom version, the value is RSS2.
  93.      * @var array 
  94.      */
  95.     protected $compatMap = array(
  96.         'title' => array('title'),
  97.         'rights' => array('copyright'),
  98.         'updated' => array('lastBuildDate'),
  99.         'subtitle' => array('description'),
  100.         'date' => array('pubDate'),
  101.         'author' => array('managingEditor'));
  102.  
  103.     protected $namespaces = array(
  104.         'dc' => 'http://purl.org/rss/1.0/modules/dc/',
  105.         'content' => 'http://purl.org/rss/1.0/modules/content/');
  106.  
  107.     /**
  108.      * Our constructor does nothing more than its parent.
  109.      * 
  110.      * @param    DOMDocument    $xml    A DOM object representing the feed
  111.      * @param    bool (optional) $string    Whether or not to validate this feed
  112.      */
  113.     function __construct(DOMDocument $model$strict = false)
  114.     {
  115.         $this->model = $model;
  116.  
  117.         if ($strict{
  118.             if ($this->model->relaxNGValidate($this->relax)) {
  119.                 throw new XML_Feed_Parser_Exception('Failed required validation');
  120.             }
  121.         }
  122.  
  123.         $this->xpath = new DOMXPath($this->model);
  124.         foreach ($this->namespaces as $key => $value{
  125.             $this->xpath->registerNamespace($key$value);
  126.         }
  127.         $this->numberEntries = $this->count('item');
  128.     }
  129.  
  130.     /**
  131.      * Retrieves an entry by ID, if the ID is specified with the guid element
  132.      *
  133.      * This is not really something that will work with RSS2 as it does not have
  134.      * clear restrictions on the global uniqueness of IDs. But we can emulate
  135.      * it by allowing access based on the 'guid' element. If DOMXPath::evaluate
  136.      * is available, we also use that to store a reference to the entry in the array
  137.      * used by getEntryByOffset so that method does not have to seek out the entry
  138.      * if it's requested that way.
  139.      *
  140.      * @param    string    $id    any valid ID.
  141.      * @return    XML_Feed_Parser_RSS2Element 
  142.      */
  143.     function getEntryById($id)
  144.     {
  145.         if (isset($this->idMappings[$id])) {
  146.             return $this->entries[$this->idMappings[$id]];
  147.         }
  148.  
  149.         $entries $this->xpath->query("//item[guid='$id']");
  150.         if ($entries->length > 0{
  151.             $entry = new $this->itemElement($entries->item(0)$this);
  152.             if (in_array('evaluate'get_class_methods($this->xpath))) {
  153.                 $offset $this->xpath->evaluate("count(preceding-sibling::item)"$entries->item(0));
  154.                 $this->entries[$offset$entry;
  155.             }
  156.             $this->idMappings[$id$entry;
  157.             return $entry;
  158.         }        
  159.     }
  160.  
  161.     /**
  162.      * Get a category from the element
  163.      *
  164.      * The category element is a simple text construct which can occur any number
  165.      * of times. We allow access by offset or access to an array of results.
  166.      *
  167.      * @param    string    $call    for compatibility with our overloading
  168.      * @param   array $arguments - arg 0 is the offset, arg 1 is whether to return as array
  169.      * @return  string|array|false
  170.      */
  171.     function getCategory($call$arguments = array())
  172.     {
  173.         $categories $this->model->getElementsByTagName('category');
  174.         $offset = empty($arguments[0]? 0 : $arguments[0];
  175.         $array = empty($arguments[1]? false : true;
  176.         if ($categories->length <= $offset{
  177.             return false;
  178.         }
  179.         if ($array{
  180.             $list = array();
  181.             foreach ($categories as $category{
  182.                 array_push($list$category->nodeValue);
  183.             }
  184.             return $list;
  185.         }
  186.         return $categories->item($offset)->nodeValue;
  187.     }
  188.  
  189.     /**
  190.      * Get details of the image associated with the feed.
  191.      *
  192.      * @return  array|falsean array simply containing the child elements
  193.      */
  194.     protected function getImage()
  195.     {
  196.         $images $this->model->getElementsByTagName('image');
  197.         if ($images->length > 0{
  198.             $image $images->item(0);
  199.             $desc $image->getElementsByTagName('description');
  200.             $description $desc->length ? $desc->item(0)->nodeValue : false;
  201.             $heigh $image->getElementsByTagName('height')
  202.             $height $heigh->length ? $heigh->item(0)->nodeValue : false;
  203.             $widt $image->getElementsByTagName('width')
  204.             $width $widt->length ? $widt->item(0)->nodeValue : false;
  205.             return array(
  206.                 'title' => $image->getElementsByTagName('title')->item(0)->nodeValue,
  207.                 'link' => $image->getElementsByTagName('link')->item(0)->nodeValue,
  208.                 'url' => $image->getElementsByTagName('url')->item(0)->nodeValue,
  209.                 'description' => $description,
  210.                 'height' => $height,
  211.                 'width' => $width);
  212.         }
  213.         return false;
  214.     }
  215.  
  216.     /**
  217.      * The textinput element is little used, but in the interests of
  218.      * completeness...
  219.      *
  220.      * @return  array|false
  221.      */
  222.     function getTextInput()
  223.     {
  224.         $inputs $this->model->getElementsByTagName('input');
  225.         if ($inputs->length > 0{
  226.             $input $inputs->item(0);
  227.             return array(
  228.                 'title' => $input->getElementsByTagName('title')->item(0)->value,
  229.                 'description' => 
  230.                     $input->getElementsByTagName('description')->item(0)->value,
  231.                 'name' => $input->getElementsByTagName('name')->item(0)->value,
  232.                 'link' => $input->getElementsByTagName('link')->item(0)->value);
  233.         }
  234.         return false;
  235.     }
  236.  
  237.     /**
  238.      * Utility function for getSkipDays and getSkipHours
  239.      *
  240.      * This is a general function used by both getSkipDays and getSkipHours. It simply
  241.      * returns an array of the values of the children of the appropriate tag.
  242.      *
  243.      * @param   string      $tagName    The tag name (getSkipDays or getSkipHours)
  244.      * @return  array|false
  245.      */
  246.     protected function getSkips($tagName)
  247.     {
  248.         $hours $this->model->getElementsByTagName($tagName);
  249.         if ($hours->length == 0{
  250.             return false;
  251.         }
  252.         $skipHours = array();
  253.         foreach($hours->item(0)->childNodes as $hour{
  254.             if ($hour instanceof DOMElement{
  255.                 array_push($skipHours$hour->nodeValue);
  256.             }
  257.         }
  258.         return $skipHours;
  259.     }
  260.  
  261.     /**
  262.      * Retrieve skipHours data
  263.      *
  264.      * The skiphours element provides a list of hours on which this feed should
  265.      * not be checked. We return an array of those hours (integers, 24 hour clock)
  266.      *
  267.      * @return  array 
  268.      */    
  269.     function getSkipHours()
  270.     {
  271.         return $this->getSkips('skipHours');
  272.     }
  273.  
  274.     /**
  275.      * Retrieve skipDays data
  276.      *
  277.      * The skipdays element provides a list of days on which this feed should
  278.      * not be checked. We return an array of those days.
  279.      *
  280.      * @return  array 
  281.      */
  282.     function getSkipDays()
  283.     {
  284.         return $this->getSkips('skipDays');
  285.     }
  286.  
  287.     /**
  288.      * Return content of the little-used 'cloud' element
  289.      *
  290.      * The cloud element is rarely used. It is designed to provide some details
  291.      * of a location to update the feed.
  292.      *
  293.      * @return  array   an array of the attributes of the element
  294.      */
  295.     function getCloud()
  296.     {
  297.         $cloud $this->model->getElementsByTagName('cloud');
  298.         if ($cloud->length == 0{
  299.             return false;
  300.         }
  301.         $cloudData = array();
  302.         foreach ($cloud->item(0)->attributes as $attribute{
  303.             $cloudData[$attribute->name$attribute->value;
  304.         }
  305.         return $cloudData;
  306.     }
  307.     
  308.     /**
  309.      * Get link URL
  310.      *
  311.      * In RSS2 a link is a text element but in order to ensure that we resolve
  312.      * URLs properly we have a special function for them. We maintain the
  313.      * parameter used by the atom getLink method, though we only use the offset
  314.      * parameter.
  315.      *
  316.      * @param   int     $offset The position of the link within the feed. Starts from 0
  317.      * @param   string  $attribute  The attribute of the link element required
  318.      * @param   array   $params An array of other parameters. Not used.
  319.      * @return  string 
  320.      */
  321.     function getLink($offset$attribute 'href'$params = array())
  322.     {
  323.         $links $this->model->getElementsByTagName('link');
  324.  
  325.         if ($links->length <= $offset{
  326.             return false;
  327.         }
  328.         $link $links->item($offset);
  329.         return $this->addBase($link->nodeValue$link);
  330.     }
  331. }
  332.  
  333. ?>

Documentation generated on Mon, 09 Apr 2007 11:02:06 -0400 by phpDocumentor 1.3.0. PEAR Logo Copyright © PHP Group 2004.