Mercurial > hg > isophonics-drupal-site
comparison vendor/zendframework/zend-feed/src/PubSubHubbub/Subscriber.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 7a779792577d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 /** | |
3 * Zend Framework (http://framework.zend.com/) | |
4 * | |
5 * @link http://github.com/zendframework/zf2 for the canonical source repository | |
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) | |
7 * @license http://framework.zend.com/license/new-bsd New BSD License | |
8 */ | |
9 | |
10 namespace Zend\Feed\PubSubHubbub; | |
11 | |
12 use DateInterval; | |
13 use DateTime; | |
14 use Traversable; | |
15 use Zend\Feed\Uri; | |
16 use Zend\Http\Request as HttpRequest; | |
17 use Zend\Stdlib\ArrayUtils; | |
18 | |
19 class Subscriber | |
20 { | |
21 /** | |
22 * An array of URLs for all Hub Servers to subscribe/unsubscribe. | |
23 * | |
24 * @var array | |
25 */ | |
26 protected $hubUrls = []; | |
27 | |
28 /** | |
29 * An array of optional parameters to be included in any | |
30 * (un)subscribe requests. | |
31 * | |
32 * @var array | |
33 */ | |
34 protected $parameters = []; | |
35 | |
36 /** | |
37 * The URL of the topic (Rss or Atom feed) which is the subject of | |
38 * our current intent to subscribe to/unsubscribe from updates from | |
39 * the currently configured Hub Servers. | |
40 * | |
41 * @var string | |
42 */ | |
43 protected $topicUrl = ''; | |
44 | |
45 /** | |
46 * The URL Hub Servers must use when communicating with this Subscriber | |
47 * | |
48 * @var string | |
49 */ | |
50 protected $callbackUrl = ''; | |
51 | |
52 /** | |
53 * The number of seconds for which the subscriber would like to have the | |
54 * subscription active. Defaults to null, i.e. not sent, to setup a | |
55 * permanent subscription if possible. | |
56 * | |
57 * @var int | |
58 */ | |
59 protected $leaseSeconds = null; | |
60 | |
61 /** | |
62 * The preferred verification mode (sync or async). By default, this | |
63 * Subscriber prefers synchronous verification, but is considered | |
64 * desirable to support asynchronous verification if possible. | |
65 * | |
66 * Zend\Feed\Pubsubhubbub\Subscriber will always send both modes, whose | |
67 * order of occurrence in the parameter list determines this preference. | |
68 * | |
69 * @var string | |
70 */ | |
71 protected $preferredVerificationMode = PubSubHubbub::VERIFICATION_MODE_SYNC; | |
72 | |
73 /** | |
74 * An array of any errors including keys for 'response', 'hubUrl'. | |
75 * The response is the actual Zend\Http\Response object. | |
76 * | |
77 * @var array | |
78 */ | |
79 protected $errors = []; | |
80 | |
81 /** | |
82 * An array of Hub Server URLs for Hubs operating at this time in | |
83 * asynchronous verification mode. | |
84 * | |
85 * @var array | |
86 */ | |
87 protected $asyncHubs = []; | |
88 | |
89 /** | |
90 * An instance of Zend\Feed\Pubsubhubbub\Model\SubscriptionPersistence used to background | |
91 * save any verification tokens associated with a subscription or other. | |
92 * | |
93 * @var \Zend\Feed\PubSubHubbub\Model\SubscriptionPersistenceInterface | |
94 */ | |
95 protected $storage = null; | |
96 | |
97 /** | |
98 * An array of authentication credentials for HTTP Basic Authentication | |
99 * if required by specific Hubs. The array is indexed by Hub Endpoint URI | |
100 * and the value is a simple array of the username and password to apply. | |
101 * | |
102 * @var array | |
103 */ | |
104 protected $authentications = []; | |
105 | |
106 /** | |
107 * Tells the Subscriber to append any subscription identifier to the path | |
108 * of the base Callback URL. E.g. an identifier "subkey1" would be added | |
109 * to the callback URL "http://www.example.com/callback" to create a subscription | |
110 * specific Callback URL of "http://www.example.com/callback/subkey1". | |
111 * | |
112 * This is required for all Hubs using the Pubsubhubbub 0.1 Specification. | |
113 * It should be manually intercepted and passed to the Callback class using | |
114 * Zend\Feed\Pubsubhubbub\Subscriber\Callback::setSubscriptionKey(). Will | |
115 * require a route in the form "callback/:subkey" to allow the parameter be | |
116 * retrieved from an action using the Zend\Controller\Action::\getParam() | |
117 * method. | |
118 * | |
119 * @var string | |
120 */ | |
121 protected $usePathParameter = false; | |
122 | |
123 /** | |
124 * Constructor; accepts an array or Traversable instance to preset | |
125 * options for the Subscriber without calling all supported setter | |
126 * methods in turn. | |
127 * | |
128 * @param array|Traversable $options | |
129 */ | |
130 public function __construct($options = null) | |
131 { | |
132 if ($options !== null) { | |
133 $this->setOptions($options); | |
134 } | |
135 } | |
136 | |
137 /** | |
138 * Process any injected configuration options | |
139 * | |
140 * @param array|Traversable $options | |
141 * @return Subscriber | |
142 * @throws Exception\InvalidArgumentException | |
143 */ | |
144 public function setOptions($options) | |
145 { | |
146 if ($options instanceof Traversable) { | |
147 $options = ArrayUtils::iteratorToArray($options); | |
148 } | |
149 | |
150 if (!is_array($options)) { | |
151 throw new Exception\InvalidArgumentException('Array or Traversable object' | |
152 . 'expected, got ' . gettype($options)); | |
153 } | |
154 if (array_key_exists('hubUrls', $options)) { | |
155 $this->addHubUrls($options['hubUrls']); | |
156 } | |
157 if (array_key_exists('callbackUrl', $options)) { | |
158 $this->setCallbackUrl($options['callbackUrl']); | |
159 } | |
160 if (array_key_exists('topicUrl', $options)) { | |
161 $this->setTopicUrl($options['topicUrl']); | |
162 } | |
163 if (array_key_exists('storage', $options)) { | |
164 $this->setStorage($options['storage']); | |
165 } | |
166 if (array_key_exists('leaseSeconds', $options)) { | |
167 $this->setLeaseSeconds($options['leaseSeconds']); | |
168 } | |
169 if (array_key_exists('parameters', $options)) { | |
170 $this->setParameters($options['parameters']); | |
171 } | |
172 if (array_key_exists('authentications', $options)) { | |
173 $this->addAuthentications($options['authentications']); | |
174 } | |
175 if (array_key_exists('usePathParameter', $options)) { | |
176 $this->usePathParameter($options['usePathParameter']); | |
177 } | |
178 if (array_key_exists('preferredVerificationMode', $options)) { | |
179 $this->setPreferredVerificationMode( | |
180 $options['preferredVerificationMode'] | |
181 ); | |
182 } | |
183 return $this; | |
184 } | |
185 | |
186 /** | |
187 * Set the topic URL (RSS or Atom feed) to which the intended (un)subscribe | |
188 * event will relate | |
189 * | |
190 * @param string $url | |
191 * @return Subscriber | |
192 * @throws Exception\InvalidArgumentException | |
193 */ | |
194 public function setTopicUrl($url) | |
195 { | |
196 if (empty($url) || !is_string($url) || !Uri::factory($url)->isValid()) { | |
197 throw new Exception\InvalidArgumentException('Invalid parameter "url"' | |
198 .' of "' . $url . '" must be a non-empty string and a valid' | |
199 .' URL'); | |
200 } | |
201 $this->topicUrl = $url; | |
202 return $this; | |
203 } | |
204 | |
205 /** | |
206 * Set the topic URL (RSS or Atom feed) to which the intended (un)subscribe | |
207 * event will relate | |
208 * | |
209 * @return string | |
210 * @throws Exception\RuntimeException | |
211 */ | |
212 public function getTopicUrl() | |
213 { | |
214 if (empty($this->topicUrl)) { | |
215 throw new Exception\RuntimeException('A valid Topic (RSS or Atom' | |
216 . ' feed) URL MUST be set before attempting any operation'); | |
217 } | |
218 return $this->topicUrl; | |
219 } | |
220 | |
221 /** | |
222 * Set the number of seconds for which any subscription will remain valid | |
223 * | |
224 * @param int $seconds | |
225 * @return Subscriber | |
226 * @throws Exception\InvalidArgumentException | |
227 */ | |
228 public function setLeaseSeconds($seconds) | |
229 { | |
230 $seconds = intval($seconds); | |
231 if ($seconds <= 0) { | |
232 throw new Exception\InvalidArgumentException('Expected lease seconds' | |
233 . ' must be an integer greater than zero'); | |
234 } | |
235 $this->leaseSeconds = $seconds; | |
236 return $this; | |
237 } | |
238 | |
239 /** | |
240 * Get the number of lease seconds on subscriptions | |
241 * | |
242 * @return int | |
243 */ | |
244 public function getLeaseSeconds() | |
245 { | |
246 return $this->leaseSeconds; | |
247 } | |
248 | |
249 /** | |
250 * Set the callback URL to be used by Hub Servers when communicating with | |
251 * this Subscriber | |
252 * | |
253 * @param string $url | |
254 * @return Subscriber | |
255 * @throws Exception\InvalidArgumentException | |
256 */ | |
257 public function setCallbackUrl($url) | |
258 { | |
259 if (empty($url) || !is_string($url) || !Uri::factory($url)->isValid()) { | |
260 throw new Exception\InvalidArgumentException('Invalid parameter "url"' | |
261 . ' of "' . $url . '" must be a non-empty string and a valid' | |
262 . ' URL'); | |
263 } | |
264 $this->callbackUrl = $url; | |
265 return $this; | |
266 } | |
267 | |
268 /** | |
269 * Get the callback URL to be used by Hub Servers when communicating with | |
270 * this Subscriber | |
271 * | |
272 * @return string | |
273 * @throws Exception\RuntimeException | |
274 */ | |
275 public function getCallbackUrl() | |
276 { | |
277 if (empty($this->callbackUrl)) { | |
278 throw new Exception\RuntimeException('A valid Callback URL MUST be' | |
279 . ' set before attempting any operation'); | |
280 } | |
281 return $this->callbackUrl; | |
282 } | |
283 | |
284 /** | |
285 * Set preferred verification mode (sync or async). By default, this | |
286 * Subscriber prefers synchronous verification, but does support | |
287 * asynchronous if that's the Hub Server's utilised mode. | |
288 * | |
289 * Zend\Feed\Pubsubhubbub\Subscriber will always send both modes, whose | |
290 * order of occurrence in the parameter list determines this preference. | |
291 * | |
292 * @param string $mode Should be 'sync' or 'async' | |
293 * @return Subscriber | |
294 * @throws Exception\InvalidArgumentException | |
295 */ | |
296 public function setPreferredVerificationMode($mode) | |
297 { | |
298 if ($mode !== PubSubHubbub::VERIFICATION_MODE_SYNC | |
299 && $mode !== PubSubHubbub::VERIFICATION_MODE_ASYNC | |
300 ) { | |
301 throw new Exception\InvalidArgumentException('Invalid preferred' | |
302 . ' mode specified: "' . $mode . '" but should be one of' | |
303 . ' Zend\Feed\Pubsubhubbub::VERIFICATION_MODE_SYNC or' | |
304 . ' Zend\Feed\Pubsubhubbub::VERIFICATION_MODE_ASYNC'); | |
305 } | |
306 $this->preferredVerificationMode = $mode; | |
307 return $this; | |
308 } | |
309 | |
310 /** | |
311 * Get preferred verification mode (sync or async). | |
312 * | |
313 * @return string | |
314 */ | |
315 public function getPreferredVerificationMode() | |
316 { | |
317 return $this->preferredVerificationMode; | |
318 } | |
319 | |
320 /** | |
321 * Add a Hub Server URL supported by Publisher | |
322 * | |
323 * @param string $url | |
324 * @return Subscriber | |
325 * @throws Exception\InvalidArgumentException | |
326 */ | |
327 public function addHubUrl($url) | |
328 { | |
329 if (empty($url) || !is_string($url) || !Uri::factory($url)->isValid()) { | |
330 throw new Exception\InvalidArgumentException('Invalid parameter "url"' | |
331 . ' of "' . $url . '" must be a non-empty string and a valid' | |
332 . ' URL'); | |
333 } | |
334 $this->hubUrls[] = $url; | |
335 return $this; | |
336 } | |
337 | |
338 /** | |
339 * Add an array of Hub Server URLs supported by Publisher | |
340 * | |
341 * @param array $urls | |
342 * @return Subscriber | |
343 */ | |
344 public function addHubUrls(array $urls) | |
345 { | |
346 foreach ($urls as $url) { | |
347 $this->addHubUrl($url); | |
348 } | |
349 return $this; | |
350 } | |
351 | |
352 /** | |
353 * Remove a Hub Server URL | |
354 * | |
355 * @param string $url | |
356 * @return Subscriber | |
357 */ | |
358 public function removeHubUrl($url) | |
359 { | |
360 if (!in_array($url, $this->getHubUrls())) { | |
361 return $this; | |
362 } | |
363 $key = array_search($url, $this->hubUrls); | |
364 unset($this->hubUrls[$key]); | |
365 return $this; | |
366 } | |
367 | |
368 /** | |
369 * Return an array of unique Hub Server URLs currently available | |
370 * | |
371 * @return array | |
372 */ | |
373 public function getHubUrls() | |
374 { | |
375 $this->hubUrls = array_unique($this->hubUrls); | |
376 return $this->hubUrls; | |
377 } | |
378 | |
379 /** | |
380 * Add authentication credentials for a given URL | |
381 * | |
382 * @param string $url | |
383 * @param array $authentication | |
384 * @return Subscriber | |
385 * @throws Exception\InvalidArgumentException | |
386 */ | |
387 public function addAuthentication($url, array $authentication) | |
388 { | |
389 if (empty($url) || !is_string($url) || !Uri::factory($url)->isValid()) { | |
390 throw new Exception\InvalidArgumentException('Invalid parameter "url"' | |
391 . ' of "' . $url . '" must be a non-empty string and a valid' | |
392 . ' URL'); | |
393 } | |
394 $this->authentications[$url] = $authentication; | |
395 return $this; | |
396 } | |
397 | |
398 /** | |
399 * Add authentication credentials for hub URLs | |
400 * | |
401 * @param array $authentications | |
402 * @return Subscriber | |
403 */ | |
404 public function addAuthentications(array $authentications) | |
405 { | |
406 foreach ($authentications as $url => $authentication) { | |
407 $this->addAuthentication($url, $authentication); | |
408 } | |
409 return $this; | |
410 } | |
411 | |
412 /** | |
413 * Get all hub URL authentication credentials | |
414 * | |
415 * @return array | |
416 */ | |
417 public function getAuthentications() | |
418 { | |
419 return $this->authentications; | |
420 } | |
421 | |
422 /** | |
423 * Set flag indicating whether or not to use a path parameter | |
424 * | |
425 * @param bool $bool | |
426 * @return Subscriber | |
427 */ | |
428 public function usePathParameter($bool = true) | |
429 { | |
430 $this->usePathParameter = $bool; | |
431 return $this; | |
432 } | |
433 | |
434 /** | |
435 * Add an optional parameter to the (un)subscribe requests | |
436 * | |
437 * @param string $name | |
438 * @param string|null $value | |
439 * @return Subscriber | |
440 * @throws Exception\InvalidArgumentException | |
441 */ | |
442 public function setParameter($name, $value = null) | |
443 { | |
444 if (is_array($name)) { | |
445 $this->setParameters($name); | |
446 return $this; | |
447 } | |
448 if (empty($name) || !is_string($name)) { | |
449 throw new Exception\InvalidArgumentException('Invalid parameter "name"' | |
450 . ' of "' . $name . '" must be a non-empty string'); | |
451 } | |
452 if ($value === null) { | |
453 $this->removeParameter($name); | |
454 return $this; | |
455 } | |
456 if (empty($value) || (!is_string($value) && $value !== null)) { | |
457 throw new Exception\InvalidArgumentException('Invalid parameter "value"' | |
458 . ' of "' . $value . '" must be a non-empty string'); | |
459 } | |
460 $this->parameters[$name] = $value; | |
461 return $this; | |
462 } | |
463 | |
464 /** | |
465 * Add an optional parameter to the (un)subscribe requests | |
466 * | |
467 * @param array $parameters | |
468 * @return Subscriber | |
469 */ | |
470 public function setParameters(array $parameters) | |
471 { | |
472 foreach ($parameters as $name => $value) { | |
473 $this->setParameter($name, $value); | |
474 } | |
475 return $this; | |
476 } | |
477 | |
478 /** | |
479 * Remove an optional parameter for the (un)subscribe requests | |
480 * | |
481 * @param string $name | |
482 * @return Subscriber | |
483 * @throws Exception\InvalidArgumentException | |
484 */ | |
485 public function removeParameter($name) | |
486 { | |
487 if (empty($name) || !is_string($name)) { | |
488 throw new Exception\InvalidArgumentException('Invalid parameter "name"' | |
489 . ' of "' . $name . '" must be a non-empty string'); | |
490 } | |
491 if (array_key_exists($name, $this->parameters)) { | |
492 unset($this->parameters[$name]); | |
493 } | |
494 return $this; | |
495 } | |
496 | |
497 /** | |
498 * Return an array of optional parameters for (un)subscribe requests | |
499 * | |
500 * @return array | |
501 */ | |
502 public function getParameters() | |
503 { | |
504 return $this->parameters; | |
505 } | |
506 | |
507 /** | |
508 * Sets an instance of Zend\Feed\Pubsubhubbub\Model\SubscriptionPersistence used to background | |
509 * save any verification tokens associated with a subscription or other. | |
510 * | |
511 * @param Model\SubscriptionPersistenceInterface $storage | |
512 * @return Subscriber | |
513 */ | |
514 public function setStorage(Model\SubscriptionPersistenceInterface $storage) | |
515 { | |
516 $this->storage = $storage; | |
517 return $this; | |
518 } | |
519 | |
520 /** | |
521 * Gets an instance of Zend\Feed\Pubsubhubbub\Storage\StoragePersistence used | |
522 * to background save any verification tokens associated with a subscription | |
523 * or other. | |
524 * | |
525 * @return Model\SubscriptionPersistenceInterface | |
526 * @throws Exception\RuntimeException | |
527 */ | |
528 public function getStorage() | |
529 { | |
530 if ($this->storage === null) { | |
531 throw new Exception\RuntimeException('No storage vehicle ' | |
532 . 'has been set.'); | |
533 } | |
534 return $this->storage; | |
535 } | |
536 | |
537 /** | |
538 * Subscribe to one or more Hub Servers using the stored Hub URLs | |
539 * for the given Topic URL (RSS or Atom feed) | |
540 * | |
541 * @return void | |
542 */ | |
543 public function subscribeAll() | |
544 { | |
545 $this->_doRequest('subscribe'); | |
546 } | |
547 | |
548 /** | |
549 * Unsubscribe from one or more Hub Servers using the stored Hub URLs | |
550 * for the given Topic URL (RSS or Atom feed) | |
551 * | |
552 * @return void | |
553 */ | |
554 public function unsubscribeAll() | |
555 { | |
556 $this->_doRequest('unsubscribe'); | |
557 } | |
558 | |
559 /** | |
560 * Returns a boolean indicator of whether the notifications to Hub | |
561 * Servers were ALL successful. If even one failed, FALSE is returned. | |
562 * | |
563 * @return bool | |
564 */ | |
565 public function isSuccess() | |
566 { | |
567 if (count($this->errors) > 0) { | |
568 return false; | |
569 } | |
570 return true; | |
571 } | |
572 | |
573 /** | |
574 * Return an array of errors met from any failures, including keys: | |
575 * 'response' => the Zend\Http\Response object from the failure | |
576 * 'hubUrl' => the URL of the Hub Server whose notification failed | |
577 * | |
578 * @return array | |
579 */ | |
580 public function getErrors() | |
581 { | |
582 return $this->errors; | |
583 } | |
584 | |
585 /** | |
586 * Return an array of Hub Server URLs who returned a response indicating | |
587 * operation in Asynchronous Verification Mode, i.e. they will not confirm | |
588 * any (un)subscription immediately but at a later time (Hubs may be | |
589 * doing this as a batch process when load balancing) | |
590 * | |
591 * @return array | |
592 */ | |
593 public function getAsyncHubs() | |
594 { | |
595 return $this->asyncHubs; | |
596 } | |
597 | |
598 /** | |
599 * Executes an (un)subscribe request | |
600 * | |
601 * @param string $mode | |
602 * @return void | |
603 * @throws Exception\RuntimeException | |
604 */ | |
605 protected function _doRequest($mode) | |
606 { | |
607 $client = $this->_getHttpClient(); | |
608 $hubs = $this->getHubUrls(); | |
609 if (empty($hubs)) { | |
610 throw new Exception\RuntimeException('No Hub Server URLs' | |
611 . ' have been set so no subscriptions can be attempted'); | |
612 } | |
613 $this->errors = []; | |
614 $this->asyncHubs = []; | |
615 foreach ($hubs as $url) { | |
616 if (array_key_exists($url, $this->authentications)) { | |
617 $auth = $this->authentications[$url]; | |
618 $client->setAuth($auth[0], $auth[1]); | |
619 } | |
620 $client->setUri($url); | |
621 $client->setRawBody($params = $this->_getRequestParameters($url, $mode)); | |
622 $response = $client->send(); | |
623 if ($response->getStatusCode() !== 204 | |
624 && $response->getStatusCode() !== 202 | |
625 ) { | |
626 $this->errors[] = [ | |
627 'response' => $response, | |
628 'hubUrl' => $url, | |
629 ]; | |
630 /** | |
631 * At first I thought it was needed, but the backend storage will | |
632 * allow tracking async without any user interference. It's left | |
633 * here in case the user is interested in knowing what Hubs | |
634 * are using async verification modes so they may update Models and | |
635 * move these to asynchronous processes. | |
636 */ | |
637 } elseif ($response->getStatusCode() == 202) { | |
638 $this->asyncHubs[] = [ | |
639 'response' => $response, | |
640 'hubUrl' => $url, | |
641 ]; | |
642 } | |
643 } | |
644 } | |
645 | |
646 /** | |
647 * Get a basic prepared HTTP client for use | |
648 * | |
649 * @return \Zend\Http\Client | |
650 */ | |
651 protected function _getHttpClient() | |
652 { | |
653 $client = PubSubHubbub::getHttpClient(); | |
654 $client->setMethod(HttpRequest::METHOD_POST); | |
655 $client->setOptions(['useragent' => 'Zend_Feed_Pubsubhubbub_Subscriber/' | |
656 . Version::VERSION]); | |
657 return $client; | |
658 } | |
659 | |
660 /** | |
661 * Return a list of standard protocol/optional parameters for addition to | |
662 * client's POST body that are specific to the current Hub Server URL | |
663 * | |
664 * @param string $hubUrl | |
665 * @param string $mode | |
666 * @return string | |
667 * @throws Exception\InvalidArgumentException | |
668 */ | |
669 protected function _getRequestParameters($hubUrl, $mode) | |
670 { | |
671 if (!in_array($mode, ['subscribe', 'unsubscribe'])) { | |
672 throw new Exception\InvalidArgumentException('Invalid mode specified: "' | |
673 . $mode . '" which should have been "subscribe" or "unsubscribe"'); | |
674 } | |
675 | |
676 $params = [ | |
677 'hub.mode' => $mode, | |
678 'hub.topic' => $this->getTopicUrl(), | |
679 ]; | |
680 | |
681 if ($this->getPreferredVerificationMode() | |
682 == PubSubHubbub::VERIFICATION_MODE_SYNC | |
683 ) { | |
684 $vmodes = [ | |
685 PubSubHubbub::VERIFICATION_MODE_SYNC, | |
686 PubSubHubbub::VERIFICATION_MODE_ASYNC, | |
687 ]; | |
688 } else { | |
689 $vmodes = [ | |
690 PubSubHubbub::VERIFICATION_MODE_ASYNC, | |
691 PubSubHubbub::VERIFICATION_MODE_SYNC, | |
692 ]; | |
693 } | |
694 $params['hub.verify'] = []; | |
695 foreach ($vmodes as $vmode) { | |
696 $params['hub.verify'][] = $vmode; | |
697 } | |
698 | |
699 /** | |
700 * Establish a persistent verify_token and attach key to callback | |
701 * URL's path/query_string | |
702 */ | |
703 $key = $this->_generateSubscriptionKey($params, $hubUrl); | |
704 $token = $this->_generateVerifyToken(); | |
705 $params['hub.verify_token'] = $token; | |
706 | |
707 // Note: query string only usable with PuSH 0.2 Hubs | |
708 if (!$this->usePathParameter) { | |
709 $params['hub.callback'] = $this->getCallbackUrl() | |
710 . '?xhub.subscription=' . PubSubHubbub::urlencode($key); | |
711 } else { | |
712 $params['hub.callback'] = rtrim($this->getCallbackUrl(), '/') | |
713 . '/' . PubSubHubbub::urlencode($key); | |
714 } | |
715 if ($mode == 'subscribe' && $this->getLeaseSeconds() !== null) { | |
716 $params['hub.lease_seconds'] = $this->getLeaseSeconds(); | |
717 } | |
718 | |
719 // hub.secret not currently supported | |
720 $optParams = $this->getParameters(); | |
721 foreach ($optParams as $name => $value) { | |
722 $params[$name] = $value; | |
723 } | |
724 | |
725 // store subscription to storage | |
726 $now = new DateTime(); | |
727 $expires = null; | |
728 if (isset($params['hub.lease_seconds'])) { | |
729 $expires = $now->add(new DateInterval('PT' . $params['hub.lease_seconds'] . 'S')) | |
730 ->format('Y-m-d H:i:s'); | |
731 } | |
732 $data = [ | |
733 'id' => $key, | |
734 'topic_url' => $params['hub.topic'], | |
735 'hub_url' => $hubUrl, | |
736 'created_time' => $now->format('Y-m-d H:i:s'), | |
737 'lease_seconds' => $params['hub.lease_seconds'], | |
738 'verify_token' => hash('sha256', $params['hub.verify_token']), | |
739 'secret' => null, | |
740 'expiration_time' => $expires, | |
741 'subscription_state' => ($mode == 'unsubscribe')? PubSubHubbub::SUBSCRIPTION_TODELETE : PubSubHubbub::SUBSCRIPTION_NOTVERIFIED, | |
742 ]; | |
743 $this->getStorage()->setSubscription($data); | |
744 | |
745 return $this->_toByteValueOrderedString( | |
746 $this->_urlEncode($params) | |
747 ); | |
748 } | |
749 | |
750 /** | |
751 * Simple helper to generate a verification token used in (un)subscribe | |
752 * requests to a Hub Server. Follows no particular method, which means | |
753 * it might be improved/changed in future. | |
754 * | |
755 * @return string | |
756 */ | |
757 protected function _generateVerifyToken() | |
758 { | |
759 if (!empty($this->testStaticToken)) { | |
760 return $this->testStaticToken; | |
761 } | |
762 return uniqid(rand(), true) . time(); | |
763 } | |
764 | |
765 /** | |
766 * Simple helper to generate a verification token used in (un)subscribe | |
767 * requests to a Hub Server. | |
768 * | |
769 * @param array $params | |
770 * @param string $hubUrl The Hub Server URL for which this token will apply | |
771 * @return string | |
772 */ | |
773 protected function _generateSubscriptionKey(array $params, $hubUrl) | |
774 { | |
775 $keyBase = $params['hub.topic'] . $hubUrl; | |
776 $key = md5($keyBase); | |
777 | |
778 return $key; | |
779 } | |
780 | |
781 /** | |
782 * URL Encode an array of parameters | |
783 * | |
784 * @param array $params | |
785 * @return array | |
786 */ | |
787 protected function _urlEncode(array $params) | |
788 { | |
789 $encoded = []; | |
790 foreach ($params as $key => $value) { | |
791 if (is_array($value)) { | |
792 $ekey = PubSubHubbub::urlencode($key); | |
793 $encoded[$ekey] = []; | |
794 foreach ($value as $duplicateKey) { | |
795 $encoded[$ekey][] | |
796 = PubSubHubbub::urlencode($duplicateKey); | |
797 } | |
798 } else { | |
799 $encoded[PubSubHubbub::urlencode($key)] | |
800 = PubSubHubbub::urlencode($value); | |
801 } | |
802 } | |
803 return $encoded; | |
804 } | |
805 | |
806 /** | |
807 * Order outgoing parameters | |
808 * | |
809 * @param array $params | |
810 * @return array | |
811 */ | |
812 protected function _toByteValueOrderedString(array $params) | |
813 { | |
814 $return = []; | |
815 uksort($params, 'strnatcmp'); | |
816 foreach ($params as $key => $value) { | |
817 if (is_array($value)) { | |
818 foreach ($value as $keyduplicate) { | |
819 $return[] = $key . '=' . $keyduplicate; | |
820 } | |
821 } else { | |
822 $return[] = $key . '=' . $value; | |
823 } | |
824 } | |
825 return implode('&', $return); | |
826 } | |
827 | |
828 /** | |
829 * This is STRICTLY for testing purposes only... | |
830 */ | |
831 protected $testStaticToken = null; | |
832 | |
833 final public function setTestStaticToken($token) | |
834 { | |
835 $this->testStaticToken = (string) $token; | |
836 } | |
837 } |