CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 2.9 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.9
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Packages

  • Cake
    • Cache
      • Engine
    • Configure
    • Console
      • Command
        • Task
    • Controller
      • Component
        • Acl
        • Auth
    • Core
    • Error
    • Event
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
      • Validator
    • Network
      • Email
      • Http
    • Routing
      • Filter
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper
  • None

Classes

  • CakeRequest
  • CakeResponse
  • CakeSocket
   1: <?php
   2: /**
   3:  * CakeResponse
   4:  *
   5:  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
   6:  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
   7:  *
   8:  * Licensed under The MIT License
   9:  * For full copyright and license information, please see the LICENSE.txt
  10:  * Redistributions of files must retain the above copyright notice.
  11:  *
  12:  * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  13:  * @link          http://cakephp.org CakePHP(tm) Project
  14:  * @package       Cake.Network
  15:  * @since         CakePHP(tm) v 2.0
  16:  * @license       http://www.opensource.org/licenses/mit-license.php MIT License
  17:  */
  18: 
  19: App::uses('File', 'Utility');
  20: 
  21: /**
  22:  * CakeResponse is responsible for managing the response text, status and headers of a HTTP response.
  23:  *
  24:  * By default controllers will use this class to render their response. If you are going to use
  25:  * a custom response class it should subclass this object in order to ensure compatibility.
  26:  *
  27:  * @package       Cake.Network
  28:  */
  29: class CakeResponse {
  30: 
  31: /**
  32:  * Holds HTTP response statuses
  33:  *
  34:  * @var array
  35:  */
  36:     protected $_statusCodes = array(
  37:         100 => 'Continue',
  38:         101 => 'Switching Protocols',
  39:         200 => 'OK',
  40:         201 => 'Created',
  41:         202 => 'Accepted',
  42:         203 => 'Non-Authoritative Information',
  43:         204 => 'No Content',
  44:         205 => 'Reset Content',
  45:         206 => 'Partial Content',
  46:         300 => 'Multiple Choices',
  47:         301 => 'Moved Permanently',
  48:         302 => 'Found',
  49:         303 => 'See Other',
  50:         304 => 'Not Modified',
  51:         305 => 'Use Proxy',
  52:         307 => 'Temporary Redirect',
  53:         400 => 'Bad Request',
  54:         401 => 'Unauthorized',
  55:         402 => 'Payment Required',
  56:         403 => 'Forbidden',
  57:         404 => 'Not Found',
  58:         405 => 'Method Not Allowed',
  59:         406 => 'Not Acceptable',
  60:         407 => 'Proxy Authentication Required',
  61:         408 => 'Request Time-out',
  62:         409 => 'Conflict',
  63:         410 => 'Gone',
  64:         411 => 'Length Required',
  65:         412 => 'Precondition Failed',
  66:         413 => 'Request Entity Too Large',
  67:         414 => 'Request-URI Too Large',
  68:         415 => 'Unsupported Media Type',
  69:         416 => 'Requested range not satisfiable',
  70:         417 => 'Expectation Failed',
  71:         429 => 'Too Many Requests',
  72:         500 => 'Internal Server Error',
  73:         501 => 'Not Implemented',
  74:         502 => 'Bad Gateway',
  75:         503 => 'Service Unavailable',
  76:         504 => 'Gateway Time-out',
  77:         505 => 'Unsupported Version'
  78:     );
  79: 
  80: /**
  81:  * Holds known mime type mappings
  82:  *
  83:  * @var array
  84:  */
  85:     protected $_mimeTypes = array(
  86:         'html' => array('text/html', '*/*'),
  87:         'json' => 'application/json',
  88:         'xml' => array('application/xml', 'text/xml'),
  89:         'rss' => 'application/rss+xml',
  90:         'ai' => 'application/postscript',
  91:         'bcpio' => 'application/x-bcpio',
  92:         'bin' => 'application/octet-stream',
  93:         'ccad' => 'application/clariscad',
  94:         'cdf' => 'application/x-netcdf',
  95:         'class' => 'application/octet-stream',
  96:         'cpio' => 'application/x-cpio',
  97:         'cpt' => 'application/mac-compactpro',
  98:         'csh' => 'application/x-csh',
  99:         'csv' => array('text/csv', 'application/vnd.ms-excel'),
 100:         'dcr' => 'application/x-director',
 101:         'dir' => 'application/x-director',
 102:         'dms' => 'application/octet-stream',
 103:         'doc' => 'application/msword',
 104:         'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
 105:         'drw' => 'application/drafting',
 106:         'dvi' => 'application/x-dvi',
 107:         'dwg' => 'application/acad',
 108:         'dxf' => 'application/dxf',
 109:         'dxr' => 'application/x-director',
 110:         'eot' => 'application/vnd.ms-fontobject',
 111:         'eps' => 'application/postscript',
 112:         'exe' => 'application/octet-stream',
 113:         'ez' => 'application/andrew-inset',
 114:         'flv' => 'video/x-flv',
 115:         'gtar' => 'application/x-gtar',
 116:         'gz' => 'application/x-gzip',
 117:         'bz2' => 'application/x-bzip',
 118:         '7z' => 'application/x-7z-compressed',
 119:         'hdf' => 'application/x-hdf',
 120:         'hqx' => 'application/mac-binhex40',
 121:         'ico' => 'image/x-icon',
 122:         'ips' => 'application/x-ipscript',
 123:         'ipx' => 'application/x-ipix',
 124:         'js' => 'application/javascript',
 125:         'jsonapi' => 'application/vnd.api+json',
 126:         'latex' => 'application/x-latex',
 127:         'lha' => 'application/octet-stream',
 128:         'lsp' => 'application/x-lisp',
 129:         'lzh' => 'application/octet-stream',
 130:         'man' => 'application/x-troff-man',
 131:         'me' => 'application/x-troff-me',
 132:         'mif' => 'application/vnd.mif',
 133:         'ms' => 'application/x-troff-ms',
 134:         'nc' => 'application/x-netcdf',
 135:         'oda' => 'application/oda',
 136:         'otf' => 'font/otf',
 137:         'pdf' => 'application/pdf',
 138:         'pgn' => 'application/x-chess-pgn',
 139:         'pot' => 'application/vnd.ms-powerpoint',
 140:         'pps' => 'application/vnd.ms-powerpoint',
 141:         'ppt' => 'application/vnd.ms-powerpoint',
 142:         'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
 143:         'ppz' => 'application/vnd.ms-powerpoint',
 144:         'pre' => 'application/x-freelance',
 145:         'prt' => 'application/pro_eng',
 146:         'ps' => 'application/postscript',
 147:         'roff' => 'application/x-troff',
 148:         'scm' => 'application/x-lotusscreencam',
 149:         'set' => 'application/set',
 150:         'sh' => 'application/x-sh',
 151:         'shar' => 'application/x-shar',
 152:         'sit' => 'application/x-stuffit',
 153:         'skd' => 'application/x-koan',
 154:         'skm' => 'application/x-koan',
 155:         'skp' => 'application/x-koan',
 156:         'skt' => 'application/x-koan',
 157:         'smi' => 'application/smil',
 158:         'smil' => 'application/smil',
 159:         'sol' => 'application/solids',
 160:         'spl' => 'application/x-futuresplash',
 161:         'src' => 'application/x-wais-source',
 162:         'step' => 'application/STEP',
 163:         'stl' => 'application/SLA',
 164:         'stp' => 'application/STEP',
 165:         'sv4cpio' => 'application/x-sv4cpio',
 166:         'sv4crc' => 'application/x-sv4crc',
 167:         'svg' => 'image/svg+xml',
 168:         'svgz' => 'image/svg+xml',
 169:         'swf' => 'application/x-shockwave-flash',
 170:         't' => 'application/x-troff',
 171:         'tar' => 'application/x-tar',
 172:         'tcl' => 'application/x-tcl',
 173:         'tex' => 'application/x-tex',
 174:         'texi' => 'application/x-texinfo',
 175:         'texinfo' => 'application/x-texinfo',
 176:         'tr' => 'application/x-troff',
 177:         'tsp' => 'application/dsptype',
 178:         'ttc' => 'font/ttf',
 179:         'ttf' => 'font/ttf',
 180:         'unv' => 'application/i-deas',
 181:         'ustar' => 'application/x-ustar',
 182:         'vcd' => 'application/x-cdlink',
 183:         'vda' => 'application/vda',
 184:         'xlc' => 'application/vnd.ms-excel',
 185:         'xll' => 'application/vnd.ms-excel',
 186:         'xlm' => 'application/vnd.ms-excel',
 187:         'xls' => 'application/vnd.ms-excel',
 188:         'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
 189:         'xlw' => 'application/vnd.ms-excel',
 190:         'zip' => 'application/zip',
 191:         'aif' => 'audio/x-aiff',
 192:         'aifc' => 'audio/x-aiff',
 193:         'aiff' => 'audio/x-aiff',
 194:         'au' => 'audio/basic',
 195:         'kar' => 'audio/midi',
 196:         'mid' => 'audio/midi',
 197:         'midi' => 'audio/midi',
 198:         'mp2' => 'audio/mpeg',
 199:         'mp3' => 'audio/mpeg',
 200:         'mpga' => 'audio/mpeg',
 201:         'ogg' => 'audio/ogg',
 202:         'oga' => 'audio/ogg',
 203:         'spx' => 'audio/ogg',
 204:         'ra' => 'audio/x-realaudio',
 205:         'ram' => 'audio/x-pn-realaudio',
 206:         'rm' => 'audio/x-pn-realaudio',
 207:         'rpm' => 'audio/x-pn-realaudio-plugin',
 208:         'snd' => 'audio/basic',
 209:         'tsi' => 'audio/TSP-audio',
 210:         'wav' => 'audio/x-wav',
 211:         'aac' => 'audio/aac',
 212:         'asc' => 'text/plain',
 213:         'c' => 'text/plain',
 214:         'cc' => 'text/plain',
 215:         'css' => 'text/css',
 216:         'etx' => 'text/x-setext',
 217:         'f' => 'text/plain',
 218:         'f90' => 'text/plain',
 219:         'h' => 'text/plain',
 220:         'hh' => 'text/plain',
 221:         'htm' => array('text/html', '*/*'),
 222:         'ics' => 'text/calendar',
 223:         'm' => 'text/plain',
 224:         'rtf' => 'text/rtf',
 225:         'rtx' => 'text/richtext',
 226:         'sgm' => 'text/sgml',
 227:         'sgml' => 'text/sgml',
 228:         'tsv' => 'text/tab-separated-values',
 229:         'tpl' => 'text/template',
 230:         'txt' => 'text/plain',
 231:         'text' => 'text/plain',
 232:         'avi' => 'video/x-msvideo',
 233:         'fli' => 'video/x-fli',
 234:         'mov' => 'video/quicktime',
 235:         'movie' => 'video/x-sgi-movie',
 236:         'mpe' => 'video/mpeg',
 237:         'mpeg' => 'video/mpeg',
 238:         'mpg' => 'video/mpeg',
 239:         'qt' => 'video/quicktime',
 240:         'viv' => 'video/vnd.vivo',
 241:         'vivo' => 'video/vnd.vivo',
 242:         'ogv' => 'video/ogg',
 243:         'webm' => 'video/webm',
 244:         'mp4' => 'video/mp4',
 245:         'm4v' => 'video/mp4',
 246:         'f4v' => 'video/mp4',
 247:         'f4p' => 'video/mp4',
 248:         'm4a' => 'audio/mp4',
 249:         'f4a' => 'audio/mp4',
 250:         'f4b' => 'audio/mp4',
 251:         'gif' => 'image/gif',
 252:         'ief' => 'image/ief',
 253:         'jpg' => 'image/jpeg',
 254:         'jpeg' => 'image/jpeg',
 255:         'jpe' => 'image/jpeg',
 256:         'pbm' => 'image/x-portable-bitmap',
 257:         'pgm' => 'image/x-portable-graymap',
 258:         'png' => 'image/png',
 259:         'pnm' => 'image/x-portable-anymap',
 260:         'ppm' => 'image/x-portable-pixmap',
 261:         'ras' => 'image/cmu-raster',
 262:         'rgb' => 'image/x-rgb',
 263:         'tif' => 'image/tiff',
 264:         'tiff' => 'image/tiff',
 265:         'xbm' => 'image/x-xbitmap',
 266:         'xpm' => 'image/x-xpixmap',
 267:         'xwd' => 'image/x-xwindowdump',
 268:         'psd' => array(
 269:             'application/photoshop',
 270:             'application/psd',
 271:             'image/psd',
 272:             'image/x-photoshop',
 273:             'image/photoshop',
 274:             'zz-application/zz-winassoc-psd'
 275:         ),
 276:         'ice' => 'x-conference/x-cooltalk',
 277:         'iges' => 'model/iges',
 278:         'igs' => 'model/iges',
 279:         'mesh' => 'model/mesh',
 280:         'msh' => 'model/mesh',
 281:         'silo' => 'model/mesh',
 282:         'vrml' => 'model/vrml',
 283:         'wrl' => 'model/vrml',
 284:         'mime' => 'www/mime',
 285:         'pdb' => 'chemical/x-pdb',
 286:         'xyz' => 'chemical/x-pdb',
 287:         'javascript' => 'application/javascript',
 288:         'form' => 'application/x-www-form-urlencoded',
 289:         'file' => 'multipart/form-data',
 290:         'xhtml' => array('application/xhtml+xml', 'application/xhtml', 'text/xhtml'),
 291:         'xhtml-mobile' => 'application/vnd.wap.xhtml+xml',
 292:         'atom' => 'application/atom+xml',
 293:         'amf' => 'application/x-amf',
 294:         'wap' => array('text/vnd.wap.wml', 'text/vnd.wap.wmlscript', 'image/vnd.wap.wbmp'),
 295:         'wml' => 'text/vnd.wap.wml',
 296:         'wmlscript' => 'text/vnd.wap.wmlscript',
 297:         'wbmp' => 'image/vnd.wap.wbmp',
 298:         'woff' => 'application/x-font-woff',
 299:         'webp' => 'image/webp',
 300:         'appcache' => 'text/cache-manifest',
 301:         'manifest' => 'text/cache-manifest',
 302:         'htc' => 'text/x-component',
 303:         'rdf' => 'application/xml',
 304:         'crx' => 'application/x-chrome-extension',
 305:         'oex' => 'application/x-opera-extension',
 306:         'xpi' => 'application/x-xpinstall',
 307:         'safariextz' => 'application/octet-stream',
 308:         'webapp' => 'application/x-web-app-manifest+json',
 309:         'vcf' => 'text/x-vcard',
 310:         'vtt' => 'text/vtt',
 311:         'mkv' => 'video/x-matroska',
 312:         'pkpass' => 'application/vnd.apple.pkpass',
 313:         'ajax' => 'text/html'
 314:     );
 315: 
 316: /**
 317:  * Protocol header to send to the client
 318:  *
 319:  * @var string
 320:  */
 321:     protected $_protocol = 'HTTP/1.1';
 322: 
 323: /**
 324:  * Status code to send to the client
 325:  *
 326:  * @var int
 327:  */
 328:     protected $_status = 200;
 329: 
 330: /**
 331:  * Content type to send. This can be an 'extension' that will be transformed using the $_mimetypes array
 332:  * or a complete mime-type
 333:  *
 334:  * @var int
 335:  */
 336:     protected $_contentType = 'text/html';
 337: 
 338: /**
 339:  * Buffer list of headers
 340:  *
 341:  * @var array
 342:  */
 343:     protected $_headers = array();
 344: 
 345: /**
 346:  * Buffer string for response message
 347:  *
 348:  * @var string
 349:  */
 350:     protected $_body = null;
 351: 
 352: /**
 353:  * File object for file to be read out as response
 354:  *
 355:  * @var File
 356:  */
 357:     protected $_file = null;
 358: 
 359: /**
 360:  * File range. Used for requesting ranges of files.
 361:  *
 362:  * @var array
 363:  */
 364:     protected $_fileRange = null;
 365: 
 366: /**
 367:  * The charset the response body is encoded with
 368:  *
 369:  * @var string
 370:  */
 371:     protected $_charset = 'UTF-8';
 372: 
 373: /**
 374:  * Holds all the cache directives that will be converted
 375:  * into headers when sending the request
 376:  *
 377:  * @var string
 378:  */
 379:     protected $_cacheDirectives = array();
 380: 
 381: /**
 382:  * Holds cookies to be sent to the client
 383:  *
 384:  * @var array
 385:  */
 386:     protected $_cookies = array();
 387: 
 388: /**
 389:  * Constructor
 390:  *
 391:  * @param array $options list of parameters to setup the response. Possible values are:
 392:  *  - body: the response text that should be sent to the client
 393:  *  - statusCodes: additional allowable response codes
 394:  *  - status: the HTTP status code to respond with
 395:  *  - type: a complete mime-type string or an extension mapped in this class
 396:  *  - charset: the charset for the response body
 397:  */
 398:     public function __construct(array $options = array()) {
 399:         if (isset($options['body'])) {
 400:             $this->body($options['body']);
 401:         }
 402:         if (isset($options['statusCodes'])) {
 403:             $this->httpCodes($options['statusCodes']);
 404:         }
 405:         if (isset($options['status'])) {
 406:             $this->statusCode($options['status']);
 407:         }
 408:         if (isset($options['type'])) {
 409:             $this->type($options['type']);
 410:         }
 411:         if (!isset($options['charset'])) {
 412:             $options['charset'] = Configure::read('App.encoding');
 413:         }
 414:         $this->charset($options['charset']);
 415:     }
 416: 
 417: /**
 418:  * Sends the complete response to the client including headers and message body.
 419:  * Will echo out the content in the response body.
 420:  *
 421:  * @return void
 422:  */
 423:     public function send() {
 424:         if (isset($this->_headers['Location']) && $this->_status === 200) {
 425:             $this->statusCode(302);
 426:         }
 427: 
 428:         $codeMessage = $this->_statusCodes[$this->_status];
 429:         $this->_setCookies();
 430:         $this->_sendHeader("{$this->_protocol} {$this->_status} {$codeMessage}");
 431:         $this->_setContent();
 432:         $this->_setContentLength();
 433:         $this->_setContentType();
 434:         foreach ($this->_headers as $header => $values) {
 435:             foreach ((array)$values as $value) {
 436:                 $this->_sendHeader($header, $value);
 437:             }
 438:         }
 439:         if ($this->_file) {
 440:             $this->_sendFile($this->_file, $this->_fileRange);
 441:             $this->_file = $this->_fileRange = null;
 442:         } else {
 443:             $this->_sendContent($this->_body);
 444:         }
 445:     }
 446: 
 447: /**
 448:  * Sets the cookies that have been added via CakeResponse::cookie() before any
 449:  * other output is sent to the client. Will set the cookies in the order they
 450:  * have been set.
 451:  *
 452:  * @return void
 453:  */
 454:     protected function _setCookies() {
 455:         foreach ($this->_cookies as $name => $c) {
 456:             setcookie(
 457:                 $name, $c['value'], $c['expire'], $c['path'],
 458:                 $c['domain'], $c['secure'], $c['httpOnly']
 459:             );
 460:         }
 461:     }
 462: 
 463: /**
 464:  * Formats the Content-Type header based on the configured contentType and charset
 465:  * the charset will only be set in the header if the response is of type text
 466:  *
 467:  * @return void
 468:  */
 469:     protected function _setContentType() {
 470:         if (in_array($this->_status, array(304, 204))) {
 471:             return;
 472:         }
 473:         $whitelist = array(
 474:             'application/javascript', 'application/json', 'application/xml', 'application/rss+xml'
 475:         );
 476: 
 477:         $charset = false;
 478:         if ($this->_charset &&
 479:             (strpos($this->_contentType, 'text/') === 0 || in_array($this->_contentType, $whitelist))
 480:         ) {
 481:             $charset = true;
 482:         }
 483: 
 484:         if ($charset) {
 485:             $this->header('Content-Type', "{$this->_contentType}; charset={$this->_charset}");
 486:         } else {
 487:             $this->header('Content-Type', "{$this->_contentType}");
 488:         }
 489:     }
 490: 
 491: /**
 492:  * Sets the response body to an empty text if the status code is 204 or 304
 493:  *
 494:  * @return void
 495:  */
 496:     protected function _setContent() {
 497:         if (in_array($this->_status, array(304, 204))) {
 498:             $this->body('');
 499:         }
 500:     }
 501: 
 502: /**
 503:  * Calculates the correct Content-Length and sets it as a header in the response
 504:  * Will not set the value if already set or if the output is compressed.
 505:  *
 506:  * @return void
 507:  */
 508:     protected function _setContentLength() {
 509:         $shouldSetLength = !isset($this->_headers['Content-Length']) && !in_array($this->_status, range(301, 307));
 510:         if (isset($this->_headers['Content-Length']) && $this->_headers['Content-Length'] === false) {
 511:             unset($this->_headers['Content-Length']);
 512:             return;
 513:         }
 514:         if ($shouldSetLength && !$this->outputCompressed()) {
 515:             $offset = ob_get_level() ? ob_get_length() : 0;
 516:             if (ini_get('mbstring.func_overload') & 2 && function_exists('mb_strlen')) {
 517:                 $this->length($offset + mb_strlen($this->_body, '8bit'));
 518:             } else {
 519:                 $this->length($this->_headers['Content-Length'] = $offset + strlen($this->_body));
 520:             }
 521:         }
 522:     }
 523: 
 524: /**
 525:  * Sends a header to the client.
 526:  *
 527:  * Will skip sending headers if headers have already been sent.
 528:  *
 529:  * @param string $name the header name
 530:  * @param string $value the header value
 531:  * @return void
 532:  */
 533:     protected function _sendHeader($name, $value = null) {
 534:         if (headers_sent($filename, $linenum)) {
 535:             return;
 536:         }
 537:         if ($value === null) {
 538:             header($name);
 539:         } else {
 540:             header("{$name}: {$value}");
 541:         }
 542:     }
 543: 
 544: /**
 545:  * Sends a content string to the client.
 546:  *
 547:  * @param string $content string to send as response body
 548:  * @return void
 549:  */
 550:     protected function _sendContent($content) {
 551:         echo $content;
 552:     }
 553: 
 554: /**
 555:  * Buffers a header string to be sent
 556:  * Returns the complete list of buffered headers
 557:  *
 558:  * ### Single header
 559:  * e.g `header('Location', 'http://example.com');`
 560:  *
 561:  * ### Multiple headers
 562:  * e.g `header(array('Location' => 'http://example.com', 'X-Extra' => 'My header'));`
 563:  *
 564:  * ### String header
 565:  * e.g `header('WWW-Authenticate: Negotiate');`
 566:  *
 567:  * ### Array of string headers
 568:  * e.g `header(array('WWW-Authenticate: Negotiate', 'Content-type: application/pdf'));`
 569:  *
 570:  * Multiple calls for setting the same header name will have the same effect as setting the header once
 571:  * with the last value sent for it
 572:  *  e.g `header('WWW-Authenticate: Negotiate'); header('WWW-Authenticate: Not-Negotiate');`
 573:  * will have the same effect as only doing `header('WWW-Authenticate: Not-Negotiate');`
 574:  *
 575:  * @param string|array $header An array of header strings or a single header string
 576:  *  - an associative array of "header name" => "header value" is also accepted
 577:  *  - an array of string headers is also accepted
 578:  * @param string|array $value The header value(s)
 579:  * @return array list of headers to be sent
 580:  */
 581:     public function header($header = null, $value = null) {
 582:         if ($header === null) {
 583:             return $this->_headers;
 584:         }
 585:         $headers = is_array($header) ? $header : array($header => $value);
 586:         foreach ($headers as $header => $value) {
 587:             if (is_numeric($header)) {
 588:                 list($header, $value) = array($value, null);
 589:             }
 590:             if ($value === null && strpos($header, ':') !== false) {
 591:                 list($header, $value) = explode(':', $header, 2);
 592:             }
 593:             $this->_headers[$header] = is_array($value) ? array_map('trim', $value) : trim($value);
 594:         }
 595:         return $this->_headers;
 596:     }
 597: 
 598: /**
 599:  * Accessor for the location header.
 600:  *
 601:  * Get/Set the Location header value.
 602:  *
 603:  * @param null|string $url Either null to get the current location, or a string to set one.
 604:  * @return string|null When setting the location null will be returned. When reading the location
 605:  *    a string of the current location header value (if any) will be returned.
 606:  */
 607:     public function location($url = null) {
 608:         if ($url === null) {
 609:             $headers = $this->header();
 610:             return isset($headers['Location']) ? $headers['Location'] : null;
 611:         }
 612:         $this->header('Location', $url);
 613:         return null;
 614:     }
 615: 
 616: /**
 617:  * Buffers the response message to be sent
 618:  * if $content is null the current buffer is returned
 619:  *
 620:  * @param string $content the string message to be sent
 621:  * @return string current message buffer if $content param is passed as null
 622:  */
 623:     public function body($content = null) {
 624:         if ($content === null) {
 625:             return $this->_body;
 626:         }
 627:         return $this->_body = $content;
 628:     }
 629: 
 630: /**
 631:  * Sets the HTTP status code to be sent
 632:  * if $code is null the current code is returned
 633:  *
 634:  * @param int $code the HTTP status code
 635:  * @return int current status code
 636:  * @throws CakeException When an unknown status code is reached.
 637:  */
 638:     public function statusCode($code = null) {
 639:         if ($code === null) {
 640:             return $this->_status;
 641:         }
 642:         if (!isset($this->_statusCodes[$code])) {
 643:             throw new CakeException(__d('cake_dev', 'Unknown status code'));
 644:         }
 645:         return $this->_status = $code;
 646:     }
 647: 
 648: /**
 649:  * Queries & sets valid HTTP response codes & messages.
 650:  *
 651:  * @param int|array $code If $code is an integer, then the corresponding code/message is
 652:  *        returned if it exists, null if it does not exist. If $code is an array, then the
 653:  *        keys are used as codes and the values as messages to add to the default HTTP
 654:  *        codes. The codes must be integers greater than 99 and less than 1000. Keep in
 655:  *        mind that the HTTP specification outlines that status codes begin with a digit
 656:  *        between 1 and 5, which defines the class of response the client is to expect.
 657:  *        Example:
 658:  *
 659:  *        httpCodes(404); // returns array(404 => 'Not Found')
 660:  *
 661:  *        httpCodes(array(
 662:  *            381 => 'Unicorn Moved',
 663:  *            555 => 'Unexpected Minotaur'
 664:  *        )); // sets these new values, and returns true
 665:  *
 666:  *        httpCodes(array(
 667:  *            0 => 'Nothing Here',
 668:  *            -1 => 'Reverse Infinity',
 669:  *            12345 => 'Universal Password',
 670:  *            'Hello' => 'World'
 671:  *        )); // throws an exception due to invalid codes
 672:  *
 673:  *        For more on HTTP status codes see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1
 674:  *
 675:  * @return mixed associative array of the HTTP codes as keys, and the message
 676:  *    strings as values, or null of the given $code does not exist.
 677:  * @throws CakeException If an attempt is made to add an invalid status code
 678:  */
 679:     public function httpCodes($code = null) {
 680:         if (empty($code)) {
 681:             return $this->_statusCodes;
 682:         }
 683:         if (is_array($code)) {
 684:             $codes = array_keys($code);
 685:             $min = min($codes);
 686:             if (!is_int($min) || $min < 100 || max($codes) > 999) {
 687:                 throw new CakeException(__d('cake_dev', 'Invalid status code'));
 688:             }
 689:             $this->_statusCodes = $code + $this->_statusCodes;
 690:             return true;
 691:         }
 692:         if (!isset($this->_statusCodes[$code])) {
 693:             return null;
 694:         }
 695:         return array($code => $this->_statusCodes[$code]);
 696:     }
 697: 
 698: /**
 699:  * Sets the response content type. It can be either a file extension
 700:  * which will be mapped internally to a mime-type or a string representing a mime-type
 701:  * if $contentType is null the current content type is returned
 702:  * if $contentType is an associative array, content type definitions will be stored/replaced
 703:  *
 704:  * ### Setting the content type
 705:  *
 706:  * e.g `type('jpg');`
 707:  *
 708:  * ### Returning the current content type
 709:  *
 710:  * e.g `type();`
 711:  *
 712:  * ### Storing content type definitions
 713:  *
 714:  * e.g `type(array('keynote' => 'application/keynote', 'bat' => 'application/bat'));`
 715:  *
 716:  * ### Replacing a content type definition
 717:  *
 718:  * e.g `type(array('jpg' => 'text/plain'));`
 719:  *
 720:  * @param string $contentType Content type key.
 721:  * @return mixed current content type or false if supplied an invalid content type
 722:  */
 723:     public function type($contentType = null) {
 724:         if ($contentType === null) {
 725:             return $this->_contentType;
 726:         }
 727:         if (is_array($contentType)) {
 728:             foreach ($contentType as $type => $definition) {
 729:                 $this->_mimeTypes[$type] = $definition;
 730:             }
 731:             return $this->_contentType;
 732:         }
 733:         if (isset($this->_mimeTypes[$contentType])) {
 734:             $contentType = $this->_mimeTypes[$contentType];
 735:             $contentType = is_array($contentType) ? current($contentType) : $contentType;
 736:         }
 737:         if (strpos($contentType, '/') === false) {
 738:             return false;
 739:         }
 740:         return $this->_contentType = $contentType;
 741:     }
 742: 
 743: /**
 744:  * Returns the mime type definition for an alias
 745:  *
 746:  * e.g `getMimeType('pdf'); // returns 'application/pdf'`
 747:  *
 748:  * @param string $alias the content type alias to map
 749:  * @return mixed string mapped mime type or false if $alias is not mapped
 750:  */
 751:     public function getMimeType($alias) {
 752:         if (isset($this->_mimeTypes[$alias])) {
 753:             return $this->_mimeTypes[$alias];
 754:         }
 755:         return false;
 756:     }
 757: 
 758: /**
 759:  * Maps a content-type back to an alias
 760:  *
 761:  * e.g `mapType('application/pdf'); // returns 'pdf'`
 762:  *
 763:  * @param string|array $ctype Either a string content type to map, or an array of types.
 764:  * @return mixed Aliases for the types provided.
 765:  */
 766:     public function mapType($ctype) {
 767:         if (is_array($ctype)) {
 768:             return array_map(array($this, 'mapType'), $ctype);
 769:         }
 770: 
 771:         foreach ($this->_mimeTypes as $alias => $types) {
 772:             if (in_array($ctype, (array)$types)) {
 773:                 return $alias;
 774:             }
 775:         }
 776:         return null;
 777:     }
 778: 
 779: /**
 780:  * Sets the response charset
 781:  * if $charset is null the current charset is returned
 782:  *
 783:  * @param string $charset Character set string.
 784:  * @return string current charset
 785:  */
 786:     public function charset($charset = null) {
 787:         if ($charset === null) {
 788:             return $this->_charset;
 789:         }
 790:         return $this->_charset = $charset;
 791:     }
 792: 
 793: /**
 794:  * Sets the correct headers to instruct the client to not cache the response
 795:  *
 796:  * @return void
 797:  */
 798:     public function disableCache() {
 799:         $this->header(array(
 800:             'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT',
 801:             'Last-Modified' => gmdate("D, d M Y H:i:s") . " GMT",
 802:             'Cache-Control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
 803:         ));
 804:     }
 805: 
 806: /**
 807:  * Sets the correct headers to instruct the client to cache the response.
 808:  *
 809:  * @param string|int $since a valid time since the response text has not been modified
 810:  * @param string|int $time a valid time for cache expiry
 811:  * @return void
 812:  */
 813:     public function cache($since, $time = '+1 day') {
 814:         if (!is_int($time)) {
 815:             $time = strtotime($time);
 816:         }
 817:         $this->header(array(
 818:             'Date' => gmdate("D, j M Y G:i:s ", time()) . 'GMT'
 819:         ));
 820:         $this->modified($since);
 821:         $this->expires($time);
 822:         $this->sharable(true);
 823:         $this->maxAge($time - time());
 824:     }
 825: 
 826: /**
 827:  * Sets whether a response is eligible to be cached by intermediate proxies
 828:  * This method controls the `public` or `private` directive in the Cache-Control
 829:  * header
 830:  *
 831:  * @param bool $public If set to true, the Cache-Control header will be set as public
 832:  *   if set to false, the response will be set to private
 833:  *   if no value is provided, it will return whether the response is sharable or not
 834:  * @param int $time time in seconds after which the response should no longer be considered fresh
 835:  * @return bool
 836:  */
 837:     public function sharable($public = null, $time = null) {
 838:         if ($public === null) {
 839:             $public = array_key_exists('public', $this->_cacheDirectives);
 840:             $private = array_key_exists('private', $this->_cacheDirectives);
 841:             $noCache = array_key_exists('no-cache', $this->_cacheDirectives);
 842:             if (!$public && !$private && !$noCache) {
 843:                 return null;
 844:             }
 845:             $sharable = $public || !($private || $noCache);
 846:             return $sharable;
 847:         }
 848:         if ($public) {
 849:             $this->_cacheDirectives['public'] = true;
 850:             unset($this->_cacheDirectives['private']);
 851:         } else {
 852:             $this->_cacheDirectives['private'] = true;
 853:             unset($this->_cacheDirectives['public']);
 854:         }
 855: 
 856:         $this->maxAge($time);
 857:         if (!$time) {
 858:             $this->_setCacheControl();
 859:         }
 860:         return (bool)$public;
 861:     }
 862: 
 863: /**
 864:  * Sets the Cache-Control s-maxage directive.
 865:  * The max-age is the number of seconds after which the response should no longer be considered
 866:  * a good candidate to be fetched from a shared cache (like in a proxy server).
 867:  * If called with no parameters, this function will return the current max-age value if any
 868:  *
 869:  * @param int $seconds if null, the method will return the current s-maxage value
 870:  * @return int
 871:  */
 872:     public function sharedMaxAge($seconds = null) {
 873:         if ($seconds !== null) {
 874:             $this->_cacheDirectives['s-maxage'] = $seconds;
 875:             $this->_setCacheControl();
 876:         }
 877:         if (isset($this->_cacheDirectives['s-maxage'])) {
 878:             return $this->_cacheDirectives['s-maxage'];
 879:         }
 880:         return null;
 881:     }
 882: 
 883: /**
 884:  * Sets the Cache-Control max-age directive.
 885:  * The max-age is the number of seconds after which the response should no longer be considered
 886:  * a good candidate to be fetched from the local (client) cache.
 887:  * If called with no parameters, this function will return the current max-age value if any
 888:  *
 889:  * @param int $seconds if null, the method will return the current max-age value
 890:  * @return int
 891:  */
 892:     public function maxAge($seconds = null) {
 893:         if ($seconds !== null) {
 894:             $this->_cacheDirectives['max-age'] = $seconds;
 895:             $this->_setCacheControl();
 896:         }
 897:         if (isset($this->_cacheDirectives['max-age'])) {
 898:             return $this->_cacheDirectives['max-age'];
 899:         }
 900:         return null;
 901:     }
 902: 
 903: /**
 904:  * Sets the Cache-Control must-revalidate directive.
 905:  * must-revalidate indicates that the response should not be served
 906:  * stale by a cache under any circumstance without first revalidating
 907:  * with the origin.
 908:  * If called with no parameters, this function will return whether must-revalidate is present.
 909:  *
 910:  * @param bool $enable If null returns whether directive is set, if boolean
 911:  *   sets or unsets directive.
 912:  * @return bool
 913:  */
 914:     public function mustRevalidate($enable = null) {
 915:         if ($enable !== null) {
 916:             if ($enable) {
 917:                 $this->_cacheDirectives['must-revalidate'] = true;
 918:             } else {
 919:                 unset($this->_cacheDirectives['must-revalidate']);
 920:             }
 921:             $this->_setCacheControl();
 922:         }
 923:         return array_key_exists('must-revalidate', $this->_cacheDirectives);
 924:     }
 925: 
 926: /**
 927:  * Helper method to generate a valid Cache-Control header from the options set
 928:  * in other methods
 929:  *
 930:  * @return void
 931:  */
 932:     protected function _setCacheControl() {
 933:         $control = '';
 934:         foreach ($this->_cacheDirectives as $key => $val) {
 935:             $control .= $val === true ? $key : sprintf('%s=%s', $key, $val);
 936:             $control .= ', ';
 937:         }
 938:         $control = rtrim($control, ', ');
 939:         $this->header('Cache-Control', $control);
 940:     }
 941: 
 942: /**
 943:  * Sets the Expires header for the response by taking an expiration time
 944:  * If called with no parameters it will return the current Expires value
 945:  *
 946:  * ## Examples:
 947:  *
 948:  * `$response->expires('now')` Will Expire the response cache now
 949:  * `$response->expires(new DateTime('+1 day'))` Will set the expiration in next 24 hours
 950:  * `$response->expires()` Will return the current expiration header value
 951:  *
 952:  * @param string|DateTime $time Valid time string or DateTime object.
 953:  * @return string
 954:  */
 955:     public function expires($time = null) {
 956:         if ($time !== null) {
 957:             $date = $this->_getUTCDate($time);
 958:             $this->_headers['Expires'] = $date->format('D, j M Y H:i:s') . ' GMT';
 959:         }
 960:         if (isset($this->_headers['Expires'])) {
 961:             return $this->_headers['Expires'];
 962:         }
 963:         return null;
 964:     }
 965: 
 966: /**
 967:  * Sets the Last-Modified header for the response by taking a modification time
 968:  * If called with no parameters it will return the current Last-Modified value
 969:  *
 970:  * ## Examples:
 971:  *
 972:  * `$response->modified('now')` Will set the Last-Modified to the current time
 973:  * `$response->modified(new DateTime('+1 day'))` Will set the modification date in the past 24 hours
 974:  * `$response->modified()` Will return the current Last-Modified header value
 975:  *
 976:  * @param string|DateTime $time Valid time string or DateTime object.
 977:  * @return string
 978:  */
 979:     public function modified($time = null) {
 980:         if ($time !== null) {
 981:             $date = $this->_getUTCDate($time);
 982:             $this->_headers['Last-Modified'] = $date->format('D, j M Y H:i:s') . ' GMT';
 983:         }
 984:         if (isset($this->_headers['Last-Modified'])) {
 985:             return $this->_headers['Last-Modified'];
 986:         }
 987:         return null;
 988:     }
 989: 
 990: /**
 991:  * Sets the response as Not Modified by removing any body contents
 992:  * setting the status code to "304 Not Modified" and removing all
 993:  * conflicting headers
 994:  *
 995:  * @return void
 996:  */
 997:     public function notModified() {
 998:         $this->statusCode(304);
 999:         $this->body('');
1000:         $remove = array(
1001:             'Allow',
1002:             'Content-Encoding',
1003:             'Content-Language',
1004:             'Content-Length',
1005:             'Content-MD5',
1006:             'Content-Type',
1007:             'Last-Modified'
1008:         );
1009:         foreach ($remove as $header) {
1010:             unset($this->_headers[$header]);
1011:         }
1012:     }
1013: 
1014: /**
1015:  * Sets the Vary header for the response, if an array is passed,
1016:  * values will be imploded into a comma separated string. If no
1017:  * parameters are passed, then an array with the current Vary header
1018:  * value is returned
1019:  *
1020:  * @param string|array $cacheVariances a single Vary string or an array
1021:  *   containing the list for variances.
1022:  * @return array
1023:  */
1024:     public function vary($cacheVariances = null) {
1025:         if ($cacheVariances !== null) {
1026:             $cacheVariances = (array)$cacheVariances;
1027:             $this->_headers['Vary'] = implode(', ', $cacheVariances);
1028:         }
1029:         if (isset($this->_headers['Vary'])) {
1030:             return explode(', ', $this->_headers['Vary']);
1031:         }
1032:         return null;
1033:     }
1034: 
1035: /**
1036:  * Sets the response Etag, Etags are a strong indicative that a response
1037:  * can be cached by a HTTP client. A bad way of generating Etags is
1038:  * creating a hash of the response output, instead generate a unique
1039:  * hash of the unique components that identifies a request, such as a
1040:  * modification time, a resource Id, and anything else you consider it
1041:  * makes it unique.
1042:  *
1043:  * Second parameter is used to instruct clients that the content has
1044:  * changed, but sematicallly, it can be used as the same thing. Think
1045:  * for instance of a page with a hit counter, two different page views
1046:  * are equivalent, but they differ by a few bytes. This leaves off to
1047:  * the Client the decision of using or not the cached page.
1048:  *
1049:  * If no parameters are passed, current Etag header is returned.
1050:  *
1051:  * @param string $tag Tag to set.
1052:  * @param bool $weak whether the response is semantically the same as
1053:  *   other with the same hash or not
1054:  * @return string
1055:  */
1056:     public function etag($tag = null, $weak = false) {
1057:         if ($tag !== null) {
1058:             $this->_headers['Etag'] = sprintf('%s"%s"', ($weak) ? 'W/' : null, $tag);
1059:         }
1060:         if (isset($this->_headers['Etag'])) {
1061:             return $this->_headers['Etag'];
1062:         }
1063:         return null;
1064:     }
1065: 
1066: /**
1067:  * Returns a DateTime object initialized at the $time param and using UTC
1068:  * as timezone
1069:  *
1070:  * @param DateTime|int|string $time Valid time string or unix timestamp or DateTime object.
1071:  * @return DateTime
1072:  */
1073:     protected function _getUTCDate($time = null) {
1074:         if ($time instanceof DateTime) {
1075:             $result = clone $time;
1076:         } elseif (is_int($time)) {
1077:             $result = new DateTime(date('Y-m-d H:i:s', $time));
1078:         } else {
1079:             $result = new DateTime($time);
1080:         }
1081:         $result->setTimeZone(new DateTimeZone('UTC'));
1082:         return $result;
1083:     }
1084: 
1085: /**
1086:  * Sets the correct output buffering handler to send a compressed response. Responses will
1087:  * be compressed with zlib, if the extension is available.
1088:  *
1089:  * @return bool false if client does not accept compressed responses or no handler is available, true otherwise
1090:  */
1091:     public function compress() {
1092:         $compressionEnabled = ini_get("zlib.output_compression") !== '1' &&
1093:             extension_loaded("zlib") &&
1094:             (strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false);
1095:         return $compressionEnabled && ob_start('ob_gzhandler');
1096:     }
1097: 
1098: /**
1099:  * Returns whether the resulting output will be compressed by PHP
1100:  *
1101:  * @return bool
1102:  */
1103:     public function outputCompressed() {
1104:         return strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false
1105:             && (ini_get("zlib.output_compression") === '1' || in_array('ob_gzhandler', ob_list_handlers()));
1106:     }
1107: 
1108: /**
1109:  * Sets the correct headers to instruct the browser to download the response as a file.
1110:  *
1111:  * @param string $filename the name of the file as the browser will download the response
1112:  * @return void
1113:  */
1114:     public function download($filename) {
1115:         $this->header('Content-Disposition', 'attachment; filename="' . $filename . '"');
1116:     }
1117: 
1118: /**
1119:  * Sets the protocol to be used when sending the response. Defaults to HTTP/1.1
1120:  * If called with no arguments, it will return the current configured protocol
1121:  *
1122:  * @param string $protocol Protocol to be used for sending response.
1123:  * @return string protocol currently set
1124:  */
1125:     public function protocol($protocol = null) {
1126:         if ($protocol !== null) {
1127:             $this->_protocol = $protocol;
1128:         }
1129:         return $this->_protocol;
1130:     }
1131: 
1132: /**
1133:  * Sets the Content-Length header for the response
1134:  * If called with no arguments returns the last Content-Length set
1135:  *
1136:  * @param int $bytes Number of bytes
1137:  * @return int|null
1138:  */
1139:     public function length($bytes = null) {
1140:         if ($bytes !== null) {
1141:             $this->_headers['Content-Length'] = $bytes;
1142:         }
1143:         if (isset($this->_headers['Content-Length'])) {
1144:             return $this->_headers['Content-Length'];
1145:         }
1146:         return null;
1147:     }
1148: 
1149: /**
1150:  * Checks whether a response has not been modified according to the 'If-None-Match'
1151:  * (Etags) and 'If-Modified-Since' (last modification date) request
1152:  * headers. If the response is detected to be not modified, it
1153:  * is marked as so accordingly so the client can be informed of that.
1154:  *
1155:  * In order to mark a response as not modified, you need to set at least
1156:  * the Last-Modified etag response header before calling this method. Otherwise
1157:  * a comparison will not be possible.
1158:  *
1159:  * @param CakeRequest $request Request object
1160:  * @return bool whether the response was marked as not modified or not.
1161:  */
1162:     public function checkNotModified(CakeRequest $request) {
1163:         $etags = preg_split('/\s*,\s*/', $request->header('If-None-Match'), null, PREG_SPLIT_NO_EMPTY);
1164:         $modifiedSince = $request->header('If-Modified-Since');
1165:         if ($responseTag = $this->etag()) {
1166:             $etagMatches = in_array('*', $etags) || in_array($responseTag, $etags);
1167:         }
1168:         if ($modifiedSince) {
1169:             $timeMatches = strtotime($this->modified()) === strtotime($modifiedSince);
1170:         }
1171:         $checks = compact('etagMatches', 'timeMatches');
1172:         if (empty($checks)) {
1173:             return false;
1174:         }
1175:         $notModified = !in_array(false, $checks, true);
1176:         if ($notModified) {
1177:             $this->notModified();
1178:         }
1179:         return $notModified;
1180:     }
1181: 
1182: /**
1183:  * String conversion. Fetches the response body as a string.
1184:  * Does *not* send headers.
1185:  *
1186:  * @return string
1187:  */
1188:     public function __toString() {
1189:         return (string)$this->_body;
1190:     }
1191: 
1192: /**
1193:  * Getter/Setter for cookie configs
1194:  *
1195:  * This method acts as a setter/getter depending on the type of the argument.
1196:  * If the method is called with no arguments, it returns all configurations.
1197:  *
1198:  * If the method is called with a string as argument, it returns either the
1199:  * given configuration if it is set, or null, if it's not set.
1200:  *
1201:  * If the method is called with an array as argument, it will set the cookie
1202:  * configuration to the cookie container.
1203:  *
1204:  * ### Options (when setting a configuration)
1205:  *  - name: The Cookie name
1206:  *  - value: Value of the cookie
1207:  *  - expire: Time the cookie expires in
1208:  *  - path: Path the cookie applies to
1209:  *  - domain: Domain the cookie is for.
1210:  *  - secure: Is the cookie https?
1211:  *  - httpOnly: Is the cookie available in the client?
1212:  *
1213:  * ## Examples
1214:  *
1215:  * ### Getting all cookies
1216:  *
1217:  * `$this->cookie()`
1218:  *
1219:  * ### Getting a certain cookie configuration
1220:  *
1221:  * `$this->cookie('MyCookie')`
1222:  *
1223:  * ### Setting a cookie configuration
1224:  *
1225:  * `$this->cookie((array) $options)`
1226:  *
1227:  * @param array $options Either null to get all cookies, string for a specific cookie
1228:  *  or array to set cookie.
1229:  * @return mixed
1230:  */
1231:     public function cookie($options = null) {
1232:         if ($options === null) {
1233:             return $this->_cookies;
1234:         }
1235: 
1236:         if (is_string($options)) {
1237:             if (!isset($this->_cookies[$options])) {
1238:                 return null;
1239:             }
1240:             return $this->_cookies[$options];
1241:         }
1242: 
1243:         $defaults = array(
1244:             'name' => 'CakeCookie[default]',
1245:             'value' => '',
1246:             'expire' => 0,
1247:             'path' => '/',
1248:             'domain' => '',
1249:             'secure' => false,
1250:             'httpOnly' => false
1251:         );
1252:         $options += $defaults;
1253: 
1254:         $this->_cookies[$options['name']] = $options;
1255:     }
1256: 
1257: /**
1258:  * Setup access for origin and methods on cross origin requests
1259:  *
1260:  * This method allow multiple ways to setup the domains, see the examples
1261:  *
1262:  * ### Full URI
1263:  * e.g `cors($request, 'http://www.cakephp.org');`
1264:  *
1265:  * ### URI with wildcard
1266:  * e.g `cors($request, 'http://*.cakephp.org');`
1267:  *
1268:  * ### Ignoring the requested protocol
1269:  * e.g `cors($request, 'www.cakephp.org');`
1270:  *
1271:  * ### Any URI
1272:  * e.g `cors($request, '*');`
1273:  *
1274:  * ### Whitelist of URIs
1275:  * e.g `cors($request, array('http://www.cakephp.org', '*.google.com', 'https://myproject.github.io'));`
1276:  *
1277:  * @param CakeRequest $request Request object
1278:  * @param string|array $allowedDomains List of allowed domains, see method description for more details
1279:  * @param string|array $allowedMethods List of HTTP verbs allowed
1280:  * @param string|array $allowedHeaders List of HTTP headers allowed
1281:  * @return void
1282:  */
1283:     public function cors(CakeRequest $request, $allowedDomains, $allowedMethods = array(), $allowedHeaders = array()) {
1284:         $origin = $request->header('Origin');
1285:         if (!$origin) {
1286:             return;
1287:         }
1288: 
1289:         $allowedDomains = $this->_normalizeCorsDomains((array)$allowedDomains, $request->is('ssl'));
1290:         foreach ($allowedDomains as $domain) {
1291:             if (!preg_match($domain['preg'], $origin)) {
1292:                 continue;
1293:             }
1294:             $this->header('Access-Control-Allow-Origin', $domain['original'] === '*' ? '*' : $origin);
1295:             $allowedMethods && $this->header('Access-Control-Allow-Methods', implode(', ', (array)$allowedMethods));
1296:             $allowedHeaders && $this->header('Access-Control-Allow-Headers', implode(', ', (array)$allowedHeaders));
1297:             break;
1298:         }
1299:     }
1300: 
1301: /**
1302:  * Normalize the origin to regular expressions and put in an array format
1303:  *
1304:  * @param array $domains Domains to normalize
1305:  * @param bool $requestIsSSL Whether it's a SSL request.
1306:  * @return array
1307:  */
1308:     protected function _normalizeCorsDomains($domains, $requestIsSSL = false) {
1309:         $result = array();
1310:         foreach ($domains as $domain) {
1311:             if ($domain === '*') {
1312:                 $result[] = array('preg' => '@.@', 'original' => '*');
1313:                 continue;
1314:             }
1315: 
1316:             $original = $preg = $domain;
1317:             if (strpos($domain, '://') === false) {
1318:                 $preg = ($requestIsSSL ? 'https://' : 'http://') . $domain;
1319:             }
1320:             $preg = '@' . str_replace('*', '.*', $domain) . '@';
1321:             $result[] = compact('original', 'preg');
1322:         }
1323:         return $result;
1324:     }
1325: 
1326: /**
1327:  * Setup for display or download the given file.
1328:  *
1329:  * If $_SERVER['HTTP_RANGE'] is set a slice of the file will be
1330:  * returned instead of the entire file.
1331:  *
1332:  * ### Options keys
1333:  *
1334:  * - name: Alternate download name
1335:  * - download: If `true` sets download header and forces file to be downloaded rather than displayed in browser
1336:  *
1337:  * @param string $path Path to file. If the path is not an absolute path that resolves
1338:  *   to a file, `APP` will be prepended to the path.
1339:  * @param array $options Options See above.
1340:  * @return void
1341:  * @throws NotFoundException
1342:  */
1343:     public function file($path, $options = array()) {
1344:         $options += array(
1345:             'name' => null,
1346:             'download' => null
1347:         );
1348: 
1349:         if (strpos($path, '../') !== false || strpos($path, '..\\') !== false) {
1350:             throw new NotFoundException(__d(
1351:                 'cake_dev',
1352:                 'The requested file contains `..` and will not be read.'
1353:             ));
1354:         }
1355: 
1356:         if (!is_file($path)) {
1357:             $path = APP . $path;
1358:         }
1359: 
1360:         $file = new File($path);
1361:         if (!$file->exists() || !$file->readable()) {
1362:             if (Configure::read('debug')) {
1363:                 throw new NotFoundException(__d('cake_dev', 'The requested file %s was not found or not readable', $path));
1364:             }
1365:             throw new NotFoundException(__d('cake', 'The requested file was not found'));
1366:         }
1367: 
1368:         $extension = strtolower($file->ext());
1369:         $download = $options['download'];
1370:         if ((!$extension || $this->type($extension) === false) && $download === null) {
1371:             $download = true;
1372:         }
1373: 
1374:         $fileSize = $file->size();
1375:         if ($download) {
1376:             $agent = env('HTTP_USER_AGENT');
1377: 
1378:             if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent)) {
1379:                 $contentType = 'application/octet-stream';
1380:             } elseif (preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
1381:                 $contentType = 'application/force-download';
1382:             }
1383: 
1384:             if (!empty($contentType)) {
1385:                 $this->type($contentType);
1386:             }
1387:             if ($options['name'] === null) {
1388:                 $name = $file->name;
1389:             } else {
1390:                 $name = $options['name'];
1391:             }
1392:             $this->download($name);
1393:             $this->header('Content-Transfer-Encoding', 'binary');
1394:         }
1395: 
1396:         $this->header('Accept-Ranges', 'bytes');
1397:         $httpRange = env('HTTP_RANGE');
1398:         if (isset($httpRange)) {
1399:             $this->_fileRange($file, $httpRange);
1400:         } else {
1401:             $this->header('Content-Length', $fileSize);
1402:         }
1403: 
1404:         $this->_clearBuffer();
1405:         $this->_file = $file;
1406:     }
1407: 
1408: /**
1409:  * Apply a file range to a file and set the end offset.
1410:  *
1411:  * If an invalid range is requested a 416 Status code will be used
1412:  * in the response.
1413:  *
1414:  * @param File $file The file to set a range on.
1415:  * @param string $httpRange The range to use.
1416:  * @return void
1417:  */
1418:     protected function _fileRange($file, $httpRange) {
1419:         $fileSize = $file->size();
1420:         $lastByte = $fileSize - 1;
1421:         $start = 0;
1422:         $end = $lastByte;
1423: 
1424:         preg_match('/^bytes\s*=\s*(\d+)?\s*-\s*(\d+)?$/', $httpRange, $matches);
1425:         if ($matches) {
1426:             $start = $matches[1];
1427:             $end = isset($matches[2]) ? $matches[2] : '';
1428:         }
1429: 
1430:         if ($start === '') {
1431:             $start = $fileSize - $end;
1432:             $end = $lastByte;
1433:         }
1434:         if ($end === '') {
1435:             $end = $lastByte;
1436:         }
1437: 
1438:         if ($start > $end || $end > $lastByte || $start > $lastByte) {
1439:             $this->statusCode(416);
1440:             $this->header(array(
1441:                 'Content-Range' => 'bytes 0-' . $lastByte . '/' . $fileSize
1442:             ));
1443:             return;
1444:         }
1445: 
1446:         $this->header(array(
1447:             'Content-Length' => $end - $start + 1,
1448:             'Content-Range' => 'bytes ' . $start . '-' . $end . '/' . $fileSize
1449:         ));
1450: 
1451:         $this->statusCode(206);
1452:         $this->_fileRange = array($start, $end);
1453:     }
1454: 
1455: /**
1456:  * Reads out a file, and echos the content to the client.
1457:  *
1458:  * @param File $file File object
1459:  * @param array $range The range to read out of the file.
1460:  * @return bool True is whole file is echoed successfully or false if client connection is lost in between
1461:  */
1462:     protected function _sendFile($file, $range) {
1463:         $compress = $this->outputCompressed();
1464:         $file->open('rb');
1465: 
1466:         $end = $start = false;
1467:         if ($range) {
1468:             list($start, $end) = $range;
1469:         }
1470:         if ($start !== false) {
1471:             $file->offset($start);
1472:         }
1473: 
1474:         $bufferSize = 8192;
1475:         set_time_limit(0);
1476:         session_write_close();
1477:         while (!feof($file->handle)) {
1478:             if (!$this->_isActive()) {
1479:                 $file->close();
1480:                 return false;
1481:             }
1482:             $offset = $file->offset();
1483:             if ($end && $offset >= $end) {
1484:                 break;
1485:             }
1486:             if ($end && $offset + $bufferSize >= $end) {
1487:                 $bufferSize = $end - $offset + 1;
1488:             }
1489:             echo fread($file->handle, $bufferSize);
1490:             if (!$compress) {
1491:                 $this->_flushBuffer();
1492:             }
1493:         }
1494:         $file->close();
1495:         return true;
1496:     }
1497: 
1498: /**
1499:  * Returns true if connection is still active
1500:  *
1501:  * @return bool
1502:  */
1503:     protected function _isActive() {
1504:         return connection_status() === CONNECTION_NORMAL && !connection_aborted();
1505:     }
1506: 
1507: /**
1508:  * Clears the contents of the topmost output buffer and discards them
1509:  *
1510:  * @return bool
1511:  */
1512:     protected function _clearBuffer() {
1513:         if (ob_get_length()) {
1514:             return ob_end_clean();
1515:         }
1516:         return true;
1517:     }
1518: 
1519: /**
1520:  * Flushes the contents of the output buffer
1521:  *
1522:  * @return void
1523:  */
1524:     protected function _flushBuffer() {
1525:         //@codingStandardsIgnoreStart
1526:         @flush();
1527:         if (ob_get_level()) {
1528:             @ob_flush();
1529:         }
1530:         //@codingStandardsIgnoreEnd
1531:     }
1532: 
1533: }
1534: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs