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.10 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.10
      • 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 (https://cakephp.org)
   6:  * Copyright (c) Cake Software Foundation, Inc. (https://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. (https://cakefoundation.org)
  13:  * @link          https://cakephp.org CakePHP(tm) Project
  14:  * @package       Cake.Network
  15:  * @since         CakePHP(tm) v 2.0
  16:  * @license       https://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 string
 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 array
 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 array|null|true associative array of the HTTP codes as keys, and the message
 676:  *    strings as values, or null of the given $code does not exist. `true` if `$code` is
 677:  *    an array of valid codes.
 678:  * @throws CakeException If an attempt is made to add an invalid status code
 679:  */
 680:     public function httpCodes($code = null) {
 681:         if (empty($code)) {
 682:             return $this->_statusCodes;
 683:         }
 684:         if (is_array($code)) {
 685:             $codes = array_keys($code);
 686:             $min = min($codes);
 687:             if (!is_int($min) || $min < 100 || max($codes) > 999) {
 688:                 throw new CakeException(__d('cake_dev', 'Invalid status code'));
 689:             }
 690:             $this->_statusCodes = $code + $this->_statusCodes;
 691:             return true;
 692:         }
 693:         if (!isset($this->_statusCodes[$code])) {
 694:             return null;
 695:         }
 696:         return array($code => $this->_statusCodes[$code]);
 697:     }
 698: 
 699: /**
 700:  * Sets the response content type. It can be either a file extension
 701:  * which will be mapped internally to a mime-type or a string representing a mime-type
 702:  * if $contentType is null the current content type is returned
 703:  * if $contentType is an associative array, content type definitions will be stored/replaced
 704:  *
 705:  * ### Setting the content type
 706:  *
 707:  * e.g `type('jpg');`
 708:  *
 709:  * ### Returning the current content type
 710:  *
 711:  * e.g `type();`
 712:  *
 713:  * ### Storing content type definitions
 714:  *
 715:  * e.g `type(array('keynote' => 'application/keynote', 'bat' => 'application/bat'));`
 716:  *
 717:  * ### Replacing a content type definition
 718:  *
 719:  * e.g `type(array('jpg' => 'text/plain'));`
 720:  *
 721:  * @param array|string|null $contentType Content type key.
 722:  * @return string|false current content type or false if supplied an invalid content type
 723:  */
 724:     public function type($contentType = null) {
 725:         if ($contentType === null) {
 726:             return $this->_contentType;
 727:         }
 728:         if (is_array($contentType)) {
 729:             foreach ($contentType as $type => $definition) {
 730:                 $this->_mimeTypes[$type] = $definition;
 731:             }
 732:             return $this->_contentType;
 733:         }
 734:         if (isset($this->_mimeTypes[$contentType])) {
 735:             $contentType = $this->_mimeTypes[$contentType];
 736:             $contentType = is_array($contentType) ? current($contentType) : $contentType;
 737:         }
 738:         if (strpos($contentType, '/') === false) {
 739:             return false;
 740:         }
 741:         return $this->_contentType = $contentType;
 742:     }
 743: 
 744: /**
 745:  * Returns the mime type definition for an alias
 746:  *
 747:  * e.g `getMimeType('pdf'); // returns 'application/pdf'`
 748:  *
 749:  * @param string $alias the content type alias to map
 750:  * @return mixed string mapped mime type or false if $alias is not mapped
 751:  */
 752:     public function getMimeType($alias) {
 753:         if (isset($this->_mimeTypes[$alias])) {
 754:             return $this->_mimeTypes[$alias];
 755:         }
 756:         return false;
 757:     }
 758: 
 759: /**
 760:  * Maps a content-type back to an alias
 761:  *
 762:  * e.g `mapType('application/pdf'); // returns 'pdf'`
 763:  *
 764:  * @param string|array $ctype Either a string content type to map, or an array of types.
 765:  * @return mixed Aliases for the types provided.
 766:  */
 767:     public function mapType($ctype) {
 768:         if (is_array($ctype)) {
 769:             return array_map(array($this, 'mapType'), $ctype);
 770:         }
 771: 
 772:         foreach ($this->_mimeTypes as $alias => $types) {
 773:             if (in_array($ctype, (array)$types)) {
 774:                 return $alias;
 775:             }
 776:         }
 777:         return null;
 778:     }
 779: 
 780: /**
 781:  * Sets the response charset
 782:  * if $charset is null the current charset is returned
 783:  *
 784:  * @param string $charset Character set string.
 785:  * @return string current charset
 786:  */
 787:     public function charset($charset = null) {
 788:         if ($charset === null) {
 789:             return $this->_charset;
 790:         }
 791:         return $this->_charset = $charset;
 792:     }
 793: 
 794: /**
 795:  * Sets the correct headers to instruct the client to not cache the response
 796:  *
 797:  * @return void
 798:  */
 799:     public function disableCache() {
 800:         $this->header(array(
 801:             'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT',
 802:             'Last-Modified' => gmdate("D, d M Y H:i:s") . " GMT",
 803:             'Cache-Control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
 804:         ));
 805:     }
 806: 
 807: /**
 808:  * Sets the correct headers to instruct the client to cache the response.
 809:  *
 810:  * @param string|int $since a valid time since the response text has not been modified
 811:  * @param string|int $time a valid time for cache expiry
 812:  * @return void
 813:  */
 814:     public function cache($since, $time = '+1 day') {
 815:         if (!is_int($time)) {
 816:             $time = strtotime($time);
 817:         }
 818:         $this->header(array(
 819:             'Date' => gmdate("D, j M Y G:i:s ", time()) . 'GMT'
 820:         ));
 821:         $this->modified($since);
 822:         $this->expires($time);
 823:         $this->sharable(true);
 824:         $this->maxAge($time - time());
 825:     }
 826: 
 827: /**
 828:  * Sets whether a response is eligible to be cached by intermediate proxies
 829:  * This method controls the `public` or `private` directive in the Cache-Control
 830:  * header
 831:  *
 832:  * @param bool $public If set to true, the Cache-Control header will be set as public
 833:  *   if set to false, the response will be set to private
 834:  *   if no value is provided, it will return whether the response is sharable or not
 835:  * @param int $time time in seconds after which the response should no longer be considered fresh
 836:  * @return bool
 837:  */
 838:     public function sharable($public = null, $time = null) {
 839:         if ($public === null) {
 840:             $public = array_key_exists('public', $this->_cacheDirectives);
 841:             $private = array_key_exists('private', $this->_cacheDirectives);
 842:             $noCache = array_key_exists('no-cache', $this->_cacheDirectives);
 843:             if (!$public && !$private && !$noCache) {
 844:                 return null;
 845:             }
 846:             $sharable = $public || !($private || $noCache);
 847:             return $sharable;
 848:         }
 849:         if ($public) {
 850:             $this->_cacheDirectives['public'] = true;
 851:             unset($this->_cacheDirectives['private']);
 852:         } else {
 853:             $this->_cacheDirectives['private'] = true;
 854:             unset($this->_cacheDirectives['public']);
 855:         }
 856: 
 857:         $this->maxAge($time);
 858:         if ((int)$time === 0) {
 859:             $this->_setCacheControl();
 860:         }
 861:         return (bool)$public;
 862:     }
 863: 
 864: /**
 865:  * Sets the Cache-Control s-maxage directive.
 866:  * The max-age is the number of seconds after which the response should no longer be considered
 867:  * a good candidate to be fetched from a shared cache (like in a proxy server).
 868:  * If called with no parameters, this function will return the current max-age value if any
 869:  *
 870:  * @param int $seconds if null, the method will return the current s-maxage value
 871:  * @return int
 872:  */
 873:     public function sharedMaxAge($seconds = null) {
 874:         if ($seconds !== null) {
 875:             $this->_cacheDirectives['s-maxage'] = $seconds;
 876:             $this->_setCacheControl();
 877:         }
 878:         if (isset($this->_cacheDirectives['s-maxage'])) {
 879:             return $this->_cacheDirectives['s-maxage'];
 880:         }
 881:         return null;
 882:     }
 883: 
 884: /**
 885:  * Sets the Cache-Control max-age directive.
 886:  * The max-age is the number of seconds after which the response should no longer be considered
 887:  * a good candidate to be fetched from the local (client) cache.
 888:  * If called with no parameters, this function will return the current max-age value if any
 889:  *
 890:  * @param int $seconds if null, the method will return the current max-age value
 891:  * @return int
 892:  */
 893:     public function maxAge($seconds = null) {
 894:         if ($seconds !== null) {
 895:             $this->_cacheDirectives['max-age'] = $seconds;
 896:             $this->_setCacheControl();
 897:         }
 898:         if (isset($this->_cacheDirectives['max-age'])) {
 899:             return $this->_cacheDirectives['max-age'];
 900:         }
 901:         return null;
 902:     }
 903: 
 904: /**
 905:  * Sets the Cache-Control must-revalidate directive.
 906:  * must-revalidate indicates that the response should not be served
 907:  * stale by a cache under any circumstance without first revalidating
 908:  * with the origin.
 909:  * If called with no parameters, this function will return whether must-revalidate is present.
 910:  *
 911:  * @param bool $enable If null returns whether directive is set, if boolean
 912:  *   sets or unsets directive.
 913:  * @return bool
 914:  */
 915:     public function mustRevalidate($enable = null) {
 916:         if ($enable !== null) {
 917:             if ($enable) {
 918:                 $this->_cacheDirectives['must-revalidate'] = true;
 919:             } else {
 920:                 unset($this->_cacheDirectives['must-revalidate']);
 921:             }
 922:             $this->_setCacheControl();
 923:         }
 924:         return array_key_exists('must-revalidate', $this->_cacheDirectives);
 925:     }
 926: 
 927: /**
 928:  * Helper method to generate a valid Cache-Control header from the options set
 929:  * in other methods
 930:  *
 931:  * @return void
 932:  */
 933:     protected function _setCacheControl() {
 934:         $control = '';
 935:         foreach ($this->_cacheDirectives as $key => $val) {
 936:             $control .= $val === true ? $key : sprintf('%s=%s', $key, $val);
 937:             $control .= ', ';
 938:         }
 939:         $control = rtrim($control, ', ');
 940:         $this->header('Cache-Control', $control);
 941:     }
 942: 
 943: /**
 944:  * Sets the Expires header for the response by taking an expiration time
 945:  * If called with no parameters it will return the current Expires value
 946:  *
 947:  * ## Examples:
 948:  *
 949:  * `$response->expires('now')` Will Expire the response cache now
 950:  * `$response->expires(new DateTime('+1 day'))` Will set the expiration in next 24 hours
 951:  * `$response->expires()` Will return the current expiration header value
 952:  *
 953:  * @param string|DateTime $time Valid time string or DateTime object.
 954:  * @return string
 955:  */
 956:     public function expires($time = null) {
 957:         if ($time !== null) {
 958:             $date = $this->_getUTCDate($time);
 959:             $this->_headers['Expires'] = $date->format('D, j M Y H:i:s') . ' GMT';
 960:         }
 961:         if (isset($this->_headers['Expires'])) {
 962:             return $this->_headers['Expires'];
 963:         }
 964:         return null;
 965:     }
 966: 
 967: /**
 968:  * Sets the Last-Modified header for the response by taking a modification time
 969:  * If called with no parameters it will return the current Last-Modified value
 970:  *
 971:  * ## Examples:
 972:  *
 973:  * `$response->modified('now')` Will set the Last-Modified to the current time
 974:  * `$response->modified(new DateTime('+1 day'))` Will set the modification date in the past 24 hours
 975:  * `$response->modified()` Will return the current Last-Modified header value
 976:  *
 977:  * @param string|DateTime $time Valid time string or DateTime object.
 978:  * @return string
 979:  */
 980:     public function modified($time = null) {
 981:         if ($time !== null) {
 982:             $date = $this->_getUTCDate($time);
 983:             $this->_headers['Last-Modified'] = $date->format('D, j M Y H:i:s') . ' GMT';
 984:         }
 985:         if (isset($this->_headers['Last-Modified'])) {
 986:             return $this->_headers['Last-Modified'];
 987:         }
 988:         return null;
 989:     }
 990: 
 991: /**
 992:  * Sets the response as Not Modified by removing any body contents
 993:  * setting the status code to "304 Not Modified" and removing all
 994:  * conflicting headers
 995:  *
 996:  * @return void
 997:  */
 998:     public function notModified() {
 999:         $this->statusCode(304);
1000:         $this->body('');
1001:         $remove = array(
1002:             'Allow',
1003:             'Content-Encoding',
1004:             'Content-Language',
1005:             'Content-Length',
1006:             'Content-MD5',
1007:             'Content-Type',
1008:             'Last-Modified'
1009:         );
1010:         foreach ($remove as $header) {
1011:             unset($this->_headers[$header]);
1012:         }
1013:     }
1014: 
1015: /**
1016:  * Sets the Vary header for the response, if an array is passed,
1017:  * values will be imploded into a comma separated string. If no
1018:  * parameters are passed, then an array with the current Vary header
1019:  * value is returned
1020:  *
1021:  * @param string|array $cacheVariances a single Vary string or an array
1022:  *   containing the list for variances.
1023:  * @return array
1024:  */
1025:     public function vary($cacheVariances = null) {
1026:         if ($cacheVariances !== null) {
1027:             $cacheVariances = (array)$cacheVariances;
1028:             $this->_headers['Vary'] = implode(', ', $cacheVariances);
1029:         }
1030:         if (isset($this->_headers['Vary'])) {
1031:             return explode(', ', $this->_headers['Vary']);
1032:         }
1033:         return null;
1034:     }
1035: 
1036: /**
1037:  * Sets the response Etag, Etags are a strong indicative that a response
1038:  * can be cached by a HTTP client. A bad way of generating Etags is
1039:  * creating a hash of the response output, instead generate a unique
1040:  * hash of the unique components that identifies a request, such as a
1041:  * modification time, a resource Id, and anything else you consider it
1042:  * makes it unique.
1043:  *
1044:  * Second parameter is used to instruct clients that the content has
1045:  * changed, but sematicallly, it can be used as the same thing. Think
1046:  * for instance of a page with a hit counter, two different page views
1047:  * are equivalent, but they differ by a few bytes. This leaves off to
1048:  * the Client the decision of using or not the cached page.
1049:  *
1050:  * If no parameters are passed, current Etag header is returned.
1051:  *
1052:  * @param string $tag Tag to set.
1053:  * @param bool $weak whether the response is semantically the same as
1054:  *   other with the same hash or not
1055:  * @return string
1056:  */
1057:     public function etag($tag = null, $weak = false) {
1058:         if ($tag !== null) {
1059:             $this->_headers['Etag'] = sprintf('%s"%s"', ($weak) ? 'W/' : null, $tag);
1060:         }
1061:         if (isset($this->_headers['Etag'])) {
1062:             return $this->_headers['Etag'];
1063:         }
1064:         return null;
1065:     }
1066: 
1067: /**
1068:  * Returns a DateTime object initialized at the $time param and using UTC
1069:  * as timezone
1070:  *
1071:  * @param DateTime|int|string $time Valid time string or unix timestamp or DateTime object.
1072:  * @return DateTime
1073:  */
1074:     protected function _getUTCDate($time = null) {
1075:         if ($time instanceof DateTime) {
1076:             $result = clone $time;
1077:         } elseif (is_int($time)) {
1078:             $result = new DateTime(date('Y-m-d H:i:s', $time));
1079:         } else {
1080:             $result = new DateTime($time);
1081:         }
1082:         $result->setTimeZone(new DateTimeZone('UTC'));
1083:         return $result;
1084:     }
1085: 
1086: /**
1087:  * Sets the correct output buffering handler to send a compressed response. Responses will
1088:  * be compressed with zlib, if the extension is available.
1089:  *
1090:  * @return bool false if client does not accept compressed responses or no handler is available, true otherwise
1091:  */
1092:     public function compress() {
1093:         $compressionEnabled = ini_get("zlib.output_compression") !== '1' &&
1094:             extension_loaded("zlib") &&
1095:             (strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false);
1096:         return $compressionEnabled && ob_start('ob_gzhandler');
1097:     }
1098: 
1099: /**
1100:  * Returns whether the resulting output will be compressed by PHP
1101:  *
1102:  * @return bool
1103:  */
1104:     public function outputCompressed() {
1105:         return strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false
1106:             && (ini_get("zlib.output_compression") === '1' || in_array('ob_gzhandler', ob_list_handlers()));
1107:     }
1108: 
1109: /**
1110:  * Sets the correct headers to instruct the browser to download the response as a file.
1111:  *
1112:  * @param string $filename the name of the file as the browser will download the response
1113:  * @return void
1114:  */
1115:     public function download($filename) {
1116:         $this->header('Content-Disposition', 'attachment; filename="' . $filename . '"');
1117:     }
1118: 
1119: /**
1120:  * Sets the protocol to be used when sending the response. Defaults to HTTP/1.1
1121:  * If called with no arguments, it will return the current configured protocol
1122:  *
1123:  * @param string $protocol Protocol to be used for sending response.
1124:  * @return string protocol currently set
1125:  */
1126:     public function protocol($protocol = null) {
1127:         if ($protocol !== null) {
1128:             $this->_protocol = $protocol;
1129:         }
1130:         return $this->_protocol;
1131:     }
1132: 
1133: /**
1134:  * Sets the Content-Length header for the response
1135:  * If called with no arguments returns the last Content-Length set
1136:  *
1137:  * @param int $bytes Number of bytes
1138:  * @return int|null
1139:  */
1140:     public function length($bytes = null) {
1141:         if ($bytes !== null) {
1142:             $this->_headers['Content-Length'] = $bytes;
1143:         }
1144:         if (isset($this->_headers['Content-Length'])) {
1145:             return $this->_headers['Content-Length'];
1146:         }
1147:         return null;
1148:     }
1149: 
1150: /**
1151:  * Checks whether a response has not been modified according to the 'If-None-Match'
1152:  * (Etags) and 'If-Modified-Since' (last modification date) request
1153:  * headers. If the response is detected to be not modified, it
1154:  * is marked as so accordingly so the client can be informed of that.
1155:  *
1156:  * In order to mark a response as not modified, you need to set at least
1157:  * the Last-Modified etag response header before calling this method. Otherwise
1158:  * a comparison will not be possible.
1159:  *
1160:  * @param CakeRequest $request Request object
1161:  * @return bool whether the response was marked as not modified or not.
1162:  */
1163:     public function checkNotModified(CakeRequest $request) {
1164:         $ifNoneMatchHeader = $request->header('If-None-Match');
1165:         $etags = array();
1166:         if (is_string($ifNoneMatchHeader)) {
1167:             $etags = preg_split('/\s*,\s*/', $ifNoneMatchHeader, null, PREG_SPLIT_NO_EMPTY);
1168:         }
1169:         $modifiedSince = $request->header('If-Modified-Since');
1170:         $checks = array();
1171:         if ($responseTag = $this->etag()) {
1172:             $checks[] = in_array('*', $etags) || in_array($responseTag, $etags);
1173:         }
1174:         if ($modifiedSince) {
1175:             $checks[] = strtotime($this->modified()) === strtotime($modifiedSince);
1176:         }
1177:         if (empty($checks)) {
1178:             return false;
1179:         }
1180:         $notModified = !in_array(false, $checks, true);
1181:         if ($notModified) {
1182:             $this->notModified();
1183:         }
1184:         return $notModified;
1185:     }
1186: 
1187: /**
1188:  * String conversion. Fetches the response body as a string.
1189:  * Does *not* send headers.
1190:  *
1191:  * @return string
1192:  */
1193:     public function __toString() {
1194:         return (string)$this->_body;
1195:     }
1196: 
1197: /**
1198:  * Getter/Setter for cookie configs
1199:  *
1200:  * This method acts as a setter/getter depending on the type of the argument.
1201:  * If the method is called with no arguments, it returns all configurations.
1202:  *
1203:  * If the method is called with a string as argument, it returns either the
1204:  * given configuration if it is set, or null, if it's not set.
1205:  *
1206:  * If the method is called with an array as argument, it will set the cookie
1207:  * configuration to the cookie container.
1208:  *
1209:  * ### Options (when setting a configuration)
1210:  *  - name: The Cookie name
1211:  *  - value: Value of the cookie
1212:  *  - expire: Time the cookie expires in
1213:  *  - path: Path the cookie applies to
1214:  *  - domain: Domain the cookie is for.
1215:  *  - secure: Is the cookie https?
1216:  *  - httpOnly: Is the cookie available in the client?
1217:  *
1218:  * ## Examples
1219:  *
1220:  * ### Getting all cookies
1221:  *
1222:  * `$this->cookie()`
1223:  *
1224:  * ### Getting a certain cookie configuration
1225:  *
1226:  * `$this->cookie('MyCookie')`
1227:  *
1228:  * ### Setting a cookie configuration
1229:  *
1230:  * `$this->cookie((array) $options)`
1231:  *
1232:  * @param array|string $options Either null to get all cookies, string for a specific cookie
1233:  *  or array to set cookie.
1234:  * @return mixed
1235:  */
1236:     public function cookie($options = null) {
1237:         if ($options === null) {
1238:             return $this->_cookies;
1239:         }
1240: 
1241:         if (is_string($options)) {
1242:             if (!isset($this->_cookies[$options])) {
1243:                 return null;
1244:             }
1245:             return $this->_cookies[$options];
1246:         }
1247: 
1248:         $defaults = array(
1249:             'name' => 'CakeCookie[default]',
1250:             'value' => '',
1251:             'expire' => 0,
1252:             'path' => '/',
1253:             'domain' => '',
1254:             'secure' => false,
1255:             'httpOnly' => false
1256:         );
1257:         $options += $defaults;
1258: 
1259:         $this->_cookies[$options['name']] = $options;
1260:     }
1261: 
1262: /**
1263:  * Setup access for origin and methods on cross origin requests
1264:  *
1265:  * This method allow multiple ways to setup the domains, see the examples
1266:  *
1267:  * ### Full URI
1268:  * e.g `cors($request, 'https://www.cakephp.org');`
1269:  *
1270:  * ### URI with wildcard
1271:  * e.g `cors($request, 'http://*.cakephp.org');`
1272:  *
1273:  * ### Ignoring the requested protocol
1274:  * e.g `cors($request, 'www.cakephp.org');`
1275:  *
1276:  * ### Any URI
1277:  * e.g `cors($request, '*');`
1278:  *
1279:  * ### Whitelist of URIs
1280:  * e.g `cors($request, array('https://www.cakephp.org', '*.google.com', 'https://myproject.github.io'));`
1281:  *
1282:  * @param CakeRequest $request Request object
1283:  * @param string|array $allowedDomains List of allowed domains, see method description for more details
1284:  * @param string|array $allowedMethods List of HTTP verbs allowed
1285:  * @param string|array $allowedHeaders List of HTTP headers allowed
1286:  * @return void
1287:  */
1288:     public function cors(CakeRequest $request, $allowedDomains, $allowedMethods = array(), $allowedHeaders = array()) {
1289:         $origin = $request->header('Origin');
1290:         if (!$origin) {
1291:             return;
1292:         }
1293: 
1294:         $allowedDomains = $this->_normalizeCorsDomains((array)$allowedDomains, $request->is('ssl'));
1295:         foreach ($allowedDomains as $domain) {
1296:             if (!preg_match($domain['preg'], $origin)) {
1297:                 continue;
1298:             }
1299:             $this->header('Access-Control-Allow-Origin', $domain['original'] === '*' ? '*' : $origin);
1300:             $allowedMethods && $this->header('Access-Control-Allow-Methods', implode(', ', (array)$allowedMethods));
1301:             $allowedHeaders && $this->header('Access-Control-Allow-Headers', implode(', ', (array)$allowedHeaders));
1302:             break;
1303:         }
1304:     }
1305: 
1306: /**
1307:  * Normalize the origin to regular expressions and put in an array format
1308:  *
1309:  * @param array $domains Domains to normalize
1310:  * @param bool $requestIsSSL Whether it's a SSL request.
1311:  * @return array
1312:  */
1313:     protected function _normalizeCorsDomains($domains, $requestIsSSL = false) {
1314:         $result = array();
1315:         foreach ($domains as $domain) {
1316:             if ($domain === '*') {
1317:                 $result[] = array('preg' => '@.@', 'original' => '*');
1318:                 continue;
1319:             }
1320:             $original = $domain;
1321:             $preg = '@' . str_replace('*', '.*', $domain) . '@';
1322:             $result[] = compact('original', 'preg');
1323:         }
1324:         return $result;
1325:     }
1326: 
1327: /**
1328:  * Setup for display or download the given file.
1329:  *
1330:  * If $_SERVER['HTTP_RANGE'] is set a slice of the file will be
1331:  * returned instead of the entire file.
1332:  *
1333:  * ### Options keys
1334:  *
1335:  * - name: Alternate download name
1336:  * - download: If `true` sets download header and forces file to be downloaded rather than displayed in browser
1337:  *
1338:  * @param string $path Path to file. If the path is not an absolute path that resolves
1339:  *   to a file, `APP` will be prepended to the path.
1340:  * @param array $options Options See above.
1341:  * @return void
1342:  * @throws NotFoundException
1343:  */
1344:     public function file($path, $options = array()) {
1345:         $options += array(
1346:             'name' => null,
1347:             'download' => null
1348:         );
1349: 
1350:         if (strpos($path, '../') !== false || strpos($path, '..\\') !== false) {
1351:             throw new NotFoundException(__d(
1352:                 'cake_dev',
1353:                 'The requested file contains `..` and will not be read.'
1354:             ));
1355:         }
1356: 
1357:         if (!is_file($path)) {
1358:             $path = APP . $path;
1359:         }
1360: 
1361:         $file = new File($path);
1362:         if (!$file->exists() || !$file->readable()) {
1363:             if (Configure::read('debug')) {
1364:                 throw new NotFoundException(__d('cake_dev', 'The requested file %s was not found or not readable', $path));
1365:             }
1366:             throw new NotFoundException(__d('cake', 'The requested file was not found'));
1367:         }
1368: 
1369:         $extension = strtolower($file->ext());
1370:         $download = $options['download'];
1371:         if ((!$extension || $this->type($extension) === false) && $download === null) {
1372:             $download = true;
1373:         }
1374: 
1375:         $fileSize = $file->size();
1376:         if ($download) {
1377:             $agent = env('HTTP_USER_AGENT');
1378: 
1379:             if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent)) {
1380:                 $contentType = 'application/octet-stream';
1381:             } elseif (preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
1382:                 $contentType = 'application/force-download';
1383:             }
1384: 
1385:             if (!empty($contentType)) {
1386:                 $this->type($contentType);
1387:             }
1388:             if ($options['name'] === null) {
1389:                 $name = $file->name;
1390:             } else {
1391:                 $name = $options['name'];
1392:             }
1393:             $this->download($name);
1394:             $this->header('Content-Transfer-Encoding', 'binary');
1395:         }
1396: 
1397:         $this->header('Accept-Ranges', 'bytes');
1398:         $httpRange = env('HTTP_RANGE');
1399:         if (isset($httpRange)) {
1400:             $this->_fileRange($file, $httpRange);
1401:         } else {
1402:             $this->header('Content-Length', $fileSize);
1403:         }
1404: 
1405:         $this->_clearBuffer();
1406:         $this->_file = $file;
1407:     }
1408: 
1409: /**
1410:  * Apply a file range to a file and set the end offset.
1411:  *
1412:  * If an invalid range is requested a 416 Status code will be used
1413:  * in the response.
1414:  *
1415:  * @param File $file The file to set a range on.
1416:  * @param string $httpRange The range to use.
1417:  * @return void
1418:  */
1419:     protected function _fileRange($file, $httpRange) {
1420:         $fileSize = $file->size();
1421:         $lastByte = $fileSize - 1;
1422:         $start = 0;
1423:         $end = $lastByte;
1424: 
1425:         preg_match('/^bytes\s*=\s*(\d+)?\s*-\s*(\d+)?$/', $httpRange, $matches);
1426:         if ($matches) {
1427:             $start = $matches[1];
1428:             $end = isset($matches[2]) ? $matches[2] : '';
1429:         }
1430: 
1431:         if ($start === '') {
1432:             $start = $fileSize - $end;
1433:             $end = $lastByte;
1434:         }
1435:         if ($end === '') {
1436:             $end = $lastByte;
1437:         }
1438: 
1439:         if ($start > $end || $end > $lastByte || $start > $lastByte) {
1440:             $this->statusCode(416);
1441:             $this->header(array(
1442:                 'Content-Range' => 'bytes 0-' . $lastByte . '/' . $fileSize
1443:             ));
1444:             return;
1445:         }
1446: 
1447:         $this->header(array(
1448:             'Content-Length' => $end - $start + 1,
1449:             'Content-Range' => 'bytes ' . $start . '-' . $end . '/' . $fileSize
1450:         ));
1451: 
1452:         $this->statusCode(206);
1453:         $this->_fileRange = array($start, $end);
1454:     }
1455: 
1456: /**
1457:  * Reads out a file, and echos the content to the client.
1458:  *
1459:  * @param File $file File object
1460:  * @param array $range The range to read out of the file.
1461:  * @return bool True is whole file is echoed successfully or false if client connection is lost in between
1462:  */
1463:     protected function _sendFile($file, $range) {
1464:         $compress = $this->outputCompressed();
1465:         $file->open('rb');
1466: 
1467:         $end = $start = false;
1468:         if ($range && is_array($range)) {
1469:             list($start, $end) = $range;
1470:         }
1471:         if ($start !== false) {
1472:             $file->offset($start);
1473:         }
1474: 
1475:         $bufferSize = 8192;
1476:         set_time_limit(0);
1477:         session_write_close();
1478:         while (!feof($file->handle)) {
1479:             if (!$this->_isActive()) {
1480:                 $file->close();
1481:                 return false;
1482:             }
1483:             $offset = $file->offset();
1484:             if ($end && $offset >= $end) {
1485:                 break;
1486:             }
1487:             if ($end && $offset + $bufferSize >= $end) {
1488:                 $bufferSize = $end - $offset + 1;
1489:             }
1490:             echo fread($file->handle, $bufferSize);
1491:             if (!$compress) {
1492:                 $this->_flushBuffer();
1493:             }
1494:         }
1495:         $file->close();
1496:         return true;
1497:     }
1498: 
1499: /**
1500:  * Returns true if connection is still active
1501:  *
1502:  * @return bool
1503:  */
1504:     protected function _isActive() {
1505:         return connection_status() === CONNECTION_NORMAL && !connection_aborted();
1506:     }
1507: 
1508: /**
1509:  * Clears the contents of the topmost output buffer and discards them
1510:  *
1511:  * @return bool
1512:  */
1513:     protected function _clearBuffer() {
1514:         if (ob_get_length()) {
1515:             return ob_end_clean();
1516:         }
1517:         return true;
1518:     }
1519: 
1520: /**
1521:  * Flushes the contents of the output buffer
1522:  *
1523:  * @return void
1524:  */
1525:     protected function _flushBuffer() {
1526:         //@codingStandardsIgnoreStart
1527:         @flush();
1528:         if (ob_get_level()) {
1529:             @ob_flush();
1530:         }
1531:         //@codingStandardsIgnoreEnd
1532:     }
1533: 
1534: }
1535: 
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