| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!-- Reviewed: no -->
- <sect1 id="zend.feed.pubsubhubbub.introduction">
- <title>Zend_Feed_Pubsubhubbub</title>
- <para>
- <classname>Zend_Feed_Pubsubhubbub</classname> is an implementation of the PubSubHubbub Core
- 0.2 Specification (Working Draft). It offers implementations of a Pubsubhubbub Publisher and
- Subscriber suited to Zend Framework and other <acronym>PHP</acronym> applications.
- </para>
- <sect2 id="zend.feed.pubsubhubbub.what.is.pubsubhubbub">
- <title>What is Pubsubhubbub?</title>
- <para>
- Pubsubhubbub is an open, simple web-scale pubsub protocol. A common use case to enable
- blogs (Publishers) to "push" updates from their <acronym>RSS</acronym> or Atom feeds
- (Topics) to end Subscribers. These Subscribers will have subscribed to the blog's
- <acronym>RSS</acronym> or Atom feed via a Hub, a central server which is notified of any
- updates by the Publisher and which then distributes these updates to all Subscribers.
- Any feed may advertise that it supports one or more Hubs using an Atom namespaced link
- element with a rel attribute of "hub".
- </para>
- <para>
- Pubsubhubbub has garnered attention because it is a pubsub protocol which is easy to
- implement and which operates over <acronym>HTTP</acronym>. Its philosophy is to replace
- the traditional model where blog feeds have been polled at regular intervals to detect
- and retrieve updates. Depending on the frequency of polling, this can take a lot of time
- to propagate updates to interested parties from planet aggregators to desktop readers.
- With a pubsub system in place, updates are not simply polled by Subscribers, they are
- pushed to Subscribers, elimenating any delay. For this reason, Pubsubhubbub forms part
- of what has been dubbed the real-time web.
- </para>
- <para>
- The protocol does not exist in isolation. Pubsub systems have been around for a while,
- such as the familiar Jabber Publish-Subscribe protocol, <acronym>XEP-0060</acronym>, or
- the less well known rssCloud (described in 2001). However these have not achieved
- widespread adoption typically due to either their complexity, poor timing or lack of
- suitability for web applications. rssCloud, which was recently revived as a response to
- the appearance of Pubsubhubbub, has also seen its usage increase significantly though it
- lacks a formal specification and currently does not support Atom 1.0 feeds.
- </para>
- <para>
- Perhaps surprisingly given its relative early age, Pubsubhubbub is already in use
- including in Google Reader, Feedburner, and there are plugins available for Wordpress
- blogs.
- </para>
- </sect2>
- <sect2 id="zend.feed.pubsubhubbub.architecture">
- <title>Architecture</title>
- <para>
- <classname>Zend_Feed_Pubsubhubbub</classname> implements two sides of the Pubsubhubbub
- 0.2 Specification: a Publisher and a Subscriber. It does not currently implement a Hub
- Server though this is in progress for a future Zend Framework release.
- </para>
- <para>
- A Publisher is responsible for notifying all supported Hubs (many can be supported to
- add redundancy to the system) of any updates to its feeds, whether they be Atom or
- <acronym>RSS</acronym> based. This is achieved by pinging the supported Hub Servers with
- the <acronym>URL</acronym> of the updated feed. In Pubsubhubbub terminology, any
- updatable resource capable of being subscribed to is referred to as a Topic. Once a ping
- is received, the Hub will request the updated feed, process it for updated items, and
- forward all updates to all Subscribers subscribed to that feed.
- </para>
- <para>
- A Subscriber is any party or application which subscribes to one or more Hubs to receive
- updates from a Topic hosted by a Publisher. The Subscriber never directly communicates
- with the Publisher since the Hub acts as an intermediary, accepting subscriptions and
- sending updates to subscribed Subscribers. The Subscriber therefore communicates only
- with the Hub, either to subscribe or unsubscribe to Topics, or when it receives updates
- from the Hub. This communication design ("Fat Pings") effectively removes the
- possibility of a "Thundering Herd" issue. This occurs in a pubsub system where the Hub
- merely informs Subscribers that an update is available, prompting all Subscribers to
- immediately retrieve the feed from the Publisher giving rise to a traffic spike. In
- Pubsubhubbub, the Hub distributes the actual update in a "Fat Ping" so the Publisher is
- not subjected to any traffic spike.
- </para>
- <para>
- <classname>Zend_Feed_Pubsubhubbub</classname> implements Pubsubhubbub Publishers and
- Subscribers with the
- classes <classname>Zend_Feed_Pubsubhubbub_Publisher</classname> and
- <classname>Zend_Feed_Pubsubhubbub_Subscriber</classname>. In addition, the Subscriber
- implementation may handle any feed updates forwarded from a Hub by using
- <classname>Zend_Feed_Pubsubhubbub_Subscriber_Callback</classname>. These classes, their
- use cases, and <acronym>API</acronym>s are covered in subsequent sections.
- </para>
- </sect2>
- <sect2 id="zend.feed.pubsubhubbub.zend.feed.pubsubhubbub.publisher">
- <title>Zend_Feed_Pubsubhubbub_Publisher</title>
- <para>
- In Pubsubhubbub, the Publisher is the party who publishes a live feed and frequently
- updates it with new content. This may be a blog, an aggregator, or even a web service
- with a public feed based <acronym>API</acronym>. In order for these updates to be pushed
- to Subscribers, the Publisher must notify all of its supported Hubs that an update has
- occured using a simple <acronym>HTTP</acronym> <acronym>POST</acronym> request
- containing the <acronym>URI</acronym> or the updated Topic (i.e the updated
- <acronym>RSS</acronym> or Atom feed). The Hub will confirm receipt of the notification,
- fetch the updated feed, and forward any updates to any Subscribers who have subscribed
- to that Hub for updates from the relevant feed.
- </para>
- <para>
- By design, this means the Publisher has very little to do except send these Hub pings
- whenever its feeds change. As a result, the Publisher implementation is extremely
- simple to use and requires very little work to setup and use when feeds are updated.
- </para>
- <para>
- <classname>Zend_Feed_Pubsubhubbub_Publisher</classname> implements a full Pubsubhubbub
- Publisher. Its setup for use is also simple, requiring mainly that it is configured with
- the <acronym>URI</acronym> endpoint for all Hubs to be notified of updates, and the
- <acronym>URI</acronym>s of all Topics to be included in the notifications.
- </para>
- <para>
- The following example shows a Publisher notifying a collection of Hubs about updates to
- a pair of local <acronym>RSS</acronym> and Atom feeds. The class retains a collection of
- errors which include the Hub <acronym>URL</acronym>s, so the notification can be
- re-attempted later and/or logged if any notifications happen to fail. Each resulting
- error array also includes a "response" key containing the related
- <acronym>HTTP</acronym> response object. In the event of any errors, it is strongly
- recommended to attempt the operation for failed Hub Endpoints at least once more at a
- future time. This may require the use of either a scheduled task for this purpose or a
- job queue though such extra steps are optional.
- </para>
- <programlisting language="php"><![CDATA[
- $publisher = new Zend_Feed_Pubsubhubbub_Publisher;
- $publisher->addHubUrls(array(
- 'http://pubsubhubbub.appspot.com/',
- 'http://hubbub.example.com',
- ));
- $publisher->addUpdatedTopicUrls(array(
- 'http://www.example.net/rss',
- 'http://www.example.net/atom',
- ));
- $publisher->notifyAll();
- if (!$publisher->isSuccess()) {
- // check for errors
- $errors = $publisher->getErrors();
- $failedHubs = array()
- foreach ($errors as $error) {
- $failedHubs[] = $error['hubUrl'];
- }
- }
- // reschedule notifications for the failed Hubs in $failedHubs
- ]]></programlisting>
- <para>
- If you prefer having more concrete control over the Publisher, the methods
- <methodname>addHubUrls()</methodname> and <methodname>addUpdatedTopicUrls()</methodname>
- pass each array value to the singular <methodname>addHubUrl()</methodname> and
- <methodname>addUpdatedTopicUrl()</methodname> public methods. There are also matching
- <methodname>removeUpdatedTopicUrl()</methodname> and
- <methodname>removeHubUrl()</methodname> methods.
- </para>
- <para>
- You can also skip setting Hub <acronym>URI</acronym>s, and notify each in turn using the
- <methodname>notifyHub()</methodname> method which accepts the <acronym>URI</acronym> of
- a Hub endpoint as its only argument.
- </para>
- <para>
- There are no other tasks to cover. The Publisher implementation is very simple since
- most of the feed processing and distribution is handled by the selected Hubs. It is
- however important to detect errors and reschedule notifications as soon as possible
- (with a reasonable maximum number of retries) to ensure notifications reach all
- Subscribers. In many cases as a final alternative, Hubs may frequently poll your
- feeds to offer some additional tolerance for failures both in terms of their own
- temporary downtime or Publisher errors or downtime.
- </para>
- </sect2>
- <sect2 id="zend.feed.pubsubhubbub.zend.feed.pubsubhubbub.subscriber">
- <title>Zend_Feed_Pubsubhubbub_Subscriber</title>
- <para>
- In Pubsubhubbub, the Subscriber is the party who wishes to receive updates to any Topic
- (<acronym>RSS</acronym> or Atom feed). They achieve this by subscribing to one or more
- of the Hubs advertised by that Topic, usually as a set of one or more Atom 1.0 links
- with a rel attribute of "hub". The Hub from that point forward will send an Atom or
- <acronym>RSS</acronym> feed containing all updates to that Subscriber's Callback
- <acronym>URL</acronym> when it receives an update notification from the Publisher. In
- this way, the Subscriber need never actually visit the original feed (though it's still
- recommended at some level to ensure updates are retrieved if ever a Hub goes offline).
- All subscription requests must contain the <acronym>URI</acronym> of the Topic being
- subscribed and a Callback <acronym>URL</acronym> which the Hub will use to confirm the
- subscription and to forward updates.
- </para>
- <para>
- The Subsciber therefore has two roles. To create and manage subscriptions, including
- subscribing for new Topics with a Hub, unsubscribing (if necessary), and periodically
- renewing subscriptions since they may have a limited validity as set by the Hub. This is
- handled by <classname>Zend_Feed_Pubsubhubbub_Subscriber</classname>.
- </para>
- <para>
- The second role is to accept updates sent by a Hub to the Subscriber's Callback
- <acronym>URL</acronym>, i.e. the <acronym>URI</acronym> the Subscriber has assigned to
- handle updates. The Callback <acronym>URL</acronym> also handles events where the Hub
- contacts the Subscriber to confirm all subscriptions and unsubscriptions. This is
- handled by using an instance of
- <classname>Zend_Feed_Pubsubhubbub_Subscriber_Callback</classname> when the Callback
- <acronym>URL</acronym> is accessed.
- </para>
- <important>
- <para>
- <classname>Zend_Feed_Pubsubhubbub_Subscriber</classname> implements the Pubsubhubbub
- 0.2 Specification. As this is a new specification version not all Hubs currently
- implement it. The new specification allows the Callback <acronym>URL</acronym> to
- include a query string which is used by this class, but not supported by all Hubs.
- In the interests of maximising compatibility it is therefore recommended that the
- query string component of the Subscriber Callback <acronym>URI</acronym> be
- presented as a path element, i.e. recognised as a parameter in the route associated
- with the Callback <acronym>URI</acronym> and used by the application's Router.
- </para>
- </important>
- <sect3
- id="zend.feed.pubsubhubbub.zend.feed.pubsubhubbub.subscriber.subscribing.and.unsubscribing">
- <title>Subscribing and Unsubscribing</title>
- <para>
- <classname>Zend_Feed_Pubsubhubbub_Subscriber</classname> implements a full
- Pubsubhubbub Subscriber capable of subscribing to, or unsubscribing from, any Topic
- via any Hub advertised by that Topic. It operates in conjunction with
- <classname>Zend_Feed_Pubsubhubbub_Subscriber_Callback</classname> which accepts
- requests from a Hub to confirm all subscription or unsubscription attempts (to
- prevent third-party misuse).
- </para>
- <para>
- Any subscription (or unsubscription) requires the relevant information before
- proceeding, i.e. the <acronym>URI</acronym> of the Topic (Atom or
- <acronym>RSS</acronym> feed) to be subscribed to for updates, and the
- <acronym>URI</acronym> of the endpoint for the Hub which will handle the
- subscription and forwarding of the updates. The lifetime of a subscription may
- be determined by the Hub but most Hubs should support automatic subscription
- refreshes by checking with the Subscriber. This is supported by
- <classname>Zend_Feed_Pubsubhubbub_Subscriber_Callback</classname> and requires no
- other work on your part. It is still strongly recommended that you use the Hub
- sourced subscription time to live (ttl) to schedule the creation of new
- subscriptions (the process is identical to that for any new subscription) to refresh
- it with the Hub. While it should not be necessary per se, it covers cases where a
- Hub may not support automatic subscription refreshing and rules out Hub errors for
- additional redundancy.
- </para>
- <para>
- With the relevant information to hand, a subscription can be attempted as
- demonstrated below:
- </para>
- <programlisting language="php"><![CDATA[
- $storage = new Zend_Feed_Pubsubhubbub_Model_Subscription;
- $subscriber = new Zend_Feed_Pubsubhubbub_Subscriber;
- $subscriber->setStorage($storage);
- $subscriber->addHubUrl('http://hubbub.example.com');
- $subscriber->setTopicUrl('http://www.example.net/rss.xml');
- $subscriber->setCallbackUrl('http://www.mydomain.com/hubbub/callback');
- $subscriber->subscribeAll();
- ]]></programlisting>
- <para>
- In order to store subscriptions and offer access to this data for general use,
- the component requires a database (a schema is provided later in this section).
- By default, it is assumed the table name is "subscription" and it utilises
- <classname>Zend_Db_Table_Abstract</classname> in the background meaning it
- will use the default adapter you have set for your application. You may also
- pass a specific custom <classname>Zend_Db_Table_Abstract</classname> instance
- into the associated model
- <classname>Zend_Feed_Pubsubhubbub_Model_Subscription</classname>. This custom
- adapter may be as simple in intent as changing the table name to use or as complex
- as you deem necessary.
- </para>
- <para>
- While this Model is offered as a default ready-to-roll solution, you may create your
- own Model using any other backend or database layer (e.g. Doctrine) so long as the
- resulting class implements the interface
- <classname>Zend_Feed_Pubsubhubbub_Model_SubscriptionInterface</classname>.
- </para>
- <para>
- An example schema (MySQL) for a subscription table accessible by the provided model
- may look similar to:
- </para>
- <programlisting language="sql"><![CDATA[
- CREATE TABLE IF NOT EXISTS `subscription` (
- `id` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
- `topic_url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
- `hub_url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
- `created_time` datetime DEFAULT NULL,
- `lease_seconds` bigint(20) DEFAULT NULL,
- `verify_token` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
- `secret` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
- `expiration_time` datetime DEFAULT NULL,
- `subscription_state` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
- ]]></programlisting>
- <para>
- Behind the scenes, the Subscriber above will send a request to the Hub endpoint
- containing the following parameters (based on the previous example):
- </para>
- <table
- id="zend.feed.pubsubhubbub.zend.feed.pubsubhubbub.subscriber.subscribing.and.unsubscribing.table">
- <title>Subscription request parameters</title>
- <tgroup cols="3">
- <thead>
- <row>
- <entry>Parameter</entry>
- <entry>Value</entry>
- <entry>Explanation</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry><property>hub.callback</property></entry>
- <entry>http://www.mydomain.com/hubbub/callback?xhub.subscription=5536df06b5dcb966edab3a4c4d56213c16a8184</entry>
- <entry>
- <para>
- The <acronym>URI</acronym> used by a Hub to contact the
- Subscriber and either request confirmation of a (un)subscription
- request or send updates from subscribed feeds. The appended
- query string contains a custom parameter (hence the xhub
- designation). It is a query string parameter preserved by the
- Hub and resent with all Subscriber requests. Its purpose is to
- allow the Subscriber to identify and look up the subscription
- associated with any Hub request in a backend storage medium.
- This is a non-standard parameter used by this component in
- preference to encoding a subscription key in the
- <acronym>URI</acronym> path which is more difficult to implement
- in a Zend Framework application.
- </para>
- <para>
- Nevertheless, since not all Hubs support query string
- parameters, we still strongly recommend adding the subscription
- key as a path component in the form
- http://www.mydomain.com/hubbub/callback/5536df06b5dcb966edab3a4c4d56213c16a8184.
- To accomplish this, it requires defining a route capable of
- parsing out the final value of the key and then retrieving the
- value and passing it to the Subscriber Callback object. The
- value would be passed into the method
- <methodname>Zend_Pubsubhubbub_Subscriber_Callback::setSubscriptionKey()</methodname>.
- A detailed example is offered later.
- </para>
- </entry>
- </row>
- <row>
- <entry><property>hub.lease_seconds</property></entry>
- <entry>2592000</entry>
- <entry>
- <para>
- The number of seconds for which the Subscriber would like a new
- subscription to remain valid for (i.e. a
- <acronym>TTL</acronym>). Hubs may enforce their own maximum
- subscription period. All subscriptions should be renewed by
- simply re-subscribing before the subscription period ends to
- ensure continuity of updates. Hubs should additionally attempt
- to automatically refresh subscriptions before they expire by
- contacting Subscribers (handled automatically by the Callback
- class).
- </para>
- </entry>
- </row>
- <row>
- <entry><property>hub.mode</property></entry>
- <entry>subscribe</entry>
- <entry>
- <para>
- Simple value indicating this is a subscription request.
- Unsubscription requests would use the "unsubscribe" value.
- </para>
- </entry>
- </row>
- <row>
- <entry><property>hub.topic</property></entry>
- <entry>http://www.example.net/rss.xml</entry>
- <entry>
- <para>
- The <acronym>URI</acronym> of the topic (i.e. Atom or
- <acronym>RSS</acronym> feed) which the Subscriber wishes to
- subscribe to for updates.
- </para>
- </entry>
- </row>
- <row>
- <entry><property>hub.verify</property></entry>
- <entry>sync</entry>
- <entry>
- <para>
- Indicates to the Hub the preferred mode of verifying
- subscriptions or unsubscriptions. It is repeated twice in order
- of preference. Technically this component does not distinguish
- between the two modes and treats both equally.
- </para>
- </entry>
- </row>
- <row>
- <entry><property>hub.verify</property></entry>
- <entry>async</entry>
- <entry>
- <para>
- Indicates to the Hub the preferred mode of verifying
- subscriptions or unsubscriptions. It is repeated twice in order
- of preference. Technically this component does not distinguish
- between the two modes and treats both equally.
- </para>
- </entry>
- </row>
- <row>
- <entry><property>hub.verify_token</property></entry>
- <entry>3065919804abcaa7212ae89.879827871253878386</entry>
- <entry>
- <para>
- A verification token returned to the Subscriber by the Hub when
- it is confirming a subscription or unsubscription. Offers a
- measure of reliance that the confirmation request originates
- from the correct Hub to prevent misuse.
- </para>
- </entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- <para>
- You can modify several of these parameters to indicate a different preference. For
- example, you can set a different lease seconds value using
- <methodname>Zend_Pubsubhubbub_Subscriber::setLeaseSeconds()</methodname> or show a
- preference for the async verify mode by using
- <methodname>setPreferredVerificationMode(Zend_Feed_Pubsubhubbub::VERIFICATION_MODE_ASYNC)</methodname>.
- However the Hubs retain the capability to enforce their own preferences and for this
- reason the component is deliberately designed to work across almost any set of
- options with minimum end-user configuration required. Conventions are great when
- they work!
- </para>
- <note>
- <para>
- While Hubs may require the use of a specific verification mode (both are
- supported by <classname>Zend_Pubsubhubbub</classname>), you may indicate a
- specific preference using the
- <methodname>setPreferredVerificationMode()</methodname> method. In "sync"
- (synchronous) mode, the Hub attempts to confirm a subscription as soon as it is
- received, and before responding to the subscription request. In "async"
- (asynchronous) mode, the Hub will return a response to the subscription request
- immediately, and its verification request may occur at a later time. Since
- <classname>Zend_Pubsubhubbub</classname> implements the Subscriber verification
- role as a separate callback class and requires the use of a backend storage
- medium, it actually supports both transparently though in terms of end-user
- performance, asynchronous verification is very much preferred to eliminate the
- impact of a poorly performing Hub tying up end-user server resources and
- connections for too long.
- </para>
- </note>
- <para>
- Unsubscribing from a Topic follows the exact same pattern as the previous example,
- with the exception that we should call <methodname>unsubscribeAll()</methodname>
- instead. The parameters included are identical to a subscription request with the
- exception that "<property>hub.mode</property>" is set to "unsubscribe".
- </para>
- <para>
- By default, a new instance of <classname>Zend_Pubsubhubbub_Subscriber</classname>
- will attempt to use a database backed storage medium which defaults to using the
- default <classname>Zend_Db</classname> adapter with a table name of "subscription".
- It is recommended to set a custom storage solution where these defaults are not apt
- either by passing in a new Model supporting the required interface or by passing a
- new instance of <classname>Zend_Db_Table_Abstract</classname> to the default Model's
- constructor to change the used table name.
- </para>
- </sect3>
- <sect3 id="zend.feed.pubsubhubbub.zend.feed.pubsubhubbub.subscriber.handling.hub.callbacks">
- <title>Handling Subscriber Callbacks</title>
- <para>
- Whenever a subscription or unsubscription request is made, the Hub must verify the
- request by forwarding a new verification request to the Callback
- <acronym>URL</acronym> set in the subscription or unsubscription parameters. To
- handle these Hub requests, which will include all future communications containing
- Topic (feed) updates, the Callback <acronym>URL</acronym> should trigger the
- execution of an instance of
- <classname>Zend_Pubsubhubbub_Subscriber_Callback</classname> to handle the request.
- </para>
- <para>
- The Callback class should be configured to use the same storage medium as the
- Subscriber class. Using it is quite simple since most of its work is performed
- internally.
- </para>
- <programlisting language="php"><![CDATA[
- $storage = new Zend_Feed_Pubsubhubbub_Model_Subscription;
- $callback = new Zend_Feed_Pubsubhubbub_Subscriber_Callback;
- $callback->setStorage($storage);
- $callback->handle();
- $callback->sendResponse();
- /**
- * Check if the callback resulting in the receipt of a feed update.
- * Otherwise it was either a (un)sub verification request or invalid request.
- * Typically we need do nothing other than add feed update handling - the rest
- * is handled internally by the class.
- */
- if ($callback->hasFeedUpdate()) {
- $feedString = $callback->getFeedUpdate();
- /**
- * Process the feed update asynchronously to avoid a Hub timeout.
- */
- }
- ]]></programlisting>
- <note>
- <para>
- It should be noted that
- <classname>Zend_Feed_Pubsubhubbub_Subscriber_Callback</classname> may
- independently parse any incoming query string and other parameters. This is
- necessary since <acronym>PHP</acronym> alters the structure and keys of a query
- string when it is parsed into the <varname>$_GET</varname> or
- <varname>$_POST</varname> superglobals. For example, all duplicate keys are
- ignored and periods are converted to underscores. Pubsubhubbub features both of
- these in the query strings it generates.
- </para>
- </note>
- <important>
- <para>
- It is essential that developers recognise that Hubs are only concerned with
- sending requests and receiving a response which verifies its receipt. If a feed
- update is received, it should never be processed on the spot since this leaves
- the Hub waiting for a response. Rather, any processing should be offloaded to
- another process or deferred until after a response has been returned to the Hub.
- One symptom of a failure to promptly complete Hub requests is that a Hub may
- continue to attempt delivery of the update or verification request leading to
- duplicated update attempts being processed by the Subscriber. This appears
- problematic - but in reality a Hub may apply a timeout of just a few seconds,
- and if no response is received within that time it may disconnect (assuming a
- delivery failure) and retry later. Note that Hubs are expected to distribute
- vast volumes of updates so their resources are stretched - please do process
- feeds asynchronously (e.g. in a separate process or a job queue or even a cron
- scheduled task) as much as possible.
- </para>
- </important>
- </sect3>
- <sect3
- id="zend.feed.pubsubhubbub.zend.feed.pubsubhubbub.subscriber.setting.up.and.using.a.callback.url.route">
- <title>Setting Up And Using A Callback URL Route</title>
- <para>
- As noted earlier, the
- <classname>Zend_Feed_Pubsubhubbub_Subscriber_Callback</classname> class receives the
- combined key associated with any subscription from the Hub via one of two methods.
- The technically preferred method is to add this key to the Callback
- <acronym>URL</acronym> employed by the Hub in all future requests using a query
- string parameter with the key "xhub.subscription". However, for historical reasons,
- primarily that this was not supported in Pubsubhubbub 0.1 (it was recently added in
- 0.2 only), it is strongly recommended to use the most compatible means of adding
- this key to the Callback <acronym>URL</acronym> by appending it to the
- <acronym>URL</acronym>'s path.
- </para>
- <para>
- Thus the <acronym>URL</acronym>
- http://www.example.com/callback?xhub.subscription=key would become
- http://www.example.com/callback/key.
- </para>
- <para>
- Since the query string method is the default in anticipation of a greater level
- of future support for the full 0.2 specification, this requires some additional work
- to implement.
- </para>
- <para>
- The first step to make the
- <classname>Zend_Feed_Pubsubhubbub_Subscriber_Callback</classname> class aware of the
- path contained subscription key. It's manually injected therefore since it also
- requires manually defining a route for this purpose. This is achieved simply by
- called the method
- <methodname>Zend_Feed_Pubsubhubbub_Subscriber_Callback::setSubscriptionKey()</methodname>
- with the parameter being the key value available from the Router. The example below
- demonstrates this using a Zend Framework controller.
- </para>
- <programlisting language="php"><![CDATA[
- class CallbackController extends Zend_Controller_Action
- {
- public function indexAction()
- {
- $storage = new Zend_Feed_Pubsubhubbub_Model_Subscription;
- $callback = new Zend_Feed_Pubsubhubbub_Subscriber_Callback;
- $callback->setStorage($storage);
- /**
- * Inject subscription key parsing from URL path using
- * a parameter from Router.
- */
- $subscriptionKey = $this->_getParam('subkey');
- $callback->setSubscriptionKey($subscriptionKey);
- $callback->handle();
- $callback->sendResponse();
- /**
- * Check if the callback resulting in the receipt of a feed update.
- * Otherwise it was either a (un)sub verification request or invalid
- * request. Typically we need do nothing other than add feed update
- * handling - the rest is handled internally by the class.
- */
- if ($callback->hasFeedUpdate()) {
- $feedString = $callback->getFeedUpdate();
- /**
- * Process the feed update asynchronously to avoid a Hub timeout.
- */
- }
- }
- }
- ]]></programlisting>
- <para>
- Actually adding the route which would map the path-appended key
- to a parameter for retrieval from a controller can be accomplished using
- a Route configuration such as the <acronym>INI</acronym> formatted example below for
- use with <classname>Zend_Application</classname> bootstrapping.
- </para>
- <programlisting language="dosini"><![CDATA[
- ; Callback Route to enable appending a PuSH Subscription's lookup key
- resources.router.routes.callback.route = "callback/:subkey"
- resources.router.routes.callback.defaults.module = "default"
- resources.router.routes.callback.defaults.controller = "callback"
- resources.router.routes.callback.defaults.action = "index"
- ]]></programlisting>
- </sect3>
- </sect2>
- </sect1>
|