1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
19:
20: 21: 22: 23: 24: 25: 26: 27:
28: class CakeResponse {
29:
30: 31: 32: 33: 34:
35: protected $_statusCodes = array(
36: 100 => 'Continue',
37: 101 => 'Switching Protocols',
38: 200 => 'OK',
39: 201 => 'Created',
40: 202 => 'Accepted',
41: 203 => 'Non-Authoritative Information',
42: 204 => 'No Content',
43: 205 => 'Reset Content',
44: 206 => 'Partial Content',
45: 300 => 'Multiple Choices',
46: 301 => 'Moved Permanently',
47: 302 => 'Found',
48: 303 => 'See Other',
49: 304 => 'Not Modified',
50: 305 => 'Use Proxy',
51: 307 => 'Temporary Redirect',
52: 400 => 'Bad Request',
53: 401 => 'Unauthorized',
54: 402 => 'Payment Required',
55: 403 => 'Forbidden',
56: 404 => 'Not Found',
57: 405 => 'Method Not Allowed',
58: 406 => 'Not Acceptable',
59: 407 => 'Proxy Authentication Required',
60: 408 => 'Request Time-out',
61: 409 => 'Conflict',
62: 410 => 'Gone',
63: 411 => 'Length Required',
64: 412 => 'Precondition Failed',
65: 413 => 'Request Entity Too Large',
66: 414 => 'Request-URI Too Large',
67: 415 => 'Unsupported Media Type',
68: 416 => 'Requested range not satisfiable',
69: 417 => 'Expectation Failed',
70: 500 => 'Internal Server Error',
71: 501 => 'Not Implemented',
72: 502 => 'Bad Gateway',
73: 503 => 'Service Unavailable',
74: 504 => 'Gateway Time-out'
75: );
76:
77: 78: 79: 80: 81:
82: protected $_mimeTypes = array(
83: 'ai' => 'application/postscript',
84: 'bcpio' => 'application/x-bcpio',
85: 'bin' => 'application/octet-stream',
86: 'ccad' => 'application/clariscad',
87: 'cdf' => 'application/x-netcdf',
88: 'class' => 'application/octet-stream',
89: 'cpio' => 'application/x-cpio',
90: 'cpt' => 'application/mac-compactpro',
91: 'csh' => 'application/x-csh',
92: 'csv' => array('text/csv', 'application/vnd.ms-excel', 'text/plain'),
93: 'dcr' => 'application/x-director',
94: 'dir' => 'application/x-director',
95: 'dms' => 'application/octet-stream',
96: 'doc' => 'application/msword',
97: 'drw' => 'application/drafting',
98: 'dvi' => 'application/x-dvi',
99: 'dwg' => 'application/acad',
100: 'dxf' => 'application/dxf',
101: 'dxr' => 'application/x-director',
102: 'eot' => 'application/vnd.ms-fontobject',
103: 'eps' => 'application/postscript',
104: 'exe' => 'application/octet-stream',
105: 'ez' => 'application/andrew-inset',
106: 'flv' => 'video/x-flv',
107: 'gtar' => 'application/x-gtar',
108: 'gz' => 'application/x-gzip',
109: 'bz2' => 'application/x-bzip',
110: '7z' => 'application/x-7z-compressed',
111: 'hdf' => 'application/x-hdf',
112: 'hqx' => 'application/mac-binhex40',
113: 'ico' => 'image/vnd.microsoft.icon',
114: 'ips' => 'application/x-ipscript',
115: 'ipx' => 'application/x-ipix',
116: 'js' => 'text/javascript',
117: 'latex' => 'application/x-latex',
118: 'lha' => 'application/octet-stream',
119: 'lsp' => 'application/x-lisp',
120: 'lzh' => 'application/octet-stream',
121: 'man' => 'application/x-troff-man',
122: 'me' => 'application/x-troff-me',
123: 'mif' => 'application/vnd.mif',
124: 'ms' => 'application/x-troff-ms',
125: 'nc' => 'application/x-netcdf',
126: 'oda' => 'application/oda',
127: 'otf' => 'font/otf',
128: 'pdf' => 'application/pdf',
129: 'pgn' => 'application/x-chess-pgn',
130: 'pot' => 'application/mspowerpoint',
131: 'pps' => 'application/mspowerpoint',
132: 'ppt' => 'application/mspowerpoint',
133: 'ppz' => 'application/mspowerpoint',
134: 'pre' => 'application/x-freelance',
135: 'prt' => 'application/pro_eng',
136: 'ps' => 'application/postscript',
137: 'roff' => 'application/x-troff',
138: 'scm' => 'application/x-lotusscreencam',
139: 'set' => 'application/set',
140: 'sh' => 'application/x-sh',
141: 'shar' => 'application/x-shar',
142: 'sit' => 'application/x-stuffit',
143: 'skd' => 'application/x-koan',
144: 'skm' => 'application/x-koan',
145: 'skp' => 'application/x-koan',
146: 'skt' => 'application/x-koan',
147: 'smi' => 'application/smil',
148: 'smil' => 'application/smil',
149: 'sol' => 'application/solids',
150: 'spl' => 'application/x-futuresplash',
151: 'src' => 'application/x-wais-source',
152: 'step' => 'application/STEP',
153: 'stl' => 'application/SLA',
154: 'stp' => 'application/STEP',
155: 'sv4cpio' => 'application/x-sv4cpio',
156: 'sv4crc' => 'application/x-sv4crc',
157: 'svg' => 'image/svg+xml',
158: 'svgz' => 'image/svg+xml',
159: 'swf' => 'application/x-shockwave-flash',
160: 't' => 'application/x-troff',
161: 'tar' => 'application/x-tar',
162: 'tcl' => 'application/x-tcl',
163: 'tex' => 'application/x-tex',
164: 'texi' => 'application/x-texinfo',
165: 'texinfo' => 'application/x-texinfo',
166: 'tr' => 'application/x-troff',
167: 'tsp' => 'application/dsptype',
168: 'ttf' => 'font/ttf',
169: 'unv' => 'application/i-deas',
170: 'ustar' => 'application/x-ustar',
171: 'vcd' => 'application/x-cdlink',
172: 'vda' => 'application/vda',
173: 'xlc' => 'application/vnd.ms-excel',
174: 'xll' => 'application/vnd.ms-excel',
175: 'xlm' => 'application/vnd.ms-excel',
176: 'xls' => 'application/vnd.ms-excel',
177: 'xlw' => 'application/vnd.ms-excel',
178: 'zip' => 'application/zip',
179: 'aif' => 'audio/x-aiff',
180: 'aifc' => 'audio/x-aiff',
181: 'aiff' => 'audio/x-aiff',
182: 'au' => 'audio/basic',
183: 'kar' => 'audio/midi',
184: 'mid' => 'audio/midi',
185: 'midi' => 'audio/midi',
186: 'mp2' => 'audio/mpeg',
187: 'mp3' => 'audio/mpeg',
188: 'mpga' => 'audio/mpeg',
189: 'ogg' => 'audio/ogg',
190: 'oga' => 'audio/ogg',
191: 'spx' => 'audio/ogg',
192: 'ra' => 'audio/x-realaudio',
193: 'ram' => 'audio/x-pn-realaudio',
194: 'rm' => 'audio/x-pn-realaudio',
195: 'rpm' => 'audio/x-pn-realaudio-plugin',
196: 'snd' => 'audio/basic',
197: 'tsi' => 'audio/TSP-audio',
198: 'wav' => 'audio/x-wav',
199: 'aac' => 'audio/aac',
200: 'asc' => 'text/plain',
201: 'c' => 'text/plain',
202: 'cc' => 'text/plain',
203: 'css' => 'text/css',
204: 'etx' => 'text/x-setext',
205: 'f' => 'text/plain',
206: 'f90' => 'text/plain',
207: 'h' => 'text/plain',
208: 'hh' => 'text/plain',
209: 'html' => array('text/html', '*/*'),
210: 'htm' => array('text/html', '*/*'),
211: 'm' => 'text/plain',
212: 'rtf' => 'text/rtf',
213: 'rtx' => 'text/richtext',
214: 'sgm' => 'text/sgml',
215: 'sgml' => 'text/sgml',
216: 'tsv' => 'text/tab-separated-values',
217: 'tpl' => 'text/template',
218: 'txt' => 'text/plain',
219: 'text' => 'text/plain',
220: 'xml' => array('application/xml', 'text/xml'),
221: 'avi' => 'video/x-msvideo',
222: 'fli' => 'video/x-fli',
223: 'mov' => 'video/quicktime',
224: 'movie' => 'video/x-sgi-movie',
225: 'mpe' => 'video/mpeg',
226: 'mpeg' => 'video/mpeg',
227: 'mpg' => 'video/mpeg',
228: 'qt' => 'video/quicktime',
229: 'viv' => 'video/vnd.vivo',
230: 'vivo' => 'video/vnd.vivo',
231: 'ogv' => 'video/ogg',
232: 'webm' => 'video/webm',
233: 'mp4' => 'video/mp4',
234: 'gif' => 'image/gif',
235: 'ief' => 'image/ief',
236: 'jpe' => 'image/jpeg',
237: 'jpeg' => 'image/jpeg',
238: 'jpg' => 'image/jpeg',
239: 'pbm' => 'image/x-portable-bitmap',
240: 'pgm' => 'image/x-portable-graymap',
241: 'png' => 'image/png',
242: 'pnm' => 'image/x-portable-anymap',
243: 'ppm' => 'image/x-portable-pixmap',
244: 'ras' => 'image/cmu-raster',
245: 'rgb' => 'image/x-rgb',
246: 'tif' => 'image/tiff',
247: 'tiff' => 'image/tiff',
248: 'xbm' => 'image/x-xbitmap',
249: 'xpm' => 'image/x-xpixmap',
250: 'xwd' => 'image/x-xwindowdump',
251: 'ice' => 'x-conference/x-cooltalk',
252: 'iges' => 'model/iges',
253: 'igs' => 'model/iges',
254: 'mesh' => 'model/mesh',
255: 'msh' => 'model/mesh',
256: 'silo' => 'model/mesh',
257: 'vrml' => 'model/vrml',
258: 'wrl' => 'model/vrml',
259: 'mime' => 'www/mime',
260: 'pdb' => 'chemical/x-pdb',
261: 'xyz' => 'chemical/x-pdb',
262: 'javascript' => 'text/javascript',
263: 'json' => 'application/json',
264: 'form' => 'application/x-www-form-urlencoded',
265: 'file' => 'multipart/form-data',
266: 'xhtml' => array('application/xhtml+xml', 'application/xhtml', 'text/xhtml'),
267: 'xhtml-mobile' => 'application/vnd.wap.xhtml+xml',
268: 'rss' => 'application/rss+xml',
269: 'atom' => 'application/atom+xml',
270: 'amf' => 'application/x-amf',
271: 'wap' => array('text/vnd.wap.wml', 'text/vnd.wap.wmlscript', 'image/vnd.wap.wbmp'),
272: 'wml' => 'text/vnd.wap.wml',
273: 'wmlscript' => 'text/vnd.wap.wmlscript',
274: 'wbmp' => 'image/vnd.wap.wbmp',
275: );
276:
277: 278: 279: 280: 281:
282: protected $_protocol = 'HTTP/1.1';
283:
284: 285: 286: 287: 288:
289: protected $_status = 200;
290:
291: 292: 293: 294: 295: 296:
297: protected $_contentType = 'text/html';
298:
299: 300: 301: 302: 303:
304: protected $_headers = array();
305:
306: 307: 308: 309: 310:
311: protected $_body = null;
312:
313: 314: 315: 316: 317:
318: protected $_charset = 'UTF-8';
319:
320: 321: 322: 323: 324: 325: 326: 327: 328:
329: public function __construct(array $options = array()) {
330: if (isset($options['body'])) {
331: $this->body($options['body']);
332: }
333: if (isset($options['status'])) {
334: $this->statusCode($options['status']);
335: }
336: if (isset($options['type'])) {
337: $this->type($options['type']);
338: }
339: if (isset($options['charset'])) {
340: $this->charset($options['charset']);
341: }
342: }
343:
344: 345: 346: 347: 348: 349:
350: public function send() {
351: if (isset($this->_headers['Location']) && $this->_status === 200) {
352: $this->statusCode(302);
353: }
354:
355: $codeMessage = $this->_statusCodes[$this->_status];
356: $this->_sendHeader("{$this->_protocol} {$this->_status} {$codeMessage}");
357: $this->_sendHeader('Content-Type', "{$this->_contentType}; charset={$this->_charset}");
358: $this->_setContentLength();
359: foreach ($this->_headers as $header => $value) {
360: $this->_sendHeader($header, $value);
361: }
362: $this->_sendContent($this->_body);
363: }
364:
365: 366: 367: 368: 369: 370:
371: protected function _setContentLength() {
372: $shouldSetLength = empty($this->_headers['Content-Length']) && !in_array($this->_status, range(301, 307));
373: if ($shouldSetLength && !$this->outputCompressed()) {
374: $offset = ob_get_level() ? ob_get_length() : 0;
375: if (ini_get('mbstring.func_overload') & 2 && function_exists('mb_strlen')) {
376: $this->_headers['Content-Length'] = $offset + mb_strlen($this->_body, '8bit');
377: } else {
378: $this->_headers['Content-Length'] = $offset + strlen($this->_body);
379: }
380: }
381: }
382:
383: 384: 385: 386: 387: 388: 389:
390: protected function _sendHeader($name, $value = null) {
391: if (!headers_sent()) {
392: if (is_null($value)) {
393: header($name);
394: } else {
395: header("{$name}: {$value}");
396: }
397: }
398: }
399:
400: 401: 402: 403: 404: 405:
406: protected function _sendContent($content) {
407: echo $content;
408: }
409:
410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436:
437: public function header($header = null, $value = null) {
438: if (is_null($header)) {
439: return $this->_headers;
440: }
441: if (is_array($header)) {
442: foreach ($header as $h => $v) {
443: if (is_numeric($h)) {
444: $this->header($v);
445: continue;
446: }
447: $this->_headers[$h] = trim($v);
448: }
449: return $this->_headers;
450: }
451:
452: if (!is_null($value)) {
453: $this->_headers[$header] = $value;
454: return $this->_headers;
455: }
456:
457: list($header, $value) = explode(':', $header, 2);
458: $this->_headers[$header] = trim($value);
459: return $this->_headers;
460: }
461:
462: 463: 464: 465: 466: 467: 468:
469: public function body($content = null) {
470: if (is_null($content)) {
471: return $this->_body;
472: }
473: return $this->_body = $content;
474: }
475:
476: 477: 478: 479: 480: 481: 482: 483:
484: public function statusCode($code = null) {
485: if (is_null($code)) {
486: return $this->_status;
487: }
488: if (!isset($this->_statusCodes[$code])) {
489: throw new CakeException(__d('cake_dev', 'Unknown status code'));
490: }
491: return $this->_status = $code;
492: }
493:
494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511:
512: public function httpCodes($code = null) {
513: if (empty($code)) {
514: return $this->_statusCodes;
515: }
516:
517: if (is_array($code)) {
518: $this->_statusCodes = $code + $this->_statusCodes;
519: return true;
520: }
521:
522: if (!isset($this->_statusCodes[$code])) {
523: return null;
524: }
525: return array($code => $this->_statusCodes[$code]);
526: }
527:
528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552:
553: public function type($contentType = null) {
554: if (is_null($contentType)) {
555: return $this->_contentType;
556: }
557: if (is_array($contentType)) {
558: $type = key($contentType);
559: $defitition = current($contentType);
560: $this->_mimeTypes[$type] = $defitition;
561: return $this->_contentType;
562: }
563: if (isset($this->_mimeTypes[$contentType])) {
564: $contentType = $this->_mimeTypes[$contentType];
565: $contentType = is_array($contentType) ? current($contentType) : $contentType;
566: }
567: if (strpos($contentType, '/') === false) {
568: return false;
569: }
570: return $this->_contentType = $contentType;
571: }
572:
573: 574: 575: 576: 577: 578: 579: 580:
581: public function getMimeType($alias) {
582: if (isset($this->_mimeTypes[$alias])) {
583: return $this->_mimeTypes[$alias];
584: }
585: return false;
586: }
587:
588: 589: 590: 591: 592: 593: 594: 595:
596: public function mapType($ctype) {
597: if (is_array($ctype)) {
598: return array_map(array($this, 'mapType'), $ctype);
599: }
600:
601: foreach ($this->_mimeTypes as $alias => $types) {
602: if (is_array($types) && in_array($ctype, $types)) {
603: return $alias;
604: } elseif (is_string($types) && $types == $ctype) {
605: return $alias;
606: }
607: }
608: return null;
609: }
610:
611: 612: 613: 614: 615: 616: 617:
618: public function charset($charset = null) {
619: if (is_null($charset)) {
620: return $this->_charset;
621: }
622: return $this->_charset = $charset;
623: }
624:
625: 626: 627: 628: 629:
630: public function disableCache() {
631: $this->header(array(
632: 'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT',
633: 'Last-Modified' => gmdate("D, d M Y H:i:s") . " GMT",
634: 'Cache-Control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0',
635: 'Pragma' => 'no-cache'
636: ));
637: }
638:
639: 640: 641: 642: 643: 644: 645:
646: public function cache($since, $time = '+1 day') {
647: if (!is_integer($time)) {
648: $time = strtotime($time);
649: }
650: $this->header(array(
651: 'Date' => gmdate("D, j M Y G:i:s ", time()) . 'GMT',
652: 'Last-Modified' => gmdate("D, j M Y G:i:s ", $since) . 'GMT',
653: 'Expires' => gmdate("D, j M Y H:i:s", $time) . " GMT",
654: 'Cache-Control' => 'public, max-age=' . ($time - time()),
655: 'Pragma' => 'cache'
656: ));
657: }
658:
659: 660: 661: 662: 663: 664:
665: public function compress() {
666: $compressionEnabled = ini_get("zlib.output_compression") !== '1' &&
667: extension_loaded("zlib") &&
668: (strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false);
669: return $compressionEnabled && ob_start('ob_gzhandler');
670: }
671:
672: 673: 674: 675: 676:
677: public function outputCompressed() {
678: return strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false
679: && (ini_get("zlib.output_compression") === '1' || in_array('ob_gzhandler', ob_list_handlers()));
680: }
681:
682: 683: 684: 685: 686: 687:
688: public function download($filename) {
689: $this->header('Content-Disposition', 'attachment; filename="' . $filename . '"');
690: }
691:
692: 693: 694: 695: 696: 697:
698: public function __toString() {
699: return (string)$this->_body;
700: }
701: }
702: