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