PageRenderTime 44ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/Feed/Rss.php

https://bitbucket.org/winponta/zend
PHP | 529 lines | 338 code | 63 blank | 128 comment | 61 complexity | d0e8af1ec0a57c21d23aac8f8cb534fa MD5 | raw file
  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-2012 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id: Rss.php 24593 2012-01-05 20:35:02Z matthew $
  20. */
  21. /**
  22. * @see Zend_Feed_Abstract
  23. */
  24. require_once 'Zend/Feed/Abstract.php';
  25. /**
  26. * @see Zend_Feed_Entry_Rss
  27. */
  28. require_once 'Zend/Feed/Entry/Rss.php';
  29. /**
  30. * RSS channel class
  31. *
  32. * The Zend_Feed_Rss class is a concrete subclass of
  33. * Zend_Feed_Abstract meant for representing RSS channels. It does not
  34. * add any methods to its parent, just provides a classname to check
  35. * against with the instanceof operator, and expects to be handling
  36. * RSS-formatted data instead of Atom.
  37. *
  38. * @category Zend
  39. * @package Zend_Feed
  40. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  41. * @license http://framework.zend.com/license/new-bsd New BSD License
  42. */
  43. class Zend_Feed_Rss extends Zend_Feed_Abstract
  44. {
  45. /**
  46. * The classname for individual channel elements.
  47. *
  48. * @var string
  49. */
  50. protected $_entryClassName = 'Zend_Feed_Entry_Rss';
  51. /**
  52. * The element name for individual channel elements (RSS <item>s).
  53. *
  54. * @var string
  55. */
  56. protected $_entryElementName = 'item';
  57. /**
  58. * The default namespace for RSS channels.
  59. *
  60. * @var string
  61. */
  62. protected $_defaultNamespace = 'rss';
  63. /**
  64. * Override Zend_Feed_Abstract to set up the $_element and $_entries aliases.
  65. *
  66. * @return void
  67. * @throws Zend_Feed_Exception
  68. */
  69. public function __wakeup()
  70. {
  71. parent::__wakeup();
  72. // Find the base channel element and create an alias to it.
  73. $rdfTags = $this->_element->getElementsByTagNameNS('http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'RDF');
  74. if ($rdfTags->length != 0) {
  75. $this->_element = $rdfTags->item(0);
  76. } else {
  77. $this->_element = $this->_element->getElementsByTagName('channel')->item(0);
  78. }
  79. if (!$this->_element) {
  80. /**
  81. * @see Zend_Feed_Exception
  82. */
  83. require_once 'Zend/Feed/Exception.php';
  84. throw new Zend_Feed_Exception('No root <channel> element found, cannot parse channel.');
  85. }
  86. // Find the entries and save a pointer to them for speed and
  87. // simplicity.
  88. $this->_buildEntryCache();
  89. }
  90. /**
  91. * Make accessing some individual elements of the channel easier.
  92. *
  93. * Special accessors 'item' and 'items' are provided so that if
  94. * you wish to iterate over an RSS channel's items, you can do so
  95. * using foreach ($channel->items as $item) or foreach
  96. * ($channel->item as $item).
  97. *
  98. * @param string $var The property to access.
  99. * @return mixed
  100. */
  101. public function __get($var)
  102. {
  103. switch ($var) {
  104. case 'item':
  105. // fall through to the next case
  106. case 'items':
  107. return $this;
  108. default:
  109. return parent::__get($var);
  110. }
  111. }
  112. /**
  113. * Generate the header of the feed when working in write mode
  114. *
  115. * @param array $array the data to use
  116. * @return DOMElement root node
  117. */
  118. protected function _mapFeedHeaders($array)
  119. {
  120. $channel = $this->_element->createElement('channel');
  121. $title = $this->_element->createElement('title');
  122. $title->appendChild($this->_element->createCDATASection($array->title));
  123. $channel->appendChild($title);
  124. $link = $this->_element->createElement('link', $array->link);
  125. $channel->appendChild($link);
  126. $desc = isset($array->description) ? $array->description : '';
  127. $description = $this->_element->createElement('description');
  128. $description->appendChild($this->_element->createCDATASection($desc));
  129. $channel->appendChild($description);
  130. $pubdate = isset($array->lastUpdate) ? $array->lastUpdate : time();
  131. $pubdate = $this->_element->createElement('pubDate', date(DATE_RSS, $pubdate));
  132. $channel->appendChild($pubdate);
  133. if (isset($array->published)) {
  134. $lastBuildDate = $this->_element->createElement('lastBuildDate', date(DATE_RSS, $array->published));
  135. $channel->appendChild($lastBuildDate);
  136. }
  137. $editor = '';
  138. if (!empty($array->email)) {
  139. $editor .= $array->email;
  140. }
  141. if (!empty($array->author)) {
  142. $editor .= ' (' . $array->author . ')';
  143. }
  144. if (!empty($editor)) {
  145. $author = $this->_element->createElement('managingEditor', ltrim($editor));
  146. $channel->appendChild($author);
  147. }
  148. if (isset($array->webmaster)) {
  149. $channel->appendChild($this->_element->createElement('webMaster', $array->webmaster));
  150. }
  151. if (!empty($array->copyright)) {
  152. $copyright = $this->_element->createElement('copyright', $array->copyright);
  153. $channel->appendChild($copyright);
  154. }
  155. if (isset($array->category)) {
  156. $category = $this->_element->createElement('category', $array->category);
  157. $channel->appendChild($category);
  158. }
  159. if (!empty($array->image)) {
  160. $image = $this->_element->createElement('image');
  161. $url = $this->_element->createElement('url', $array->image);
  162. $image->appendChild($url);
  163. $imagetitle = $this->_element->createElement('title');
  164. $imagetitle->appendChild($this->_element->createCDATASection($array->title));
  165. $image->appendChild($imagetitle);
  166. $imagelink = $this->_element->createElement('link', $array->link);
  167. $image->appendChild($imagelink);
  168. $channel->appendChild($image);
  169. }
  170. $generator = !empty($array->generator) ? $array->generator : 'Zend_Feed';
  171. $generator = $this->_element->createElement('generator', $generator);
  172. $channel->appendChild($generator);
  173. if (!empty($array->language)) {
  174. $language = $this->_element->createElement('language', $array->language);
  175. $channel->appendChild($language);
  176. }
  177. $doc = $this->_element->createElement('docs', 'http://blogs.law.harvard.edu/tech/rss');
  178. $channel->appendChild($doc);
  179. if (isset($array->cloud)) {
  180. $cloud = $this->_element->createElement('cloud');
  181. $cloud->setAttribute('domain', $array->cloud['uri']->getHost());
  182. $cloud->setAttribute('port', $array->cloud['uri']->getPort());
  183. $cloud->setAttribute('path', $array->cloud['uri']->getPath());
  184. $cloud->setAttribute('registerProcedure', $array->cloud['procedure']);
  185. $cloud->setAttribute('protocol', $array->cloud['protocol']);
  186. $channel->appendChild($cloud);
  187. }
  188. if (isset($array->ttl)) {
  189. $ttl = $this->_element->createElement('ttl', $array->ttl);
  190. $channel->appendChild($ttl);
  191. }
  192. if (isset($array->rating)) {
  193. $rating = $this->_element->createElement('rating', $array->rating);
  194. $channel->appendChild($rating);
  195. }
  196. if (isset($array->textInput)) {
  197. $textinput = $this->_element->createElement('textInput');
  198. $textinput->appendChild($this->_element->createElement('title', $array->textInput['title']));
  199. $textinput->appendChild($this->_element->createElement('description', $array->textInput['description']));
  200. $textinput->appendChild($this->_element->createElement('name', $array->textInput['name']));
  201. $textinput->appendChild($this->_element->createElement('link', $array->textInput['link']));
  202. $channel->appendChild($textinput);
  203. }
  204. if (isset($array->skipHours)) {
  205. $skipHours = $this->_element->createElement('skipHours');
  206. foreach ($array->skipHours as $hour) {
  207. $skipHours->appendChild($this->_element->createElement('hour', $hour));
  208. }
  209. $channel->appendChild($skipHours);
  210. }
  211. if (isset($array->skipDays)) {
  212. $skipDays = $this->_element->createElement('skipDays');
  213. foreach ($array->skipDays as $day) {
  214. $skipDays->appendChild($this->_element->createElement('day', $day));
  215. }
  216. $channel->appendChild($skipDays);
  217. }
  218. if (isset($array->itunes)) {
  219. $this->_buildiTunes($channel, $array);
  220. }
  221. return $channel;
  222. }
  223. /**
  224. * Adds the iTunes extensions to a root node
  225. *
  226. * @param DOMElement $root
  227. * @param array $array
  228. * @return void
  229. */
  230. private function _buildiTunes(DOMElement $root, $array)
  231. {
  232. /* author node */
  233. $author = '';
  234. if (isset($array->itunes->author)) {
  235. $author = $array->itunes->author;
  236. } elseif (isset($array->author)) {
  237. $author = $array->author;
  238. }
  239. if (!empty($author)) {
  240. $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:author', $author);
  241. $root->appendChild($node);
  242. }
  243. /* owner node */
  244. $author = '';
  245. $email = '';
  246. if (isset($array->itunes->owner)) {
  247. if (isset($array->itunes->owner['name'])) {
  248. $author = $array->itunes->owner['name'];
  249. }
  250. if (isset($array->itunes->owner['email'])) {
  251. $email = $array->itunes->owner['email'];
  252. }
  253. }
  254. if (empty($author) && isset($array->author)) {
  255. $author = $array->author;
  256. }
  257. if (empty($email) && isset($array->email)) {
  258. $email = $array->email;
  259. }
  260. if (!empty($author) || !empty($email)) {
  261. $owner = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:owner');
  262. if (!empty($author)) {
  263. $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:name', $author);
  264. $owner->appendChild($node);
  265. }
  266. if (!empty($email)) {
  267. $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:email', $email);
  268. $owner->appendChild($node);
  269. }
  270. $root->appendChild($owner);
  271. }
  272. $image = '';
  273. if (isset($array->itunes->image)) {
  274. $image = $array->itunes->image;
  275. } elseif (isset($array->image)) {
  276. $image = $array->image;
  277. }
  278. if (!empty($image)) {
  279. $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:image');
  280. $node->setAttribute('href', $image);
  281. $root->appendChild($node);
  282. }
  283. $subtitle = '';
  284. if (isset($array->itunes->subtitle)) {
  285. $subtitle = $array->itunes->subtitle;
  286. } elseif (isset($array->description)) {
  287. $subtitle = $array->description;
  288. }
  289. if (!empty($subtitle)) {
  290. $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:subtitle', $subtitle);
  291. $root->appendChild($node);
  292. }
  293. $summary = '';
  294. if (isset($array->itunes->summary)) {
  295. $summary = $array->itunes->summary;
  296. } elseif (isset($array->description)) {
  297. $summary = $array->description;
  298. }
  299. if (!empty($summary)) {
  300. $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:summary', $summary);
  301. $root->appendChild($node);
  302. }
  303. if (isset($array->itunes->block)) {
  304. $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:block', $array->itunes->block);
  305. $root->appendChild($node);
  306. }
  307. if (isset($array->itunes->explicit)) {
  308. $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:explicit', $array->itunes->explicit);
  309. $root->appendChild($node);
  310. }
  311. if (isset($array->itunes->keywords)) {
  312. $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:keywords', $array->itunes->keywords);
  313. $root->appendChild($node);
  314. }
  315. if (isset($array->itunes->new_feed_url)) {
  316. $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:new-feed-url', $array->itunes->new_feed_url);
  317. $root->appendChild($node);
  318. }
  319. if (isset($array->itunes->category)) {
  320. foreach ($array->itunes->category as $i => $category) {
  321. $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:category');
  322. $node->setAttribute('text', $category['main']);
  323. $root->appendChild($node);
  324. $add_end_category = false;
  325. if (!empty($category['sub'])) {
  326. $add_end_category = true;
  327. $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:category');
  328. $node->setAttribute('text', $category['sub']);
  329. $root->appendChild($node);
  330. }
  331. if ($i > 0 || $add_end_category) {
  332. $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:category');
  333. $root->appendChild($node);
  334. }
  335. }
  336. }
  337. }
  338. /**
  339. * Generate the entries of the feed when working in write mode
  340. *
  341. * The following nodes are constructed for each feed entry
  342. * <item>
  343. * <title>entry title</title>
  344. * <link>url to feed entry</link>
  345. * <guid>url to feed entry</guid>
  346. * <description>short text</description>
  347. * <content:encoded>long version, can contain html</content:encoded>
  348. * </item>
  349. *
  350. * @param DOMElement $root the root node to use
  351. * @param array $array the data to use
  352. * @return void
  353. */
  354. protected function _mapFeedEntries(DOMElement $root, $array)
  355. {
  356. Zend_Feed::registerNamespace('content', 'http://purl.org/rss/1.0/modules/content/');
  357. foreach ($array as $dataentry) {
  358. $item = $this->_element->createElement('item');
  359. $title = $this->_element->createElement('title');
  360. $title->appendChild($this->_element->createCDATASection($dataentry->title));
  361. $item->appendChild($title);
  362. if (isset($dataentry->author)) {
  363. $author = $this->_element->createElement('author', $dataentry->author);
  364. $item->appendChild($author);
  365. }
  366. $link = $this->_element->createElement('link', $dataentry->link);
  367. $item->appendChild($link);
  368. if (isset($dataentry->guid)) {
  369. $guid = $this->_element->createElement('guid', $dataentry->guid);
  370. if (!Zend_Uri::check($dataentry->guid)) {
  371. $guid->setAttribute('isPermaLink', 'false');
  372. }
  373. $item->appendChild($guid);
  374. }
  375. $description = $this->_element->createElement('description');
  376. $description->appendChild($this->_element->createCDATASection($dataentry->description));
  377. $item->appendChild($description);
  378. if (isset($dataentry->content)) {
  379. $content = $this->_element->createElement('content:encoded');
  380. $content->appendChild($this->_element->createCDATASection($dataentry->content));
  381. $item->appendChild($content);
  382. }
  383. $pubdate = isset($dataentry->lastUpdate) ? $dataentry->lastUpdate : time();
  384. $pubdate = $this->_element->createElement('pubDate', date(DATE_RSS, $pubdate));
  385. $item->appendChild($pubdate);
  386. if (isset($dataentry->category)) {
  387. foreach ($dataentry->category as $category) {
  388. $node = $this->_element->createElement('category', $category['term']);
  389. if (isset($category['scheme'])) {
  390. $node->setAttribute('domain', $category['scheme']);
  391. }
  392. $item->appendChild($node);
  393. }
  394. }
  395. if (isset($dataentry->source)) {
  396. $source = $this->_element->createElement('source', $dataentry->source['title']);
  397. $source->setAttribute('url', $dataentry->source['url']);
  398. $item->appendChild($source);
  399. }
  400. if (isset($dataentry->comments)) {
  401. $comments = $this->_element->createElement('comments', $dataentry->comments);
  402. $item->appendChild($comments);
  403. }
  404. if (isset($dataentry->commentRss)) {
  405. $comments = $this->_element->createElementNS('http://wellformedweb.org/CommentAPI/',
  406. 'wfw:commentRss',
  407. $dataentry->commentRss);
  408. $item->appendChild($comments);
  409. }
  410. if (isset($dataentry->enclosure)) {
  411. foreach ($dataentry->enclosure as $enclosure) {
  412. $node = $this->_element->createElement('enclosure');
  413. $node->setAttribute('url', $enclosure['url']);
  414. if (isset($enclosure['type'])) {
  415. $node->setAttribute('type', $enclosure['type']);
  416. }
  417. if (isset($enclosure['length'])) {
  418. $node->setAttribute('length', $enclosure['length']);
  419. }
  420. $item->appendChild($node);
  421. }
  422. }
  423. $root->appendChild($item);
  424. }
  425. }
  426. /**
  427. * Override Zend_Feed_Element to include <rss> root node
  428. *
  429. * @return string
  430. */
  431. public function saveXml()
  432. {
  433. // Return a complete document including XML prologue.
  434. $doc = new DOMDocument($this->_element->ownerDocument->version,
  435. $this->_element->ownerDocument->actualEncoding);
  436. $root = $doc->createElement('rss');
  437. // Use rss version 2.0
  438. $root->setAttribute('version', '2.0');
  439. // Content namespace
  440. $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:content', 'http://purl.org/rss/1.0/modules/content/');
  441. $root->appendChild($doc->importNode($this->_element, true));
  442. // Append root node
  443. $doc->appendChild($root);
  444. // Format output
  445. $doc->formatOutput = true;
  446. return $doc->saveXML();
  447. }
  448. /**
  449. * Send feed to a http client with the correct header
  450. *
  451. * @return void
  452. * @throws Zend_Feed_Exception if headers have already been sent
  453. */
  454. public function send()
  455. {
  456. if (headers_sent()) {
  457. /**
  458. * @see Zend_Feed_Exception
  459. */
  460. require_once 'Zend/Feed/Exception.php';
  461. throw new Zend_Feed_Exception('Cannot send RSS because headers have already been sent.');
  462. }
  463. header('Content-Type: application/rss+xml; charset=' . $this->_element->ownerDocument->actualEncoding);
  464. echo $this->saveXml();
  465. }
  466. }