Rss.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  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_Reader
  17. * @copyright Copyright (c) 2005-2009 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_Reader
  23. */
  24. require_once 'Zend/Feed/Reader.php';
  25. /**
  26. * @see Zend_Feed_Reader_EntryInterface
  27. */
  28. require_once 'Zend/Feed/Reader/EntryInterface.php';
  29. /**
  30. * @see Zend_Feed_Reader_EntryAbstract
  31. */
  32. require_once 'Zend/Feed/Reader/EntryAbstract.php';
  33. /**
  34. * @see Zend_Feed_Reader_Extension_DublinCore_Entry
  35. */
  36. require_once 'Zend/Feed/Reader/Extension/DublinCore/Entry.php';
  37. /**
  38. * @see Zend_Feed_Reader_Extension_Content_Entry
  39. */
  40. require_once 'Zend/Feed/Reader/Extension/Content/Entry.php';
  41. /**
  42. * @see Zend_Feed_Reader_Extension_Atom_Entry
  43. */
  44. require_once 'Zend/Feed/Reader/Extension/Atom/Entry.php';
  45. /**
  46. * @see Zend_Feed_Reader_Extension_WellformedWeb_Entry
  47. */
  48. require_once 'Zend/Feed/Reader/Extension/WellFormedWeb/Entry.php';
  49. /**
  50. * @see Zend_Feed_Reader_Extension_Slash_Entry
  51. */
  52. require_once 'Zend/Feed/Reader/Extension/Slash/Entry.php';
  53. /**
  54. * @see Zend_Feed_Reader_Extension_Thread_Entry
  55. */
  56. require_once 'Zend/Feed/Reader/Extension/Thread/Entry.php';
  57. /**
  58. * @see Zend_Date
  59. */
  60. require_once 'Zend/Date.php';
  61. require_once 'Zend/Feed/Reader/Collection/Category.php';
  62. /**
  63. * @category Zend
  64. * @package Zend_Feed_Reader
  65. * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
  66. * @license http://framework.zend.com/license/new-bsd New BSD License
  67. */
  68. class Zend_Feed_Reader_Entry_Rss extends Zend_Feed_Reader_EntryAbstract implements Zend_Feed_Reader_EntryInterface
  69. {
  70. /**
  71. * XPath query for RDF
  72. *
  73. * @var string
  74. */
  75. protected $_xpathQueryRdf = '';
  76. /**
  77. * XPath query for RSS
  78. *
  79. * @var string
  80. */
  81. protected $_xpathQueryRss = '';
  82. /**
  83. * Constructor
  84. *
  85. * @param Zend_Feed_Entry_Abstract $entry
  86. * @param string $entryKey
  87. * @param string $type
  88. * @return void
  89. */
  90. public function __construct(DOMElement $entry, $entryKey, $type = null)
  91. {
  92. parent::__construct($entry, $entryKey, $type);
  93. $this->_xpathQueryRss = '//item[' . ($this->_entryKey+1) . ']';
  94. $this->_xpathQueryRdf = '//rss:item[' . ($this->_entryKey+1) . ']';
  95. $pluginLoader = Zend_Feed_Reader::getPluginLoader();
  96. $dublinCoreClass = $pluginLoader->getClassName('DublinCore_Entry');
  97. $this->_extensions['DublinCore_Entry'] = new $dublinCoreClass($entry, $entryKey, $type);
  98. $contentClass = $pluginLoader->getClassName('Content_Entry');
  99. $this->_extensions['Content_Entry'] = new $contentClass($entry, $entryKey, $type);
  100. $atomClass = $pluginLoader->getClassName('Atom_Entry');
  101. $this->_extensions['Atom_Entry'] = new $atomClass($entry, $entryKey, $type);
  102. $wfwClass = $pluginLoader->getClassName('WellFormedWeb_Entry');
  103. $this->_extensions['WellFormedWeb_Entry'] = new $wfwClass($entry, $entryKey, $type);
  104. $slashClass = $pluginLoader->getClassName('Slash_Entry');
  105. $this->_extensions['Slash_Entry'] = new $slashClass($entry, $entryKey, $type);
  106. $threadClass = $pluginLoader->getClassName('Thread_Entry');
  107. $this->_extensions['Thread_Entry'] = new $threadClass($entry, $entryKey, $type);
  108. }
  109. /**
  110. * Get an author entry
  111. *
  112. * @param DOMElement $element
  113. * @return string
  114. */
  115. public function getAuthor($index = 0)
  116. {
  117. $authors = $this->getAuthors();
  118. if (isset($authors[$index])) {
  119. return $authors[$index];
  120. }
  121. return null;
  122. }
  123. /**
  124. * Get an array with feed authors
  125. *
  126. * @return array
  127. */
  128. public function getAuthors()
  129. {
  130. if (array_key_exists('authors', $this->_data)) {
  131. return $this->_data['authors'];
  132. }
  133. $authors = array();
  134. // @todo: create a list from all potential sources rather than from alternatives
  135. if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 &&
  136. $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
  137. $list = $this->_xpath->evaluate($this->_xpathQueryRss.'//author');
  138. } else {
  139. $list = $this->_xpath->evaluate($this->_xpathQueryRdf.'//rss:author');
  140. }
  141. if (!$list->length) {
  142. if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
  143. $list = $this->_xpath->query('//author');
  144. } else {
  145. $list = $this->_xpath->query('//rss:author');
  146. }
  147. }
  148. if ($list->length) {
  149. foreach ($list as $author) {
  150. if ($this->getType() == Zend_Feed_Reader::TYPE_RSS_20
  151. && preg_match("/\(([^\)]+)\)/", $author->nodeValue, $matches, PREG_OFFSET_CAPTURE)
  152. ) {
  153. // source name from RSS 2.0 <author>
  154. // format "joe@example.com (Joe Bloggs)"
  155. $authors[] = $matches[1][0];
  156. } else {
  157. $authors[] = $author->nodeValue;
  158. }
  159. }
  160. $authors = array_unique($authors);
  161. }
  162. if (empty($authors)) {
  163. $authors = $this->getExtension('DublinCore')->getAuthors();
  164. }
  165. if (empty($authors)) {
  166. $authors = $this->getExtension('Atom')->getAuthors();
  167. }
  168. $this->_data['authors'] = $authors;
  169. return $this->_data['authors'];
  170. }
  171. /**
  172. * Get the entry content
  173. *
  174. * @return string
  175. */
  176. public function getContent()
  177. {
  178. if (array_key_exists('content', $this->_data)) {
  179. return $this->_data['content'];
  180. }
  181. $content = $this->getExtension('Content')->getContent();
  182. if (!$content) {
  183. $content = $this->getDescription();
  184. }
  185. if (empty($content)) {
  186. $content = $this->getExtension('Atom')->getContent();
  187. }
  188. $this->_data['content'] = $content;
  189. return $this->_data['content'];
  190. }
  191. /**
  192. * Get the entry's date of creation
  193. *
  194. * @return string
  195. */
  196. public function getDateCreated()
  197. {
  198. return $this->getDateModified();
  199. }
  200. /**
  201. * Get the entry's date of modification
  202. *
  203. * @return string
  204. */
  205. public function getDateModified()
  206. {
  207. if (array_key_exists('datemodified', $this->_data)) {
  208. return $this->_data['datemodified'];
  209. }
  210. $dateModified = null;
  211. $date = null;
  212. if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10
  213. && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090
  214. ) {
  215. $dateModified = $this->_xpath->evaluate('string('.$this->_xpathQueryRss.'/pubDate)');
  216. if ($dateModified) {
  217. $dateStandards = array(Zend_Date::RSS, Zend_Date::RFC_822,
  218. Zend_Date::RFC_2822, Zend_Date::DATES);
  219. $date = new Zend_Date;
  220. foreach ($dateStandards as $standard) {
  221. try {
  222. $date->set($dateModified, $standard);
  223. break;
  224. } catch (Zend_Date_Exception $e) {
  225. if ($standard == Zend_Date::DATES) {
  226. require_once 'Zend/Feed/Exception.php';
  227. throw new Zend_Feed_Exception(
  228. 'Could not load date due to unrecognised'
  229. .' format (should follow RFC 822 or 2822):'
  230. . $e->getMessage()
  231. );
  232. }
  233. }
  234. }
  235. }
  236. }
  237. if (!$date) {
  238. $date = $this->getExtension('DublinCore')->getDate();
  239. }
  240. if (!$date) {
  241. $date = $this->getExtension('Atom')->getDateModified();
  242. }
  243. if (!$date) {
  244. $date = null;
  245. }
  246. $this->_data['datemodified'] = $date;
  247. return $this->_data['datemodified'];
  248. }
  249. /**
  250. * Get the entry description
  251. *
  252. * @return string
  253. */
  254. public function getDescription()
  255. {
  256. if (array_key_exists('description', $this->_data)) {
  257. return $this->_data['description'];
  258. }
  259. $description = null;
  260. if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10
  261. && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090
  262. ) {
  263. $description = $this->_xpath->evaluate('string('.$this->_xpathQueryRss.'/description)');
  264. } else {
  265. $description = $this->_xpath->evaluate('string('.$this->_xpathQueryRdf.'/rss:description)');
  266. }
  267. if (!$description) {
  268. $description = $this->getExtension('DublinCore')->getDescription();
  269. }
  270. if (empty($description)) {
  271. $description = $this->getExtension('Atom')->getDescription();
  272. }
  273. if (!$description) {
  274. $description = null;
  275. } else {
  276. $description = html_entity_decode($description, ENT_QUOTES, $this->getEncoding());
  277. }
  278. $this->_data['description'] = $description;
  279. return $this->_data['description'];
  280. }
  281. /**
  282. * Get the entry enclosure
  283. * @return string
  284. */
  285. public function getEnclosure()
  286. {
  287. if (array_key_exists('enclosure', $this->_data)) {
  288. return $this->_data['enclosure'];
  289. }
  290. $enclosure = null;
  291. if ($this->getType() == Zend_Feed_Reader::TYPE_RSS_20) {
  292. $nodeList = $this->_xpath->query($this->_xpathQueryRss . '/enclosure');
  293. if ($nodeList->length > 0) {
  294. $enclosure = new stdClass();
  295. $enclosure->url = $nodeList->item(0)->getAttribute('url');
  296. $enclosure->length = $nodeList->item(0)->getAttribute('length');
  297. $enclosure->type = $nodeList->item(0)->getAttribute('type');
  298. }
  299. }
  300. if (!$enclosure) {
  301. $enclosure = $this->getExtension('Atom')->getEnclosure();
  302. }
  303. $this->_data['enclosure'] = $enclosure;
  304. return $this->_data['enclosure'];
  305. }
  306. /**
  307. * Get the entry ID
  308. *
  309. * @return string
  310. */
  311. public function getId()
  312. {
  313. if (array_key_exists('id', $this->_data)) {
  314. return $this->_data['id'];
  315. }
  316. $id = null;
  317. if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10
  318. && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090
  319. ) {
  320. $id = $this->_xpath->evaluate('string('.$this->_xpathQueryRss.'/guid)');
  321. }
  322. if (!$id) {
  323. $id = $this->getExtension('DublinCore')->getId();
  324. }
  325. if (empty($id)) {
  326. $id = $this->getExtension('Atom')->getId();
  327. }
  328. if (!$id) {
  329. if ($this->getPermalink()) {
  330. $id = $this->getPermalink();
  331. } elseif ($this->getTitle()) {
  332. $id = $this->getTitle();
  333. } else {
  334. $id = null;
  335. }
  336. }
  337. $this->_data['id'] = $id;
  338. return $this->_data['id'];
  339. }
  340. /**
  341. * Get a specific link
  342. *
  343. * @param int $index
  344. * @return string
  345. */
  346. public function getLink($index = 0)
  347. {
  348. if (!array_key_exists('links', $this->_data)) {
  349. $this->getLinks();
  350. }
  351. if (isset($this->_data['links'][$index])) {
  352. return $this->_data['links'][$index];
  353. }
  354. return null;
  355. }
  356. /**
  357. * Get all links
  358. *
  359. * @return array
  360. */
  361. public function getLinks()
  362. {
  363. if (array_key_exists('links', $this->_data)) {
  364. return $this->_data['links'];
  365. }
  366. $links = array();
  367. if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 &&
  368. $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
  369. $list = $this->_xpath->query($this->_xpathQueryRss.'//link');
  370. } else {
  371. $list = $this->_xpath->query($this->_xpathQueryRdf.'//rss:link');
  372. }
  373. if (!$list->length) {
  374. $links = $this->getExtension('Atom')->getLinks();
  375. } else {
  376. foreach ($list as $link) {
  377. $links[] = $link->nodeValue;
  378. }
  379. }
  380. $this->_data['links'] = $links;
  381. return $this->_data['links'];
  382. }
  383. /**
  384. * Get all categories
  385. *
  386. * @return Zend_Feed_Reader_Collection_Category
  387. */
  388. public function getCategories()
  389. {
  390. if (array_key_exists('categories', $this->_data)) {
  391. return $this->_data['categories'];
  392. }
  393. if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 &&
  394. $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
  395. $list = $this->_xpath->query($this->_xpathQueryRss.'//category');
  396. } else {
  397. $list = $this->_xpath->query($this->_xpathQueryRdf.'//rss:category');
  398. }
  399. if ($list->length) {
  400. $categoryCollection = new Zend_Feed_Reader_Collection_Category;
  401. foreach ($list as $category) {
  402. $categoryCollection[] = array(
  403. 'term' => $category->nodeValue,
  404. 'scheme' => $category->getAttribute('domain'),
  405. 'label' => $category->nodeValue,
  406. );
  407. }
  408. } else {
  409. $categoryCollection = $this->getExtension('DublinCore')->getCategories();
  410. }
  411. $this->_data['categories'] = $categoryCollection;
  412. return $this->_data['categories'];
  413. }
  414. /**
  415. * Get a permalink to the entry
  416. *
  417. * @return string
  418. */
  419. public function getPermalink()
  420. {
  421. return $this->getLink(0);
  422. }
  423. /**
  424. * Get the entry title
  425. *
  426. * @return string
  427. */
  428. public function getTitle()
  429. {
  430. if (array_key_exists('title', $this->_data)) {
  431. return $this->_data['title'];
  432. }
  433. $title = null;
  434. if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10
  435. && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090
  436. ) {
  437. $title = $this->_xpath->evaluate('string('.$this->_xpathQueryRss.'/title)');
  438. } else {
  439. $title = $this->_xpath->evaluate('string('.$this->_xpathQueryRdf.'/rss:title)');
  440. }
  441. if (!$title) {
  442. $title = $this->getExtension('DublinCore')->getTitle();
  443. }
  444. if (!$title) {
  445. $title = $this->getExtension('Atom')->getTitle();
  446. }
  447. if (!$title) {
  448. $title = null;
  449. }
  450. $this->_data['title'] = $title;
  451. return $this->_data['title'];
  452. }
  453. /**
  454. * Get the number of comments/replies for current entry
  455. *
  456. * @return string|null
  457. */
  458. public function getCommentCount()
  459. {
  460. if (array_key_exists('commentcount', $this->_data)) {
  461. return $this->_data['commentcount'];
  462. }
  463. $commentcount = $this->getExtension('Slash')->getCommentCount();
  464. if (!$commentcount) {
  465. $commentcount = $this->getExtension('Thread')->getCommentCount();
  466. }
  467. if (!$commentcount) {
  468. $commentcount = $this->getExtension('Atom')->getCommentCount();
  469. }
  470. if (!$commentcount) {
  471. $commentcount = null;
  472. }
  473. $this->_data['commentcount'] = $commentcount;
  474. return $this->_data['commentcount'];
  475. }
  476. /**
  477. * Returns a URI pointing to the HTML page where comments can be made on this entry
  478. *
  479. * @return string
  480. */
  481. public function getCommentLink()
  482. {
  483. if (array_key_exists('commentlink', $this->_data)) {
  484. return $this->_data['commentlink'];
  485. }
  486. $commentlink = null;
  487. if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10
  488. && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090
  489. ) {
  490. $commentlink = $this->_xpath->evaluate('string('.$this->_xpathQueryRss.'/comments)');
  491. }
  492. if (!$commentlink) {
  493. $commentlink = $this->getExtension('Atom')->getCommentLink();
  494. }
  495. if (!$commentlink) {
  496. $commentlink = null;
  497. }
  498. $this->_data['commentlink'] = $commentlink;
  499. return $this->_data['commentlink'];
  500. }
  501. /**
  502. * Returns a URI pointing to a feed of all comments for this entry
  503. *
  504. * @return string
  505. */
  506. public function getCommentFeedLink()
  507. {
  508. if (array_key_exists('commentfeedlink', $this->_data)) {
  509. return $this->_data['commentfeedlink'];
  510. }
  511. $commentfeedlink = $this->getExtension('WellFormedWeb')->getCommentFeedLink();
  512. if (!$commentfeedlink) {
  513. $commentfeedlink = $this->getExtension('Atom')->getCommentFeedLink('rss');
  514. }
  515. if (!$commentfeedlink) {
  516. $commentfeedlink = $this->getExtension('Atom')->getCommentFeedLink('rdf');
  517. }
  518. if (!$commentfeedlink) {
  519. $commentfeedlink = null;
  520. }
  521. $this->_data['commentfeedlink'] = $commentfeedlink;
  522. return $this->_data['commentfeedlink'];
  523. }
  524. /**
  525. * Set the XPath query (incl. on all Extensions)
  526. *
  527. * @param DOMXPath $xpath
  528. */
  529. public function setXpath(DOMXPath $xpath)
  530. {
  531. parent::setXpath($xpath);
  532. foreach ($this->_extensions as $extension) {
  533. $extension->setXpath($this->_xpath);
  534. }
  535. }
  536. }