2
0

Abstract.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Feed
  17. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id$
  20. */
  21. /**
  22. * @see Zend_Feed_Element
  23. */
  24. require_once 'Zend/Feed/Element.php';
  25. /** @see Zend_Xml_Security */
  26. require_once 'Zend/Xml/Security.php';
  27. /**
  28. * The Zend_Feed_Abstract class is an abstract class representing feeds.
  29. *
  30. * Zend_Feed_Abstract implements two core PHP 5 interfaces: ArrayAccess and
  31. * Iterator. In both cases the collection being treated as an array is
  32. * considered to be the entry collection, such that iterating over the
  33. * feed takes you through each of the feed.s entries.
  34. *
  35. * @category Zend
  36. * @package Zend_Feed
  37. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  38. * @license http://framework.zend.com/license/new-bsd New BSD License
  39. */
  40. abstract class Zend_Feed_Abstract extends Zend_Feed_Element implements Iterator, Countable
  41. {
  42. /**
  43. * Current index on the collection of feed entries for the
  44. * Iterator implementation.
  45. *
  46. * @var integer
  47. */
  48. protected $_entryIndex = 0;
  49. /**
  50. * Cache of feed entries.
  51. *
  52. * @var array
  53. */
  54. protected $_entries;
  55. /**
  56. * Feed constructor
  57. *
  58. * The Zend_Feed_Abstract constructor takes the URI of a feed or a
  59. * feed represented as a string and loads it as XML.
  60. *
  61. * @param string $uri The full URI of the feed to load, or NULL if not retrieved via HTTP or as an array.
  62. * @param string $string The feed as a string, or NULL if retrieved via HTTP or as an array.
  63. * @param Zend_Feed_Builder_Interface $builder The feed as a builder instance or NULL if retrieved as a string or via HTTP.
  64. * @return void
  65. * @throws Zend_Feed_Exception If loading the feed failed.
  66. */
  67. public function __construct($uri = null, $string = null, Zend_Feed_Builder_Interface $builder = null)
  68. {
  69. if ($uri !== null) {
  70. // Retrieve the feed via HTTP
  71. $client = Zend_Feed::getHttpClient();
  72. $client->setUri($uri);
  73. $response = $client->request('GET');
  74. if ($response->getStatus() !== 200) {
  75. /**
  76. * @see Zend_Feed_Exception
  77. */
  78. require_once 'Zend/Feed/Exception.php';
  79. throw new Zend_Feed_Exception('Feed failed to load, got response code ' . $response->getStatus() . '; request: ' . $client->getLastRequest() . "\nresponse: " . $response->asString());
  80. }
  81. $this->_element = $this->_importFeedFromString($response->getBody());
  82. $this->__wakeup();
  83. } elseif ($string !== null) {
  84. // Retrieve the feed from $string
  85. $this->_element = $string;
  86. $this->__wakeup();
  87. } else {
  88. // Generate the feed from the array
  89. $header = $builder->getHeader();
  90. $this->_element = new DOMDocument('1.0', $header['charset']);
  91. $root = $this->_mapFeedHeaders($header);
  92. $this->_mapFeedEntries($root, $builder->getEntries());
  93. $this->_element = $root;
  94. $this->_buildEntryCache();
  95. }
  96. }
  97. /**
  98. * Load the feed as an XML DOMDocument object
  99. *
  100. * @return void
  101. * @throws Zend_Feed_Exception
  102. */
  103. public function __wakeup()
  104. {
  105. @ini_set('track_errors', 1);
  106. $doc = new DOMDocument;
  107. $doc = @Zend_Xml_Security::scan($this->_element, $doc);
  108. @ini_restore('track_errors');
  109. if (!$doc) {
  110. // prevent the class to generate an undefined variable notice (ZF-2590)
  111. if (!isset($php_errormsg)) {
  112. if (function_exists('xdebug_is_enabled')) {
  113. $php_errormsg = '(error message not available, when XDebug is running)';
  114. } else {
  115. $php_errormsg = '(error message not available)';
  116. }
  117. }
  118. /**
  119. * @see Zend_Feed_Exception
  120. */
  121. require_once 'Zend/Feed/Exception.php';
  122. throw new Zend_Feed_Exception("DOMDocument cannot parse XML: $php_errormsg");
  123. }
  124. $this->_element = $doc;
  125. }
  126. /**
  127. * Prepare for serialiation
  128. *
  129. * @return array
  130. */
  131. public function __sleep()
  132. {
  133. $this->_element = $this->saveXML();
  134. return array('_element');
  135. }
  136. /**
  137. * Cache the individual feed elements so they don't need to be
  138. * searched for on every operation.
  139. *
  140. * @return void
  141. */
  142. protected function _buildEntryCache()
  143. {
  144. $this->_entries = array();
  145. foreach ($this->_element->childNodes as $child) {
  146. if ($child->localName == $this->_entryElementName) {
  147. $this->_entries[] = $child;
  148. }
  149. }
  150. }
  151. /**
  152. * Get the number of entries in this feed object.
  153. *
  154. * @return integer Entry count.
  155. */
  156. public function count()
  157. {
  158. return count($this->_entries);
  159. }
  160. /**
  161. * Required by the Iterator interface.
  162. *
  163. * @return void
  164. */
  165. public function rewind()
  166. {
  167. $this->_entryIndex = 0;
  168. }
  169. /**
  170. * Required by the Iterator interface.
  171. *
  172. * @return mixed The current row, or null if no rows.
  173. */
  174. public function current()
  175. {
  176. return new $this->_entryClassName(
  177. null,
  178. $this->_entries[$this->_entryIndex]);
  179. }
  180. /**
  181. * Required by the Iterator interface.
  182. *
  183. * @return mixed The current row number (starts at 0), or NULL if no rows
  184. */
  185. public function key()
  186. {
  187. return $this->_entryIndex;
  188. }
  189. /**
  190. * Required by the Iterator interface.
  191. *
  192. * @return mixed The next row, or null if no more rows.
  193. */
  194. public function next()
  195. {
  196. ++$this->_entryIndex;
  197. }
  198. /**
  199. * Required by the Iterator interface.
  200. *
  201. * @return boolean Whether the iteration is valid
  202. */
  203. public function valid()
  204. {
  205. return 0 <= $this->_entryIndex && $this->_entryIndex < $this->count();
  206. }
  207. /**
  208. * Generate the header of the feed when working in write mode
  209. *
  210. * @param array $array the data to use
  211. * @return DOMElement root node
  212. */
  213. abstract protected function _mapFeedHeaders($array);
  214. /**
  215. * Generate the entries of the feed when working in write mode
  216. *
  217. * @param DOMElement $root the root node to use
  218. * @param array $array the data to use
  219. * @return DOMElement root node
  220. */
  221. abstract protected function _mapFeedEntries(DOMElement $root, $array);
  222. /**
  223. * Send feed to a http client with the correct header
  224. *
  225. * @throws Zend_Feed_Exception if headers have already been sent
  226. * @return void
  227. */
  228. abstract public function send();
  229. /**
  230. * Import a feed from a string
  231. *
  232. * Protects against XXE attack vectors.
  233. *
  234. * @param string $feed
  235. * @return string
  236. * @throws Zend_Feed_Exception on detection of an XXE vector
  237. */
  238. protected function _importFeedFromString($feed)
  239. {
  240. if (trim($feed) == '') {
  241. require_once 'Zend/Feed/Exception.php';
  242. throw new Zend_Feed_Exception('Remote feed being imported'
  243. . ' is an Empty string or comes from an empty HTTP response');
  244. }
  245. $doc = new DOMDocument;
  246. $doc = Zend_Xml_Security::scan($feed, $doc);
  247. if (!$doc) {
  248. // prevent the class to generate an undefined variable notice (ZF-2590)
  249. // Build error message
  250. $error = libxml_get_last_error();
  251. if ($error && $error->message) {
  252. $errormsg = "DOMDocument cannot parse XML: {$error->message}";
  253. } else {
  254. $errormsg = "DOMDocument cannot parse XML";
  255. }
  256. /**
  257. * @see Zend_Feed_Exception
  258. */
  259. require_once 'Zend/Feed/Exception.php';
  260. throw new Zend_Feed_Exception($errormsg);
  261. }
  262. return $doc->saveXML($doc->documentElement);
  263. }
  264. }