blob: 7d7263108ed17132940d04b3dda1d395d368e53f [file] [log] [blame]
<?php
/**
* File containing the ezcFeed 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
* under the License.
*
* @package Feed
* @version //autogentag//
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @filesource
*/
/**
* Class defining a feed.
*
* A feed has a type (eg. RSS1, RSS2 or ATOM). The feed type defines which
* processor is used to parse and generate that type.
*
* The following feed processors are supported by the Feed component:
* - ATOM ({@link ezcFeedAtom}) -
* {@link http://atompub.org/rfc4287.html RFC4287}
* - RSS1 ({@link ezcFeedRss1}) -
* {@link http://web.resource.org/rss/1.0/spec Specifications}
* - RSS2 ({@link ezcFeedRss2}) -
* {@link http://www.rssboard.org/rss-specification Specifications}
*
* A new processor can be defined by creating a class which extends the class
* {@link ezcFeedProcessor} and implements the interface {@link ezcFeedParser}.
* The new class needs to be added to the supported feed types list by calling
* the function {@link registerFeed}.
*
* The following modules are supported by the Feed component:
* - Content ({@link ezcFeedContentModule}) -
* {@link http://purl.org/rss/1.0/modules/content/ Specifications}
* - CreativeCommons ({@link ezcFeedCreativeCommonsModule}) -
* {@link http://backend.userland.com/creativeCommonsRssModule Specifications}
* - DublinCore ({@link ezcFeedDublinCoreModule}) -
* {@link http://dublincore.org/documents/dces/ Specifications}
* - Geo ({@link ezcFeedGeoModule}) -
* {@link http://www.w3.org/2003/01/geo/ Specifications}
* - GeoRss ({@link ezcFeedGeoRssModule}) -
* {@link http://www.georss.org/georss/ Specifications}
* - iTunes ({@link ezcFeedITunesModule}) -
* {@link http://www.apple.com/itunes/store/podcaststechspecs.html Specifications}
*
* A new module can be defined by creating a class which extends the class
* {@link ezcFeedModule}. The new class needs to be added to the supported modules
* list by calling {@link registerModule}.
*
* A feed object can be created in different ways:
* - by calling the constructor (with the optional feed type). Example:
*
* <code>
* $feed = new ezcFeed();
* </code>
*
* - by parsing an existing XML file or URL. The feed type of the resulting
* ezcFeed object will be autodetected. Example:
*
* <code>
* $feed = ezcFeed::parse( 'http://www.example.com/rss2.xml' ); // URL
* $feed = ezcFeed::parse( 'http://username:password@www.example.com/rss2.xml' ); // URL with HTTP authentication
* $feed = ezcFeed::parse( '/tmp/rss2.xml' ); // local file
* </code>
*
* - by parsing an XML document stored in a string variable. The feed type of
* the resulting ezcFeed object will be autodetected. Example:
*
* <code>
* $feed = ezcFeed::parseContent( $xmlString );
* </code>
*
* Parsing a feed (in the following examples $feed is an existing ezcFeed object):
* - get a value from the feed object. Example:
*
* <code>
* $title = $feed->title->__toString();
* </code>
*
* - iterate over the items in the feed. Example:
*
* <code>
* <?php
* // retrieve the titles from the feed items
* foreach ( $feed->item as $item )
* {
* $titles[] = $item->title->__toString();
* }
* </code>
*
* - parse a module. Example of parsing the Geo module (ezcFeedGeoModule):
*
* <code>
* <?php
* $locations = array();
* foreach ( $feed->item as $item )
* {
* if ( isset( $item->Geo ) )
* {
* $locations[] = array(
* 'title' => $item->title->__toString(),
* 'alt' => isset( $item->Geo->alt ) ? $item->Geo->alt->__toString() : null,
* 'lat' => isset( $item->Geo->lat ) ? $item->Geo->lat->__toString() : null,
* 'long' => isset( $item->Geo->long ) ? $item->Geo->long->__toString() : null
* );
* }
* }
* ?>
* </code>
*
* - iterate over the loaded modules in a feed item. Example:
*
* <code>
* <?php
* // display the names and namespaces of the modules loaded in the feed item $item
* foreach ( $item->getModules() as $moduleName => $module )
* {
* echo $moduleName . ':' . $module->getNamespace();
* }
* ?>
* </code>
*
* Generating a feed:
* - create a feed object. Example:
*
* <code>
* $feed = new ezcFeed();
* </code>
*
* - set a value to the feed object. Example:
*
* <code>
* $feed->title = 'News';
* </code>
*
* - add a new item to the feed. Example:
*
* <code>
* <?php
* $item = $feed->add( 'item' );
* $item->title = 'Item title';
* ?>
* </code>
*
* - add a new module to the feed item. Example:
*
* <code>
* <?php
* $item = $feed->add( 'item' );
* $module = $item->addModule( 'Content' );
* $content->encoded = 'text content which will be encoded';
* ?>
* </code>
*
* - generate an XML document from the {@link ezcFeed} object. The result
* string should be saved to a file, and a link to a file made accessible
* to users of the application. Example:
*
* <code>
* <?php
* $xmlAtom = $feed->generate( 'atom' );
* $xmlRss1 = $feed->generate( 'rss1' );
* $xmlRss2 = $feed->generate( 'rss2' );
* ?>
* </code>
*
* Note: Assigning values to feed elements should be done in a way that will not
* break the resulting XML document. In other words, encoding of special characters
* to HTML entities is not done by default, and the developer is responsible with
* calling htmlentities() himself when assigning values to feed elements. Example:
* if the feed title contains the "&" character, it is the responsability of the
* developer to encode it properly as "&amp;".
*
* Example of creating a feed with a user-defined type:
*
* <code>
* <?php
* ezcFeed::registerFeed( 'opml', 'myOpmlHandler');
*
* $feed = new ezcFeed();
* // add properties to $feed
*
* $xml = $feed->generate( 'opml' );
* ?>
* </code>
*
* In the above example, myOpmlHandler extends {@link ezcFeedProcessor} and
* implements {@link ezcFeedParser}.
*
* Example of creating a feed with a user-defined module:
*
* <code>
* <?php
* ezcFeed::registerModule( 'Slash', 'mySlashHandler', 'slash');
*
* $feed = new ezcFeed();
* $item = $feed->add( 'item' );
* $slash = $item->addModule( 'Slash' );
* // add properties for the Slash module to $slash
*
* $xml = $feed->generate( 'rss2' ); // or the feed type which is needed
* ?>
* </code>
*
* In the above example mySlashHandler extends {@link ezcFeedModule}.
*
* @property array(ezcFeedPersonElement) $author
* Author(s) of the feed. Equivalents:
* ATOM-author (required, multiple),
* RSS1-none,
* RSS2-managingEditor (optional, recommended, single).
* @property array(ezcFeedCategoryElement) $category
* Categories for the feed. Equivalents:
* ATOM-category (optional, multiple),
* RSS1-none,
* RSS2-category (optional, multiple).
* @property ezcFeedCloudElement $cloud
* Allows processes to register with a cloud to be notified of updates
* to the channel, implementing a lightweight publish-subscribe
* protocol for RSS feeds. Equivalents:
* ATOM-none,
* RSS1-none,
* RSS2-cloud (optional, not recommended, single).
* @property array(ezcFeedPersonElement) $contributor
* Contributor(s) for the feed. Equivalents:
* ATOM-contributor (optional, not recommended, multiple),
* RSS1-none,
* RSS2-none.
* @property ezcFeedTextElement $copyright
* Copyright information for the feed. Equivalents:
* ATOM-rights (optional, single),
* RSS1-none,
* RSS2-copyright (optional, single).
* @property ezcFeedTextElement $description
* A short description of the feed. Equivalents:
* ATOM-subtitle (required, single),
* RSS1-description (required, single),
* RSS2-description (required, single).
* @property ezcFeedTextElement $docs
* An URL that points to the documentation for the format used in the
* feed file. Equivalents:
* ATOM-none,
* RSS1-none,
* RSS2-docs (optional, not recommended, single) - usual value is
* {@link http://www.rssboard.org/rss-specification}.
* @property ezcFeedGeneratorElement $generator
* Indicates the software used to generate the feed. Equivalents:
* ATOM-generator (optional, single),
* RSS1-none,
* RSS2-generator (optional, single).
* @property ezcFeedImageElement $icon
* An icon for a feed, similar with favicon.ico for websites. Equivalents:
* ATOM-icon (optional, not recommended, single),
* RSS1-none,
* RSS2-none.
* @property ezcFeedIdElement $id
* A universally unique and permanent identifier for a feed. For
* example, it can be an Internet domain name. Equivalents:
* ATOM-id (required, single),
* RSS1-about (required, single),
* RSS2-id (optional, single).
* @property ezcFeedImageElement $image
* An image associated with the feed. Equivalents:
* ATOM-logo (optional, single),
* RSS1-image (optional, single),
* RSS2-image (optional, single).
* @property-read array(ezcFeedEntryElement) $item
* Feed items (entries). Equivalents:
* ATOM-entry (optional, recommended, multiple),
* RSS1-item (required, multiple),
* RSS2-item (required, multiple).
* @property ezcFeedTextElement $language
* The language for the feed. Equivalents:
* ATOM-xml:lang attribute for title, description, copyright, content,
* comments (optional, single) - accessed as language through ezcFeed,
* RSS1-none,
* RSS2-language (optional, single).
* @property array(ezcFeedLinkElement) $link
* URLs to the HTML websites corresponding to the channel. Equivalents:
* ATOM-link (required one link with rel='self', multiple),
* RSS1-link (required, single),
* RSS2-link (required, single).
* @property ezcFeedDateElement $published
* The time the feed was published. Equivalents:
* ATOM-none,
* RSS1-none,
* RSS2-pubDate (optional, not recommended, single).
* @property ezcFeedTextElement $rating
* The {@link http://www.w3.org/PICS/ PICS} rating for the channel. Equivalents:
* ATOM-none,
* RSS1-none,
* RSS2-rating (optional, not recommended, single).
* @property ezcFeedSkipDaysElement $skipDays
* A hint for aggregators telling them which days they can skip when
* reading the feed. Equivalents:
* ATOM-none,
* RSS1-none,
* RSS2-skipDays (optional, not recommended, single).
* @property ezcFeedSkipHoursElement $skipHours
* A hint for aggregators telling them which hours they can skip when
* reading the feed. Equivalents:
* ATOM-none,
* RSS1-none,
* RSS2-skipHours (optional, not recommended, single).
* @property ezcFeedTextInputElement $textInput
* Specifies a text input box that can be displayed with the feed. Equivalents:
* ATOM-none,
* RSS1-textinput (optional, not recommended, single),
* RSS2-textInput (optional, not recommended, single).
* @property ezcFeedTextElement $title
* Human readable title for the feed. For example, it can be the same
* as the website title. Equivalents:
* ATOM-title (required, single),
* RSS1-title (required, single),
* RSS2-title (required, single).
* @property ezcFeedTextElement $ttl
* Number of minutes that indicates how long a channel can be cached
* before refreshing from the source. Equivalents:
* ATOM-none,
* RSS1-none,
* RSS2-ttl (optional, not recommended, single).
* @property ezcFeedDateElement $updated
* The last time the feed was updated. Equivalents:
* ATOM-updated (required, single),
* RSS1-none,
* RSS2-lastBuildDate (optional, recommended, single).
* @property ezcFeedPersonElement $webMaster
* The email address of the webmaster responsible for the feed. Equivalents:
* ATOM-none,
* RSS1-none,
* RSS2-webMaster (optional, not recommended, single).
*
* @todo parse() and parseContent() should(?) handle common broken XML files
* (for example if the first line is not <?xml version="1.0"?>)
*
* @package Feed
* @version //autogentag//
* @mainclass
*/
class ezcFeed
{
/**
* The version of the feed generator, to be included in the generated feeds.
*/
const GENERATOR_VERSION = '//autogentag//';
/**
* The uri of the feed generator, to be included in the generated feeds.
*/
const GENERATOR_URI = 'http://ezcomponents.org/docs/tutorials/Feed';
/**
* Holds a list of all supported feed types.
*
* @var array(string=>string)
*/
private static $supportedFeedTypes = array();
/**
* Holds a list of all supported modules.
*
* @var array(string=>string)
*/
private static $supportedModules = array();
/**
* Holds a list of all supported modules prefixes.
*
* @var array(string=>string)
*/
private static $supportedModulesPrefixes = array();
/**
* Holds the feed type (eg. 'rss1').
*
* @var string
*/
private $feedType;
/**
* Holds the feed content type (eg. 'application/rss+xml').
*
* @var string
*/
private $contentType;
/**
* Holds the feed elements (ezcFeedElement).
*
* @var array(string=>mixed)
*/
private $elements;
/**
* Holds the modules used by this feed.
*
* @var array(ezcFeedModule)
*/
private $modules = array();
/**
* Creates a new feed object.
*
* The $type value is used when calling generate() without specifying a
* feed type to output.
*
* @throws ezcFeedUnsupportedTypeException
* if the feed type $type is not supported
*
* @param string $type The type of feed to create
*/
public function __construct( $type = null )
{
self::initSupportedTypes();
if ( $type !== null )
{
$type = strtolower( $type );
if ( !isset( self::$supportedFeedTypes[$type] ) )
{
throw new ezcFeedUnsupportedTypeException( $type );
}
$this->feedType = $type;
$className = self::$supportedFeedTypes[$type];
$this->contentType = constant( "{$className}::CONTENT_TYPE" );
}
// set default values
$version = ( ezcFeed::GENERATOR_VERSION === '//auto' . 'gentag//' ) ? 'dev' : ezcFeed::GENERATOR_VERSION;
$generator = $this->add( 'generator' );
$generator->name = 'eZ Components Feed';
$generator->version = $version;
$generator->url = ezcFeed::GENERATOR_URI;
}
/**
* Sets the property $name to $value.
*
* @param string $name The property name
* @param mixed $value The property value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'author':
case 'contributor':
case 'webMaster':
$element = $this->add( $name );
$element->name = $value;
break;
case 'title':
case 'description':
case 'docs':
case 'ttl':
case 'rating':
case 'language':
case 'copyright':
$element = $this->add( $name );
$element->text = $value;
break;
case 'generator':
$element = $this->add( $name );
$element->name = $value;
break;
case 'item':
$element = $this->add( $name );
break;
case 'published':
case 'updated':
$element = $this->add( $name );
$element->date = $value;
break;
case 'textInput':
$element = $this->add( $name );
break;
case 'skipDays':
$element = $this->add( $name );
break;
case 'skipHours':
$element = $this->add( $name );
break;
case 'link':
$element = $this->add( $name );
$element->href = $value;
break;
case 'generator':
$element = $this->add( $name );
$element->name = $value;
break;
case 'image':
case 'icon':
$element = $this->add( $name );
$element->link = $value;
break;
case 'id':
$element = $this->add( $name );
$element->id = $value;
break;
default:
$supportedModules = ezcFeed::getSupportedModules();
if ( isset( $supportedModules[$name] ) )
{
$this->setModule( $name, $value );
return;
}
}
}
/**
* Returns the value of property $name.
*
* @throws ezcBasePropertyNotFoundException
* If the property $name does not exist.
*
* @param string $name The property name
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'author':
case 'category':
case 'cloud':
case 'contributor':
case 'copyright':
case 'description':
case 'docs':
case 'generator':
case 'icon':
case 'id':
case 'image':
case 'item':
case 'language':
case 'link':
case 'published':
case 'rating':
case 'skipDays':
case 'skipHours':
case 'textInput':
case 'title':
case 'ttl':
case 'updated':
case 'webMaster':
if ( isset( $this->elements[$name] ) )
{
return $this->elements[$name];
}
break;
default:
$supportedModules = ezcFeed::getSupportedModules();
if ( isset( $supportedModules[$name] ) )
{
if ( $this->hasModule( $name ) )
{
return $this->getModule( $name );
}
else
{
throw new ezcFeedUndefinedModuleException( $name );
}
}
throw new ezcFeedUnsupportedModuleException( $name );
}
}
/**
* Returns if the property $name is set.
*
* @param string $name The property name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'author':
case 'category':
case 'cloud':
case 'contributor':
case 'copyright':
case 'description':
case 'docs':
case 'generator':
case 'icon':
case 'id':
case 'image':
case 'item':
case 'language':
case 'link':
case 'published':
case 'rating':
case 'skipDays':
case 'skipHours':
case 'textInput':
case 'title':
case 'ttl':
case 'updated':
case 'webMaster':
return isset( $this->elements[$name] );
default:
$supportedModules = ezcFeed::getSupportedModules();
if ( isset( $supportedModules[$name] ) )
{
return $this->hasModule( $name );
}
}
return false;
}
/**
* Adds a new module to this item and returns it.
*
* @param string $name The name of the module to add
* @return ezcFeedModule
*/
public function addModule( $name )
{
$this->$name = ezcFeedModule::create( $name, 'feed' );
return $this->$name;
}
/**
* Associates the module $module with the name $name.
*
* @param string $name The name of the module associate
* @param ezcFeedModule $module The module to set under the name $name
*/
public function setModule( $name, ezcFeedModule $module )
{
$this->modules[$name] = $module;
}
/**
* Returns the loaded module $name.
*
* @param string $name The name of the module to return
* @return ezcFeedModule
*/
public function getModule( $name )
{
return $this->modules[$name];
}
/**
* Returns true if the module $name is loaded, false otherwise.
*
* @param string $name The name of the module to check if loaded for this item
* @return bool
*/
public function hasModule( $name )
{
return isset( $this->modules[$name] );
}
/**
* Returns an array with all the modules loaded at feed-level.
*
* @return array(ezcFeedModule)
*/
public function getModules()
{
return $this->modules;
}
/**
* Adds a new ezcFeedElement element with name $name and returns it.
*
* @throws ezcFeedUnsupportedElementException
* if the element $name is not supported
*
* @param string $name The element name
* @return ezcFeedElement|null
*/
public function add( $name )
{
switch ( $name )
{
case 'item':
$element = new ezcFeedEntryElement();
$this->elements[$name][] = $element;
break;
case 'author':
case 'contributor':
case 'webMaster':
$element = new ezcFeedPersonElement();
$this->elements[$name][] = $element;
break;
case 'image':
case 'icon':
$element = new ezcFeedImageElement();
$this->elements[$name] = $element;
break;
case 'category':
$element = new ezcFeedCategoryElement();
$this->elements[$name][] = $element;
break;
case 'textInput':
$element = new ezcFeedTextInputElement();
$this->elements[$name] = $element;
break;
case 'title':
case 'description':
case 'copyright':
case 'rating':
case 'comments':
case 'ttl':
case 'language':
case 'docs':
$element = new ezcFeedTextElement();
$this->elements[$name] = $element;
break;
case 'skipDays':
$element = new ezcFeedSkipDaysElement();
$this->elements[$name] = $element;
break;
case 'skipHours':
$element = new ezcFeedSkipHoursElement();
$this->elements[$name] = $element;
break;
case 'link':
$element = new ezcFeedLinkElement();
$this->elements[$name][] = $element;
break;
case 'generator':
$element = new ezcFeedGeneratorElement();
$this->elements[$name] = $element;
break;
case 'cloud':
$element = new ezcFeedCloudElement();
$this->elements[$name] = $element;
break;
case 'id':
$element = new ezcFeedIdElement();
$this->elements[$name] = $element;
break;
case 'updated':
case 'published':
$element = new ezcFeedDateElement();
$this->elements[$name] = $element;
break;
default:
throw new ezcFeedUnsupportedElementException( $name );
}
return $element;
}
/**
* Generates and returns an XML document of type $type from the current
* object.
*
* If the type was defined when creating the ezcFeed object, then that
* type will be used if no type is specified when calling generate().
*
* If no type was specified when calling the constructor and no type
* was specified when calling generate then an exception will be thrown.
*
* @throws ezcFeedUnsupportedTypeException
* if the feed type $type is not supported
*
* @param string $type The feed type to generate
* @return string
*/
public function generate( $type = null )
{
if ( $this->feedType === null
&& $type === null )
{
throw new ezcFeedUnsupportedTypeException( null );
}
if ( $type !== null )
{
$type = strtolower( $type );
if ( !isset( self::$supportedFeedTypes[$type] ) )
{
throw new ezcFeedUnsupportedTypeException( $type );
}
}
if ( $type !== null )
{
$this->feedType = $type;
}
$className = self::$supportedFeedTypes[$this->feedType];
$generator = new $className( $this );
$this->contentType = constant( "{$className}::CONTENT_TYPE" );
return $generator->generate();
}
/**
* Parses the XML document in the $uri and returns an ezcFeed object with
* the type autodetected from the XML document.
*
* Example of parsing an XML document stored at an URL:
* <code>
* $feed = ezcFeed::parse( 'http://www.example.com/rss2.xml' );
* </code>
*
* Example of parsing an XML document protected with HTTP authentication:
* <code>
* $feed = ezcFeed::parse( 'http://username:password@www.example.com/rss2.xml' );
* </code>
*
* If trying to parse an XML document protected with HTTP authentication
* without providing a valid username and password, the exception
* {@link ezcFeedParseErrorException} will be thrown.
*
* Example of parsing an XML document stored in a local file:
* <code>
* $feed = ezcFeed::parse( '/tmp/feed.xml' );
* </code>
*
* @throws ezcBaseFileNotFoundException
* If the XML file at $uri could not be found.
* @throws ezcFeedParseErrorException
* If the content at $uri is not a valid XML document.
*
* @param string $uri An URI which stores an XML document
* @return ezcFeed
*/
public static function parse( $uri )
{
if ( !file_exists( $uri ) )
{
// hide the notices caused by getaddrinfo (php_network_getaddresses)
// in case of unreachable hosts ("Name or service not known")
$headers = @get_headers( $uri );
// HTTP headers
// 200 = OK
// 301 = moved permanently
// 302 = found
// 307 = temporary redirect
if ( preg_match( "@200|301|302|307@", $headers[0] ) === 0 )
{
throw new ezcBaseFileNotFoundException( $uri );
}
}
$xml = new DOMDocument();
$oldSetting = libxml_use_internal_errors( true );
$retval = $xml->load( $uri );
libxml_use_internal_errors( $oldSetting );
if ( $retval === false )
{
libxml_clear_errors();
throw new ezcFeedParseErrorException( $uri, "It is not a valid XML file" );
}
return self::dispatchXml( $xml );
}
/**
* Parses the XML document stored in $content and returns an ezcFeed object
* with the type autodetected from the XML document.
*
* Example of parsing an XML document stored in a string:
* <code>
* // $xmlString contains a valid XML string
* $feed = ezcFeed::parseContent( $xmlString );
* </code>
*
* @throws ezcFeedParseErrorException
* If $content is not a valid XML document.
*
* @param string $content A string variable which stores an XML document
* @return ezcFeed
*/
public static function parseContent( $content )
{
if ( empty( $content ) )
{
throw new ezcFeedParseErrorException( null, "Content is empty" );
}
$xml = new DOMDocument();
$oldSetting = libxml_use_internal_errors( true );
$retval = $xml->loadXML( $content );
libxml_use_internal_errors( $oldSetting );
if ( $retval === false )
{
libxml_clear_errors();
throw new ezcFeedParseErrorException( null, "Content is no valid XML" );
}
return self::dispatchXml( $xml );
}
/**
* Returns the supported feed types.
*
* The array returned is (default):
* <code>
* array(
* 'rss1' => 'ezcFeedRss1',
* 'rss2' => 'ezcFeedRss2',
* 'atom' => 'ezcFeedAtom'
* );
* </code>
*
* If the function {@link registerFeed} was used to add another supported feed
* type to ezcFeed, it will show up in the returned array as well.
*
* @return array(string)
*/
public static function getSupportedTypes()
{
return self::$supportedFeedTypes;
}
/**
* Returns the supported feed modules.
*
* The array returned is (default):
* <code>
* array(
* 'Content' => 'ezcFeedContentModule',
* 'CreativeCommons' => 'ezcFeedCreativeCommonsModule',
* 'DublinCore' => 'ezcFeedDublinCoreModule',
* 'Geo' => 'ezcFeedGeoModule',
* 'GeoRss' => 'ezcFeedGeoRssModule',
* 'iTunes' => 'ezcFeedITunesModule'
* );
* </code>
*
* If the function {@link registerModule} was used to add another supported
* module type to ezcFeed, it will show up in the returned array as well.
*
* @return array(string=>string)
*/
public static function getSupportedModules()
{
return self::$supportedModules;
}
/**
* Returns the supported feed modules prefixes.
*
* The array returned is (default):
* <code>
* array(
* 'content' => 'Content',
* 'creativeCommons' => 'CreativeCommons',
* 'dc' => 'DublinCore',
* 'geo' => 'Geo',
* 'georss' => 'GeoRss',
* 'itunes' => 'iTunes'
* );
* </code>
*
* If the function {@link registerModule} was used to add another supported
* module type to ezcFeed, it will show up in the returned array as well.
*
* @return array(string=>string)
*/
public static function getSupportedModulesPrefixes()
{
return self::$supportedModulesPrefixes;
}
/**
* Returns the feed type of this feed object (eg. 'rss2').
*
* @return string
*/
public function getFeedType()
{
return $this->feedType;
}
/**
* Returns the feed content type of this feed object
* (eg. 'application/rss+xml').
*
* @return string
*/
public function getContentType()
{
return $this->contentType;
}
/**
* Adds the feed type $name to the supported list of feed types.
*
* After registering a feed type, it can be used to create or parse feed
* documents.
*
* Example of creating a feed with a user-defined type:
* <code>
* ezcFeed::registerFeed( 'opml', 'myOpmlHandler');
*
* $feed = new ezcFeed( 'opml' );
* // add properties for the Opml feed type to $feed
* </code>
*
* In the above example, myOpmlHandler extends {@link ezcFeedProcessor}
* and implements {@link ezcFeedParser}.
*
* @param string $name The feed type (eg. 'opml' )
* @param string $class The handler class for this feed type (eg. 'myOpmlHandler')
*/
public static function registerFeed( $name, $class )
{
self::$supportedFeedTypes[$name] = $class;
}
/**
* Removes a previously registered feed type from the list of supported
* feed types.
*
* @param string $name The name of the feed type to remove (eg. 'opml')
*/
public static function unregisterFeed( $name )
{
if ( isset( self::$supportedFeedTypes[$name] ) )
{
unset( self::$supportedFeedTypes[$name] );
}
}
/**
* Adds the module $name to the supported list of modules.
*
* After registering a module, it can be used to create or parse feed
* documents.
*
* Example of creating a feed with a user-defined module:
* <code>
* ezcFeed::registerModule( 'Slash', 'mySlashHandler', 'slash');
*
* $feed = new ezcFeed( 'rss2' );
* $item = $feed->add( 'item' );
* $slash = $item->addModule( 'Slash' );
* // add properties for the Slash module to $slash
* </code>
*
* @param string $name The module name (eg. 'Slash' )
* @param string $class The handler class for this module (eg. 'mySlashHandler')
* @param string $namespacePrefix The XML namespace prefix for this module (eg. 'slash')
*/
public static function registerModule( $name, $class, $namespacePrefix )
{
self::$supportedModules[$name] = $class;
self::$supportedModulesPrefixes[$namespacePrefix] = $name;
}
/**
* Removes a previously registered module from the list of supported modules.
*
* @param string $name The name of the module to remove (eg. 'Slash')
*/
public static function unregisterModule( $name )
{
if ( isset( self::$supportedModules[$name] ) )
{
$namePrefix = null;
foreach ( self::$supportedModulesPrefixes as $prefix => $module )
{
if ( $module === $name )
{
$namePrefix = $prefix;
break;
}
}
unset( self::$supportedModulesPrefixes[$prefix] );
unset( self::$supportedModules[$name] );
}
}
/**
* Parses the $xml object by dispatching it to the processor that can
* handle it.
*
* @throws ezcFeedParseErrorException
* If the $xml object could not be parsed by any available processor.
*
* @param DOMDocument $xml The XML object to parse
* @return ezcFeed
*/
private static function dispatchXml( DOMDocument $xml )
{
if ( count( self::getSupportedTypes() ) === 0 )
{
self::initSupportedTypes();
}
foreach ( self::getSupportedTypes() as $feedType => $feedClass )
{
$canParse = call_user_func( array( $feedClass, 'canParse' ), $xml );
if ( $canParse === true )
{
$feed = new ezcFeed( $feedType );
$parser = new $feedClass( $feed );
return $parser->parse( $xml );
}
}
throw new ezcFeedParseErrorException( $xml->documentURI, 'Feed type not recognized' );
}
/**
* Initializes the supported feed types and modules to the default values.
*/
private static function initSupportedTypes()
{
self::registerFeed( 'rss1', 'ezcFeedRss1' );
self::registerFeed( 'rss2', 'ezcFeedRss2' );
self::registerFeed( 'atom', 'ezcFeedAtom' );
self::registerModule( 'Content', 'ezcFeedContentModule', 'content' );
self::registerModule( 'CreativeCommons', 'ezcFeedCreativeCommonsModule', 'creativeCommons' );
self::registerModule( 'DublinCore', 'ezcFeedDublinCoreModule', 'dc' );
self::registerModule( 'Geo', 'ezcFeedGeoModule', 'geo' );
self::registerModule( 'GeoRss', 'ezcFeedGeoRssModule', 'georss' );
self::registerModule( 'iTunes', 'ezcFeedITunesModule', 'itunes' );
}
}
?>