Chris@0
|
1 <?php
|
Chris@0
|
2 /**
|
Chris@0
|
3 * Zend Framework (http://framework.zend.com/)
|
Chris@0
|
4 *
|
Chris@0
|
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
|
Chris@0
|
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
|
Chris@0
|
7 * @license http://framework.zend.com/license/new-bsd New BSD License
|
Chris@0
|
8 */
|
Chris@0
|
9
|
Chris@0
|
10 namespace Zend\Feed\PubSubHubbub;
|
Chris@0
|
11
|
Chris@0
|
12 use Traversable;
|
Chris@0
|
13 use Zend\Http\PhpEnvironment\Response as PhpResponse;
|
Chris@0
|
14 use Zend\Stdlib\ArrayUtils;
|
Chris@0
|
15
|
Chris@0
|
16 abstract class AbstractCallback implements CallbackInterface
|
Chris@0
|
17 {
|
Chris@0
|
18 /**
|
Chris@0
|
19 * An instance of Zend\Feed\Pubsubhubbub\Model\SubscriptionPersistenceInterface
|
Chris@0
|
20 * used to background save any verification tokens associated with a subscription
|
Chris@0
|
21 * or other.
|
Chris@0
|
22 *
|
Chris@0
|
23 * @var Model\SubscriptionPersistenceInterface
|
Chris@0
|
24 */
|
Chris@0
|
25 protected $storage = null;
|
Chris@0
|
26
|
Chris@0
|
27 /**
|
Chris@0
|
28 * An instance of a class handling Http Responses. This is implemented in
|
Chris@0
|
29 * Zend\Feed\Pubsubhubbub\HttpResponse which shares an unenforced interface with
|
Chris@0
|
30 * (i.e. not inherited from) Zend\Controller\Response\Http.
|
Chris@0
|
31 *
|
Chris@0
|
32 * @var HttpResponse|PhpResponse
|
Chris@0
|
33 */
|
Chris@0
|
34 protected $httpResponse = null;
|
Chris@0
|
35
|
Chris@0
|
36 /**
|
Chris@16
|
37 * The input stream to use when retrieving the request body. Defaults to
|
Chris@16
|
38 * php://input, but can be set to another value in order to force usage
|
Chris@16
|
39 * of another input method. This should primarily be used for testing
|
Chris@16
|
40 * purposes.
|
Chris@16
|
41 *
|
Chris@16
|
42 * @var string|resource String indicates a filename or stream to open;
|
Chris@16
|
43 * resource indicates an already created stream to use.
|
Chris@16
|
44 */
|
Chris@16
|
45 protected $inputStream = 'php://input';
|
Chris@16
|
46
|
Chris@16
|
47 /**
|
Chris@0
|
48 * The number of Subscribers for which any updates are on behalf of.
|
Chris@0
|
49 *
|
Chris@0
|
50 * @var int
|
Chris@0
|
51 */
|
Chris@0
|
52 protected $subscriberCount = 1;
|
Chris@0
|
53
|
Chris@0
|
54 /**
|
Chris@0
|
55 * Constructor; accepts an array or Traversable object to preset
|
Chris@0
|
56 * options for the Subscriber without calling all supported setter
|
Chris@0
|
57 * methods in turn.
|
Chris@0
|
58 *
|
Chris@0
|
59 * @param array|Traversable $options Options array or Traversable object
|
Chris@0
|
60 */
|
Chris@0
|
61 public function __construct($options = null)
|
Chris@0
|
62 {
|
Chris@0
|
63 if ($options !== null) {
|
Chris@0
|
64 $this->setOptions($options);
|
Chris@0
|
65 }
|
Chris@0
|
66 }
|
Chris@0
|
67
|
Chris@0
|
68 /**
|
Chris@0
|
69 * Process any injected configuration options
|
Chris@0
|
70 *
|
Chris@0
|
71 * @param array|Traversable $options Options array or Traversable object
|
Chris@0
|
72 * @return AbstractCallback
|
Chris@0
|
73 * @throws Exception\InvalidArgumentException
|
Chris@0
|
74 */
|
Chris@0
|
75 public function setOptions($options)
|
Chris@0
|
76 {
|
Chris@0
|
77 if ($options instanceof Traversable) {
|
Chris@0
|
78 $options = ArrayUtils::iteratorToArray($options);
|
Chris@0
|
79 }
|
Chris@0
|
80
|
Chris@12
|
81 if (! is_array($options)) {
|
Chris@0
|
82 throw new Exception\InvalidArgumentException('Array or Traversable object'
|
Chris@0
|
83 . 'expected, got ' . gettype($options));
|
Chris@0
|
84 }
|
Chris@0
|
85
|
Chris@0
|
86 if (is_array($options)) {
|
Chris@0
|
87 $this->setOptions($options);
|
Chris@0
|
88 }
|
Chris@0
|
89
|
Chris@0
|
90 if (array_key_exists('storage', $options)) {
|
Chris@0
|
91 $this->setStorage($options['storage']);
|
Chris@0
|
92 }
|
Chris@0
|
93 return $this;
|
Chris@0
|
94 }
|
Chris@0
|
95
|
Chris@0
|
96 /**
|
Chris@0
|
97 * Send the response, including all headers.
|
Chris@0
|
98 * If you wish to handle this via Zend\Http, use the getter methods
|
Chris@0
|
99 * to retrieve any data needed to be set on your HTTP Response object, or
|
Chris@0
|
100 * simply give this object the HTTP Response instance to work with for you!
|
Chris@0
|
101 *
|
Chris@0
|
102 * @return void
|
Chris@0
|
103 */
|
Chris@0
|
104 public function sendResponse()
|
Chris@0
|
105 {
|
Chris@0
|
106 $this->getHttpResponse()->send();
|
Chris@0
|
107 }
|
Chris@0
|
108
|
Chris@0
|
109 /**
|
Chris@0
|
110 * Sets an instance of Zend\Feed\Pubsubhubbub\Model\SubscriptionPersistence used
|
Chris@0
|
111 * to background save any verification tokens associated with a subscription
|
Chris@0
|
112 * or other.
|
Chris@0
|
113 *
|
Chris@0
|
114 * @param Model\SubscriptionPersistenceInterface $storage
|
Chris@0
|
115 * @return AbstractCallback
|
Chris@0
|
116 */
|
Chris@0
|
117 public function setStorage(Model\SubscriptionPersistenceInterface $storage)
|
Chris@0
|
118 {
|
Chris@0
|
119 $this->storage = $storage;
|
Chris@0
|
120 return $this;
|
Chris@0
|
121 }
|
Chris@0
|
122
|
Chris@0
|
123 /**
|
Chris@0
|
124 * Gets an instance of Zend\Feed\Pubsubhubbub\Model\SubscriptionPersistence used
|
Chris@0
|
125 * to background save any verification tokens associated with a subscription
|
Chris@0
|
126 * or other.
|
Chris@0
|
127 *
|
Chris@0
|
128 * @return Model\SubscriptionPersistenceInterface
|
Chris@0
|
129 * @throws Exception\RuntimeException
|
Chris@0
|
130 */
|
Chris@0
|
131 public function getStorage()
|
Chris@0
|
132 {
|
Chris@0
|
133 if ($this->storage === null) {
|
Chris@0
|
134 throw new Exception\RuntimeException('No storage object has been'
|
Chris@0
|
135 . ' set that subclasses Zend\Feed\Pubsubhubbub\Model\SubscriptionPersistence');
|
Chris@0
|
136 }
|
Chris@0
|
137 return $this->storage;
|
Chris@0
|
138 }
|
Chris@0
|
139
|
Chris@0
|
140 /**
|
Chris@0
|
141 * An instance of a class handling Http Responses. This is implemented in
|
Chris@0
|
142 * Zend\Feed\Pubsubhubbub\HttpResponse which shares an unenforced interface with
|
Chris@0
|
143 * (i.e. not inherited from) Zend\Controller\Response\Http.
|
Chris@0
|
144 *
|
Chris@0
|
145 * @param HttpResponse|PhpResponse $httpResponse
|
Chris@0
|
146 * @return AbstractCallback
|
Chris@0
|
147 * @throws Exception\InvalidArgumentException
|
Chris@0
|
148 */
|
Chris@0
|
149 public function setHttpResponse($httpResponse)
|
Chris@0
|
150 {
|
Chris@12
|
151 if (! $httpResponse instanceof HttpResponse && ! $httpResponse instanceof PhpResponse) {
|
Chris@0
|
152 throw new Exception\InvalidArgumentException('HTTP Response object must'
|
Chris@0
|
153 . ' implement one of Zend\Feed\Pubsubhubbub\HttpResponse or'
|
Chris@0
|
154 . ' Zend\Http\PhpEnvironment\Response');
|
Chris@0
|
155 }
|
Chris@0
|
156 $this->httpResponse = $httpResponse;
|
Chris@0
|
157 return $this;
|
Chris@0
|
158 }
|
Chris@0
|
159
|
Chris@0
|
160 /**
|
Chris@0
|
161 * An instance of a class handling Http Responses. This is implemented in
|
Chris@0
|
162 * Zend\Feed\Pubsubhubbub\HttpResponse which shares an unenforced interface with
|
Chris@0
|
163 * (i.e. not inherited from) Zend\Controller\Response\Http.
|
Chris@0
|
164 *
|
Chris@0
|
165 * @return HttpResponse|PhpResponse
|
Chris@0
|
166 */
|
Chris@0
|
167 public function getHttpResponse()
|
Chris@0
|
168 {
|
Chris@0
|
169 if ($this->httpResponse === null) {
|
Chris@0
|
170 $this->httpResponse = new HttpResponse;
|
Chris@0
|
171 }
|
Chris@0
|
172 return $this->httpResponse;
|
Chris@0
|
173 }
|
Chris@0
|
174
|
Chris@0
|
175 /**
|
Chris@0
|
176 * Sets the number of Subscribers for which any updates are on behalf of.
|
Chris@0
|
177 * In other words, is this class serving one or more subscribers? How many?
|
Chris@0
|
178 * Defaults to 1 if left unchanged.
|
Chris@0
|
179 *
|
Chris@0
|
180 * @param string|int $count
|
Chris@0
|
181 * @return AbstractCallback
|
Chris@0
|
182 * @throws Exception\InvalidArgumentException
|
Chris@0
|
183 */
|
Chris@0
|
184 public function setSubscriberCount($count)
|
Chris@0
|
185 {
|
Chris@0
|
186 $count = intval($count);
|
Chris@0
|
187 if ($count <= 0) {
|
Chris@0
|
188 throw new Exception\InvalidArgumentException('Subscriber count must be'
|
Chris@0
|
189 . ' greater than zero');
|
Chris@0
|
190 }
|
Chris@0
|
191 $this->subscriberCount = $count;
|
Chris@0
|
192 return $this;
|
Chris@0
|
193 }
|
Chris@0
|
194
|
Chris@0
|
195 /**
|
Chris@0
|
196 * Gets the number of Subscribers for which any updates are on behalf of.
|
Chris@0
|
197 * In other words, is this class serving one or more subscribers? How many?
|
Chris@0
|
198 *
|
Chris@0
|
199 * @return int
|
Chris@0
|
200 */
|
Chris@0
|
201 public function getSubscriberCount()
|
Chris@0
|
202 {
|
Chris@0
|
203 return $this->subscriberCount;
|
Chris@0
|
204 }
|
Chris@0
|
205
|
Chris@0
|
206 /**
|
Chris@0
|
207 * Attempt to detect the callback URL (specifically the path forward)
|
Chris@0
|
208 * @return string
|
Chris@0
|
209 */
|
Chris@12
|
210 // @codingStandardsIgnoreStart
|
Chris@0
|
211 protected function _detectCallbackUrl()
|
Chris@0
|
212 {
|
Chris@12
|
213 // @codingStandardsIgnoreEnd
|
Chris@17
|
214 $callbackUrl = null;
|
Chris@17
|
215
|
Chris@17
|
216 // IIS7 with URL Rewrite: make sure we get the unencoded url
|
Chris@17
|
217 // (double slash problem).
|
Chris@17
|
218 $iisUrlRewritten = isset($_SERVER['IIS_WasUrlRewritten']) ? $_SERVER['IIS_WasUrlRewritten'] : null;
|
Chris@17
|
219 $unencodedUrl = isset($_SERVER['UNENCODED_URL']) ? $_SERVER['UNENCODED_URL'] : null;
|
Chris@17
|
220 if ('1' == $iisUrlRewritten && ! empty($unencodedUrl)) {
|
Chris@17
|
221 return $unencodedUrl;
|
Chris@0
|
222 }
|
Chris@17
|
223
|
Chris@17
|
224 // HTTP proxy requests setup request URI with scheme and host [and port]
|
Chris@17
|
225 // + the URL path, only use URL path.
|
Chris@17
|
226 if (isset($_SERVER['REQUEST_URI'])) {
|
Chris@17
|
227 $callbackUrl = $this->buildCallbackUrlFromRequestUri();
|
Chris@17
|
228 }
|
Chris@17
|
229
|
Chris@17
|
230 if (null !== $callbackUrl) {
|
Chris@17
|
231 return $callbackUrl;
|
Chris@17
|
232 }
|
Chris@17
|
233
|
Chris@17
|
234 if (isset($_SERVER['ORIG_PATH_INFO'])) {
|
Chris@17
|
235 return $this->buildCallbackUrlFromOrigPathInfo();
|
Chris@17
|
236 }
|
Chris@17
|
237
|
Chris@17
|
238 return '';
|
Chris@0
|
239 }
|
Chris@0
|
240
|
Chris@0
|
241 /**
|
Chris@0
|
242 * Get the HTTP host
|
Chris@0
|
243 *
|
Chris@0
|
244 * @return string
|
Chris@0
|
245 */
|
Chris@12
|
246 // @codingStandardsIgnoreStart
|
Chris@0
|
247 protected function _getHttpHost()
|
Chris@0
|
248 {
|
Chris@12
|
249 // @codingStandardsIgnoreEnd
|
Chris@12
|
250 if (! empty($_SERVER['HTTP_HOST'])) {
|
Chris@0
|
251 return $_SERVER['HTTP_HOST'];
|
Chris@0
|
252 }
|
Chris@17
|
253
|
Chris@17
|
254 $https = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : null;
|
Chris@17
|
255 $scheme = $https === 'on' ? 'https' : 'http';
|
Chris@17
|
256 $name = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
|
Chris@17
|
257 $port = isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : 80;
|
Chris@17
|
258
|
Chris@17
|
259 if (($scheme === 'http' && $port === 80)
|
Chris@17
|
260 || ($scheme === 'https' && $port === 443)
|
Chris@0
|
261 ) {
|
Chris@0
|
262 return $name;
|
Chris@0
|
263 }
|
Chris@0
|
264
|
Chris@17
|
265 return sprintf('%s:%d', $name, $port);
|
Chris@0
|
266 }
|
Chris@0
|
267
|
Chris@0
|
268 /**
|
Chris@0
|
269 * Retrieve a Header value from either $_SERVER or Apache
|
Chris@0
|
270 *
|
Chris@0
|
271 * @param string $header
|
Chris@0
|
272 * @return bool|string
|
Chris@0
|
273 */
|
Chris@12
|
274 // @codingStandardsIgnoreStart
|
Chris@0
|
275 protected function _getHeader($header)
|
Chris@0
|
276 {
|
Chris@12
|
277 // @codingStandardsIgnoreEnd
|
Chris@0
|
278 $temp = strtoupper(str_replace('-', '_', $header));
|
Chris@12
|
279 if (! empty($_SERVER[$temp])) {
|
Chris@0
|
280 return $_SERVER[$temp];
|
Chris@0
|
281 }
|
Chris@0
|
282 $temp = 'HTTP_' . strtoupper(str_replace('-', '_', $header));
|
Chris@12
|
283 if (! empty($_SERVER[$temp])) {
|
Chris@0
|
284 return $_SERVER[$temp];
|
Chris@0
|
285 }
|
Chris@0
|
286 if (function_exists('apache_request_headers')) {
|
Chris@0
|
287 $headers = apache_request_headers();
|
Chris@12
|
288 if (! empty($headers[$header])) {
|
Chris@0
|
289 return $headers[$header];
|
Chris@0
|
290 }
|
Chris@0
|
291 }
|
Chris@0
|
292 return false;
|
Chris@0
|
293 }
|
Chris@0
|
294
|
Chris@0
|
295 /**
|
Chris@0
|
296 * Return the raw body of the request
|
Chris@0
|
297 *
|
Chris@0
|
298 * @return string|false Raw body, or false if not present
|
Chris@0
|
299 */
|
Chris@12
|
300 // @codingStandardsIgnoreStart
|
Chris@0
|
301 protected function _getRawBody()
|
Chris@0
|
302 {
|
Chris@12
|
303 // @codingStandardsIgnoreEnd
|
Chris@16
|
304 $body = is_resource($this->inputStream)
|
Chris@16
|
305 ? stream_get_contents($this->inputStream)
|
Chris@16
|
306 : file_get_contents($this->inputStream);
|
Chris@16
|
307
|
Chris@16
|
308 return strlen(trim($body)) > 0 ? $body : false;
|
Chris@0
|
309 }
|
Chris@17
|
310
|
Chris@17
|
311 /**
|
Chris@17
|
312 * Build the callback URL from the REQUEST_URI server parameter.
|
Chris@17
|
313 *
|
Chris@17
|
314 * @return string
|
Chris@17
|
315 */
|
Chris@17
|
316 private function buildCallbackUrlFromRequestUri()
|
Chris@17
|
317 {
|
Chris@17
|
318 $callbackUrl = $_SERVER['REQUEST_URI'];
|
Chris@17
|
319 $https = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : null;
|
Chris@17
|
320 $scheme = $https === 'on' ? 'https' : 'http';
|
Chris@17
|
321 if ($https === 'on') {
|
Chris@17
|
322 $scheme = 'https';
|
Chris@17
|
323 }
|
Chris@17
|
324 $schemeAndHttpHost = $scheme . '://' . $this->_getHttpHost();
|
Chris@17
|
325 if (strpos($callbackUrl, $schemeAndHttpHost) === 0) {
|
Chris@17
|
326 $callbackUrl = substr($callbackUrl, strlen($schemeAndHttpHost));
|
Chris@17
|
327 }
|
Chris@17
|
328 return $callbackUrl;
|
Chris@17
|
329 }
|
Chris@17
|
330
|
Chris@17
|
331 /**
|
Chris@17
|
332 * Build the callback URL from the ORIG_PATH_INFO server parameter.
|
Chris@17
|
333 *
|
Chris@17
|
334 * @return string
|
Chris@17
|
335 */
|
Chris@17
|
336 private function buildCallbackUrlFromOrigPathInfo()
|
Chris@17
|
337 {
|
Chris@17
|
338 $callbackUrl = $_SERVER['ORIG_PATH_INFO'];
|
Chris@17
|
339 if (! empty($_SERVER['QUERY_STRING'])) {
|
Chris@17
|
340 $callbackUrl .= '?' . $_SERVER['QUERY_STRING'];
|
Chris@17
|
341 }
|
Chris@17
|
342 return $callbackUrl;
|
Chris@17
|
343 }
|
Chris@0
|
344 }
|