Apache Zeta Components Manual :: File Source for atom.php
Source for file atom.php
Documentation is available at atom.php
* File containing the ezcFeedAtom class.
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* @version //autogentag//
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* Class providing parsing and generating of ATOM feeds.
* {@link http://atompub.org/rfc4287.html ATOM RFC4287}.
* @version //autogentag//
* Defines the feed type of this processor.
* Defines the feed content type of this processor.
* Defines the namespace for ATOM feeds.
* Creates a new ATOM processor.
* @param ezcFeed $container The feed data container used when generating
$this->feedContainer =
$container;
$this->feedType =
self::FEED_TYPE;
$this->contentType =
self::CONTENT_TYPE;
* Returns an XML string from the feed information contained in this
$this->xml =
new DOMDocument( '1.0', 'utf-8' );
$this->xml->formatOutput =
1;
$rss =
$this->xml->createElementNS( self::NAMESPACE_URI, 'feed' );
$this->root =
$this->xml->appendChild( $rss );
$this->generateRequired();
$this->generateAtLeastOne();
$this->generateOptional();
$this->generateFeedModules( $this->channel );
return $this->xml->saveXML();
* Returns true if the parser can parse the provided XML document object,
* @param DOMDocument $xml The XML document object to check for parseability
public static function canParse( DOMDocument $xml )
if ( $xml->documentElement->tagName !==
'feed' )
* Parses the provided XML document object and returns an ezcFeed object
* @throws ezcFeedParseErrorException
* If an error was encountered during parsing.
* @param DOMDocument $xml The XML document object to parse
public function parse( DOMDocument $xml )
$feed =
new ezcFeed( self::FEED_TYPE );
$channel =
$xml->documentElement;
$this->usedPrefixes =
$this->fetchUsedPrefixes( $xml );
foreach ( $channel->childNodes as $channelChild )
if ( $channelChild->nodeType ==
XML_ELEMENT_NODE )
$tagName =
$channelChild->tagName;
$this->parseTextNode( $feed, $channelChild, 'title' );
$this->parseTextNode( $feed, $channelChild, 'copyright' );
$this->parseTextNode( $feed, $channelChild, 'description' );
$feed->$tagName =
$channelChild->textContent;
$feed->$tagName =
$channelChild->textContent;
$feed->image =
$channelChild->textContent;
$element =
$feed->add( $tagName );
$attributes =
array( 'uri' =>
'url', 'version' =>
'version' );
foreach ( $attributes as $name =>
$alias )
if ( $channelChild->hasAttribute( $name ) )
$element->$alias =
$channelChild->getAttribute( $name );
$element->name =
$channelChild->textContent;
$feed->$tagName =
$channelChild->textContent;
$element =
$feed->add( $tagName );
$attributes =
array( 'term' =>
'term', 'scheme' =>
'scheme', 'label' =>
'label' );
foreach ( $attributes as $name =>
$alias )
if ( $channelChild->hasAttribute( $name ) )
$element->$alias =
$channelChild->getAttribute( $name );
$element =
$feed->add( $tagName );
$attributes =
array( 'href' =>
'href', 'rel' =>
'rel', 'hreflang' =>
'hreflang',
'type' =>
'type', 'title' =>
'title', 'length' =>
'length' );
foreach ( $attributes as $name =>
$alias )
if ( $channelChild->hasAttribute( $name ) )
$element->$alias =
$channelChild->getAttribute( $name );
$element =
$feed->add( $tagName );
$this->parsePerson( $element, $channelChild, $tagName );
$element =
$feed->add( 'item' );
$this->parseItem( $element, $channelChild );
// check if it's part of a known module/namespace
$this->parseModules( $feed, $channelChild, $tagName );
if ( $channel->hasAttribute( 'xml:lang' ) )
$feed->language =
$channel->getAttribute( 'xml:lang' );
* Adds the required feed elements to the XML document being generated.
private function generateRequired()
$elements =
array( 'id', 'title', 'updated' );
foreach ( $elements as $element )
$this->generateMetaData( $this->channel, $element, $data );
$this->generateTextNode( $this->channel, $element, $data );
// Sample date: 2003-12-13T18:30:02-05:00
$this->generateMetaData( $this->channel, $element, $data->date->format( 'c' ) );
* Adds the at-least-one feed elements to the XML document being generated.
private function generateAtLeastOne()
$needToThrowException =
false;
foreach ( $entries as $entry )
$authors =
$entry->author;
* Adds the optional feed elements to the XML document being generated.
private function generateOptional()
$elements =
array( 'author', 'link', 'category',
'contributor', 'generator', 'icon',
'image', 'copyright', 'description', 'language' );
if ( $this->link !==
null )
$this->checkLinks( $this->channel, $this->link );
foreach ( $elements as $element )
foreach ( $this->contributor as $person )
$this->generatePerson( $this->channel, 'contributor', $person );
foreach ( $this->author as $person )
$this->generatePerson( $this->channel, 'author', $person );
$this->generateGenerator( $this->channel, $this->generator );
foreach ( $data as $dataNode )
$this->generateLink( $this->channel, $dataNode );
foreach ( $data as $dataNode )
$this->generateCategory( $this->channel, $dataNode );
$this->generateTextNode( $this->channel, 'subtitle', $data );
$this->generateTextNode( $this->channel, 'rights', $data );
$this->generateMetaData( $this->channel, 'logo', $data );
$this->generateMetaData( $this->channel, 'icon', $data );
$this->generateLanguage( $this->channel, $data );
* Checks if the links are defined correctly.
* @throws ezcFeedOnlyOneValueAllowedException
* if there was more than one @rel="alternate" element in the $links array
* @param DOMNode $root The root in which to check the link elements
* @param array(ezcFeedLinkElement) $links The link elements to check
private function checkLinks( DOMNode $root, array $links )
foreach ( $links as $dataNode )
if ( ( isset
( $dataNode->rel ) &&
$dataNode->rel ===
'alternate' )
&& isset
( $dataNode->type )
&& isset
( $dataNode->hreflang ) )
foreach ( $unique as $obj )
if ( $obj['type'] ===
$dataNode->type
&&
$obj['hreflang'] ===
$dataNode->hreflang )
$parentNode =
( $root->nodeName ===
'entry' ) ?
'/feed' :
'';
$parentNode =
( $root->nodeName ===
'source' ) ?
'/feed/entry' :
$parentNode;
$unique[] =
array( 'type' =>
$dataNode->type,
'hreflang' =>
$dataNode->hreflang );
* Creates a link node in the XML document being generated.
* @param DOMNode $root The root in which to create the link
* @param ezcFeedLinkElement $dataNode The data for the link node to create
private function generateLink( DOMNode $root, ezcFeedLinkElement $dataNode )
$elementTag =
$this->xml->createElement( 'link' );
$root->appendChild( $elementTag );
$elements =
array( 'href', 'rel', 'type', 'hreflang', 'title', 'length' );
if ( !isset
( $dataNode->href ) )
$parentNode =
( $root->nodeName ===
'entry' ) ?
'/feed' :
'';
$parentNode =
( $root->nodeName ===
'source' ) ?
'/feed/entry' :
$parentNode;
foreach ( $elements as $attribute )
if ( isset
( $dataNode->$attribute ) )
$this->addAttribute( $elementTag, $attribute, $dataNode->$attribute );
* Creates a generator node in the XML document being generated.
* @param DOMNode $root The root in which to create the generator node
* @param ezcFeedGeneratorElement $generator The data for the generator node to create
private function generateGenerator( DOMNode $root, ezcFeedGeneratorElement $generator )
$name =
$generator->name;
$version =
$generator->version;
$elementTag =
$this->xml->createElement( 'generator', $name );
$root->appendChild( $elementTag );
$this->addAttribute( $elementTag, 'version', $version );
$this->addAttribute( $elementTag, 'uri', $url );
* Creates a category node in the XML document being generated.
* @throws ezcFeedRequiredMetaDataMissingException
* if the required attributes are missing
* @param DOMNode $root The root in which to create the category node
* @param ezcFeedCategoryElement $dataNode The data for the category node to create
private function generateCategory( DOMNode $root, ezcFeedCategoryElement $dataNode )
$elementTag =
$this->xml->createElement( 'category' );
$root->appendChild( $elementTag );
$parentNode =
( $root->nodeName ===
'entry' ) ?
'/feed' :
'';
$parentNode =
( $root->nodeName ===
'source' ) ?
'/feed/entry' :
$parentNode;
$attributes =
array( 'term' =>
'term', 'scheme' =>
'scheme', 'label' =>
'label' );
$required =
array( 'term' );
$optional =
array( 'scheme', 'label' );
foreach ( $attributes as $attribute =>
$alias )
if ( isset
( $dataNode->$alias ) )
$val =
$dataNode->$alias;
$this->addAttribute( $elementTag, $attribute, $val );
else if ( in_array( $attribute, $required ) )
* Creates an XML node in the XML document being generated.
* @param DOMNode $root The root in which to create the node $element
* @param string $element The name of the node to create
* @param ezcFeedTextElement $dataNode The data for the node to create
private function generateTextNode( DOMNode $root, $element, ezcFeedTextElement $dataNode )
$elementTag =
$this->xml->createElement( $element );
$root->appendChild( $elementTag );
if ( isset
( $dataNode->type ) )
$this->addAttribute( $elementTag, 'type', $val );
$this->addAttribute( $elementTag, 'type', $val );
$this->addAttribute( $elementTag, 'xmlns:xhtml', 'http://www.w3.org/1999/xhtml' );
$xhtmlTag =
$this->xml->createElement( 'xhtml:div', $dataNode->__toString() );
$elementTag->appendChild( $xhtmlTag );
// same as the default case
$this->addAttribute( $elementTag, 'type', $val );
if ( isset
( $dataNode->language ) )
if ( $dataNode->type ===
'xhtml' )
$this->addAttribute( $elementTag->parentNode, 'xml:lang', $dataNode->language );
$this->addAttribute( $elementTag, 'xml:lang', $dataNode->language );
$elementTag->nodeValue =
$dataNode;
* Creates an attribute on an XML node containing the language in it.
* @param DOMNode $root The root in which to create the attribute
* @param ezcFeedTextElement $dataNode The data for the node to create
private function generateLanguage( DOMNode $root, ezcFeedTextElement $dataNode )
$this->addAttribute( $root, 'xml:lang', $dataNode->text );
* Creates an XML node in the XML document being generated.
* @param DOMNode $root The root in which to create the node $element
* @param string $element The name of the node to create
* @param array(string=>mixed) $dataNode The data for the node to create
private function generateContentNode( DOMNode $root, $element, $dataNode )
$elementTag =
$this->xml->createElement( $element );
$root->appendChild( $elementTag );
if ( isset
( $dataNode->type ) )
$this->addAttribute( $elementTag, 'type', $val );
$this->addAttribute( $elementTag, 'type', $val );
$this->addAttribute( $elementTag, 'xmlns:xhtml', 'http://www.w3.org/1999/xhtml' );
$xhtmlTag =
$this->xml->createElement( 'xhtml:div', $dataNode->__toString() );
$elementTag->appendChild( $xhtmlTag );
$this->addAttribute( $elementTag, 'type', $val );
// @todo: implement to assign the text in $dataNode as an XML node into $elementTag
$this->addAttribute( $elementTag, 'type', $val );
$this->addAttribute( $elementTag, 'type', $val );
else if ( $val !==
null )
// @todo: make 76 and "\n" options?
$this->addAttribute( $elementTag, 'type', $val );
$this->addAttribute( $elementTag, 'type', $val );
if ( $dataNode->src !==
null )
$this->addAttribute( $elementTag, 'src', $dataNode->src );
if ( $dataNode->language !==
null )
if ( $dataNode->type ===
'xhtml' )
$this->addAttribute( $elementTag->parentNode, 'xml:lang', $dataNode->language );
$this->addAttribute( $elementTag, 'xml:lang', $dataNode->language );
$elementTag->nodeValue =
$dataNode;
* Creates an XML person node in the XML document being generated.
* @param DOMNode $root The root in which to create the node $element
* @param string $element The name of the node to create
* @param ezcFeedPersonElement $feedElement The person feed element (author, contributor)
private function generatePerson( DOMNode $root, $element, ezcFeedPersonElement $feedElement )
$elementTag =
$this->xml->createElement( $element );
$root->appendChild( $elementTag );
$parentNode =
( $root->nodeName ===
'entry' ) ?
'/feed' :
'';
$parentNode =
( $root->nodeName ===
'source' ) ?
'/feed/entry' :
$parentNode;
$name =
$feedElement->name;
$email =
$feedElement->email;
$uri =
$feedElement->uri;
$this->generateMetaData( $elementTag, 'name', $name );
$this->generateMetaData( $elementTag, 'email', $email );
$this->generateMetaData( $elementTag, 'uri', $uri );
* Creates an XML source node in the XML document being generated.
* @param DOMNode $root The root in which to create the source node
* @param ezcFeedSourceElement $feedElement The feed source
private function generateSource( DOMNode $root, ezcFeedSourceElement $feedElement )
$elementTag =
$this->xml->createElement( 'source' );
$root->appendChild( $elementTag );
$elements =
array( 'id', 'title', 'updated',
'author', 'link', 'category',
'contributor', 'generator', 'icon',
'image', 'copyright', 'description' );
foreach ( $elements as $child )
$data =
$feedElement->$child;
$this->generateTextNode( $elementTag, 'title', $data );
$this->generateTextNode( $elementTag, 'subtitle', $data );
$this->generateTextNode( $elementTag, 'rights', $data );
foreach ( $data as $dataNode )
$this->generatePerson( $elementTag, 'contributor', $dataNode );
foreach ( $data as $dataNode )
$this->generatePerson( $elementTag, 'author', $dataNode );
$this->generateGenerator( $elementTag, $data );
foreach ( $data as $dataNode )
$this->generateLink( $elementTag, $dataNode );
foreach ( $data as $dataNode )
$this->generateCategory( $elementTag, $dataNode );
$this->generateMetaData( $elementTag, 'logo', $data );
$this->generateMetaData( $elementTag, 'icon', $data );
// Sample date: 2003-12-13T18:30:02-05:00
$this->generateMetaData( $elementTag, 'updated', $data->date->format( 'c' ) );
$this->generateMetaData( $elementTag, 'id', $data );
* Adds the feed entry elements to the XML document being generated.
* @throws ezcFeedRequiredMetaDataException
* if the required elements or attributes are not present
private function generateItems()
foreach ( $entries as $entry )
$entryTag =
$this->xml->createElement( 'entry' );
$this->channel->appendChild( $entryTag );
if ( !is_null( $entry->language ) )
$this->addAttribute( $entryTag, 'xml:lang', $entry->language );
$elements =
array( 'id', 'title', 'updated' );
foreach ( $elements as $element )
$data =
$entry->$element;
$this->generateMetaData( $entryTag, $element, $data );
$this->generateTextNode( $entryTag, $element, $data );
// Sample date: 2003-12-13T18:30:02-05:00
$this->generateMetaData( $entryTag, $element, $data->date->format( 'c' ) );
// ensure the ATOM rules are applied
$content =
$entry->content;
$summary =
$entry->description;
$contentPresent =
!is_null( $content );
$contentSrcPresent =
$contentPresent &&
is_object( $content ) &&
!is_null( $content->src );
if ( $contentPresent &&
is_object( $content )
&&
( in_array( $content->type, array( 'text', 'html', 'xhtml', null ) )
||
preg_match( '@[+/]xml$@i', $content->type ) !==
0
$summaryPresent =
!is_null( $summary );
$linkAlternatePresent =
false;
foreach ( $links as $link )
// if the rel attribute is not set or if it is "alternate"
// then there is at least one rel="alternate" link in the feed entry
if ( !isset
( $link->rel )
||
$link->rel ===
'alternate' )
$linkAlternatePresent =
true;
if ( !$linkAlternatePresent &&
!$summaryPresent )
if ( !$linkAlternatePresent )
if ( ( $contentSrcPresent ||
$contentBase64 ) &&
!$summaryPresent )
$elements =
array( 'author', 'content', 'link', 'description',
'category', 'contributor', 'published', 'copyright',
if ( $entry->link !==
null )
$this->checkLinks( $entryTag, $entry->link );
foreach ( $elements as $element )
$data =
$entry->$element;
$this->generateTextNode( $entryTag, 'summary', $data );
$this->generateTextNode( $entryTag, 'rights', $data );
$this->generateContentNode( $entryTag, $element, $data );
foreach ( $data as $dataNode )
$this->generatePerson( $entryTag, $element, $dataNode );
foreach ( $data as $dataNode )
$this->generateLink( $entryTag, $dataNode );
// Sample date: 2003-12-13T18:30:02-05:00
$this->generateMetaData( $entryTag, $element, $data->date->format( 'c' ) );
foreach ( $data as $dataNode )
$this->generateCategory( $entryTag, $dataNode );
$this->generateSource( $entryTag, $data );
// convert RSS2 enclosure elements in ATOM link elements
foreach ( $data as $dataNode )
$link->href =
$dataNode->url;
$link->length =
$dataNode->length;
$link->type =
$dataNode->type;
$link->rel =
'enclosure';
$this->generateLink( $entryTag, $link );
$this->generateItemModules( $entry, $entryTag );
* Parses the provided XML element object and stores it as a feed item in
* the provided ezcFeed object.
* @param ezcFeedEntryElement $element The feed element object that will contain the feed item
* @param DOMElement $xml The XML element object to parse
private function parseItem( ezcFeedEntryElement $element, DOMElement $xml )
foreach ( $xml->childNodes as $itemChild )
if ( $itemChild->nodeType ===
XML_ELEMENT_NODE )
$tagName =
$itemChild->tagName;
$element->$tagName =
$itemChild->textContent;
$this->parseTextNode( $element, $itemChild, 'title' );
$this->parseTextNode( $element, $itemChild, 'copyright' );
$this->parseTextNode( $element, $itemChild, 'description' );
$element->$tagName =
$itemChild->textContent;
$subElement =
$element->add( $tagName );
foreach ( $itemChild->childNodes as $subChild )
if ( $subChild->nodeType ===
XML_ELEMENT_NODE )
$subTagName =
$subChild->tagName;
if ( in_array( $subTagName, array( 'name', 'email', 'uri' ) ) )
$subElement->$subTagName =
$subChild->textContent;
$type =
$itemChild->getAttribute( 'type' );
$src =
$itemChild->getAttribute( 'src' );
$subElement =
$element->add( $tagName );
$nodes =
$itemChild->childNodes;
if ( $nodes instanceof
DOMNodeList )
for ( $i =
0; $i <
$nodes->length; $i++
)
if ( $nodes->item( $i ) instanceof
DOMElement )
$contentNode =
$nodes->item( $i );
$subElement->text =
$contentNode->nodeValue;
$subElement->type =
$type;
$subElement->text =
$itemChild->textContent;
$subElement->type =
$type;
$subElement->text =
$itemChild->textContent;
$subElement->type =
$type;
$subElement->text =
$itemChild->textContent;
foreach ( $itemChild->childNodes as $node )
if ( $node->nodeType ===
XML_ELEMENT_NODE )
$doc =
new DOMDocument( '1.0', 'UTF-8' );
$copyNode =
$doc->importNode( $node, true );
$doc->appendChild( $copyNode );
$subElement->text =
$doc->saveXML();
$subElement->type =
$type;
$subElement->text =
$itemChild->textContent;
$subElement->type =
$type;
$subElement->type =
$type;
$language =
$itemChild->getAttribute( 'xml:lang' );
if ( !empty( $language ) )
$subElement->language =
$language;
$subElement =
$element->add( $tagName );
$attributes =
array( 'href' =>
'href', 'rel' =>
'rel', 'hreflang' =>
'hreflang',
'type' =>
'type', 'title' =>
'title', 'length' =>
'length' );
foreach ( $attributes as $name =>
$alias )
if ( $itemChild->hasAttribute( $name ) )
$subElement->$alias =
$itemChild->getAttribute( $name );
$subElement =
$element->add( $tagName );
$attributes =
array( 'term' =>
'term', 'scheme' =>
'scheme', 'label' =>
'label' );
foreach ( $attributes as $name =>
$alias )
if ( $itemChild->hasAttribute( $name ) )
$subElement->$alias =
$itemChild->getAttribute( $name );
$subElement =
$element->add( $tagName );
$this->parseSource( $subElement, $itemChild );
$this->parseModules( $element, $itemChild, $tagName );
if ( $xml->hasAttribute( 'xml:lang' ) )
$element->language =
$xml->getAttribute( 'xml:lang' );
* Parses the provided XML element object $xml and stores it as a text element
* @param ezcFeed|ezcFeedEntryElement$feed The feed object in which to store the parsed XML element as a text element
* @param DOMElement $xml The XML element object to parse
* @param string $element The name of the feed element object that will contain the text
private function parseTextNode( $feed, DOMElement $xml, $element )
$type =
$xml->getAttribute( 'type' );
$nodes =
$xml->childNodes;
if ( $nodes instanceof
DOMNodeList )
$contentNode =
$nodes->item( 1 );
$feed->$element =
$contentNode->nodeValue;
$feed->$element =
$xml->nodeValue;
// same case as 'default'
$feed->$element =
$xml->nodeValue;
$feed->$element->type =
$type;
if ( $xml->hasAttribute( 'xml:lang' ) )
$feed->$element->language =
$xml->getAttribute( 'xml:lang' );
* Parses the provided XML element object and stores it as a feed person (author
* or contributor - based on $type) in the provided ezcFeedPersonElement object.
* @param ezcFeedPersonElement $element The feed element object that will contain the feed person
* @param DOMElement $xml The XML element object to parse
* @param string $type The type of the person (author, contributor)
private function parsePerson( $element, DOMElement $xml, $type )
foreach ( $xml->childNodes as $itemChild )
if ( $itemChild->nodeType ===
XML_ELEMENT_NODE )
$tagName =
$itemChild->tagName;
$element->$tagName =
$itemChild->textContent;
* Parses the provided XML element object and stores it as a feed source
* element in the provided ezcFeedSourceElement object.
* @param ezcFeedSourceElement $element The feed element object that will contain the feed source
* @param DOMElement $xml The XML element object to parse
private function parseSource( ezcFeedSourceElement $element, DOMElement $xml )
foreach ( $xml->childNodes as $sourceChild )
if ( $sourceChild->nodeType ===
XML_ELEMENT_NODE )
$tagName =
$sourceChild->tagName;
$this->parseTextNode( $element, $sourceChild, 'title' );
$this->parseTextNode( $element, $sourceChild, 'copyright' );
$this->parseTextNode( $element, $sourceChild, 'description' );
$subElement =
$element->add( 'id' );
$subElement->id =
$sourceChild->textContent;
$subElement =
$element->add( 'generator' );
$subElement->name =
$sourceChild->textContent;
$attributes =
array( 'uri' =>
'url', 'version' =>
'version' );
foreach ( $attributes as $name =>
$alias )
if ( $sourceChild->hasAttribute( $name ) )
$subElement->$alias =
$sourceChild->getAttribute( $name );
$subElement =
$element->add( 'image' );
$subElement->link =
$sourceChild->textContent;
$subElement =
$element->add( 'icon' );
$subElement->link =
$sourceChild->textContent;
$element->$tagName =
$sourceChild->textContent;
$subElement =
$element->add( $tagName );
$attributes =
array( 'term' =>
'term', 'scheme' =>
'scheme', 'label' =>
'label' );
foreach ( $attributes as $name =>
$alias )
if ( $sourceChild->hasAttribute( $name ) )
$subElement->$alias =
$sourceChild->getAttribute( $name );
$subElement =
$element->add( $tagName );
$attributes =
array( 'href' =>
'href', 'rel' =>
'rel', 'hreflang' =>
'hreflang',
'type' =>
'type', 'title' =>
'title', 'length' =>
'length' );
foreach ( $attributes as $name =>
$alias )
if ( $sourceChild->hasAttribute( $name ) )
$subElement->$alias =
$sourceChild->getAttribute( $name );
$subElement =
$element->add( $tagName );
$this->parsePerson( $subElement, $sourceChild, $tagName );