File indexing completed on 2024-05-12 06:02:31

0001 <?php
0002 
0003 /**
0004  * Zend Framework
0005  *
0006  * LICENSE
0007  *
0008  * This source file is subject to the new BSD license that is bundled
0009  * with this package in the file LICENSE.txt.
0010  * It is also available through the world-wide-web at this URL:
0011  * http://framework.zend.com/license/new-bsd
0012  * If you did not receive a copy of the license and are unable to
0013  * obtain it through the world-wide-web, please send an email
0014  * to license@zend.com so we can send you a copy immediately.
0015  *
0016  * @category   Zend
0017  * @package    Zend_Feed
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 
0024 /**
0025  * @see Zend_Feed_Element
0026  */
0027 // require_once 'Zend/Feed/Element.php';
0028 
0029 /** @see Zend_Xml_Security */
0030 // require_once 'Zend/Xml/Security.php';
0031 
0032 /**
0033  * The Zend_Feed_Abstract class is an abstract class representing feeds.
0034  *
0035  * Zend_Feed_Abstract implements two core PHP 5 interfaces: ArrayAccess and
0036  * Iterator. In both cases the collection being treated as an array is
0037  * considered to be the entry collection, such that iterating over the
0038  * feed takes you through each of the feed.s entries.
0039  *
0040  * @category   Zend
0041  * @package    Zend_Feed
0042  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0043  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0044  */
0045 abstract class Zend_Feed_Abstract extends Zend_Feed_Element implements Iterator, Countable
0046 {
0047     /**
0048      * Current index on the collection of feed entries for the
0049      * Iterator implementation.
0050      *
0051      * @var integer
0052      */
0053     protected $_entryIndex = 0;
0054 
0055     /**
0056      * Cache of feed entries.
0057      *
0058      * @var array
0059      */
0060     protected $_entries;
0061 
0062     /**
0063      * Feed constructor
0064      *
0065      * The Zend_Feed_Abstract constructor takes the URI of a feed or a
0066      * feed represented as a string and loads it as XML.
0067      *
0068      * @param  string $uri The full URI of the feed to load, or NULL if not retrieved via HTTP or as an array.
0069      * @param  string $string The feed as a string, or NULL if retrieved via HTTP or as an array.
0070      * @param  Zend_Feed_Builder_Interface $builder The feed as a builder instance or NULL if retrieved as a string or via HTTP.
0071      * @return void
0072      * @throws Zend_Feed_Exception If loading the feed failed.
0073      */
0074     public function __construct($uri = null, $string = null, Zend_Feed_Builder_Interface $builder = null)
0075     {
0076         if ($uri !== null) {
0077             // Retrieve the feed via HTTP
0078             $client = Zend_Feed::getHttpClient();
0079             $client->setUri($uri);
0080             $response = $client->request('GET');
0081             if ($response->getStatus() !== 200) {
0082                 /**
0083                  * @see Zend_Feed_Exception
0084                  */
0085                 // require_once 'Zend/Feed/Exception.php';
0086                 throw new Zend_Feed_Exception('Feed failed to load, got response code ' . $response->getStatus() . '; request: ' . $client->getLastRequest() . "\nresponse: " . $response->asString());
0087             }
0088             $this->_element = $this->_importFeedFromString($response->getBody());
0089             $this->__wakeup();
0090         } elseif ($string !== null) {
0091             // Retrieve the feed from $string
0092             $this->_element = $string;
0093             $this->__wakeup();
0094         } else {
0095             // Generate the feed from the array
0096             $header = $builder->getHeader();
0097             $this->_element = new DOMDocument('1.0', $header['charset']);
0098             $root = $this->_mapFeedHeaders($header);
0099             $this->_mapFeedEntries($root, $builder->getEntries());
0100             $this->_element = $root;
0101             $this->_buildEntryCache();
0102         }
0103     }
0104 
0105 
0106     /**
0107      * Load the feed as an XML DOMDocument object
0108      *
0109      * @return void
0110      * @throws Zend_Feed_Exception
0111      */
0112     public function __wakeup()
0113     {
0114         @ini_set('track_errors', 1);
0115         $doc = new DOMDocument;
0116         $doc = @Zend_Xml_Security::scan($this->_element, $doc);
0117         @ini_restore('track_errors');
0118 
0119         if (!$doc) {
0120             // prevent the class to generate an undefined variable notice (ZF-2590)
0121             if (!isset($php_errormsg)) {
0122                 if (function_exists('xdebug_is_enabled')) {
0123                     $php_errormsg = '(error message not available, when XDebug is running)';
0124                 } else {
0125                     $php_errormsg = '(error message not available)';
0126                 }
0127             }
0128 
0129             /**
0130              * @see Zend_Feed_Exception
0131              */
0132             // require_once 'Zend/Feed/Exception.php';
0133             throw new Zend_Feed_Exception("DOMDocument cannot parse XML: $php_errormsg");
0134         }
0135 
0136         $this->_element = $doc;
0137     }
0138 
0139 
0140     /**
0141      * Prepare for serialiation
0142      *
0143      * @return array
0144      */
0145     public function __sleep()
0146     {
0147         $this->_element = $this->saveXML();
0148 
0149         return array('_element');
0150     }
0151 
0152 
0153     /**
0154      * Cache the individual feed elements so they don't need to be
0155      * searched for on every operation.
0156      *
0157      * @return void
0158      */
0159     protected function _buildEntryCache()
0160     {
0161         $this->_entries = array();
0162         foreach ($this->_element->childNodes as $child) {
0163             if ($child->localName == $this->_entryElementName) {
0164                 $this->_entries[] = $child;
0165             }
0166         }
0167     }
0168 
0169 
0170     /**
0171      * Get the number of entries in this feed object.
0172      *
0173      * @return integer Entry count.
0174      */
0175     public function count()
0176     {
0177         return count($this->_entries);
0178     }
0179 
0180 
0181     /**
0182      * Required by the Iterator interface.
0183      *
0184      * @return void
0185      */
0186     public function rewind()
0187     {
0188         $this->_entryIndex = 0;
0189     }
0190 
0191 
0192     /**
0193      * Required by the Iterator interface.
0194      *
0195      * @return mixed The current row, or null if no rows.
0196      */
0197     public function current()
0198     {
0199         return new $this->_entryClassName(
0200             null,
0201             $this->_entries[$this->_entryIndex]);
0202     }
0203 
0204 
0205     /**
0206      * Required by the Iterator interface.
0207      *
0208      * @return mixed The current row number (starts at 0), or NULL if no rows
0209      */
0210     public function key()
0211     {
0212         return $this->_entryIndex;
0213     }
0214 
0215 
0216     /**
0217      * Required by the Iterator interface.
0218      *
0219      * @return mixed The next row, or null if no more rows.
0220      */
0221     public function next()
0222     {
0223         ++$this->_entryIndex;
0224     }
0225 
0226 
0227     /**
0228      * Required by the Iterator interface.
0229      *
0230      * @return boolean Whether the iteration is valid
0231      */
0232     public function valid()
0233     {
0234         return 0 <= $this->_entryIndex && $this->_entryIndex < $this->count();
0235     }
0236 
0237     /**
0238      * Generate the header of the feed when working in write mode
0239      *
0240      * @param  array $array the data to use
0241      * @return DOMElement root node
0242      */
0243     abstract protected function _mapFeedHeaders($array);
0244 
0245     /**
0246      * Generate the entries of the feed when working in write mode
0247      *
0248      * @param  DOMElement $root the root node to use
0249      * @param  array $array the data to use
0250      * @return DOMElement root node
0251      */
0252     abstract protected function _mapFeedEntries(DOMElement $root, $array);
0253 
0254     /**
0255      * Send feed to a http client with the correct header
0256      *
0257      * @throws Zend_Feed_Exception if headers have already been sent
0258      * @return void
0259      */
0260     abstract public function send();
0261 
0262     /**
0263      * Import a feed from a string
0264      *
0265      * Protects against XXE attack vectors.
0266      * 
0267      * @param  string $feed 
0268      * @return string
0269      * @throws Zend_Feed_Exception on detection of an XXE vector
0270      */
0271     protected function _importFeedFromString($feed)
0272     {
0273         if (trim($feed) == '') {
0274             // require_once 'Zend/Feed/Exception.php';
0275             throw new Zend_Feed_Exception('Remote feed being imported'
0276             . ' is an Empty string or comes from an empty HTTP response');
0277         }
0278         $doc = new DOMDocument;
0279         $doc = Zend_Xml_Security::scan($feed, $doc);
0280 
0281         if (!$doc) {
0282             // prevent the class to generate an undefined variable notice (ZF-2590)
0283             // Build error message
0284             $error = libxml_get_last_error();
0285             if ($error && $error->message) {
0286                 $errormsg = "DOMDocument cannot parse XML: {$error->message}";
0287             } else {
0288                 $errormsg = "DOMDocument cannot parse XML";
0289             }
0290 
0291 
0292             /**
0293              * @see Zend_Feed_Exception
0294              */
0295             // require_once 'Zend/Feed/Exception.php';
0296             throw new Zend_Feed_Exception($errormsg);
0297         }
0298 
0299         return $doc->saveXML($doc->documentElement);
0300     }
0301 }