Autodoc
  • Namespace
  • Class
  • Tree

Namespaces

  • BlueTihi
    • Context
  • Brickrouge
    • Element
      • Nodes
    • Renderer
    • Widget
  • ICanBoogie
    • ActiveRecord
    • AutoConfig
    • CLDR
    • Composer
    • Core
    • Event
    • Exception
    • HTTP
      • Dispatcher
      • Request
    • I18n
      • Translator
    • Mailer
    • Modules
      • Taxonomy
        • Support
      • Thumbnailer
        • Versions
    • Object
    • Operation
      • Dispatcher
    • Prototype
    • Routes
    • Routing
      • Dispatcher
    • Session
  • Icybee
    • ActiveRecord
      • Model
    • ConfigOperation
    • Document
    • EditBlock
    • Element
      • ActionbarContextual
      • ActionbarSearch
      • ActionbarToolbar
    • FormBlock
    • Installer
    • ManageBlock
    • Modules
      • Articles
      • Cache
        • Collection
        • ManageBlock
      • Comments
        • ManageBlock
      • Contents
        • ManageBlock
      • Dashboard
      • Editor
        • Collection
      • Files
        • File
        • ManageBlock
      • Forms
        • Form
        • ManageBlock
      • I18n
      • Images
        • ManageBlock
      • Members
      • Modules
        • ManageBlock
      • Nodes
        • ManageBlock
        • Module
      • Pages
        • BreadcrumbElement
        • LanguagesElement
        • ManageBlock
        • NavigationBranchElement
        • NavigationElement
        • Page
        • PageController
      • Registry
      • Search
      • Seo
      • Sites
        • ManageBlock
      • Taxonomy
        • Terms
          • ManageBlock
        • Vocabulary
          • ManageBlock
      • Users
        • ManageBlock
        • NonceLogin
        • Roles
      • Views
        • ActiveRecordProvider
        • Collection
        • View
    • Operation
      • ActiveRecord
      • Constructor
      • Module
      • Widget
    • Rendering
  • None
  • Patron
  • PHP

Classes

  • CacheControlHeader
  • CallableDispatcher
  • ContentDispositionHeader
  • ContentTypeHeader
  • DateHeader
  • Dispatcher
  • File
  • FileList
  • Header
  • HeaderParameter
  • Headers
  • Helpers
  • RedirectResponse
  • Request
  • Response
  • WeightedDispatcher

Interfaces

  • IDispatcher

Exceptions

  • ForceRedirect
  • HTTPError
  • MethodNotSupported
  • NotFound
  • ServiceUnavailable
  • StatusCodeNotValid

Functions

  • dispatch
  • get_dispatcher
  • get_initial_request
  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  * A response to a HTTP request.
 18  *
 19  * @property integer $status The HTTP status code.
 20  * See: {@link set_status()} {@link get_status()}
 21  * @property string $status_message The HTTP status message.
 22  * See: {@link set_status_message()} {@link get_status_message()}
 23  * @property int $ttl Adjusts the `s-maxage` part of the `Cache-Control` header field definition according to the `Age` header field definition.
 24  * See: {@link set_ttl()} {@link get_ttl()}
 25  *
 26  * @property int $age Shortcut to the `Age` header field definition.
 27  * See: {@link set_age()} {@link get_age()}
 28  * @property \ICanBoogie\HTTP\CacheControlHeader $cache_control Shortcut to the `Cache-Control` header field definition.
 29  * See: {@link set_cache_control()} {@link get_cache_control()}
 30  * @property int $content_length Shortcut to the `Content-Length` header field definition.
 31  * See: {@link set_content_length()} {@link get_content_length()}
 32  * @property \ICanBoogie\HTTP\ContentTypeHeader $content_type Shortcut to the `Content-Type` header field definition.
 33  * See: {@link set_content_type()} {@link get_content_type()}
 34  * @property \ICanBoogie\HTTP\DateHeader $date Shortcut to the `Date` header field definition.
 35  * See: {@link set_date()} {@link get_date()}
 36  * @property string $etag Shortcut to the `Etag` header field definition.
 37  * See: {@link set_etag()} {@link get_etag()}
 38  * @property \ICanBoogie\HTTP\DateHeader $expires Shortcut to the `Expires` header field definition.
 39  * See: {@link set_expires()} {@link get_expires()}
 40  * @property \ICanBoogie\HTTP\DateHeader $last_modified Shortcut to the `Last-Modified` header field definition.
 41  * See: {@link set_last_modified()} {@link get_last_modified()}
 42  * @property string $location Shortcut to the `Location` header field definition.
 43  * See: {@link set_location()} {@link get_location()}
 44  *
 45  * @property string|\Closure $body The body of the response.
 46  * See: {@link set_body()} {@link get_body()}
 47  *
 48  * @property-read boolean $is_cacheable {@link get_is_cacheable()}
 49  * @property-read boolean $is_client_error {@link get_is_client_error()}
 50  * @property-read boolean $is_empty {@link get_is_empty()}
 51  * @property-read boolean $is_forbidden {@link get_is_forbidden()}
 52  * @property-read boolean $is_fresh {@link get_is_fresh()}
 53  * @property-read boolean $is_informational {@link get_is_informational()}
 54  * @property-read boolean $is_not_found {@link get_is_not_found()}
 55  * @property-read boolean $is_ok {@link get_is_ok()}
 56  * @property-read boolean $is_private {@link get_is_private()}
 57  * @property-read boolean $is_redirect {@link get_is_redirect()}
 58  * @property-read boolean $is_server_error {@link get_is_server_error()}
 59  * @property-read boolean $is_successful {@link get_is_successful()}
 60  * @property-read boolean $is_valid {@link get_is_valid()}
 61  * @property-read boolean $is_validateable {@link get_is_validateable()}
 62  *
 63  * @see http://tools.ietf.org/html/rfc2616
 64  */
 65 class Response extends \ICanBoogie\Object
 66 {
 67     /**
 68      * HTTP status messages.
 69      *
 70      * @var array[int]string
 71      */
 72     static public $status_messages = array
 73     (
 74         100 => "Continue",
 75         101 => "Switching Protocols",
 76 
 77         200 => "OK",
 78         201 => "Created",
 79         202 => "Accepted",
 80         203 => "Non-Authoritative Information",
 81         204 => "No Content",
 82         205 => "Reset Content",
 83         206 => "Partial Content",
 84 
 85         300 => "Multiple Choices",
 86         301 => "Moved Permanently",
 87         302 => "Found",
 88         303 => "See Other",
 89         304 => "Not Modified",
 90         305 => "Use Proxy",
 91         307 => "Temporary Redirect",
 92 
 93         400 => "Bad Request",
 94         401 => "Unauthorized",
 95         402 => "Payment Required",
 96         403 => "Forbidden",
 97         404 => "Not Found",
 98         405 => "Method Not Allowed",
 99         406 => "Not Acceptable",
100         407 => "Proxy Authentication Required",
101         408 => "Request Timeout",
102         409 => "Conflict",
103         410 => "Gone",
104         411 => "Length Required",
105         412 => "Precondition Failed",
106         413 => "Request Entity Too Large",
107         414 => "Request-URI Too Long",
108         415 => "Unsupported Media Type",
109         416 => "Requested Range Not Satisfiable",
110         417 => "Expectation Failed",
111         418 => "I'm a teapot",
112 
113         500 => "Internal Server Error",
114         501 => "Not Implemented",
115         502 => "Bad Gateway",
116         503 => "Service Unavailable",
117         504 => "Gateway Timeout",
118         505 => "HTTP Version Not Supported"
119     );
120 
121     /**
122      * Response headers.
123      *
124      * @var Headers
125      */
126     public $headers;
127 
128     /**
129      * The HTTP protocol version (1.0 or 1.1), defaults to '1.0'
130      *
131      * @var string
132      */
133     public $version = '1.0';
134 
135     /**
136      * Initializes the {@link $body}, {@link $header}, {@link $date} and {@link $status}
137      * properties.
138      *
139      * @param mixed $body The body of the response.
140      * @param int $status The status code of the response.
141      * @param Headers|array $headers The initial header fields of the response.
142      */
143     public function __construct($body=null, $status=200, $headers=array())
144     {
145         if (is_array($headers))
146         {
147             $headers = new Headers($headers);
148         }
149         else if (!($headers instanceof Headers))
150         {
151             throw new \InvalidArgumentException("$headers must be an array or a ICanBoogie\HTTP\Headers instance. Given: " . gettype($headers));
152         }
153 
154         $this->headers = $headers;
155 
156         if (!$this->headers['Date'])
157         {
158             $this->date = 'now';
159         }
160 
161         $this->set_status($status);
162         $this->set_body($body);
163     }
164 
165     /**
166      * Handles read-only properties.
167      *
168      * @throws PropertyNotWritable in attempt to write one of the following properties:
169      * {@link $is_valid}, {@link $is_informational}, {@link $is_successful}, {@link $is_redirect),
170      * {@link $is_client_error}, {@link $is_server_error}, {@link $is_ok}, {@link $is_forbidden},
171      * {@link $is_not_found}, {@link $is_empty}, {@link $is_validateable}, {@link $is_cacheable},
172      * {@link $is_fresh}
173      */
174     public function __set($property, $value)
175     {
176         static $readonly = array
177         (
178             'is_valid',
179             'is_informational',
180             'is_successful',
181             'is_redirect',
182             'is_client_error',
183             'is_server_error',
184             'is_ok',
185             'is_forbidden',
186             'is_not_found',
187             'is_empty',
188             'is_validateable',
189             'is_cacheable',
190             'is_fresh'
191         );
192 
193         if (in_array($property, $readonly))
194         {
195             throw new PropertyNotWritable(array($property, $this));
196         }
197 
198         parent::__set($property, $value);
199     }
200 
201     /**
202      * Clones the {@link $headers] property.
203      */
204     public function __clone()
205     {
206         $this->headers = clone $this->headers;
207     }
208 
209     /**
210      * Renders the response as an HTTP string.
211      *
212      * @return string
213      */
214     public function __toString()
215     {
216         ob_start();
217 
218         $this->echo_body($this->body);
219 
220         $body = ob_get_clean();
221 
222         return "HTTP/{$this->version} {$this->status} {$this->status_message}\r\n"
223         . $this->headers
224         . "\r\n"
225         . $body;
226     }
227 
228     /**
229      * Issues the HTTP response.
230      *
231      * The header is modified according to the {@link version}, {@link status} and
232      * {@link status_message} properties.
233      *
234      * The usual behaviour of the response is to echo its body and then terminate the script. But
235      * if its body is `null` the following happens :
236      *
237      * - If the {@link $location} property is defined the script is terminated.
238      *
239      * - If the {@link $is_ok} property is falsy **the method returns**.
240      *
241      * Note: If the body is a string, or an object implementing the `__toString()` method, the
242      * `Content-Length` header is automatically defined to the lenght of the body string.
243      *
244      * Note: If the body is an instance of {@link Closure} it MUST echo the response's body.
245      */
246     public function __invoke()
247     {
248         #
249         # If the body is a string we add the `Content-Length`
250         #
251 
252         $body = $this->body;
253 
254         if (is_object($body) && method_exists($body, '__toString'))
255         {
256             $body = (string) $body;
257         }
258 
259         if (is_numeric($body) || is_string($body))
260         {
261             $this->headers['Content-Length'] = strlen($body);
262         }
263 
264         #
265         # send headers
266         #
267 
268         if (headers_sent($file, $line))
269         {
270             trigger_error(\ICanBoogie\format
271             (
272                 "Cannot modify header information because it was already sent. Output started at !at.", array
273                 (
274                     'at' => $file . ':' . $line
275                 )
276             ));
277         }
278         else
279         {
280             header_remove('Pragma');
281             header_remove('X-Powered-By');
282 
283             header("HTTP/{$this->version} {$this->status} {$this->status_message}");
284 
285             $this->headers();
286         }
287 
288         if ($body === null)
289         {
290             if ($this->location)
291             {
292                 exit;
293             }
294             else if (!$this->is_ok)
295             {
296                 return;
297             }
298         }
299 
300         $this->echo_body($body);
301 
302         exit;
303     }
304 
305     /**
306      * Status of the HTTP response.
307      *
308      * @var int
309      */
310     private $status;
311 
312     /**
313      * Message describing the status code.
314      *
315      * @var string
316      */
317     public $status_message;
318 
319     /**
320      * Sets response status code and optionally status message.
321      *
322      * This method is the setter for the {@link $status} property.
323      *
324      * @param integer|array $status HTTP status code or HTTP status code and HTTP status message.
325      *
326      * @throws \InvalidArgumentException When the HTTP status code is not valid.
327      */
328     protected function set_status($status)
329     {
330         $status_message = null;
331 
332         if (is_array($status))
333         {
334             list($status, $status_message) = $status;
335         }
336 
337         $this->status = (int) $status;
338 
339         if (!$this->is_valid)
340         {
341             throw new StatusCodeNotValid($status);
342         }
343 
344         if ($status_message === null)
345         {
346             unset($this->status_message);
347         }
348         else
349         {
350             $this->status_message = $status_message;
351         }
352     }
353 
354     /**
355      * Returns the response status code.
356      *
357      * This method is the getter for the {@link $status} property.
358      *
359      * @return integer
360      */
361     protected function get_status()
362     {
363         return $this->status;
364     }
365 
366     /**
367      * The response body.
368      *
369      * @var mixed
370      *
371      * @see set_body(), get_body()
372      */
373     private $body;
374 
375     /**
376      * Sets the response body.
377      *
378      * The body can be any data type that can be converted into a string this includes numeric and
379      * objects implementing the {@link __toString()} method.
380      *
381      * Note: This method is the setter for the {@link $body} property.
382      *
383      * @param string|\Closure|null $body
384      *
385      * @throws \UnexpectedValueException when the body cannot be converted to a string.
386      */
387     protected function set_body($body)
388     {
389         if ($body !== null
390         && !($body instanceof \Closure)
391         && !is_numeric($body)
392         && !is_string($body)
393         && !(is_object($body) && method_exists($body, '__toString')))
394         {
395             throw new \UnexpectedValueException(\ICanBoogie\format
396             (
397                 'The Response body must be a string, an object implementing the __toString() method or be callable, %type given. !value', array
398                 (
399                     'type' => gettype($body),
400                     'value' => $body
401                 )
402             ));
403         }
404 
405         if ($body === null)
406         {
407             $this->headers['Content-Length'] = null;
408         }
409 
410         $this->body = $body;
411     }
412 
413     /**
414      * Returns the response body.
415      *
416      * Note: This method is the getter for the {@link $body} property.
417      *
418      * @return string
419      */
420     protected function get_body()
421     {
422         return $this->body;
423     }
424 
425     /**
426      * Echo the body.
427      *
428      * @param mixed $body
429      */
430     protected function echo_body($body)
431     {
432         if ($body instanceof \Closure)
433         {
434             $body($this);
435         }
436         else
437         {
438             echo $body;
439         }
440     }
441 
442     /**
443      * Returns the message associated with the status code.
444      *
445      * This method is the volatile getter for the {@link $status_message} property and is only
446      * called if the property is not accessible.
447      *
448      * @return string
449      */
450     protected function get_status_message()
451     {
452         return self::$status_messages[$this->status];
453     }
454 
455     /**
456      * Sets the value of the `Location` header field.
457      *
458      * @param string|null $url
459      */
460     protected function set_location($url)
461     {
462         if ($url !== null && !$url)
463         {
464             throw new \InvalidArgumentException('Cannot redirect to an empty URL.');
465         }
466 
467         $this->headers['Location'] = $url;
468     }
469 
470     /**
471      * Returns the value of the `Location` header field.
472      *
473      * @return string
474      */
475     protected function get_location()
476     {
477         return $this->headers['Location'];
478     }
479 
480     /**
481      * Sets the value of the `Content-Type` header field.
482      *
483      * @param string $content_type
484      */
485     protected function set_content_type($content_type)
486     {
487         $this->headers['Content-Type'] = $content_type;
488     }
489 
490     /**
491      * Returns the value of the `Content-Type` header field.
492      *
493      * @return Headers\ContentType
494      */
495     protected function get_content_type()
496     {
497         return $this->headers['Content-Type'];
498     }
499 
500     /**
501      * Sets the value of the `Content-Length` header field.
502      *
503      * @param int $length
504      */
505     protected function set_content_length($length)
506     {
507         $this->headers['Content-Length'] = $length;
508     }
509 
510     /**
511      * Returns the value of the `Content-Length` header field.
512      *
513      * @return int
514      */
515     protected function get_content_length()
516     {
517         return $this->headers['Content-Length'];
518     }
519 
520     /**
521      * Sets the value of the `Date` header field.
522      *
523      * @param mixed $time
524      */
525     protected function set_date($time)
526     {
527         $this->headers['Date'] = $time;
528     }
529 
530     /**
531      * Returns the value of the `Date` header field.
532      *
533      * @return Headers\DateHeader
534      */
535     protected function get_date()
536     {
537         return $this->headers['Date'];
538     }
539 
540     /**
541      * Sets the value of the `Age` header field.
542      *
543      * @param int $age
544      */
545     protected function set_age($age)
546     {
547         $this->headers['Age'] = $age;
548     }
549 
550     /**
551      * Returns the age of the response.
552      *
553      * @return int
554      */
555     protected function get_age()
556     {
557         $age = $this->headers['Age'];
558 
559         if ($age)
560         {
561             return $age;
562         }
563 
564         return max(time() - $this->date->format('U'), 0);
565     }
566 
567     /**
568      * Sets the value of the `Last-Modified` header field.
569      *
570      * @param mixed $time.
571      */
572     protected function set_last_modified($time)
573     {
574         $this->headers['Last-Modified'] = $time;
575     }
576 
577     /**
578      * Returns the value of the `Last-Modified` header field.
579      *
580      * @return Headers\DateHeader
581      */
582     protected function get_last_modified()
583     {
584         return $this->headers['Last-Modified'];
585     }
586 
587     /**
588      * Sets the value of the `Expires` header field.
589      *
590      * The method also calls the {@link session_cache_expire()} function.
591      *
592      * @param mixed $time.
593      */
594     protected function set_expires($time)
595     {
596         $this->headers['Expires'] = $time;
597 
598         session_cache_expire($time); // TODO-20120831: Is this required now that we have an awesome response system ?
599     }
600 
601     /**
602      * Returns the value of the `Expires` header field.
603      *
604      * @return Headers\DateHeader
605      */
606     protected function get_expires()
607     {
608         return $this->headers['Expires'];
609     }
610 
611     /**
612      * Sets the value of the `Etag` header field.
613      *
614      * @param string $value
615      */
616     protected function set_etag($value)
617     {
618         $this->headers['Etag'] = $value;
619     }
620 
621     /**
622      * Returns the value of the `Etag` header field.
623      *
624      * @return string
625      */
626     protected function get_etag()
627     {
628         return $this->headers['Etag'];
629     }
630 
631     /**
632      * Sets the directives of the `Cache-Control` header field.
633      *
634      * @param string $cache_directives
635      */
636     protected function set_cache_control($cache_directives)
637     {
638         $this->headers['Cache-Control'] = $cache_directives;
639     }
640 
641     /**
642      * Returns the `Cache-Control` header field.
643      *
644      * @return \ICanBoogie\HTTP\CacheControlHeader
645      */
646     protected function get_cache_control()
647     {
648         return $this->headers['Cache-Control'];
649     }
650 
651     /**
652      * Sets the response's time-to-live for shared caches.
653      *
654      * This method adjusts the Cache-Control/s-maxage directive.
655      *
656      * @param int $seconds The number of seconds.
657      */
658     protected function set_ttl($seconds)
659     {
660         $this->cache_control->s_max_age = $this->age->timestamp + $seconds;
661     }
662 
663     /**
664      * Returns the response's time-to-live in seconds.
665      *
666      * When the responses TTL is <= 0, the response may not be served from cache without first
667      * revalidating with the origin.
668      *
669      * @return int|null The number of seconds to live, or `null` is no freshness information
670      * is present.
671      */
672     protected function get_ttl()
673     {
674         $max_age = $this->cache_control->max_age;
675 
676         if ($max_age)
677         {
678             return $max_age - $this->age;
679         }
680     }
681 
682     /**
683      * Set the `cacheable` property of the `Cache-Control` header field to `private`.
684      *
685      * @param boolean $value
686      */
687     protected function set_is_private($value)
688     {
689         $this->cache_control->cacheable = 'private';
690     }
691 
692     /**
693      * Checks that the `cacheable` property of the `Cache-Control` header field is `private`.
694      *
695      * @return boolean
696      */
697     protected function get_is_private()
698     {
699         return $this->cache_control->cacheable == 'private';
700     }
701 
702     /**
703      * Checks if the response is valid.
704      *
705      * A response is considered valid when its status is between 100 and 600, 100 included.
706      *
707      * Note: This method is the getter for the `is_valid` magic property.
708      *
709      * @return boolean
710      */
711     protected function get_is_valid()
712     {
713         return $this->status >= 100 && $this->status < 600;
714     }
715 
716     /**
717      * Checks if the response is informational.
718      *
719      * A response is considered informational when its status is between 100 and 200, 100 included.
720      *
721      * Note: This method is the getter for the `is_informational` magic property.
722      *
723      * @return boolean
724      */
725     protected function get_is_informational()
726     {
727         return $this->status >= 100 && $this->status < 200;
728     }
729 
730     /**
731      * Checks if the response is successful.
732      *
733      * A response is considered successful when its status is between 200 and 300, 200 included.
734      *
735      * Note: This method is the getter for the `is_successful` magic property.
736      *
737      * @return boolean
738      */
739     protected function get_is_successful()
740     {
741         return $this->status >= 200 && $this->status < 300;
742     }
743 
744     /**
745      * Checks if the response is a redirection.
746      *
747      * A response is considered to be a redirection when its status is between 300 and 400, 300
748      * included.
749      *
750      * Note: This method is the getter for the `is_redirect` magic property.
751      *
752      * @return boolean
753      */
754     protected function get_is_redirect()
755     {
756         return $this->status >= 300 && $this->status < 400;
757     }
758 
759     /**
760      * Checks if the response is a client error.
761      *
762      * A response is considered a client error when its status is between 400 and 500, 400
763      * included.
764      *
765      * Note: This method is the getter for the `is_client_error` magic property.
766      *
767      * @return boolean
768      */
769     protected function get_is_client_error()
770     {
771         return $this->status >= 400 && $this->status < 500;
772     }
773 
774     /**
775      * Checks if the response is a server error.
776      *
777      * A response is considered a server error when its status is between 500 and 600, 500
778      * included.
779      *
780      * Note: This method is the getter for the `is_server_error` magic property.
781      *
782      * @return boolean
783      */
784     protected function get_is_server_error()
785     {
786         return $this->status >= 500 && $this->status < 600;
787     }
788 
789     /**
790      * Checks if the response is ok.
791      *
792      * A response is considered ok when its status is 200.
793      *
794      * Note: This method is the getter for the `is_ok` magic property.
795      *
796      * @return boolean
797      */
798     protected function get_is_ok()
799     {
800         return $this->status == 200;
801     }
802 
803     /**
804      * Checks if the response is forbidden.
805      *
806      * A response is forbidden ok when its status is 403.
807      *
808      * Note: This method is the getter for the `is_forbidden` magic property.
809      *
810      * @return boolean
811      */
812     protected function get_is_forbidden()
813     {
814         return $this->status == 403;
815     }
816 
817     /**
818      * Checks if the response is not found.
819      *
820      * A response is considered not found when its status is 404.
821      *
822      * Note: This method is the getter for the `is_not_found` magic property.
823      *
824      * @return boolean
825      */
826     protected function get_is_not_found()
827     {
828         return $this->status == 404;
829     }
830 
831     /**
832      * Checks if the response is empty.
833      *
834      * A response is considered empty when its status is 201, 204 or 304.
835      *
836      * Note: This method is the getter for the `is_empty` magic property.
837      *
838      * @return boolean
839      */
840     protected function get_is_empty()
841     {
842         static $range = array(201, 204, 304);
843 
844         return in_array($this->status, $range);
845     }
846 
847     /**
848      * Checks that the response includes header fields that can be used to validate the response
849      * with the origin server using a conditional GET request.
850      *
851      * @return boolean
852      */
853     protected function get_is_validateable()
854     {
855         return $this->headers['Last-Modified'] || $this->headers['ETag'];
856     }
857 
858     /**
859      * Checks that the response is worth caching under any circumstance.
860      *
861      * Responses marked _private_ with an explicit `Cache-Control` directive are considered
862      * uncacheable.
863      *
864      * Responses with neither a freshness lifetime (Expires, max-age) nor cache validator
865      * (`Last-Modified`, `ETag`) are considered uncacheable.
866      *
867      * @return boolean
868      */
869     protected function get_is_cacheable()
870     {
871         static $range = array(200, 203, 300, 301, 302, 404, 410);
872 
873         if (!in_array($this->status, $range))
874         {
875             return false;
876         }
877 
878         if ($this->cache_control->no_store || $this->cache_control->cacheable == 'private')
879         {
880             return false;
881         }
882 
883         return $this->is_validateable() || $this->is_fresh();
884     }
885 
886     /**
887      * Checks if the response is fresh.
888      *
889      * @return boolean
890      */
891     protected function get_is_fresh()
892     {
893         return $this->ttl > 0;
894     }
895 
896     /**
897      * @throws PropertyNotWritable in attempt to write an unsupported property.
898      *
899      * Check the following:
900      *
901      * - status_message is not writeable.
902      */
903     /*
904     protected function last_chance_set($property, $value, &$success)
905     {
906         throw new PropertyNotWritable(array($property, $this));
907     }
908     */
909 }
910 
911 /**
912  * A HTTP response doing a redirect.
913  */
914 class RedirectResponse extends Response
915 {
916     /**
917      * Initializes the `Location` header.
918      *
919      * @param string $url URL to redirect to.
920      * @param int $status Status code (default to 302).
921      * @param array $headers Additional headers.
922      *
923      * @throws \InvalidArgumentException if the provided status code is not a redirect.
924      */
925     public function __construct($url, $status=302, array $headers=array())
926     {
927         parent::__construct
928         (
929             function($response) {
930 
931                 $location = $response->location;
932                 $title = \ICanBoogie\escape($location);
933 
934                 echo <<<EOT
935 <!DOCTYPE html>
936 <html>
937 <head>
938     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
939     <meta http-equiv="refresh" content="1;url={$location}" />
940 
941     <title>Redirecting to {$title}</title>
942 </head>
943 <body>
944     Redirecting to <a href="{$location}">{$title}</a>.
945 </body>
946 </html>
947 EOT;
948             },
949 
950             $status, array('Location' => $url) + $headers
951         );
952 
953         if (!$this->is_redirect)
954         {
955             throw new \InvalidArgumentException("The HTTP status code is not a redirect: {$status}.");
956         }
957     }
958 }
Autodoc API documentation generated by ApiGen 2.8.0