3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 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\Extension\Atom;
15 use Zend\Feed\Reader\Collection;
16 use Zend\Feed\Reader\Extension;
19 class Feed extends Extension\AbstractFeed
27 public function getAuthor($index = 0)
29 $authors = $this->getAuthors();
31 if (isset($authors[$index])) {
32 return $authors[$index];
39 * Get an array with feed authors
41 * @return Collection\Author
43 public function getAuthors()
45 if (array_key_exists('authors', $this->data)) {
46 return $this->data['authors'];
49 $list = $this->xpath->query('//atom:author');
54 foreach ($list as $author) {
55 $author = $this->getAuthorFromElement($author);
56 if (! empty($author)) {
62 if (count($authors) == 0) {
63 $authors = new Collection\Author();
65 $authors = new Collection\Author(
66 Reader\Reader::arrayUnique($authors)
70 $this->data['authors'] = $authors;
72 return $this->data['authors'];
76 * Get the copyright entry
80 public function getCopyright()
82 if (array_key_exists('copyright', $this->data)) {
83 return $this->data['copyright'];
88 if ($this->getType() === Reader\Reader::TYPE_ATOM_03) {
89 $copyright = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:copyright)');
91 $copyright = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:rights)');
98 $this->data['copyright'] = $copyright;
100 return $this->data['copyright'];
104 * Get the feed creation date
106 * @return DateTime|null
108 public function getDateCreated()
110 if (array_key_exists('datecreated', $this->data)) {
111 return $this->data['datecreated'];
116 if ($this->getType() === Reader\Reader::TYPE_ATOM_03) {
117 $dateCreated = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:created)');
119 $dateCreated = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:published)');
123 $date = new DateTime($dateCreated);
126 $this->data['datecreated'] = $date;
128 return $this->data['datecreated'];
132 * Get the feed modification date
134 * @return DateTime|null
136 public function getDateModified()
138 if (array_key_exists('datemodified', $this->data)) {
139 return $this->data['datemodified'];
144 if ($this->getType() === Reader\Reader::TYPE_ATOM_03) {
145 $dateModified = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:modified)');
147 $dateModified = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:updated)');
151 $date = new DateTime($dateModified);
154 $this->data['datemodified'] = $date;
156 return $this->data['datemodified'];
160 * Get the feed description
162 * @return string|null
164 public function getDescription()
166 if (array_key_exists('description', $this->data)) {
167 return $this->data['description'];
172 if ($this->getType() === Reader\Reader::TYPE_ATOM_03) {
173 $description = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:tagline)');
175 $description = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:subtitle)');
178 if (! $description) {
182 $this->data['description'] = $description;
184 return $this->data['description'];
188 * Get the feed generator entry
190 * @return string|null
192 public function getGenerator()
194 if (array_key_exists('generator', $this->data)) {
195 return $this->data['generator'];
197 // TODO: Add uri support
198 $generator = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:generator)');
204 $this->data['generator'] = $generator;
206 return $this->data['generator'];
212 * @return string|null
214 public function getId()
216 if (array_key_exists('id', $this->data)) {
217 return $this->data['id'];
220 $id = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:id)');
223 if ($this->getLink()) {
224 $id = $this->getLink();
225 } elseif ($this->getTitle()) {
226 $id = $this->getTitle();
232 $this->data['id'] = $id;
234 return $this->data['id'];
238 * Get the feed language
240 * @return string|null
242 public function getLanguage()
244 if (array_key_exists('language', $this->data)) {
245 return $this->data['language'];
248 $language = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:lang)');
251 $language = $this->xpath->evaluate('string(//@xml:lang[1])');
258 $this->data['language'] = $language;
260 return $this->data['language'];
268 public function getImage()
270 if (array_key_exists('image', $this->data)) {
271 return $this->data['image'];
274 $imageUrl = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:logo)');
279 $image = ['uri' => $imageUrl];
282 $this->data['image'] = $image;
284 return $this->data['image'];
288 * Get the base URI of the feed (if set).
290 * @return string|null
292 public function getBaseUrl()
294 if (array_key_exists('baseUrl', $this->data)) {
295 return $this->data['baseUrl'];
298 $baseUrl = $this->xpath->evaluate('string(//@xml:base[1])');
303 $this->data['baseUrl'] = $baseUrl;
305 return $this->data['baseUrl'];
309 * Get a link to the source website
311 * @return string|null
313 public function getLink()
315 if (array_key_exists('link', $this->data)) {
316 return $this->data['link'];
321 $list = $this->xpath->query(
322 $this->getXpathPrefix() . '/atom:link[@rel="alternate"]/@href' . '|' .
323 $this->getXpathPrefix() . '/atom:link[not(@rel)]/@href'
327 $link = $list->item(0)->nodeValue;
328 $link = $this->absolutiseUri($link);
331 $this->data['link'] = $link;
333 return $this->data['link'];
337 * Get a link to the feed's XML Url
339 * @return string|null
341 public function getFeedLink()
343 if (array_key_exists('feedlink', $this->data)) {
344 return $this->data['feedlink'];
347 $link = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:link[@rel="self"]/@href)');
349 $link = $this->absolutiseUri($link);
351 $this->data['feedlink'] = $link;
353 return $this->data['feedlink'];
357 * Get an array of any supported Pusubhubbub endpoints
361 public function getHubs()
363 if (array_key_exists('hubs', $this->data)) {
364 return $this->data['hubs'];
368 $list = $this->xpath->query($this->getXpathPrefix()
369 . '//atom:link[@rel="hub"]/@href');
372 foreach ($list as $uri) {
373 $hubs[] = $this->absolutiseUri($uri->nodeValue);
379 $this->data['hubs'] = $hubs;
381 return $this->data['hubs'];
387 * @return string|null
389 public function getTitle()
391 if (array_key_exists('title', $this->data)) {
392 return $this->data['title'];
395 $title = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:title)');
401 $this->data['title'] = $title;
403 return $this->data['title'];
409 * @return Collection\Category
411 public function getCategories()
413 if (array_key_exists('categories', $this->data)) {
414 return $this->data['categories'];
417 if ($this->getType() == Reader\Reader::TYPE_ATOM_10) {
418 $list = $this->xpath->query($this->getXpathPrefix() . '/atom:category');
421 * Since Atom 0.3 did not support categories, it would have used the
422 * Dublin Core extension. However there is a small possibility Atom 0.3
423 * may have been retrofittied to use Atom 1.0 instead.
425 $this->xpath->registerNamespace('atom10', Reader\Reader::NAMESPACE_ATOM_10);
426 $list = $this->xpath->query($this->getXpathPrefix() . '/atom10:category');
430 $categoryCollection = new Collection\Category;
431 foreach ($list as $category) {
432 $categoryCollection[] = [
433 'term' => $category->getAttribute('term'),
434 'scheme' => $category->getAttribute('scheme'),
435 'label' => $category->getAttribute('label')
439 return new Collection\Category;
442 $this->data['categories'] = $categoryCollection;
444 return $this->data['categories'];
448 * Get an author entry in RSS format
450 * @param DOMElement $element
453 protected function getAuthorFromElement(DOMElement $element)
457 $emailNode = $element->getElementsByTagName('email');
458 $nameNode = $element->getElementsByTagName('name');
459 $uriNode = $element->getElementsByTagName('uri');
461 if ($emailNode->length && strlen($emailNode->item(0)->nodeValue) > 0) {
462 $author['email'] = $emailNode->item(0)->nodeValue;
465 if ($nameNode->length && strlen($nameNode->item(0)->nodeValue) > 0) {
466 $author['name'] = $nameNode->item(0)->nodeValue;
469 if ($uriNode->length && strlen($uriNode->item(0)->nodeValue) > 0) {
470 $author['uri'] = $uriNode->item(0)->nodeValue;
473 if (empty($author)) {
480 * Attempt to absolutise the URI, i.e. if a relative URI apply the
481 * xml:base value as a prefix to turn into an absolute URI.
483 protected function absolutiseUri($link)
485 if (! Uri::factory($link)->isAbsolute()) {
486 if ($this->getBaseUrl() !== null) {
487 $link = $this->getBaseUrl() . $link;
488 if (! Uri::factory($link)->isValid()) {
497 * Register the default namespaces for the current feed format
499 protected function registerNamespaces()
501 if ($this->getType() == Reader\Reader::TYPE_ATOM_10
502 || $this->getType() == Reader\Reader::TYPE_ATOM_03
504 return; // pre-registered at Feed level
506 $atomDetected = $this->getAtomType();
507 switch ($atomDetected) {
508 case Reader\Reader::TYPE_ATOM_03:
509 $this->xpath->registerNamespace('atom', Reader\Reader::NAMESPACE_ATOM_03);
512 $this->xpath->registerNamespace('atom', Reader\Reader::NAMESPACE_ATOM_10);
518 * Detect the presence of any Atom namespaces in use
520 protected function getAtomType()
522 $dom = $this->getDomDocument();
523 $prefixAtom03 = $dom->lookupPrefix(Reader\Reader::NAMESPACE_ATOM_03);
524 $prefixAtom10 = $dom->lookupPrefix(Reader\Reader::NAMESPACE_ATOM_10);
525 if ($dom->isDefaultNamespace(Reader\Reader::NAMESPACE_ATOM_10)
526 || ! empty($prefixAtom10)
528 return Reader\Reader::TYPE_ATOM_10;
530 if ($dom->isDefaultNamespace(Reader\Reader::NAMESPACE_ATOM_03)
531 || ! empty($prefixAtom03)
533 return Reader\Reader::TYPE_ATOM_03;