1 <?php
2
3 /*
4 * This file is part of the ICanBoogie package.
5 *
6 * (c) Olivier Laviale <olivier.laviale@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace ICanBoogie\HTTP;
13
14 use ICanBoogie\Accessor\AccessorTrait;
15 use ICanBoogie\Prototype\MethodNotDefined;
16
17 /**
18 * An HTTP request.
19 *
20 * <pre>
21 * <?php
22 *
23 * use ICanBoogie\HTTP\Request;
24 *
25 * # Creating the main request
26 *
27 * $request = Request::from($_SERVER);
28 *
29 * # Creating a request from scratch, with the current environment.
30 *
31 * $request = Request::from([
32 *
33 * 'uri' => '/path/to/my/page.html?page=2',
34 * 'user_agent' => 'Mozilla'
35 * 'is_get' => true,
36 * 'is_xhr' => true,
37 * 'is_local' => true
38 *
39 * ], $_SERVER);
40 * </pre>
41 *
42 * @method Response connect() connect(array $params=null)
43 * @method Response delete() delete(array $params=null)
44 * @method Response get() get(array $params=null)
45 * @method Response head() head(array $params=null)
46 * @method Response options() options(array $params=null)
47 * @method Response post() post(array $params=null)
48 * @method Response put() put(array $params=null)
49 * @method Response patch() patch(array $params=null)
50 * @method Response trace() trace(array $params=null)
51 *
52 * @property-read \ICanBoogie\HTTP\Request\Context $context the request's context.
53 * @property-read Request $parent Parent request.
54 * @property-read FileList $files The files associated with the request.
55 *
56 * @property-read boolean $authorization Authorization of the request.
57 * @property-read int $content_length Length of the request content.
58 * @property-read int $cache_control A {@link \ICanBoogie\HTTP\Headers\CacheControl} object.
59 * @property-read string $ip Remote IP of the request.
60 * @property-read boolean $is_delete Is this a `DELETE` request?
61 * @property-read boolean $is_get Is this a `GET` request?
62 * @property-read boolean $is_head Is this a `HEAD` request?
63 * @property-read boolean $is_options Is this a `OPTIONS` request?
64 * @property-read boolean $is_patch Is this a `PATCH` request?
65 * @property-read boolean $is_post Is this a `POST` request?
66 * @property-read boolean $is_put Is this a `PUT` request?
67 * @property-read boolean $is_trace Is this a `TRACE` request?
68 * @property-read boolean $is_local Is this a local request?
69 * @property-read boolean $is_xhr Is this an Ajax request?
70 * @property-read string $method Method of the request.
71 * @property-read string $normalized_path Path of the request normalized using the {@link \ICanBoogie\normalize_url_path()} function.
72 * @property-read string $path Path info of the request.
73 * @property-read string $extension The extension of the path.
74 * @property-read int $port Port of the request.
75 * @property-read string $query_string Query string of the request.
76 * @property-read string $script_name Name of the entered script.
77 * @property-read string $referer Referer of the request.
78 * @property-read string $user_agent User agent of the request.
79 * @property-read string $uri URI of the request. The `QUERY_STRING` value of the environment
80 * is overwritten when the instance is created with the {@link $uri} property.
81 *
82 * @see http://en.wikipedia.org/wiki/Uniform_resource_locator
83 */
84 class Request implements \ArrayAccess, \IteratorAggregate
85 {
86 use AccessorTrait;
87
88 /*
89 * HTTP methods as defined by the {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html Hypertext Transfer protocol 1.1}.
90 */
91 const METHOD_ANY = 'ANY';
92 const METHOD_CONNECT = 'CONNECT';
93 const METHOD_DELETE = 'DELETE';
94 const METHOD_GET = 'GET';
95 const METHOD_HEAD = 'HEAD';
96 const METHOD_OPTIONS = 'OPTIONS';
97 const METHOD_POST = 'POST';
98 const METHOD_PUT = 'PUT';
99 const METHOD_PATCH = 'PATCH';
100 const METHOD_TRACE = 'TRACE';
101
102 static public $methods = [
103
104 self::METHOD_CONNECT,
105 self::METHOD_DELETE,
106 self::METHOD_GET,
107 self::METHOD_HEAD,
108 self::METHOD_OPTIONS,
109 self::METHOD_POST,
110 self::METHOD_PUT,
111 self::METHOD_PATCH,
112 self::METHOD_TRACE
113
114 ];
115
116 static private $properties_mappers;
117
118 /**
119 * Returns request properties mappers.
120 *
121 * @return \Closure[]
122 */
123 static protected function get_properties_mappers()
124 {
125 if (self::$properties_mappers)
126 {
127 return self::$properties_mappers;
128 }
129
130 return self::$properties_mappers = static::create_properties_mappers();
131 }
132
133 /**
134 * Returns request properties mappers.
135 *
136 * @return \Closure[]
137 */
138 static protected function create_properties_mappers()
139 {
140 return [
141
142 'path_params' => function($value) { return $value; },
143 'query_params' => function($value) { return $value; },
144 'request_params' => function($value) { return $value; },
145 'cookie' => function($value) { return $value; },
146 'files' => function($value) { return $value; },
147 'headers' => function($value) { return ($value instanceof Headers) ? $value : new Headers($value); },
148
149 'cache_control' => function($value, array &$env) { $env['HTTP_CACHE_CONTROL'] = $value; },
150 'content_length' => function($value, array &$env) { $env['CONTENT_LENGTH'] = $value; },
151 'ip' => function($value, array &$env) { if ($value) $env['REMOTE_ADDR'] = $value; },
152 'is_local' => function($value, array &$env) { if ($value) $env['REMOTE_ADDR'] = '::1'; },
153 'is_delete' => function($value, array &$env) { if ($value) $env['REQUEST_METHOD'] = Request::METHOD_DELETE; },
154 'is_connect' => function($value, array &$env) { if ($value) $env['REQUEST_METHOD'] = Request::METHOD_CONNECT; },
155 'is_get' => function($value, array &$env) { if ($value) $env['REQUEST_METHOD'] = Request::METHOD_GET; },
156 'is_head' => function($value, array &$env) { if ($value) $env['REQUEST_METHOD'] = Request::METHOD_HEAD; },
157 'is_options' => function($value, array &$env) { if ($value) $env['REQUEST_METHOD'] = Request::METHOD_OPTIONS; },
158 'is_patch' => function($value, array &$env) { if ($value) $env['REQUEST_METHOD'] = Request::METHOD_PATCH; },
159 'is_post' => function($value, array &$env) { if ($value) $env['REQUEST_METHOD'] = Request::METHOD_POST; },
160 'is_put' => function($value, array &$env) { if ($value) $env['REQUEST_METHOD'] = Request::METHOD_PUT; },
161 'is_trace' => function($value, array &$env) { if ($value) $env['REQUEST_METHOD'] = Request::METHOD_TRACE; },
162 'is_xhr' => function($value, array &$env) { if ($value) $env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'; else unset($env['HTTP_X_REQUESTED_WITH']); },
163 'method' => function($value, array &$env) { if ($value) $env['REQUEST_METHOD'] = $value; },
164 'path' => function($value, array &$env) { $env['REQUEST_URI'] = $value; }, // TODO-20130521: handle query string
165 'referer' => function($value, array &$env) { $env['HTTP_REFERER'] = $value; },
166 'uri' => function($value, array &$env) { $env['REQUEST_URI'] = $value; $qs = strpos($value, '?'); $env['QUERY_STRING'] = $qs === false ? '' : substr($value, $qs + 1); },
167 'user_agent' => function($value, array &$env) { $env['HTTP_USER_AGENT'] = $value; }
168
169 ];
170 }
171
172 /**
173 * Current request.
174 *
175 * @var Request
176 */
177 static protected $current_request;
178
179 /**
180 * Returns the current request.
181 *
182 * @return Request
183 */
184 static public function get_current_request()
185 {
186 return self::$current_request;
187 }
188
189 /**
190 * Parameters extracted from the request path.
191 *
192 * @var array
193 */
194 public $path_params = [];
195
196 /**
197 * Parameters defined by the query string.
198 *
199 * @var array
200 */
201 public $query_params = [];
202
203 /**
204 * Parameters defined by the request body.
205 *
206 * @var array
207 */
208 public $request_params = [];
209
210 /**
211 * Union of {@link $path_params}, {@link $request_params} and {@link $query_params}.
212 *
213 * @var array
214 */
215 public $params;
216
217 /**
218 * General purpose container.
219 *
220 * @var Request\Context
221 */
222 protected $context;
223
224 /**
225 * The headers of the request.
226 *
227 * @var Headers
228 */
229 public $headers;
230
231 /**
232 * Request environment.
233 *
234 * @var array
235 */
236 protected $env;
237
238 /**
239 * Files associated with the request.
240 *
241 * @var FileList
242 */
243 protected $files;
244
245 protected function get_files()
246 {
247 if ($this->files instanceof FileList)
248 {
249 return $this->files;
250 }
251
252 return $this->files = FileList::from($this->files);
253 }
254
255 public $cookie;
256
257 /**
258 * Parent request.
259 *
260 * @var Request
261 */
262 protected $parent;
263
264 /**
265 * A request can be created from the `$_SERVER` super global array. In that case `$_SERVER` is
266 * used as environment the request is created with the following properties:
267 *
268 * - {@link $cookie}: a reference to the `$_COOKIE` super global array.
269 * - {@link $path_params}: initialized to an empty array.
270 * - {@link $query_params}: a reference to the `$_GET` super global array.
271 * - {@link $request_params}: a reference to the `$_POST` super global array.
272 * - {@link $files}: a reference to the `$_FILES` super global array.
273 *
274 * A request can also be created from an array of properties, in which case most of them are
275 * mapped to the `$env` constructor param. For instance, `is_xhr` set the
276 * `HTTP_X_REQUESTED_WITH` environment property to 'XMLHttpRequest'. In fact, only the
277 * following parameters are preserved:
278 *
279 * - `path_params`
280 * - `query_params`
281 * - `request_params`
282 * - `files`: The files associated with the request.
283 * - `headers`: The header fields of the request. If specified, the headers available in the
284 * environment are ignored.
285 *
286 * @param array $properties Properties of the request.
287 * @param array $env Environment, usually the `$_SERVER` array.
288 *
289 * @throws \InvalidArgumentException in attempt to use a property that is not mapped to an
290 * environment property.
291 *
292 * @return Request
293 */
294 static public function from($properties = null, array $env = [])
295 {
296 if (!$properties)
297 {
298 return new static([], $env);
299 }
300
301 if ($properties === $_SERVER)
302 {
303 return static::from_server();
304 }
305
306 if (is_string($properties) || (is_object($properties) && method_exists($properties, '__toString')))
307 {
308 return static::from_uri((string) $properties, $env);
309 }
310
311 return static::from_properties($properties, $env);
312 }
313
314 /**
315 * Creates an instance from the `$_SERVER` array.
316 *
317 * @return Request
318 */
319 static protected function from_server()
320 {
321 return static::from([
322
323 'cookie' => &$_COOKIE,
324 'path_params' => [],
325 'query_params' => &$_GET,
326 'request_params' => &$_POST,
327 'files' => &$_FILES // @codeCoverageIgnore
328
329 ], $_SERVER);
330 }
331
332 /**
333 * Creates an instance from an URI.
334 *
335 * @param string $uri
336 * @param array $env
337 *
338 * @return Request
339 */
340 static protected function from_uri($uri, array $env)
341 {
342 return static::from([ 'uri' => $uri ], $env);
343 }
344
345 /**
346 * Creates an instance from an array of properties.
347 *
348 * @param array $properties
349 * @param array $env
350 *
351 * @return Request
352 */
353 static protected function from_properties(array $properties, array $env)
354 {
355 $properties = $properties ?: [];
356
357 $mappers = static::get_properties_mappers();
358
359 foreach ($properties as $property => &$value)
360 {
361 if (empty($mappers[$property]))
362 {
363 throw new \InvalidArgumentException("Unsupported property: <q>$property</q>.");
364 }
365
366 $value = $mappers[$property]($value, $env);
367
368 if ($value === null)
369 {
370 unset($properties[$property]);
371 }
372 }
373
374 if (!empty($env['QUERY_STRING']))
375 {
376 parse_str($env['QUERY_STRING'], $properties['query_params']);
377 }
378
379 return new static($properties, $env);
380 }
381
382 /**
383 * Initialize the properties {@link $env}, {@link $headers} and {@link $context}.
384 *
385 * If the {@link $params} property is `null` it is set with an union of {@link $path_params},
386 * {@link $request_params} and {@link $query_params}.
387 *
388 * @param array $properties Initial properties.
389 * @param array $env Environment of the request, usually the `$_SERVER` super global.
390 *
391 * @throws MethodNotSupported when the request method is not supported.
392 */
393 protected function __construct(array $properties, array $env = [])
394 {
395 $this->context = new Request\Context($this);
396 $this->env = $env;
397
398 foreach ($properties as $property => $value)
399 {
400 $this->$property = $value;
401 }
402
403 $this->assert_method($this->method);
404
405 if (!$this->headers)
406 {
407 $this->headers = new Headers($env);
408 }
409
410 if ($this->params === null)
411 {
412 $this->params = $this->path_params + $this->request_params + $this->query_params;
413 }
414 }
415
416 /**
417 * Clone {@link $headers} and {@link $context}, and unset {@link $params}.
418 */
419 public function __clone()
420 {
421 $this->headers = clone $this->headers;
422 $this->context = clone $this->context;
423 unset($this->params);
424 }
425
426 /**
427 * Alias for {@link send()}.
428 *
429 * @return Response The response to the request.
430 */
431 public function __invoke()
432 {
433 return $this->send();
434 }
435
436 /**
437 * Dispatch the request.
438 *
439 * The {@link parent} property is used for request chaining.
440 *
441 * Note: If an exception is thrown during dispatch {@link $current_request} is not updated!
442 *
443 * Note: If the request is changed because of the `$method` or `$params` parameters, it
444 * is the _changed_ instance that is dispatched, not the actual instance.
445 *
446 * @param string|null $method Use this parameter to change the request method.
447 * @param array|null $params Use this parameter to change the {@link $request_params}
448 * property of the request.
449 *
450 * @return Response The response to the request.
451 *
452 * @throws \Exception re-throws exception raised during dispatch.
453 */
454 public function send($method = null, array $params = null)
455 {
456 $request = $this->adapt($method, $params);
457
458 $this->parent = self::$current_request;
459
460 self::$current_request = $request;
461
462 try
463 {
464 $response = $request->dispatch();
465
466 self::$current_request = $request->parent;
467
468 return $response;
469 }
470 catch (\Exception $e)
471 {
472 self::$current_request = $request->parent;
473
474 throw $e;
475 }
476 }
477
478 /**
479 * Dispatches the request using the {@link dispatch()} helper.
480 *
481 * @return Response
482 */
483 protected function dispatch()
484 {
485 return dispatch($this); // @codeCoverageIgnore
486 }
487
488 /**
489 * Asserts that a method is supported.
490 *
491 * @param string $method
492 *
493 * @throws MethodNotSupported
494 */
495 private function assert_method($method)
496 {
497 if (!in_array($method, self::$methods))
498 {
499 throw new MethodNotSupported($method);
500 }
501 }
502
503 /**
504 * Returns a new instance with the specified changed properties.
505 *
506 * @param array $properties
507 *
508 * @throws \InvalidArgumentException
509 *
510 * @return Request
511 */
512 public function with(array $properties)
513 {
514 $changed = clone $this;
515 $mappers = static::get_properties_mappers();
516 $env = &$changed->env;
517
518 foreach ($properties as $property => $value)
519 {
520 if (empty($mappers[$property]))
521 {
522 throw new \InvalidArgumentException("Unsupported property: <q>$property</q>.");
523 }
524
525 $value = $mappers[$property]($value, $env);
526
527 if ($value === null)
528 {
529 continue;
530 }
531
532 $changed->$property = $value;
533 }
534
535 return $changed;
536 }
537
538 /**
539 * Adapts the request to the specified method and params.
540 *
541 * @param string $method The method.
542 * @param array $params The params.
543 *
544 * @return Request The same instance is returned if the method is the same and the params
545 * are `null`. Otherwise a _changed_ request is returned.
546 */
547 protected function adapt($method, array $params = null)
548 {
549 if ((!$method || $method == $this->method) && !$params)
550 {
551 return $this;
552 }
553
554 $properties = [];
555
556 if ($method)
557 {
558 $properties = [ 'method' => $method ];
559 }
560
561 if ($params !== null)
562 {
563 $properties['request_params'] = $params;
564 $properties['path_params'] = [];
565 $properties['query_params'] = [];
566 }
567
568 return $this->with($properties);
569 }
570
571 /**
572 * Overrides the method to provide a virtual method for each request method.
573 *
574 * Example:
575 *
576 * <pre>
577 * <?php
578 *
579 * Request::from('/api/core/aloha')->get();
580 * </pre>
581 *
582 * @param $method
583 * @param $arguments
584 *
585 * @return mixed
586 */
587 public function __call($method, $arguments)
588 {
589 $http_method = strtoupper($method);
590
591 if (in_array($http_method, self::$methods))
592 {
593 array_unshift($arguments, $http_method);
594
595 return call_user_func_array([ $this, 'send' ], $arguments);
596 }
597
598 throw new MethodNotDefined($method, $this);
599 }
600
601 /**
602 * Checks if the specified param exists in the request's params.
603 *
604 * @param string $param The name of the parameter.
605 *
606 * @return bool
607 */
608 public function offsetExists($param)
609 {
610 return isset($this->params[$param]);
611 }
612
613 /**
614 * Get the specified param from the request's params.
615 *
616 * @param string $param The name of the parameter.
617 *
618 * @return mixed|null The value of the parameter, or `null` if the parameter does not exists.
619 */
620 public function offsetGet($param)
621 {
622 return isset($this->params[$param]) ? $this->params[$param] : null;
623 }
624
625 /**
626 * Set the specified param to the specified value.
627 *
628 * @param string $param The name of the parameter.
629 * @param mixed $value The value of the parameter.
630 */
631 public function offsetSet($param, $value)
632 {
633 $this->params;
634 $this->params[$param] = $value;
635 $this->request_params[$param] = $value;
636 }
637
638 /**
639 * Remove the specified param from the request's parameters.
640 *
641 * @param mixed $param
642 */
643 public function offsetUnset($param)
644 {
645 unset($this->params[$param]);
646 }
647
648 /**
649 * Returns an array iterator for the params.
650 *
651 * @return \ArrayIterator
652 */
653 public function getIterator()
654 {
655 return new \ArrayIterator($this->params);
656 }
657
658 /**
659 * Returns the parent request.
660 *
661 * @return Request
662 */
663 protected function get_parent()
664 {
665 return $this->parent;
666 }
667
668 /**
669 * Returns the request's context.
670 *
671 * @return Request\Context
672 */
673 protected function get_context()
674 {
675 return $this->context;
676 }
677
678 /**
679 * Returns the `Cache-Control` header.
680 *
681 * @return Headers\CacheControl
682 */
683 protected function get_cache_control()
684 {
685 return $this->headers['Cache-Control'];
686 }
687
688 /**
689 * Returns the script name.
690 *
691 * The setter is volatile, the value is returned from the ENV key `SCRIPT_NAME`.
692 *
693 * @return string
694 */
695 protected function get_script_name()
696 {
697 return $this->env['SCRIPT_NAME'];
698 }
699
700 /**
701 * Returns the request method.
702 *
703 * This is the getter for the `method` magic property.
704 *
705 * The method is retrieved from {@link $env}, if the key `REQUEST_METHOD` is not defined,
706 * the method defaults to {@link METHOD_GET}.
707 *
708 * @return string
709 */
710 protected function get_method()
711 {
712 $method = isset($this->env['REQUEST_METHOD']) ? $this->env['REQUEST_METHOD'] : self::METHOD_GET;
713
714 if ($method == self::METHOD_POST && !empty($this->request_params['_method']))
715 {
716 $method = strtoupper($this->request_params['_method']);
717 }
718
719 return $method;
720 }
721
722 /**
723 * Returns the query string of the request.
724 *
725 * The value is obtained from the `QUERY_STRING` key of the {@link $env} array.
726 *
727 * @return string|null
728 */
729 protected function get_query_string()
730 {
731 return isset($this->env['QUERY_STRING']) ? $this->env['QUERY_STRING'] : null;
732 }
733
734 /**
735 * Returns the content length of the request.
736 *
737 * The value is obtained from the `CONTENT_LENGTH` key of the {@link $env} array.
738 *
739 * @return int|null
740 */
741 protected function get_content_length()
742 {
743 return isset($this->env['CONTENT_LENGTH']) ? $this->env['CONTENT_LENGTH'] : null;
744 }
745
746 /**
747 * Returns the referer of the request.
748 *
749 * The value is obtained from the `HTTP_REFERER` key of the {@link $env} array.
750 *
751 * @return string|null
752 */
753 protected function get_referer()
754 {
755 return isset($this->env['HTTP_REFERER']) ? $this->env['HTTP_REFERER'] : null;
756 }
757
758 /**
759 * Returns the user agent of the request.
760 *
761 * The value is obtained from the `HTTP_USER_AGENT` key of the {@link $env} array.
762 *
763 * @return string|null
764 */
765 protected function get_user_agent()
766 {
767 return isset($this->env['HTTP_USER_AGENT']) ? $this->env['HTTP_USER_AGENT'] : null;
768 }
769
770 /**
771 * Checks if the request method is `DELETE`.
772 *
773 * @return boolean
774 */
775 protected function get_is_delete()
776 {
777 return $this->method == self::METHOD_DELETE;
778 }
779
780 /**
781 * Checks if the request method is `GET`.
782 *
783 * @return boolean
784 */
785 protected function get_is_get()
786 {
787 return $this->method == self::METHOD_GET;
788 }
789
790 /**
791 * Checks if the request method is `HEAD`.
792 *
793 * @return boolean
794 */
795 protected function get_is_head()
796 {
797 return $this->method == self::METHOD_HEAD;
798 }
799
800 /**
801 * Checks if the request method is `OPTIONS`.
802 *
803 * @return boolean
804 */
805 protected function get_is_options()
806 {
807 return $this->method == self::METHOD_OPTIONS;
808 }
809
810 /**
811 * Checks if the request method is `PATCH`.
812 *
813 * @return boolean
814 */
815 protected function get_is_patch()
816 {
817 return $this->method == self::METHOD_PATCH;
818 }
819
820 /**
821 * Checks if the request method is `POST`.
822 *
823 * @return boolean
824 */
825 protected function get_is_post()
826 {
827 return $this->method == self::METHOD_POST;
828 }
829
830 /**
831 * Checks if the request method is `PUT`.
832 *
833 * @return boolean
834 */
835 protected function get_is_put()
836 {
837 return $this->method == self::METHOD_PUT;
838 }
839
840 /**
841 * Checks if the request method is `TRACE`.
842 *
843 * @return boolean
844 */
845 protected function get_is_trace()
846 {
847 return $this->method == self::METHOD_TRACE;
848 }
849
850 /**
851 * Checks if the request is a `XMLHTTPRequest`.
852 *
853 * @return boolean
854 */
855 protected function get_is_xhr()
856 {
857 return !empty($this->env['HTTP_X_REQUESTED_WITH']) && preg_match('/XMLHttpRequest/', $this->env['HTTP_X_REQUESTED_WITH']);
858 }
859
860 /**
861 * Checks if the request is local.
862 *
863 * @return boolean
864 */
865 protected function get_is_local()
866 {
867 $ip = $this->ip;
868
869 if ($ip == '::1' || preg_match('/^127\.0\.0\.\d{1,3}$/', $ip))
870 {
871 return true;
872 }
873
874 return preg_match('/^0:0:0:0:0:0:0:1(%.*)?$/', $ip);
875 }
876
877 /**
878 * Returns the remote IP of the request.
879 *
880 * If defined, the `HTTP_X_FORWARDED_FOR` header is used to retrieve the original IP.
881 *
882 * If the `REMOTE_ADDR` header is empty the request is considered local thus `::1` is returned.
883 *
884 * @see http://en.wikipedia.org/wiki/X-Forwarded-For
885 *
886 * @return string
887 */
888 protected function get_ip()
889 {
890 $forwarded_for = $this->headers['X-Forwarded-For'];
891
892 if ($forwarded_for)
893 {
894 list($ip) = explode(',', $forwarded_for);
895
896 return $ip;
897 }
898
899 return (isset($this->env['REMOTE_ADDR']) ? $this->env['REMOTE_ADDR'] : null) ?: '::1';
900 }
901
902 protected function get_authorization()
903 {
904 if (isset($this->env['HTTP_AUTHORIZATION']))
905 {
906 return $this->env['HTTP_AUTHORIZATION'];
907 }
908 else if (isset($this->env['X-HTTP_AUTHORIZATION']))
909 {
910 return $this->env['X-HTTP_AUTHORIZATION'];
911 }
912 else if (isset($this->env['X_HTTP_AUTHORIZATION']))
913 {
914 return $this->env['X_HTTP_AUTHORIZATION'];
915 }
916 else if (isset($this->env['REDIRECT_X_HTTP_AUTHORIZATION']))
917 {
918 return $this->env['REDIRECT_X_HTTP_AUTHORIZATION'];
919 }
920
921 return null;
922 }
923
924 /**
925 * Returns the `REQUEST_URI` environment key.
926 *
927 * If the `REQUEST_URI` key is not defined by the environment, the value is fetched from
928 * the `$_SERVER` array. If the key is not defined in the `$_SERVER` array `null` is returned.
929 *
930 * @return string
931 */
932 protected function get_uri()
933 {
934 return isset($this->env['REQUEST_URI'])
935 ? $this->env['REQUEST_URI']
936 : (isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : null);
937 }
938
939 /**
940 * Returns the port of the request.
941 *
942 * @return int
943 */
944 protected function get_port()
945 {
946 return $this->env['REQUEST_PORT'];
947 }
948
949 /**
950 * Returns the path of the request, that is the `REQUEST_URI` without the query string.
951 *
952 * @return string
953 */
954 protected function get_path()
955 {
956 $uri = $this->uri;
957 $qs_pos = strpos($uri, '?');
958
959 return ($qs_pos === false) ? $uri : substr($uri, 0, $qs_pos);
960 }
961
962 /**
963 * Returns the {@link $path} property normalized using the
964 * {@link \ICanBoogie\normalize_url_path()} function.
965 *
966 * @return string
967 */
968 protected function get_normalized_path()
969 {
970 return \ICanBoogie\normalize_url_path($this->path);
971 }
972
973 /**
974 * Returns the extension of the path info.
975 *
976 * @return mixed
977 */
978 protected function get_extension()
979 {
980 return pathinfo($this->path, PATHINFO_EXTENSION);
981 }
982
983 protected function lazy_set_params($params)
984 {
985 return $params;
986 }
987
988 /**
989 * Returns the union of the {@link path_params}, {@link request_params} and
990 * {@link query_params} properties.
991 *
992 * This method is the getter of the {@link $params} magic property.
993 *
994 * @return array
995 */
996 protected function lazy_get_params()
997 {
998 return $this->path_params + $this->request_params + $this->query_params;
999 }
1000 }
1001