Chris@0: setOptions($options); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Process any injected configuration options Chris@0: * Chris@0: * @param array|Traversable $options Chris@0: * @return Subscriber Chris@0: * @throws Exception\InvalidArgumentException Chris@0: */ Chris@0: public function setOptions($options) Chris@0: { Chris@0: if ($options instanceof Traversable) { Chris@0: $options = ArrayUtils::iteratorToArray($options); Chris@0: } Chris@0: Chris@12: if (! is_array($options)) { Chris@0: throw new Exception\InvalidArgumentException('Array or Traversable object' Chris@0: . 'expected, got ' . gettype($options)); Chris@0: } Chris@0: if (array_key_exists('hubUrls', $options)) { Chris@0: $this->addHubUrls($options['hubUrls']); Chris@0: } Chris@0: if (array_key_exists('callbackUrl', $options)) { Chris@0: $this->setCallbackUrl($options['callbackUrl']); Chris@0: } Chris@0: if (array_key_exists('topicUrl', $options)) { Chris@0: $this->setTopicUrl($options['topicUrl']); Chris@0: } Chris@0: if (array_key_exists('storage', $options)) { Chris@0: $this->setStorage($options['storage']); Chris@0: } Chris@0: if (array_key_exists('leaseSeconds', $options)) { Chris@0: $this->setLeaseSeconds($options['leaseSeconds']); Chris@0: } Chris@0: if (array_key_exists('parameters', $options)) { Chris@0: $this->setParameters($options['parameters']); Chris@0: } Chris@0: if (array_key_exists('authentications', $options)) { Chris@0: $this->addAuthentications($options['authentications']); Chris@0: } Chris@0: if (array_key_exists('usePathParameter', $options)) { Chris@0: $this->usePathParameter($options['usePathParameter']); Chris@0: } Chris@0: if (array_key_exists('preferredVerificationMode', $options)) { Chris@0: $this->setPreferredVerificationMode( Chris@0: $options['preferredVerificationMode'] Chris@0: ); Chris@0: } Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Set the topic URL (RSS or Atom feed) to which the intended (un)subscribe Chris@0: * event will relate Chris@0: * Chris@0: * @param string $url Chris@0: * @return Subscriber Chris@0: * @throws Exception\InvalidArgumentException Chris@0: */ Chris@0: public function setTopicUrl($url) Chris@0: { Chris@12: if (empty($url) || ! is_string($url) || ! Uri::factory($url)->isValid()) { Chris@0: throw new Exception\InvalidArgumentException('Invalid parameter "url"' Chris@0: .' of "' . $url . '" must be a non-empty string and a valid' Chris@0: .' URL'); Chris@0: } Chris@0: $this->topicUrl = $url; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Set the topic URL (RSS or Atom feed) to which the intended (un)subscribe Chris@0: * event will relate Chris@0: * Chris@0: * @return string Chris@0: * @throws Exception\RuntimeException Chris@0: */ Chris@0: public function getTopicUrl() Chris@0: { Chris@0: if (empty($this->topicUrl)) { Chris@0: throw new Exception\RuntimeException('A valid Topic (RSS or Atom' Chris@0: . ' feed) URL MUST be set before attempting any operation'); Chris@0: } Chris@0: return $this->topicUrl; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Set the number of seconds for which any subscription will remain valid Chris@0: * Chris@0: * @param int $seconds Chris@0: * @return Subscriber Chris@0: * @throws Exception\InvalidArgumentException Chris@0: */ Chris@0: public function setLeaseSeconds($seconds) Chris@0: { Chris@0: $seconds = intval($seconds); Chris@0: if ($seconds <= 0) { Chris@0: throw new Exception\InvalidArgumentException('Expected lease seconds' Chris@0: . ' must be an integer greater than zero'); Chris@0: } Chris@0: $this->leaseSeconds = $seconds; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get the number of lease seconds on subscriptions Chris@0: * Chris@0: * @return int Chris@0: */ Chris@0: public function getLeaseSeconds() Chris@0: { Chris@0: return $this->leaseSeconds; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Set the callback URL to be used by Hub Servers when communicating with Chris@0: * this Subscriber Chris@0: * Chris@0: * @param string $url Chris@0: * @return Subscriber Chris@0: * @throws Exception\InvalidArgumentException Chris@0: */ Chris@0: public function setCallbackUrl($url) Chris@0: { Chris@12: if (empty($url) || ! is_string($url) || ! Uri::factory($url)->isValid()) { Chris@0: throw new Exception\InvalidArgumentException('Invalid parameter "url"' Chris@0: . ' of "' . $url . '" must be a non-empty string and a valid' Chris@0: . ' URL'); Chris@0: } Chris@0: $this->callbackUrl = $url; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get the callback URL to be used by Hub Servers when communicating with Chris@0: * this Subscriber Chris@0: * Chris@0: * @return string Chris@0: * @throws Exception\RuntimeException Chris@0: */ Chris@0: public function getCallbackUrl() Chris@0: { Chris@0: if (empty($this->callbackUrl)) { Chris@0: throw new Exception\RuntimeException('A valid Callback URL MUST be' Chris@0: . ' set before attempting any operation'); Chris@0: } Chris@0: return $this->callbackUrl; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Set preferred verification mode (sync or async). By default, this Chris@0: * Subscriber prefers synchronous verification, but does support Chris@0: * asynchronous if that's the Hub Server's utilised mode. Chris@0: * Chris@0: * Zend\Feed\Pubsubhubbub\Subscriber will always send both modes, whose Chris@0: * order of occurrence in the parameter list determines this preference. Chris@0: * Chris@0: * @param string $mode Should be 'sync' or 'async' Chris@0: * @return Subscriber Chris@0: * @throws Exception\InvalidArgumentException Chris@0: */ Chris@0: public function setPreferredVerificationMode($mode) Chris@0: { Chris@0: if ($mode !== PubSubHubbub::VERIFICATION_MODE_SYNC Chris@0: && $mode !== PubSubHubbub::VERIFICATION_MODE_ASYNC Chris@0: ) { Chris@0: throw new Exception\InvalidArgumentException('Invalid preferred' Chris@0: . ' mode specified: "' . $mode . '" but should be one of' Chris@0: . ' Zend\Feed\Pubsubhubbub::VERIFICATION_MODE_SYNC or' Chris@0: . ' Zend\Feed\Pubsubhubbub::VERIFICATION_MODE_ASYNC'); Chris@0: } Chris@0: $this->preferredVerificationMode = $mode; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get preferred verification mode (sync or async). Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: public function getPreferredVerificationMode() Chris@0: { Chris@0: return $this->preferredVerificationMode; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Add a Hub Server URL supported by Publisher Chris@0: * Chris@0: * @param string $url Chris@0: * @return Subscriber Chris@0: * @throws Exception\InvalidArgumentException Chris@0: */ Chris@0: public function addHubUrl($url) Chris@0: { Chris@12: if (empty($url) || ! is_string($url) || ! Uri::factory($url)->isValid()) { Chris@0: throw new Exception\InvalidArgumentException('Invalid parameter "url"' Chris@0: . ' of "' . $url . '" must be a non-empty string and a valid' Chris@0: . ' URL'); Chris@0: } Chris@0: $this->hubUrls[] = $url; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Add an array of Hub Server URLs supported by Publisher Chris@0: * Chris@0: * @param array $urls Chris@0: * @return Subscriber Chris@0: */ Chris@0: public function addHubUrls(array $urls) Chris@0: { Chris@0: foreach ($urls as $url) { Chris@0: $this->addHubUrl($url); Chris@0: } Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Remove a Hub Server URL Chris@0: * Chris@0: * @param string $url Chris@0: * @return Subscriber Chris@0: */ Chris@0: public function removeHubUrl($url) Chris@0: { Chris@12: if (! in_array($url, $this->getHubUrls())) { Chris@0: return $this; Chris@0: } Chris@0: $key = array_search($url, $this->hubUrls); Chris@0: unset($this->hubUrls[$key]); Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Return an array of unique Hub Server URLs currently available Chris@0: * Chris@0: * @return array Chris@0: */ Chris@0: public function getHubUrls() Chris@0: { Chris@0: $this->hubUrls = array_unique($this->hubUrls); Chris@0: return $this->hubUrls; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Add authentication credentials for a given URL Chris@0: * Chris@0: * @param string $url Chris@0: * @param array $authentication Chris@0: * @return Subscriber Chris@0: * @throws Exception\InvalidArgumentException Chris@0: */ Chris@0: public function addAuthentication($url, array $authentication) Chris@0: { Chris@12: if (empty($url) || ! is_string($url) || ! Uri::factory($url)->isValid()) { Chris@0: throw new Exception\InvalidArgumentException('Invalid parameter "url"' Chris@0: . ' of "' . $url . '" must be a non-empty string and a valid' Chris@0: . ' URL'); Chris@0: } Chris@0: $this->authentications[$url] = $authentication; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Add authentication credentials for hub URLs Chris@0: * Chris@0: * @param array $authentications Chris@0: * @return Subscriber Chris@0: */ Chris@0: public function addAuthentications(array $authentications) Chris@0: { Chris@0: foreach ($authentications as $url => $authentication) { Chris@0: $this->addAuthentication($url, $authentication); Chris@0: } Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get all hub URL authentication credentials Chris@0: * Chris@0: * @return array Chris@0: */ Chris@0: public function getAuthentications() Chris@0: { Chris@0: return $this->authentications; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Set flag indicating whether or not to use a path parameter Chris@0: * Chris@0: * @param bool $bool Chris@0: * @return Subscriber Chris@0: */ Chris@0: public function usePathParameter($bool = true) Chris@0: { Chris@0: $this->usePathParameter = $bool; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Add an optional parameter to the (un)subscribe requests Chris@0: * Chris@0: * @param string $name Chris@0: * @param string|null $value Chris@0: * @return Subscriber Chris@0: * @throws Exception\InvalidArgumentException Chris@0: */ Chris@0: public function setParameter($name, $value = null) Chris@0: { Chris@0: if (is_array($name)) { Chris@0: $this->setParameters($name); Chris@0: return $this; Chris@0: } Chris@12: if (empty($name) || ! is_string($name)) { Chris@0: throw new Exception\InvalidArgumentException('Invalid parameter "name"' Chris@0: . ' of "' . $name . '" must be a non-empty string'); Chris@0: } Chris@0: if ($value === null) { Chris@0: $this->removeParameter($name); Chris@0: return $this; Chris@0: } Chris@12: if (empty($value) || (! is_string($value) && $value !== null)) { Chris@0: throw new Exception\InvalidArgumentException('Invalid parameter "value"' Chris@0: . ' of "' . $value . '" must be a non-empty string'); Chris@0: } Chris@0: $this->parameters[$name] = $value; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Add an optional parameter to the (un)subscribe requests Chris@0: * Chris@0: * @param array $parameters Chris@0: * @return Subscriber Chris@0: */ Chris@0: public function setParameters(array $parameters) Chris@0: { Chris@0: foreach ($parameters as $name => $value) { Chris@0: $this->setParameter($name, $value); Chris@0: } Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Remove an optional parameter for the (un)subscribe requests Chris@0: * Chris@0: * @param string $name Chris@0: * @return Subscriber Chris@0: * @throws Exception\InvalidArgumentException Chris@0: */ Chris@0: public function removeParameter($name) Chris@0: { Chris@12: if (empty($name) || ! is_string($name)) { Chris@0: throw new Exception\InvalidArgumentException('Invalid parameter "name"' Chris@0: . ' of "' . $name . '" must be a non-empty string'); Chris@0: } Chris@0: if (array_key_exists($name, $this->parameters)) { Chris@0: unset($this->parameters[$name]); Chris@0: } Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Return an array of optional parameters for (un)subscribe requests Chris@0: * Chris@0: * @return array Chris@0: */ Chris@0: public function getParameters() Chris@0: { Chris@0: return $this->parameters; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets an instance of Zend\Feed\Pubsubhubbub\Model\SubscriptionPersistence used to background Chris@0: * save any verification tokens associated with a subscription or other. Chris@0: * Chris@0: * @param Model\SubscriptionPersistenceInterface $storage Chris@0: * @return Subscriber Chris@0: */ Chris@0: public function setStorage(Model\SubscriptionPersistenceInterface $storage) Chris@0: { Chris@0: $this->storage = $storage; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets an instance of Zend\Feed\Pubsubhubbub\Storage\StoragePersistence used Chris@0: * to background save any verification tokens associated with a subscription Chris@0: * or other. Chris@0: * Chris@0: * @return Model\SubscriptionPersistenceInterface Chris@0: * @throws Exception\RuntimeException Chris@0: */ Chris@0: public function getStorage() Chris@0: { Chris@0: if ($this->storage === null) { Chris@0: throw new Exception\RuntimeException('No storage vehicle ' Chris@0: . 'has been set.'); Chris@0: } Chris@0: return $this->storage; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Subscribe to one or more Hub Servers using the stored Hub URLs Chris@0: * for the given Topic URL (RSS or Atom feed) Chris@0: * Chris@0: * @return void Chris@0: */ Chris@0: public function subscribeAll() Chris@0: { Chris@0: $this->_doRequest('subscribe'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Unsubscribe from one or more Hub Servers using the stored Hub URLs Chris@0: * for the given Topic URL (RSS or Atom feed) Chris@0: * Chris@0: * @return void Chris@0: */ Chris@0: public function unsubscribeAll() Chris@0: { Chris@0: $this->_doRequest('unsubscribe'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns a boolean indicator of whether the notifications to Hub Chris@0: * Servers were ALL successful. If even one failed, FALSE is returned. Chris@0: * Chris@0: * @return bool Chris@0: */ Chris@0: public function isSuccess() Chris@0: { Chris@17: return ! $this->errors; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Return an array of errors met from any failures, including keys: Chris@0: * 'response' => the Zend\Http\Response object from the failure Chris@0: * 'hubUrl' => the URL of the Hub Server whose notification failed Chris@0: * Chris@0: * @return array Chris@0: */ Chris@0: public function getErrors() Chris@0: { Chris@0: return $this->errors; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Return an array of Hub Server URLs who returned a response indicating Chris@0: * operation in Asynchronous Verification Mode, i.e. they will not confirm Chris@0: * any (un)subscription immediately but at a later time (Hubs may be Chris@0: * doing this as a batch process when load balancing) Chris@0: * Chris@0: * @return array Chris@0: */ Chris@0: public function getAsyncHubs() Chris@0: { Chris@0: return $this->asyncHubs; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Executes an (un)subscribe request Chris@0: * Chris@0: * @param string $mode Chris@0: * @return void Chris@0: * @throws Exception\RuntimeException Chris@0: */ Chris@12: // @codingStandardsIgnoreStart Chris@0: protected function _doRequest($mode) Chris@0: { Chris@12: // @codingStandardsIgnoreEnd Chris@0: $client = $this->_getHttpClient(); Chris@0: $hubs = $this->getHubUrls(); Chris@0: if (empty($hubs)) { Chris@0: throw new Exception\RuntimeException('No Hub Server URLs' Chris@0: . ' have been set so no subscriptions can be attempted'); Chris@0: } Chris@0: $this->errors = []; Chris@0: $this->asyncHubs = []; Chris@0: foreach ($hubs as $url) { Chris@0: if (array_key_exists($url, $this->authentications)) { Chris@0: $auth = $this->authentications[$url]; Chris@0: $client->setAuth($auth[0], $auth[1]); Chris@0: } Chris@0: $client->setUri($url); Chris@0: $client->setRawBody($params = $this->_getRequestParameters($url, $mode)); Chris@0: $response = $client->send(); Chris@0: if ($response->getStatusCode() !== 204 Chris@0: && $response->getStatusCode() !== 202 Chris@0: ) { Chris@0: $this->errors[] = [ Chris@0: 'response' => $response, Chris@0: 'hubUrl' => $url, Chris@0: ]; Chris@0: /** Chris@0: * At first I thought it was needed, but the backend storage will Chris@0: * allow tracking async without any user interference. It's left Chris@0: * here in case the user is interested in knowing what Hubs Chris@0: * are using async verification modes so they may update Models and Chris@0: * move these to asynchronous processes. Chris@0: */ Chris@0: } elseif ($response->getStatusCode() == 202) { Chris@0: $this->asyncHubs[] = [ Chris@0: 'response' => $response, Chris@0: 'hubUrl' => $url, Chris@0: ]; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get a basic prepared HTTP client for use Chris@0: * Chris@0: * @return \Zend\Http\Client Chris@0: */ Chris@12: // @codingStandardsIgnoreStart Chris@0: protected function _getHttpClient() Chris@0: { Chris@12: // @codingStandardsIgnoreEnd Chris@0: $client = PubSubHubbub::getHttpClient(); Chris@0: $client->setMethod(HttpRequest::METHOD_POST); Chris@0: $client->setOptions(['useragent' => 'Zend_Feed_Pubsubhubbub_Subscriber/' Chris@0: . Version::VERSION]); Chris@0: return $client; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Return a list of standard protocol/optional parameters for addition to Chris@0: * client's POST body that are specific to the current Hub Server URL Chris@0: * Chris@0: * @param string $hubUrl Chris@0: * @param string $mode Chris@17: * @return array Chris@0: * @throws Exception\InvalidArgumentException Chris@0: */ Chris@12: // @codingStandardsIgnoreStart Chris@0: protected function _getRequestParameters($hubUrl, $mode) Chris@0: { Chris@12: // @codingStandardsIgnoreEnd Chris@12: if (! in_array($mode, ['subscribe', 'unsubscribe'])) { Chris@0: throw new Exception\InvalidArgumentException('Invalid mode specified: "' Chris@0: . $mode . '" which should have been "subscribe" or "unsubscribe"'); Chris@0: } Chris@0: Chris@0: $params = [ Chris@0: 'hub.mode' => $mode, Chris@0: 'hub.topic' => $this->getTopicUrl(), Chris@0: ]; Chris@0: Chris@0: if ($this->getPreferredVerificationMode() Chris@0: == PubSubHubbub::VERIFICATION_MODE_SYNC Chris@0: ) { Chris@0: $vmodes = [ Chris@0: PubSubHubbub::VERIFICATION_MODE_SYNC, Chris@0: PubSubHubbub::VERIFICATION_MODE_ASYNC, Chris@0: ]; Chris@0: } else { Chris@0: $vmodes = [ Chris@0: PubSubHubbub::VERIFICATION_MODE_ASYNC, Chris@0: PubSubHubbub::VERIFICATION_MODE_SYNC, Chris@0: ]; Chris@0: } Chris@0: $params['hub.verify'] = []; Chris@0: foreach ($vmodes as $vmode) { Chris@0: $params['hub.verify'][] = $vmode; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Establish a persistent verify_token and attach key to callback Chris@0: * URL's path/query_string Chris@0: */ Chris@0: $key = $this->_generateSubscriptionKey($params, $hubUrl); Chris@0: $token = $this->_generateVerifyToken(); Chris@0: $params['hub.verify_token'] = $token; Chris@0: Chris@0: // Note: query string only usable with PuSH 0.2 Hubs Chris@12: if (! $this->usePathParameter) { Chris@0: $params['hub.callback'] = $this->getCallbackUrl() Chris@0: . '?xhub.subscription=' . PubSubHubbub::urlencode($key); Chris@0: } else { Chris@0: $params['hub.callback'] = rtrim($this->getCallbackUrl(), '/') Chris@0: . '/' . PubSubHubbub::urlencode($key); Chris@0: } Chris@0: if ($mode == 'subscribe' && $this->getLeaseSeconds() !== null) { Chris@0: $params['hub.lease_seconds'] = $this->getLeaseSeconds(); Chris@0: } Chris@0: Chris@0: // hub.secret not currently supported Chris@0: $optParams = $this->getParameters(); Chris@0: foreach ($optParams as $name => $value) { Chris@0: $params[$name] = $value; Chris@0: } Chris@0: Chris@0: // store subscription to storage Chris@0: $now = new DateTime(); Chris@0: $expires = null; Chris@0: if (isset($params['hub.lease_seconds'])) { Chris@0: $expires = $now->add(new DateInterval('PT' . $params['hub.lease_seconds'] . 'S')) Chris@0: ->format('Y-m-d H:i:s'); Chris@0: } Chris@0: $data = [ Chris@0: 'id' => $key, Chris@0: 'topic_url' => $params['hub.topic'], Chris@0: 'hub_url' => $hubUrl, Chris@0: 'created_time' => $now->format('Y-m-d H:i:s'), Chris@0: 'lease_seconds' => $params['hub.lease_seconds'], Chris@0: 'verify_token' => hash('sha256', $params['hub.verify_token']), Chris@0: 'secret' => null, Chris@0: 'expiration_time' => $expires, Chris@12: // @codingStandardsIgnoreStart Chris@12: 'subscription_state' => ($mode == 'unsubscribe') ? PubSubHubbub::SUBSCRIPTION_TODELETE : PubSubHubbub::SUBSCRIPTION_NOTVERIFIED, Chris@12: // @codingStandardsIgnoreEnd Chris@0: ]; Chris@0: $this->getStorage()->setSubscription($data); Chris@0: Chris@0: return $this->_toByteValueOrderedString( Chris@0: $this->_urlEncode($params) Chris@0: ); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Simple helper to generate a verification token used in (un)subscribe Chris@0: * requests to a Hub Server. Follows no particular method, which means Chris@0: * it might be improved/changed in future. Chris@0: * Chris@0: * @return string Chris@0: */ Chris@12: // @codingStandardsIgnoreStart Chris@0: protected function _generateVerifyToken() Chris@0: { Chris@12: // @codingStandardsIgnoreEnd Chris@12: if (! empty($this->testStaticToken)) { Chris@0: return $this->testStaticToken; Chris@0: } Chris@0: return uniqid(rand(), true) . time(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Simple helper to generate a verification token used in (un)subscribe Chris@0: * requests to a Hub Server. Chris@0: * Chris@0: * @param array $params Chris@0: * @param string $hubUrl The Hub Server URL for which this token will apply Chris@0: * @return string Chris@0: */ Chris@12: // @codingStandardsIgnoreStart Chris@0: protected function _generateSubscriptionKey(array $params, $hubUrl) Chris@0: { Chris@12: // @codingStandardsIgnoreEnd Chris@0: $keyBase = $params['hub.topic'] . $hubUrl; Chris@0: $key = md5($keyBase); Chris@0: Chris@0: return $key; Chris@0: } Chris@0: Chris@0: /** Chris@0: * URL Encode an array of parameters Chris@0: * Chris@0: * @param array $params Chris@0: * @return array Chris@0: */ Chris@12: // @codingStandardsIgnoreStart Chris@0: protected function _urlEncode(array $params) Chris@0: { Chris@12: // @codingStandardsIgnoreEnd Chris@0: $encoded = []; Chris@0: foreach ($params as $key => $value) { Chris@0: if (is_array($value)) { Chris@0: $ekey = PubSubHubbub::urlencode($key); Chris@0: $encoded[$ekey] = []; Chris@0: foreach ($value as $duplicateKey) { Chris@0: $encoded[$ekey][] Chris@0: = PubSubHubbub::urlencode($duplicateKey); Chris@0: } Chris@0: } else { Chris@0: $encoded[PubSubHubbub::urlencode($key)] Chris@0: = PubSubHubbub::urlencode($value); Chris@0: } Chris@0: } Chris@0: return $encoded; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Order outgoing parameters Chris@0: * Chris@0: * @param array $params Chris@0: * @return array Chris@0: */ Chris@12: // @codingStandardsIgnoreStart Chris@0: protected function _toByteValueOrderedString(array $params) Chris@0: { Chris@12: // @codingStandardsIgnoreEnd Chris@0: $return = []; Chris@0: uksort($params, 'strnatcmp'); Chris@0: foreach ($params as $key => $value) { Chris@0: if (is_array($value)) { Chris@0: foreach ($value as $keyduplicate) { Chris@0: $return[] = $key . '=' . $keyduplicate; Chris@0: } Chris@0: } else { Chris@0: $return[] = $key . '=' . $value; Chris@0: } Chris@0: } Chris@0: return implode('&', $return); Chris@0: } Chris@0: Chris@0: /** Chris@0: * This is STRICTLY for testing purposes only... Chris@0: */ Chris@0: protected $testStaticToken = null; Chris@0: Chris@0: final public function setTestStaticToken($token) Chris@0: { Chris@0: $this->testStaticToken = (string) $token; Chris@0: } Chris@0: }