3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
10 namespace Zend\Feed\Reader\Feed
;
15 use Zend\Feed\Reader\Collection
;
16 use Zend\Feed\Reader\Exception
;
20 class Rss
extends AbstractFeed
26 * @param DOMDocument $dom
29 public function __construct(DOMDocument
$dom, $type = null)
31 parent
::__construct($dom, $type);
33 $manager = Reader\Reader
::getExtensionManager();
35 $feed = $manager->get('DublinCore\Feed');
36 $feed->setDomDocument($dom);
37 $feed->setType($this->data
['type']);
38 $feed->setXpath($this->xpath
);
39 $this->extensions
['DublinCore\Feed'] = $feed;
41 $feed = $manager->get('Atom\Feed');
42 $feed->setDomDocument($dom);
43 $feed->setType($this->data
['type']);
44 $feed->setXpath($this->xpath
);
45 $this->extensions
['Atom\Feed'] = $feed;
47 if ($this->getType() !== Reader\Reader
::TYPE_RSS_10
48 && $this->getType() !== Reader\Reader
::TYPE_RSS_090
50 $xpathPrefix = '/rss/channel';
52 $xpathPrefix = '/rdf:RDF/rss:channel';
54 foreach ($this->extensions
as $extension) {
55 $extension->setXpathPrefix($xpathPrefix);
65 public function getAuthor($index = 0)
67 $authors = $this->getAuthors();
69 if (isset($authors[$index])) {
70 return $authors[$index];
77 * Get an array with feed authors
81 public function getAuthors()
83 if (array_key_exists('authors', $this->data
)) {
84 return $this->data
['authors'];
88 $authorsDc = $this->getExtension('DublinCore')->getAuthors();
89 if (!empty($authorsDc)) {
90 foreach ($authorsDc as $author) {
92 'name' => $author['name']
98 * Technically RSS doesn't specific author element use at the feed level
99 * but it's supported on a "just in case" basis.
101 if ($this->getType() !== Reader\Reader
::TYPE_RSS_10
102 && $this->getType() !== Reader\Reader
::TYPE_RSS_090
) {
103 $list = $this->xpath
->query('//author');
105 $list = $this->xpath
->query('//rss:author');
108 foreach ($list as $author) {
109 $string = trim($author->nodeValue
);
113 // Pretty rough parsing - but it's a catchall
114 if (preg_match("/^.*@[^ ]*/", $string, $matches)) {
115 $data['email'] = trim($matches[0]);
116 if (preg_match("/\((.*)\)$/", $string, $matches)) {
117 $data['name'] = $matches[1];
124 if (count($authors) == 0) {
125 $authors = $this->getExtension('Atom')->getAuthors();
127 $authors = new Reader\Collection\
Author(
128 Reader\Reader
::arrayUnique($authors)
132 if (count($authors) == 0) {
136 $this->data
['authors'] = $authors;
138 return $this->data
['authors'];
142 * Get the copyright entry
144 * @return string|null
146 public function getCopyright()
148 if (array_key_exists('copyright', $this->data
)) {
149 return $this->data
['copyright'];
154 if ($this->getType() !== Reader\Reader
::TYPE_RSS_10
&&
155 $this->getType() !== Reader\Reader
::TYPE_RSS_090
) {
156 $copyright = $this->xpath
->evaluate('string(/rss/channel/copyright)');
159 if (!$copyright && $this->getExtension('DublinCore') !== null) {
160 $copyright = $this->getExtension('DublinCore')->getCopyright();
163 if (empty($copyright)) {
164 $copyright = $this->getExtension('Atom')->getCopyright();
171 $this->data
['copyright'] = $copyright;
173 return $this->data
['copyright'];
177 * Get the feed creation date
179 * @return string|null
181 public function getDateCreated()
183 return $this->getDateModified();
187 * Get the feed modification date
190 * @throws Exception\RuntimeException
192 public function getDateModified()
194 if (array_key_exists('datemodified', $this->data
)) {
195 return $this->data
['datemodified'];
198 $dateModified = null;
201 if ($this->getType() !== Reader\Reader
::TYPE_RSS_10
&&
202 $this->getType() !== Reader\Reader
::TYPE_RSS_090
) {
203 $dateModified = $this->xpath
->evaluate('string(/rss/channel/pubDate)');
204 if (!$dateModified) {
205 $dateModified = $this->xpath
->evaluate('string(/rss/channel/lastBuildDate)');
208 $dateModifiedParsed = strtotime($dateModified);
209 if ($dateModifiedParsed) {
210 $date = new DateTime('@' . $dateModifiedParsed);
212 $dateStandards = array(DateTime
::RSS
, DateTime
::RFC822
,
213 DateTime
::RFC2822
, null);
214 foreach ($dateStandards as $standard) {
216 $date = DateTime
::createFromFormat($standard, $dateModified);
218 } catch (\Exception
$e) {
219 if ($standard == null) {
220 throw new Exception\
RuntimeException(
221 'Could not load date due to unrecognised'
222 .' format (should follow RFC 822 or 2822):'
234 $date = $this->getExtension('DublinCore')->getDate();
238 $date = $this->getExtension('Atom')->getDateModified();
245 $this->data
['datemodified'] = $date;
247 return $this->data
['datemodified'];
251 * Get the feed lastBuild date
253 * @throws Exception\RuntimeException
256 public function getLastBuildDate()
258 if (array_key_exists('lastBuildDate', $this->data
)) {
259 return $this->data
['lastBuildDate'];
262 $lastBuildDate = null;
265 if ($this->getType() !== Reader\Reader
::TYPE_RSS_10
&&
266 $this->getType() !== Reader\Reader
::TYPE_RSS_090
) {
267 $lastBuildDate = $this->xpath
->evaluate('string(/rss/channel/lastBuildDate)');
268 if ($lastBuildDate) {
269 $lastBuildDateParsed = strtotime($lastBuildDate);
270 if ($lastBuildDateParsed) {
271 $date = new DateTime('@' . $lastBuildDateParsed);
273 $dateStandards = array(DateTime
::RSS
, DateTime
::RFC822
,
274 DateTime
::RFC2822
, null);
275 foreach ($dateStandards as $standard) {
277 $date = DateTime
::createFromFormat($standard, $lastBuildDateParsed);
279 } catch (\Exception
$e) {
280 if ($standard == null) {
281 throw new Exception\
RuntimeException(
282 'Could not load date due to unrecognised'
283 .' format (should follow RFC 822 or 2822):'
298 $this->data
['lastBuildDate'] = $date;
300 return $this->data
['lastBuildDate'];
304 * Get the feed description
306 * @return string|null
308 public function getDescription()
310 if (array_key_exists('description', $this->data
)) {
311 return $this->data
['description'];
316 if ($this->getType() !== Reader\Reader
::TYPE_RSS_10
&&
317 $this->getType() !== Reader\Reader
::TYPE_RSS_090
) {
318 $description = $this->xpath
->evaluate('string(/rss/channel/description)');
320 $description = $this->xpath
->evaluate('string(/rdf:RDF/rss:channel/rss:description)');
323 if (!$description && $this->getExtension('DublinCore') !== null) {
324 $description = $this->getExtension('DublinCore')->getDescription();
327 if (empty($description)) {
328 $description = $this->getExtension('Atom')->getDescription();
335 $this->data
['description'] = $description;
337 return $this->data
['description'];
343 * @return string|null
345 public function getId()
347 if (array_key_exists('id', $this->data
)) {
348 return $this->data
['id'];
353 if ($this->getType() !== Reader\Reader
::TYPE_RSS_10
&&
354 $this->getType() !== Reader\Reader
::TYPE_RSS_090
) {
355 $id = $this->xpath
->evaluate('string(/rss/channel/guid)');
358 if (!$id && $this->getExtension('DublinCore') !== null) {
359 $id = $this->getExtension('DublinCore')->getId();
363 $id = $this->getExtension('Atom')->getId();
367 if ($this->getLink()) {
368 $id = $this->getLink();
369 } elseif ($this->getTitle()) {
370 $id = $this->getTitle();
376 $this->data
['id'] = $id;
378 return $this->data
['id'];
382 * Get the feed image data
386 public function getImage()
388 if (array_key_exists('image', $this->data
)) {
389 return $this->data
['image'];
392 if ($this->getType() !== Reader\Reader
::TYPE_RSS_10
&&
393 $this->getType() !== Reader\Reader
::TYPE_RSS_090
) {
394 $list = $this->xpath
->query('/rss/channel/image');
395 $prefix = '/rss/channel/image[1]';
397 $list = $this->xpath
->query('/rdf:RDF/rss:channel/rss:image');
398 $prefix = '/rdf:RDF/rss:channel/rss:image[1]';
400 if ($list->length
> 0) {
402 $value = $this->xpath
->evaluate('string(' . $prefix . '/url)');
404 $image['uri'] = $value;
406 $value = $this->xpath
->evaluate('string(' . $prefix . '/link)');
408 $image['link'] = $value;
410 $value = $this->xpath
->evaluate('string(' . $prefix . '/title)');
412 $image['title'] = $value;
414 $value = $this->xpath
->evaluate('string(' . $prefix . '/height)');
416 $image['height'] = $value;
418 $value = $this->xpath
->evaluate('string(' . $prefix . '/width)');
420 $image['width'] = $value;
422 $value = $this->xpath
->evaluate('string(' . $prefix . '/description)');
424 $image['description'] = $value;
430 $this->data
['image'] = $image;
432 return $this->data
['image'];
436 * Get the feed language
438 * @return string|null
440 public function getLanguage()
442 if (array_key_exists('language', $this->data
)) {
443 return $this->data
['language'];
448 if ($this->getType() !== Reader\Reader
::TYPE_RSS_10
&&
449 $this->getType() !== Reader\Reader
::TYPE_RSS_090
) {
450 $language = $this->xpath
->evaluate('string(/rss/channel/language)');
453 if (!$language && $this->getExtension('DublinCore') !== null) {
454 $language = $this->getExtension('DublinCore')->getLanguage();
457 if (empty($language)) {
458 $language = $this->getExtension('Atom')->getLanguage();
462 $language = $this->xpath
->evaluate('string(//@xml:lang[1])');
469 $this->data
['language'] = $language;
471 return $this->data
['language'];
475 * Get a link to the feed
477 * @return string|null
479 public function getLink()
481 if (array_key_exists('link', $this->data
)) {
482 return $this->data
['link'];
487 if ($this->getType() !== Reader\Reader
::TYPE_RSS_10
&&
488 $this->getType() !== Reader\Reader
::TYPE_RSS_090
) {
489 $link = $this->xpath
->evaluate('string(/rss/channel/link)');
491 $link = $this->xpath
->evaluate('string(/rdf:RDF/rss:channel/rss:link)');
495 $link = $this->getExtension('Atom')->getLink();
502 $this->data
['link'] = $link;
504 return $this->data
['link'];
508 * Get a link to the feed XML
510 * @return string|null
512 public function getFeedLink()
514 if (array_key_exists('feedlink', $this->data
)) {
515 return $this->data
['feedlink'];
520 $link = $this->getExtension('Atom')->getFeedLink();
522 if ($link === null ||
empty($link)) {
523 $link = $this->getOriginalSourceUri();
526 $this->data
['feedlink'] = $link;
528 return $this->data
['feedlink'];
532 * Get the feed generator entry
534 * @return string|null
536 public function getGenerator()
538 if (array_key_exists('generator', $this->data
)) {
539 return $this->data
['generator'];
544 if ($this->getType() !== Reader\Reader
::TYPE_RSS_10
&&
545 $this->getType() !== Reader\Reader
::TYPE_RSS_090
) {
546 $generator = $this->xpath
->evaluate('string(/rss/channel/generator)');
550 if ($this->getType() !== Reader\Reader
::TYPE_RSS_10
&&
551 $this->getType() !== Reader\Reader
::TYPE_RSS_090
) {
552 $generator = $this->xpath
->evaluate('string(/rss/channel/atom:generator)');
554 $generator = $this->xpath
->evaluate('string(/rdf:RDF/rss:channel/atom:generator)');
558 if (empty($generator)) {
559 $generator = $this->getExtension('Atom')->getGenerator();
566 $this->data
['generator'] = $generator;
568 return $this->data
['generator'];
574 * @return string|null
576 public function getTitle()
578 if (array_key_exists('title', $this->data
)) {
579 return $this->data
['title'];
584 if ($this->getType() !== Reader\Reader
::TYPE_RSS_10
&&
585 $this->getType() !== Reader\Reader
::TYPE_RSS_090
) {
586 $title = $this->xpath
->evaluate('string(/rss/channel/title)');
588 $title = $this->xpath
->evaluate('string(/rdf:RDF/rss:channel/rss:title)');
591 if (!$title && $this->getExtension('DublinCore') !== null) {
592 $title = $this->getExtension('DublinCore')->getTitle();
596 $title = $this->getExtension('Atom')->getTitle();
603 $this->data
['title'] = $title;
605 return $this->data
['title'];
609 * Get an array of any supported Pusubhubbub endpoints
613 public function getHubs()
615 if (array_key_exists('hubs', $this->data
)) {
616 return $this->data
['hubs'];
619 $hubs = $this->getExtension('Atom')->getHubs();
624 $hubs = array_unique($hubs);
627 $this->data
['hubs'] = $hubs;
629 return $this->data
['hubs'];
635 * @return Reader\Collection\Category
637 public function getCategories()
639 if (array_key_exists('categories', $this->data
)) {
640 return $this->data
['categories'];
643 if ($this->getType() !== Reader\Reader
::TYPE_RSS_10
&&
644 $this->getType() !== Reader\Reader
::TYPE_RSS_090
) {
645 $list = $this->xpath
->query('/rss/channel//category');
647 $list = $this->xpath
->query('/rdf:RDF/rss:channel//rss:category');
651 $categoryCollection = new Collection\Category
;
652 foreach ($list as $category) {
653 $categoryCollection[] = array(
654 'term' => $category->nodeValue
,
655 'scheme' => $category->getAttribute('domain'),
656 'label' => $category->nodeValue
,
660 $categoryCollection = $this->getExtension('DublinCore')->getCategories();
663 if (count($categoryCollection) == 0) {
664 $categoryCollection = $this->getExtension('Atom')->getCategories();
667 $this->data
['categories'] = $categoryCollection;
669 return $this->data
['categories'];
673 * Read all entries to the internal entries array
676 protected function indexEntries()
680 if ($this->getType() !== Reader\Reader
::TYPE_RSS_10
&& $this->getType() !== Reader\Reader
::TYPE_RSS_090
) {
681 $entries = $this->xpath
->evaluate('//item');
683 $entries = $this->xpath
->evaluate('//rss:item');
686 foreach ($entries as $index => $entry) {
687 $this->entries
[$index] = $entry;
692 * Register the default namespaces for the current feed format
695 protected function registerNamespaces()
697 switch ($this->data
['type']) {
698 case Reader\Reader
::TYPE_RSS_10
:
699 $this->xpath
->registerNamespace('rdf', Reader\Reader
::NAMESPACE_RDF
);
700 $this->xpath
->registerNamespace('rss', Reader\Reader
::NAMESPACE_RSS_10
);
703 case Reader\Reader
::TYPE_RSS_090
:
704 $this->xpath
->registerNamespace('rdf', Reader\Reader
::NAMESPACE_RDF
);
705 $this->xpath
->registerNamespace('rss', Reader\Reader
::NAMESPACE_RSS_090
);