Chris@0: getAuthors(); Chris@0: Chris@0: if (isset($authors[$index])) { Chris@0: return $authors[$index]; Chris@0: } Chris@0: Chris@0: return; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get an array with feed authors Chris@0: * Chris@0: * @return Collection\Author Chris@0: */ Chris@0: public function getAuthors() Chris@0: { Chris@0: if (array_key_exists('authors', $this->data)) { Chris@0: return $this->data['authors']; Chris@0: } Chris@0: Chris@0: $authors = []; Chris@0: $list = $this->getXpath()->query($this->getXpathPrefix() . '//atom:author'); Chris@0: Chris@12: if (! $list->length) { Chris@0: /** Chris@0: * TODO: Limit query to feed level els only! Chris@0: */ Chris@0: $list = $this->getXpath()->query('//atom:author'); Chris@0: } Chris@0: Chris@0: if ($list->length) { Chris@0: foreach ($list as $author) { Chris@0: $author = $this->getAuthorFromElement($author); Chris@12: if (! empty($author)) { Chris@0: $authors[] = $author; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: if (count($authors) == 0) { Chris@0: $authors = new Collection\Author(); Chris@0: } else { Chris@0: $authors = new Collection\Author( Chris@0: Reader\Reader::arrayUnique($authors) Chris@0: ); Chris@0: } Chris@0: Chris@0: $this->data['authors'] = $authors; Chris@0: return $this->data['authors']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get the entry content Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: public function getContent() Chris@0: { Chris@0: if (array_key_exists('content', $this->data)) { Chris@0: return $this->data['content']; Chris@0: } Chris@0: Chris@0: $content = null; Chris@0: Chris@0: $el = $this->getXpath()->query($this->getXpathPrefix() . '/atom:content'); Chris@0: if ($el->length > 0) { Chris@0: $el = $el->item(0); Chris@0: $type = $el->getAttribute('type'); Chris@0: switch ($type) { Chris@0: case '': Chris@0: case 'text': Chris@0: case 'text/plain': Chris@0: case 'html': Chris@0: case 'text/html': Chris@0: $content = $el->nodeValue; Chris@0: break; Chris@0: case 'xhtml': Chris@0: $this->getXpath()->registerNamespace('xhtml', 'http://www.w3.org/1999/xhtml'); Chris@0: $xhtml = $this->getXpath()->query( Chris@0: $this->getXpathPrefix() . '/atom:content/xhtml:div' Chris@0: )->item(0); Chris@0: $d = new DOMDocument('1.0', $this->getEncoding()); Chris@0: $deep = version_compare(PHP_VERSION, '7', 'ge') ? 1 : true; Chris@0: $xhtmls = $d->importNode($xhtml, $deep); Chris@0: $d->appendChild($xhtmls); Chris@0: $content = $this->collectXhtml( Chris@0: $d->saveXML(), Chris@0: $d->lookupPrefix('http://www.w3.org/1999/xhtml') Chris@0: ); Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@12: if (! $content) { Chris@0: $content = $this->getDescription(); Chris@0: } Chris@0: Chris@0: $this->data['content'] = trim($content); Chris@0: Chris@0: return $this->data['content']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Parse out XHTML to remove the namespacing Chris@0: * Chris@0: * @param $xhtml Chris@0: * @param $prefix Chris@0: * @return mixed Chris@0: */ Chris@0: protected function collectXhtml($xhtml, $prefix) Chris@0: { Chris@12: if (! empty($prefix)) { Chris@0: $prefix = $prefix . ':'; Chris@0: } Chris@0: $matches = [ Chris@0: "/<\?xml[^<]*>[^<]*<" . $prefix . "div[^<]*/", Chris@0: "/<\/" . $prefix . "div>\s*$/" Chris@0: ]; Chris@0: $xhtml = preg_replace($matches, '', $xhtml); Chris@12: if (! empty($prefix)) { Chris@0: $xhtml = preg_replace("/(<[\/]?)" . $prefix . "([a-zA-Z]+)/", '$1$2', $xhtml); Chris@0: } Chris@0: return $xhtml; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get the entry creation date Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: public function getDateCreated() Chris@0: { Chris@0: if (array_key_exists('datecreated', $this->data)) { Chris@0: return $this->data['datecreated']; Chris@0: } Chris@0: Chris@0: $date = null; Chris@0: Chris@0: if ($this->getAtomType() === Reader\Reader::TYPE_ATOM_03) { Chris@0: $dateCreated = $this->getXpath()->evaluate('string(' . $this->getXpathPrefix() . '/atom:created)'); Chris@0: } else { Chris@0: $dateCreated = $this->getXpath()->evaluate('string(' . $this->getXpathPrefix() . '/atom:published)'); Chris@0: } Chris@0: Chris@0: if ($dateCreated) { Chris@0: $date = new DateTime($dateCreated); Chris@0: } Chris@0: Chris@0: $this->data['datecreated'] = $date; Chris@0: Chris@0: return $this->data['datecreated']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get the entry modification date Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: public function getDateModified() Chris@0: { Chris@0: if (array_key_exists('datemodified', $this->data)) { Chris@0: return $this->data['datemodified']; Chris@0: } Chris@0: Chris@0: $date = null; Chris@0: Chris@0: if ($this->getAtomType() === Reader\Reader::TYPE_ATOM_03) { Chris@0: $dateModified = $this->getXpath()->evaluate('string(' . $this->getXpathPrefix() . '/atom:modified)'); Chris@0: } else { Chris@0: $dateModified = $this->getXpath()->evaluate('string(' . $this->getXpathPrefix() . '/atom:updated)'); Chris@0: } Chris@0: Chris@0: if ($dateModified) { Chris@0: $date = new DateTime($dateModified); Chris@0: } Chris@0: Chris@0: $this->data['datemodified'] = $date; Chris@0: Chris@0: return $this->data['datemodified']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get the entry description Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: public function getDescription() Chris@0: { Chris@0: if (array_key_exists('description', $this->data)) { Chris@0: return $this->data['description']; Chris@0: } Chris@0: Chris@0: $description = $this->getXpath()->evaluate('string(' . $this->getXpathPrefix() . '/atom:summary)'); Chris@0: Chris@12: if (! $description) { Chris@0: $description = null; Chris@0: } Chris@0: Chris@0: $this->data['description'] = $description; Chris@0: Chris@0: return $this->data['description']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get the entry enclosure Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: public function getEnclosure() Chris@0: { Chris@0: if (array_key_exists('enclosure', $this->data)) { Chris@0: return $this->data['enclosure']; Chris@0: } Chris@0: Chris@0: $enclosure = null; Chris@0: Chris@0: $nodeList = $this->getXpath()->query($this->getXpathPrefix() . '/atom:link[@rel="enclosure"]'); Chris@0: Chris@0: if ($nodeList->length > 0) { Chris@0: $enclosure = new stdClass(); Chris@0: $enclosure->url = $nodeList->item(0)->getAttribute('href'); Chris@0: $enclosure->length = $nodeList->item(0)->getAttribute('length'); Chris@0: $enclosure->type = $nodeList->item(0)->getAttribute('type'); Chris@0: } Chris@0: Chris@0: $this->data['enclosure'] = $enclosure; Chris@0: Chris@0: return $this->data['enclosure']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get the entry ID Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: public function getId() Chris@0: { Chris@0: if (array_key_exists('id', $this->data)) { Chris@0: return $this->data['id']; Chris@0: } Chris@0: Chris@0: $id = $this->getXpath()->evaluate('string(' . $this->getXpathPrefix() . '/atom:id)'); Chris@0: Chris@12: if (! $id) { Chris@0: if ($this->getPermalink()) { Chris@0: $id = $this->getPermalink(); Chris@0: } elseif ($this->getTitle()) { Chris@0: $id = $this->getTitle(); Chris@0: } else { Chris@0: $id = null; Chris@0: } Chris@0: } Chris@0: Chris@0: $this->data['id'] = $id; Chris@0: Chris@0: return $this->data['id']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get the base URI of the feed (if set). Chris@0: * Chris@0: * @return string|null Chris@0: */ Chris@0: public function getBaseUrl() Chris@0: { Chris@0: if (array_key_exists('baseUrl', $this->data)) { Chris@0: return $this->data['baseUrl']; Chris@0: } Chris@0: Chris@0: $baseUrl = $this->getXpath()->evaluate( Chris@0: 'string(' Chris@0: . $this->getXpathPrefix() Chris@0: . '/@xml:base[1]' Chris@0: . ')' Chris@0: ); Chris@0: Chris@12: if (! $baseUrl) { Chris@0: $baseUrl = $this->getXpath()->evaluate('string(//@xml:base[1])'); Chris@0: } Chris@0: Chris@12: if (! $baseUrl) { Chris@0: $baseUrl = null; Chris@0: } Chris@0: Chris@0: $this->data['baseUrl'] = $baseUrl; Chris@0: Chris@0: return $this->data['baseUrl']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get a specific link Chris@0: * Chris@0: * @param int $index Chris@0: * @return string Chris@0: */ Chris@0: public function getLink($index = 0) Chris@0: { Chris@12: if (! array_key_exists('links', $this->data)) { Chris@0: $this->getLinks(); Chris@0: } Chris@0: Chris@0: if (isset($this->data['links'][$index])) { Chris@0: return $this->data['links'][$index]; Chris@0: } Chris@0: Chris@0: return; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get all links Chris@0: * Chris@0: * @return array Chris@0: */ Chris@0: public function getLinks() Chris@0: { Chris@0: if (array_key_exists('links', $this->data)) { Chris@0: return $this->data['links']; Chris@0: } Chris@0: Chris@0: $links = []; Chris@0: Chris@0: $list = $this->getXpath()->query( Chris@0: $this->getXpathPrefix() . '//atom:link[@rel="alternate"]/@href' . '|' . Chris@0: $this->getXpathPrefix() . '//atom:link[not(@rel)]/@href' Chris@0: ); Chris@0: Chris@0: if ($list->length) { Chris@0: foreach ($list as $link) { Chris@0: $links[] = $this->absolutiseUri($link->value); Chris@0: } Chris@0: } Chris@0: Chris@0: $this->data['links'] = $links; Chris@0: Chris@0: return $this->data['links']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get a permalink to the entry Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: public function getPermalink() Chris@0: { Chris@0: return $this->getLink(0); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get the entry title Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: public function getTitle() Chris@0: { Chris@0: if (array_key_exists('title', $this->data)) { Chris@0: return $this->data['title']; Chris@0: } Chris@0: Chris@0: $title = $this->getXpath()->evaluate('string(' . $this->getXpathPrefix() . '/atom:title)'); Chris@0: Chris@12: if (! $title) { Chris@0: $title = null; Chris@0: } Chris@0: Chris@0: $this->data['title'] = $title; Chris@0: Chris@0: return $this->data['title']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get the number of comments/replies for current entry Chris@0: * Chris@0: * @return int Chris@0: */ Chris@0: public function getCommentCount() Chris@0: { Chris@0: if (array_key_exists('commentcount', $this->data)) { Chris@0: return $this->data['commentcount']; Chris@0: } Chris@0: Chris@0: $count = null; Chris@0: Chris@0: $this->getXpath()->registerNamespace('thread10', 'http://purl.org/syndication/thread/1.0'); Chris@0: $list = $this->getXpath()->query( Chris@0: $this->getXpathPrefix() . '//atom:link[@rel="replies"]/@thread10:count' Chris@0: ); Chris@0: Chris@0: if ($list->length) { Chris@0: $count = $list->item(0)->value; Chris@0: } Chris@0: Chris@0: $this->data['commentcount'] = $count; Chris@0: Chris@0: return $this->data['commentcount']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns a URI pointing to the HTML page where comments can be made on this entry Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: public function getCommentLink() Chris@0: { Chris@0: if (array_key_exists('commentlink', $this->data)) { Chris@0: return $this->data['commentlink']; Chris@0: } Chris@0: Chris@0: $link = null; Chris@0: Chris@0: $list = $this->getXpath()->query( Chris@0: $this->getXpathPrefix() . '//atom:link[@rel="replies" and @type="text/html"]/@href' Chris@0: ); Chris@0: Chris@0: if ($list->length) { Chris@0: $link = $list->item(0)->value; Chris@0: $link = $this->absolutiseUri($link); Chris@0: } Chris@0: Chris@0: $this->data['commentlink'] = $link; Chris@0: Chris@0: return $this->data['commentlink']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns a URI pointing to a feed of all comments for this entry Chris@0: * Chris@0: * @param string $type Chris@0: * @return string Chris@0: */ Chris@0: public function getCommentFeedLink($type = 'atom') Chris@0: { Chris@0: if (array_key_exists('commentfeedlink', $this->data)) { Chris@0: return $this->data['commentfeedlink']; Chris@0: } Chris@0: Chris@0: $link = null; Chris@0: Chris@0: $list = $this->getXpath()->query( Chris@0: $this->getXpathPrefix() . '//atom:link[@rel="replies" and @type="application/' . $type.'+xml"]/@href' Chris@0: ); Chris@0: Chris@0: if ($list->length) { Chris@0: $link = $list->item(0)->value; Chris@0: $link = $this->absolutiseUri($link); Chris@0: } Chris@0: Chris@0: $this->data['commentfeedlink'] = $link; Chris@0: Chris@0: return $this->data['commentfeedlink']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get all categories Chris@0: * Chris@0: * @return Collection\Category Chris@0: */ Chris@0: public function getCategories() Chris@0: { Chris@0: if (array_key_exists('categories', $this->data)) { Chris@0: return $this->data['categories']; Chris@0: } Chris@0: Chris@0: if ($this->getAtomType() == Reader\Reader::TYPE_ATOM_10) { Chris@0: $list = $this->getXpath()->query($this->getXpathPrefix() . '//atom:category'); Chris@0: } else { Chris@0: /** Chris@0: * Since Atom 0.3 did not support categories, it would have used the Chris@0: * Dublin Core extension. However there is a small possibility Atom 0.3 Chris@0: * may have been retrofitted to use Atom 1.0 instead. Chris@0: */ Chris@0: $this->getXpath()->registerNamespace('atom10', Reader\Reader::NAMESPACE_ATOM_10); Chris@0: $list = $this->getXpath()->query($this->getXpathPrefix() . '//atom10:category'); Chris@0: } Chris@0: Chris@0: if ($list->length) { Chris@0: $categoryCollection = new Collection\Category; Chris@0: foreach ($list as $category) { Chris@0: $categoryCollection[] = [ Chris@0: 'term' => $category->getAttribute('term'), Chris@0: 'scheme' => $category->getAttribute('scheme'), Chris@0: 'label' => $category->getAttribute('label') Chris@0: ]; Chris@0: } Chris@0: } else { Chris@0: return new Collection\Category; Chris@0: } Chris@0: Chris@0: $this->data['categories'] = $categoryCollection; Chris@0: Chris@0: return $this->data['categories']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get source feed metadata from the entry Chris@0: * Chris@0: * @return Reader\Feed\Atom\Source|null Chris@0: */ Chris@0: public function getSource() Chris@0: { Chris@0: if (array_key_exists('source', $this->data)) { Chris@0: return $this->data['source']; Chris@0: } Chris@0: Chris@0: $source = null; Chris@0: // TODO: Investigate why _getAtomType() fails here. Is it even needed? Chris@0: if ($this->getType() == Reader\Reader::TYPE_ATOM_10) { Chris@0: $list = $this->getXpath()->query($this->getXpathPrefix() . '/atom:source[1]'); Chris@0: if ($list->length) { Chris@0: $element = $list->item(0); Chris@0: $source = new Reader\Feed\Atom\Source($element, $this->getXpathPrefix()); Chris@0: } Chris@0: } Chris@0: Chris@0: $this->data['source'] = $source; Chris@0: return $this->data['source']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Attempt to absolutise the URI, i.e. if a relative URI apply the Chris@0: * xml:base value as a prefix to turn into an absolute URI. Chris@0: * Chris@0: * @param $link Chris@0: * @return string Chris@0: */ Chris@0: protected function absolutiseUri($link) Chris@0: { Chris@12: if (! Uri::factory($link)->isAbsolute()) { Chris@0: if ($this->getBaseUrl() !== null) { Chris@0: $link = $this->getBaseUrl() . $link; Chris@12: if (! Uri::factory($link)->isValid()) { Chris@0: $link = null; Chris@0: } Chris@0: } Chris@0: } Chris@0: return $link; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get an author entry Chris@0: * Chris@0: * @param DOMElement $element Chris@0: * @return string Chris@0: */ Chris@0: protected function getAuthorFromElement(DOMElement $element) Chris@0: { Chris@0: $author = []; Chris@0: Chris@0: $emailNode = $element->getElementsByTagName('email'); Chris@0: $nameNode = $element->getElementsByTagName('name'); Chris@0: $uriNode = $element->getElementsByTagName('uri'); Chris@0: Chris@0: if ($emailNode->length && strlen($emailNode->item(0)->nodeValue) > 0) { Chris@0: $author['email'] = $emailNode->item(0)->nodeValue; Chris@0: } Chris@0: Chris@0: if ($nameNode->length && strlen($nameNode->item(0)->nodeValue) > 0) { Chris@0: $author['name'] = $nameNode->item(0)->nodeValue; Chris@0: } Chris@0: Chris@0: if ($uriNode->length && strlen($uriNode->item(0)->nodeValue) > 0) { Chris@0: $author['uri'] = $uriNode->item(0)->nodeValue; Chris@0: } Chris@0: Chris@0: if (empty($author)) { Chris@0: return; Chris@0: } Chris@0: return $author; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Register the default namespaces for the current feed format Chris@0: */ Chris@0: protected function registerNamespaces() Chris@0: { Chris@0: switch ($this->getAtomType()) { Chris@0: case Reader\Reader::TYPE_ATOM_03: Chris@0: $this->getXpath()->registerNamespace('atom', Reader\Reader::NAMESPACE_ATOM_03); Chris@0: break; Chris@0: default: Chris@0: $this->getXpath()->registerNamespace('atom', Reader\Reader::NAMESPACE_ATOM_10); Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Detect the presence of any Atom namespaces in use Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: protected function getAtomType() Chris@0: { Chris@0: $dom = $this->getDomDocument(); Chris@0: $prefixAtom03 = $dom->lookupPrefix(Reader\Reader::NAMESPACE_ATOM_03); Chris@0: $prefixAtom10 = $dom->lookupPrefix(Reader\Reader::NAMESPACE_ATOM_10); Chris@0: if ($dom->isDefaultNamespace(Reader\Reader::NAMESPACE_ATOM_03) Chris@12: || ! empty($prefixAtom03)) { Chris@0: return Reader\Reader::TYPE_ATOM_03; Chris@0: } Chris@0: if ($dom->isDefaultNamespace(Reader\Reader::NAMESPACE_ATOM_10) Chris@12: || ! empty($prefixAtom10)) { Chris@0: return Reader\Reader::TYPE_ATOM_10; Chris@0: } Chris@0: } Chris@0: }