1 <?xml version="1.0" encoding="UTF-8"?>
3 <sect1 id="zend.feed.pubsubhubbub.introduction">
4 <title>Zend_Feed_Pubsubhubbub</title>
7 <classname>Zend_Feed_Pubsubhubbub</classname> is an implementation of the PubSubHubbub Core
8 0.2 Specification (Working Draft). It offers implementations of a Pubsubhubbub Publisher and
9 Subscriber suited to Zend Framework and other <acronym>PHP</acronym> applications.
12 <sect2 id="zend.feed.pubsubhubbub.what.is.pubsubhubbub">
13 <title>What is Pubsubhubbub?</title>
16 Pubsubhubbub is an open, simple web-scale pubsub protocol. A common use case to enable
17 blogs (Publishers) to "push" updates from their <acronym>RSS</acronym> or Atom feeds
18 (Topics) to end Subscribers. These Subscribers will have subscribed to the blog's
19 <acronym>RSS</acronym> or Atom feed via a Hub, a central server which is notified of any
20 updates by the Publisher and which then distributes these updates to all Subscribers.
21 Any feed may advertise that it supports one or more Hubs using an Atom namespaced link
22 element with a rel attribute of "hub".
26 Pubsubhubbub has garnered attention because it is a pubsub protocol which is easy to
27 implement and which operates over <acronym>HTTP</acronym>. Its philosophy is to replace
28 the traditional model where blog feeds have been polled at regular intervals to detect
29 and retrieve updates. Depending on the frequency of polling, this can take a lot of time
30 to propagate updates to interested parties from planet aggregators to desktop readers.
31 With a pubsub system in place, updates are not simply polled by Subscribers, they are
32 pushed to Subscribers, elimenating any delay. For this reason, Pubsubhubbub forms part
33 of what has been dubbed the real-time web.
37 The protocol does not exist in isolation. Pubsub systems have been around for a while,
38 such as the familiar Jabber Publish-Subscribe protocol, <acronym>XEP-0060</acronym>, or
39 the less well known rssCloud (described in 2001). However these have not achieved
40 widespread adoption typically due to either their complexity, poor timing or lack of
41 suitability for web applications. rssCloud, which was recently revived as a response to
42 the appearance of Pubsubhubbub, has also seen its usage increase significantly though it
43 lacks a formal specification and currently does not support Atom 1.0 feeds.
47 Perhaps surprisingly given its relative early age, Pubsubhubbub is already in use
48 including in Google Reader, Feedburner, and there are plugins available for Wordpress
53 <sect2 id="zend.feed.pubsubhubbub.architecture">
54 <title>Architecture</title>
57 <classname>Zend_Feed_Pubsubhubbub</classname> implements two sides of the Pubsubhubbub
58 0.2 Specification: a Publisher and a Subscriber. It does not currently implement a Hub
59 Server though this is in progress for a future Zend Framework release.
63 A Publisher is responsible for notifying all supported Hubs (many can be supported to
64 add redundancy to the system) of any updates to its feeds, whether they be Atom or
65 <acronym>RSS</acronym> based. This is achieved by pinging the supported Hub Servers with
66 the <acronym>URL</acronym> of the updated feed. In Pubsubhubbub terminology, any
67 updatable resource capable of being subscribed to is referred to as a Topic. Once a ping
68 is received, the Hub will request the updated feed, process it for updated items, and
69 forward all updates to all Subscribers subscribed to that feed.
73 A Subscriber is any party or application which subscribes to one or more Hubs to receive
74 updates from a Topic hosted by a Publisher. The Subscriber never directly communicates
75 with the Publisher since the Hub acts as an intermediary, accepting subscriptions and
76 sending updates to subscribed Subscribers. The Subscriber therefore communicates only
77 with the Hub, either to subscribe or unsubscribe to Topics, or when it receives updates
78 from the Hub. This communication design ("Fat Pings") effectively removes the
79 possibility of a "Thundering Herd" issue. This occurs in a pubsub system where the Hub
80 merely informs Subscribers that an update is available, prompting all Subscribers to
81 immediately retrieve the feed from the Publisher giving rise to a traffic spike. In
82 Pubsubhubbub, the Hub distributes the actual update in a "Fat Ping" so the Publisher is
83 not subjected to any traffic spike.
87 <classname>Zend_Feed_Pubsubhubbub</classname> implements Pubsubhubbub Publishers and
89 classes <classname>Zend_Feed_Pubsubhubbub_Publisher</classname> and
90 <classname>Zend_Feed_Pubsubhubbub_Subscriber</classname>. In addition, the Subscriber
91 implementation may handle any feed updates forwarded from a Hub by using
92 <classname>Zend_Feed_Pubsubhubbub_Subscriber_Callback</classname>. These classes, their
93 use cases, and <acronym>API</acronym>s are covered in subsequent sections.
97 <sect2 id="zend.feed.pubsubhubbub.zend.feed.pubsubhubbub.publisher">
98 <title>Zend_Feed_Pubsubhubbub_Publisher</title>
101 In Pubsubhubbub, the Publisher is the party who publishes a live feed and frequently
102 updates it with new content. This may be a blog, an aggregator, or even a web service
103 with a public feed based <acronym>API</acronym>. In order for these updates to be pushed
104 to Subscribers, the Publisher must notify all of its supported Hubs that an update has
105 occured using a simple <acronym>HTTP</acronym> POST request containing the
106 <acronym>URI</acronym> or the updated Topic (i.e the updated <acronym>RSS</acronym> or
107 Atom feed). The Hub will confirm receipt of the notification, fetch the updated feed,
108 and forward any updates to any Subscribers who have subscribed to that Hub for updates
109 from the relevant feed.
113 By design, this means the Publisher has very little to do except send these Hub pings
114 whenever its feeds change. As a result, the Publisher implementation is extremely
115 simple to use and requires very little work to setup and use when feeds are updated.
119 <classname>Zend_Feed_Pubsubhubbub_Publisher</classname> implements a full Pubsubhubbub
120 Publisher. Its setup for use is also simple, requiring mainly that it is configured with
121 the <acronym>URI</acronym> endpoint for all Hubs to be notified of updates, and the
122 <acronym>URI</acronym>s of all Topics to be included in the notifications.
126 The following example shows a Publisher notifying a collection of Hubs about updates to
127 a pair of local <acronym>RSS</acronym> and Atom feeds. The class retains a collection of
128 errors which include the Hub <acronym>URL</acronym>s, so the notification can be
129 re-attempted later and/or logged if any notifications happen to fail. Each resulting
130 error array also includes a "response" key containing the related
131 <acronym>HTTP</acronym> response object. In the event of any errors, it is strongly
132 recommended to attempt the operation for failed Hub Endpoints at least once more at a
133 future time. This may require the use of either a scheduled task for this purpose or a
134 job queue though such extra steps are optional.
137 <programlisting language="php"><![CDATA[
138 $publisher = new Zend_Feed_Pubsubhubbub_Publisher;
139 $publisher->addHubUrls(array(
140 'http://pubsubhubbub.appspot.com/',
141 'http://hubbub.example.com',
143 $publisher->addUpdatedTopicUrls(array(
144 'http://www.example.net/rss',
145 'http://www.example.net/atom',
147 $publisher->notifyAll();
149 if (!$publisher->isSuccess()) {
151 $errors = $publisher->getErrors();
152 $failedHubs = array()
153 foreach ($errors as $error) {
154 $failedHubs[] = $error['hubUrl'];
158 // reschedule notifications for the failed Hubs in $failedHubs
162 If you prefer having more concrete control over the Publisher, the methods
163 <methodname>addHubUrls()</methodname> and <methodname>addUpdatedTopicUrls()</methodname>
164 pass each array value to the singular <methodname>addHubUrl()</methodname> and
165 <methodname>addUpdatedTopicUrl()</methodname> public methods. There are also matching
166 <methodname>removeUpdatedTopicUrl()</methodname> and
167 <methodname>removeHubUrl()</methodname> methods.
171 You can also skip setting Hub <acronym>URI</acronym>s, and notify each in turn using the
172 <methodname>notifyHub()</methodname> method which accepts the <acronym>URI</acronym> of
173 a Hub endpoint as its only argument.
177 There are no other tasks to cover. The Publisher implementation is very simple since
178 most of the feed processing and distribution is handled by the selected Hubs. It is
179 however important to detect errors and reschedule notifications as soon as possible
180 (with a reasonable maximum number of retries) to ensure notifications reach all
181 Subscribers. In many cases as a final alternative, Hubs may frequently poll your
182 feeds to offer some additional tolerance for failures both in terms of their own
183 temporary downtime or Publisher errors or downtime.
187 <sect2 id="zend.feed.pubsubhubbub.zend.feed.pubsubhubbub.subscriber">
188 <title>Zend_Feed_Pubsubhubbub_Subscriber</title>
191 In Pubsubhubbub, the Subscriber is the party who wishes to receive updates to any Topic
192 (<acronym>RSS</acronym> or Atom feed). They achieve this by subscribing to one or more
193 of the Hubs advertised by that Topic, usually as a set of one or more Atom 1.0 links
194 with a rel attribute of "hub". The Hub from that point forward will send an Atom or
195 <acronym>RSS</acronym> feed containing all updates to that Subscriber's Callback
196 <acronym>URL</acronym> when it receives an update notification from the Publisher. In
197 this way, the Subscriber need never actually visit the original feed (though it's still
198 recommended at some level to ensure updates are retrieved if ever a Hub goes offline).
199 All subscription requests must contain the <acronym>URI</acronym> of the Topic being
200 subscribed and a Callback <acronym>URL</acronym> which the Hub will use to confirm the
201 subscription and to forward updates.
205 The Subsciber therefore has two roles. To create and manage subscriptions, including
206 subscribing for new Topics with a Hub, unsubscribing (if necessary), and periodically
207 renewing subscriptions since they may have a limited validity as set by the Hub. This is
208 handled by <classname>Zend_Feed_Pubsubhubbub_Subscriber</classname>.
212 The second role is to accept updates sent by a Hub to the Subscriber's Callback
213 <acronym>URL</acronym>, i.e. the <acronym>URI</acronym> the Subscriber has assigned to
214 handle updates. The Callback <acronym>URL</acronym> also handles events where the Hub
215 contacts the Subscriber to confirm all subscriptions and unsubscriptions. This is
216 handled by using an instance of
217 <classname>Zend_Feed_Pubsubhubbub_Subscriber_Callback</classname> when the Callback
218 <acronym>URL</acronym> is accessed.
223 <classname>Zend_Feed_Pubsubhubbub_Subscriber</classname> implements the Pubsubhubbub
224 0.2 Specification. As this is a new specification version not all Hubs currently
225 implement it. The new specification allows the Callback <acronym>URL</acronym> to
226 include a query string which is used by this class, but not supported by all Hubs.
227 In the interests of maximising compatibility it is therefore recommended that the
228 query string component of the Subscriber Callback <acronym>URI</acronym> be
229 presented as a path element, i.e. recognised as a parameter in the route associated
230 with the Callback <acronym>URI</acronym> and used by the application's Router.
235 id="zend.feed.pubsubhubbub.zend.feed.pubsubhubbub.subscriber.subscribing.and.unsubscribing">
236 <title>Subscribing and Unsubscribing</title>
239 <classname>Zend_Feed_Pubsubhubbub_Subscriber</classname> implements a full
240 Pubsubhubbub Subscriber capable of subscribing to, or unsubscribing from, any Topic
241 via any Hub advertised by that Topic. It operates in conjunction with
242 <classname>Zend_Feed_Pubsubhubbub_Subscriber_Callback</classname> which accepts
243 requests from a Hub to confirm all subscription or unsubscription attempts (to
244 prevent third-party misuse).
248 Any subscription (or unsubscription) requires the relevant information before
249 proceeding, i.e. the <acronym>URI</acronym> of the Topic (Atom or
250 <acronym>RSS</acronym> feed) to be subscribed to for updates, and the
251 <acronym>URI</acronym> of the endpoint for the Hub which will handle the
252 subscription and forwarding of the updates. The lifetime of a subscription may
253 be determined by the Hub but most Hubs should support automatic subscription
254 refreshes by checking with the Subscriber. This is supported by
255 <classname>Zend_Feed_Pubsubhubbub_Subscriber_Callback</classname> and requires no
256 other work on your part. It is still strongly recommended that you use the Hub
257 sourced subscription time to live (ttl) to schedule the creation of new
258 subscriptions (the process is identical to that for any new subscription) to refresh
259 it with the Hub. While it should not be necessary per se, it covers cases where a
260 Hub may not support automatic subscription refreshing and rules out Hub errors for
261 additional redundancy.
265 With the relevant information to hand, a subscription can be attempted as
269 <programlisting language="php"><![CDATA[
270 $storage = new Zend_Feed_Pubsubhubbub_Model_Subscription;
272 $subscriber = new Zend_Feed_Pubsubhubbub_Subscriber;
273 $subscriber->setStorage($storage);
274 $subscriber->addHubUrl('http://hubbub.example.com');
275 $subscriber->setTopicUrl('http://www.example.net/rss.xml');
276 $subscriber->setCallbackUrl('http://www.mydomain.com/hubbub/callback');
277 $subscriber->subscribeAll();
281 In order to store subscriptions and offer access to this data for general use,
282 the component requires a database (a schema is provided later in this section).
283 By default, it is assumed the table name is "subscription" and it utilises
284 <classname>Zend_Db_Table_Abstract</classname> in the background meaning it
285 will use the default adapter you have set for your application. You may also
286 pass a specific custom <classname>Zend_Db_Table_Abstract</classname> instance
287 into the associated model
288 <classname>Zend_Feed_Pubsubhubbub_Model_Subscription</classname>. This custom
289 adapter may be as simple in intent as changing the table name to use or as complex
290 as you deem necessary.
294 While this Model is offered as a default ready-to-roll solution, you may create your
295 own Model using any other backend or database layer (e.g. Doctrine) so long as the
296 resulting class implements the interface
297 <classname>Zend_Feed_Pubsubhubbub_Model_SubscriptionInterface</classname>.
301 An example schema (MySQL) for a subscription table accessible by the provided model
305 <programlisting language="sql"><![CDATA[
306 CREATE TABLE IF NOT EXISTS `subscription` (
307 `id` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
308 `topic_url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
309 `hub_url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
310 `created_time` datetime DEFAULT NULL,
311 `lease_seconds` bigint(20) DEFAULT NULL,
312 `verify_token` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
313 `secret` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
314 `expiration_time` datetime DEFAULT NULL,
315 `subscription_state` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL,
317 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
321 Behind the scenes, the Subscriber above will send a request to the Hub endpoint
322 containing the following parameters (based on the previous example):
326 id="zend.feed.pubsubhubbub.zend.feed.pubsubhubbub.subscriber.subscribing.and.unsubscribing.table">
327 <title>Subscription request parameters</title>
332 <entry>Parameter</entry>
334 <entry>Explanation</entry>
340 <entry>hub.callback</entry>
341 <entry>http://www.mydomain.com/hubbub/callback?xhub.subscription=5536df06b5dcb966edab3a4c4d56213c16a8184</entry>
345 The <acronym>URI</acronym> used by a Hub to contact the
346 Subscriber and either request confirmation of a (un)subscription
347 request or send updates from subscribed feeds. The appended
348 query string contains a custom parameter (hence the xhub
349 designation). It is a query string parameter preserved by the
350 Hub and resent with all Subscriber requests. Its purpose is to
351 allow the Subscriber to identify and look up the subscription
352 associated with any Hub request in a backend storage medium.
353 This is a non-standard parameter used by this component in
354 preference to encoding a subscription key in the
355 <acronym>URI</acronym> path which is more difficult to implement
356 in a Zend Framework application.
360 Nevertheless, since not all Hubs support query string
361 parameters, we still strongly recommend adding the subscription
362 key as a path component in the form
363 http://www.mydomain.com/hubbub/callback/5536df06b5dcb966edab3a4c4d56213c16a8184.
364 To accomplish this, it requires defining a route capable of
365 parsing out the final value of the key and then retrieving the
366 value and passing it to the Subscriber Callback object. The
367 value would be passed into the method
368 <methodname>Zend_Pubsubhubbub_Subscriber_Callback::setSubscriptionKey()</methodname>.
369 A detailed example is offered later.
375 <entry>hub.lease_seconds</entry>
376 <entry>2592000</entry>
380 The number of seconds for which the Subscriber would like a new
381 subscription to remain valid for (i.e. a
382 <acronym>TTL</acronym>). Hubs may enforce their own maximum
383 subscription period. All subscriptions should be renewed by
384 simply re-subscribing before the subscription period ends to
385 ensure continuity of updates. Hubs should additionally attempt
386 to automatically refresh subscriptions before they expire by
387 contacting Subscribers (handled automatically by the Callback
394 <entry>hub.mode</entry>
395 <entry>subscribe</entry>
399 Simple value indicating this is a subscription request.
400 Unsubscription requests would use the "unsubscribe" value.
406 <entry>hub.topic</entry>
407 <entry>http://www.example.net/rss.xml</entry>
411 The <acronym>URI</acronym> of the topic (i.e. Atom or
412 <acronym>RSS</acronym> feed) which the Subscriber wishes to
413 subscribe to for updates.
419 <entry>hub.verify</entry>
424 Indicates to the Hub the preferred mode of verifying
425 subscriptions or unsubscriptions. It is repeated twice in order
426 of preference. Technically this component does not distinguish
427 between the two modes and treats both equally.
433 <entry>hub.verify</entry>
438 Indicates to the Hub the preferred mode of verifying
439 subscriptions or unsubscriptions. It is repeated twice in order
440 of preference. Technically this component does not distinguish
441 between the two modes and treats both equally.
447 <entry>hub.verify_token</entry>
448 <entry>3065919804abcaa7212ae89.879827871253878386</entry>
452 A verification token returned to the Subscriber by the Hub when
453 it is confirming a subscription or unsubscription. Offers a
454 measure of reliance that the confirmation request originates
455 from the correct Hub to prevent misuse.
464 You can modify several of these parameters to indicate a different preference. For
465 example, you can set a different lease seconds value using
466 <methodname>Zend_Pubsubhubbub_Subscriber::setLeaseSeconds()</methodname> or show a
467 preference for the async verify mode by using
468 <methodname>setPreferredVerificationMode(Zend_Feed_Pubsubhubbub::VERIFICATION_MODE_ASYNC)</methodname>.
469 However the Hubs retain the capability to enforce their own preferences and for this
470 reason the component is deliberately designed to work across almost any set of
471 options with minimum end-user configuration required. Conventions are great when
477 While Hubs may require the use of a specific verification mode (both are
478 supported by <classname>Zend_Pubsubhubbub</classname>), you may indicate a
479 specific preference using the
480 <methodname>setPreferredVerificationMode()</methodname> method. In "sync"
481 (synchronous) mode, the Hub attempts to confirm a subscription as soon as it is
482 received, and before responding to the subscription request. In "async"
483 (asynchronous) mode, the Hub will return a response to the subscription request
484 immediately, and its verification request may occur at a later time. Since
485 <classname>Zend_Pubsubhubbub</classname> implements the Subscriber verification
486 role as a separate callback class and requires the use of a backend storage
487 medium, it actually supports both transparently though in terms of end-user
488 performance, asynchronous verification is very much preferred to eliminate the
489 impact of a poorly performing Hub tying up end-user server resources and
490 connections for too long.
495 Unsubscribing from a Topic follows the exact same pattern as the previous example,
496 with the exception that we should call <methodname>unsubscribeAll()</methodname>
497 instead. The parameters included are identical to a subscription request with the
498 exception that "hub.mode" is set to "unsubscribe".
502 By default, a new instance of <classname>Zend_Pubsubhubbub_Subscriber</classname>
503 will attempt to use a database backed storage medium which defaults to using the
504 default <classname>Zend_Db</classname> adapter with a table name of "subscription".
505 It is recommended to set a custom storage solution where these defaults are not apt
506 either by passing in a new Model supporting the required interface or by passing a
507 new instance of <classname>Zend_Db_Table_Abstract</classname> to the default Model's
508 constructor to change the used table name.
512 <sect3 id="zend.feed.pubsubhubbub.zend.feed.pubsubhubbub.subscriber.handling.hub.callbacks">
513 <title>Handling Subscriber Callbacks</title>
516 Whenever a subscription or unsubscription request is made, the Hub must verify the
517 request by forwarding a new verification request to the Callback
518 <acronym>URL</acronym> set in the subscription or unsubscription parameters. To
519 handle these Hub requests, which will include all future communications containing
520 Topic (feed) updates, the Callback <acronym>URL</acronym> should trigger the
521 execution of an instance of
522 <classname>Zend_Pubsubhubbub_Subscriber_Callback</classname> to handle the request.
526 The Callback class should be configured to use the same storage medium as the
527 Subscriber class. Using it is quite simple since most of its work is performed
531 <programlisting language="php"><![CDATA[
532 $storage = new Zend_Feed_Pubsubhubbub_Model_Subscription;
533 $callback = new Zend_Feed_Pubsubhubbub_Subscriber_Callback;
534 $callback->setStorage($storage);
536 $callback->sendResponse();
539 * Check if the callback resulting in the receipt of a feed update.
540 * Otherwise it was either a (un)sub verification request or invalid request.
541 * Typically we need do nothing other than add feed update handling - the rest
542 * is handled internally by the class.
544 if ($callback->hasFeedUpdate()) {
545 $feedString = $callback->getFeedUpdate();
547 * Process the feed update asynchronously to avoid a Hub timeout.
554 It should be noted that
555 <classname>Zend_Feed_Pubsubhubbub_Subscriber_Callback</classname> may
556 independently parse any incoming query string and other parameters. This is
557 necessary since <acronym>PHP</acronym> alters the structure and keys of a query
558 string when it is parsed into the <varname>$_GET</varname> or
559 <varname>$_POST</varname> superglobals. For example, all duplicate keys are
560 ignored and periods are converted to underscores. Pubsubhubbub features both of
561 these in the query strings it generates.
567 It is essential that developers recognise that Hubs are only concerned with
568 sending requests and receiving a response which verifies its receipt. If a feed
569 update is received, it should never be processed on the spot since this leaves
570 the Hub waiting for a response. Rather, any processing should be offloaded to
571 another process or deferred until after a response has been returned to the Hub.
572 One symptom of a failure to promptly complete Hub requests is that a Hub may
573 continue to attempt delivery of the update or verification request leading to
574 duplicated update attempts being processed by the Subscriber. This appears
575 problematic - but in reality a Hub may apply a timeout of just a few seconds,
576 and if no response is received within that time it may disconnect (assuming a
577 delivery failure) and retry later. Note that Hubs are expected to distribute
578 vast volumes of updates so their resources are stretched - please do process
579 feeds asynchronously (e.g. in a separate process or a job queue or even a cron
580 scheduled task) as much as possible.
586 id="zend.feed.pubsubhubbub.zend.feed.pubsubhubbub.subscriber.setting.up.and.using.a.callback.url.route">
587 <title>Setting Up And Using A Callback URL Route</title>
590 As noted earlier, the
591 <classname>Zend_Feed_Pubsubhubbub_Subscriber_Callback</classname> class receives the
592 combined key associated with any subscription from the Hub via one of two methods.
593 The technically preferred method is to add this key to the Callback
594 <acronym>URL</acronym> employed by the Hub in all future requests using a query
595 string parameter with the key "xhub.subscription". However, for historical reasons,
596 primarily that this was not supported in Pubsubhubbub 0.1 (it was recently added in
597 0.2 only), it is strongly recommended to use the most compatible means of adding
598 this key to the Callback <acronym>URL</acronym> by appending it to the
599 <acronym>URL</acronym>'s path.
603 Thus the <acronym>URL</acronym>
604 http://www.example.com/callback?xhub.subscription=key would become
605 http://www.example.com/callback/key.
609 Since the query string method is the default in anticipation of a greater level
610 of future support for the full 0.2 specification, this requires some additional work
615 The first step to make the
616 <classname>Zend_Feed_Pubsubhubbub_Subscriber_Callback</classname> class aware of the
617 path contained subscription key. It's manually injected therefore since it also
618 requires manually defining a route for this purpose. This is achieved simply by
620 <methodname>Zend_Feed_Pubsubhubbub_Subscriber_Callback::setSubscriptionKey()</methodname>
621 with the parameter being the key value available from the Router. The example below
622 demonstrates this using a Zend Framework controller.
625 <programlisting language="php"><![CDATA[
626 class CallbackController extends Zend_Controller_Action
629 public function indexAction()
631 $storage = new Zend_Feed_Pubsubhubbub_Model_Subscription;
632 $callback = new Zend_Feed_Pubsubhubbub_Subscriber_Callback;
633 $callback->setStorage($storage);
635 * Inject subscription key parsing from URL path using
636 * a parameter from Router.
638 $subscriptionKey = $this->_getParam('subkey');
639 $callback->setSubscriptionKey($subscriptionKey);
641 $callback->sendResponse();
644 * Check if the callback resulting in the receipt of a feed update.
645 * Otherwise it was either a (un)sub verification request or invalid
646 * request. Typically we need do nothing other than add feed update
647 * handling - the rest is handled internally by the class.
649 if ($callback->hasFeedUpdate()) {
650 $feedString = $callback->getFeedUpdate();
652 * Process the feed update asynchronously to avoid a Hub timeout.
661 Actually adding the route which would map the path-appended key
662 to a parameter for retrieval from a controller can be accomplished using
663 a Route configuration such as the <acronym>INI</acronym> formatted example below for
664 use with <classname>Zend_Application</classname> bootstrapping.
667 <programlisting language="dosini"><![CDATA[
668 ; Callback Route to enable appending a PuSH Subscription's lookup key
669 resources.router.routes.callback.route = "callback/:subkey"
670 resources.router.routes.callback.defaults.module = "default"
671 resources.router.routes.callback.defaults.controller = "callback"
672 resources.router.routes.callback.defaults.action = "index"