CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Team
    • Issues (Github)
    • YouTube Channel
    • Get Involved
    • Bakery
    • Featured Resources
    • Newsletter
    • Certification
    • My CakePHP
    • CakeFest
    • Facebook
    • Twitter
    • Help & Support
    • Forum
    • Stack Overflow
    • IRC
    • Slack
    • Paid Support
CakePHP

C CakePHP 3.7 Red Velvet API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 3.7
      • 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

Namespaces

  • Cake
    • Auth
      • Storage
    • Cache
      • Engine
    • Collection
      • Iterator
    • Command
    • Console
      • Exception
    • Controller
      • Component
      • Exception
    • Core
      • Configure
        • Engine
      • Exception
      • Retry
    • Database
      • Driver
      • Exception
      • Expression
      • Schema
      • Statement
      • Type
    • Datasource
      • Exception
    • Error
      • Middleware
    • Event
      • Decorator
    • Filesystem
    • Form
    • Http
      • Client
        • Adapter
        • Auth
      • Cookie
      • Exception
      • Middleware
      • Session
    • I18n
      • Formatter
      • Middleware
      • Parser
    • Log
      • Engine
    • Mailer
      • Exception
      • Transport
    • Network
      • Exception
    • ORM
      • Association
      • Behavior
        • Translate
      • Exception
      • Locator
      • Rule
    • Routing
      • Exception
      • Filter
      • Middleware
      • Route
    • Shell
      • Helper
      • Task
    • TestSuite
      • Fixture
      • Stub
    • Utility
      • Exception
    • Validation
    • View
      • Exception
      • Form
      • Helper
      • Widget
  • None

Classes

  • ActionDispatcher
  • BaseApplication
  • Client
  • ControllerFactory
  • CorsBuilder
  • MiddlewareQueue
  • Response
  • ResponseEmitter
  • Runner
  • Server
  • ServerRequest
  • ServerRequestFactory
  • Session
   1: <?php
   2: /**
   3:  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
   4:  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
   5:  *
   6:  * Licensed under The MIT License
   7:  * For full copyright and license information, please see the LICENSE.txt
   8:  * Redistributions of files must retain the above copyright notice.
   9:  *
  10:  * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11:  * @link          https://cakephp.org CakePHP(tm) Project
  12:  * @since         2.0.0
  13:  * @license       https://opensource.org/licenses/mit-license.php MIT License
  14:  */
  15: namespace Cake\Http;
  16: 
  17: use Cake\Core\Configure;
  18: use Cake\Filesystem\File;
  19: use Cake\Filesystem\Folder;
  20: use Cake\Http\Cookie\Cookie;
  21: use Cake\Http\Cookie\CookieCollection;
  22: use Cake\Http\Cookie\CookieInterface;
  23: use Cake\Http\CorsBuilder;
  24: use Cake\Http\Exception\NotFoundException;
  25: use Cake\Log\Log;
  26: use DateTime;
  27: use DateTimeInterface;
  28: use DateTimeZone;
  29: use InvalidArgumentException;
  30: use Psr\Http\Message\ResponseInterface;
  31: use Psr\Http\Message\StreamInterface;
  32: use Zend\Diactoros\MessageTrait;
  33: use Zend\Diactoros\Stream;
  34: 
  35: /**
  36:  * Responses contain the response text, status and headers of a HTTP response.
  37:  */
  38: class Response implements ResponseInterface
  39: {
  40: 
  41:     use MessageTrait;
  42: 
  43:     /**
  44:      * Holds HTTP response statuses
  45:      *
  46:      * @var array
  47:      */
  48:     protected $_statusCodes = [
  49:         100 => 'Continue',
  50:         101 => 'Switching Protocols',
  51:         102 => 'Processing',
  52:         200 => 'OK',
  53:         201 => 'Created',
  54:         202 => 'Accepted',
  55:         203 => 'Non-Authoritative Information',
  56:         204 => 'No Content',
  57:         205 => 'Reset Content',
  58:         206 => 'Partial Content',
  59:         207 => 'Multi-status',
  60:         208 => 'Already Reported',
  61:         226 => 'IM used',
  62:         300 => 'Multiple Choices',
  63:         301 => 'Moved Permanently',
  64:         302 => 'Found',
  65:         303 => 'See Other',
  66:         304 => 'Not Modified',
  67:         305 => 'Use Proxy',
  68:         306 => '(Unused)',
  69:         307 => 'Temporary Redirect',
  70:         308 => 'Permanent Redirect',
  71:         400 => 'Bad Request',
  72:         401 => 'Unauthorized',
  73:         402 => 'Payment Required',
  74:         403 => 'Forbidden',
  75:         404 => 'Not Found',
  76:         405 => 'Method Not Allowed',
  77:         406 => 'Not Acceptable',
  78:         407 => 'Proxy Authentication Required',
  79:         408 => 'Request Timeout',
  80:         409 => 'Conflict',
  81:         410 => 'Gone',
  82:         411 => 'Length Required',
  83:         412 => 'Precondition Failed',
  84:         413 => 'Request Entity Too Large',
  85:         414 => 'Request-URI Too Large',
  86:         415 => 'Unsupported Media Type',
  87:         416 => 'Requested range not satisfiable',
  88:         417 => 'Expectation Failed',
  89:         418 => 'I\'m a teapot',
  90:         421 => 'Misdirected Request',
  91:         422 => 'Unprocessable Entity',
  92:         423 => 'Locked',
  93:         424 => 'Failed Dependency',
  94:         425 => 'Unordered Collection',
  95:         426 => 'Upgrade Required',
  96:         428 => 'Precondition Required',
  97:         429 => 'Too Many Requests',
  98:         431 => 'Request Header Fields Too Large',
  99:         444 => 'Connection Closed Without Response',
 100:         451 => 'Unavailable For Legal Reasons',
 101:         499 => 'Client Closed Request',
 102:         500 => 'Internal Server Error',
 103:         501 => 'Not Implemented',
 104:         502 => 'Bad Gateway',
 105:         503 => 'Service Unavailable',
 106:         504 => 'Gateway Timeout',
 107:         505 => 'Unsupported Version',
 108:         506 => 'Variant Also Negotiates',
 109:         507 => 'Insufficient Storage',
 110:         508 => 'Loop Detected',
 111:         510 => 'Not Extended',
 112:         511 => 'Network Authentication Required',
 113:         599 => 'Network Connect Timeout Error',
 114:     ];
 115: 
 116:     /**
 117:      * Holds type key to mime type mappings for known mime types.
 118:      *
 119:      * @var array
 120:      */
 121:     protected $_mimeTypes = [
 122:         'html' => ['text/html', '*/*'],
 123:         'json' => 'application/json',
 124:         'xml' => ['application/xml', 'text/xml'],
 125:         'xhtml' => ['application/xhtml+xml', 'application/xhtml', 'text/xhtml'],
 126:         'webp' => 'image/webp',
 127:         'rss' => 'application/rss+xml',
 128:         'ai' => 'application/postscript',
 129:         'bcpio' => 'application/x-bcpio',
 130:         'bin' => 'application/octet-stream',
 131:         'ccad' => 'application/clariscad',
 132:         'cdf' => 'application/x-netcdf',
 133:         'class' => 'application/octet-stream',
 134:         'cpio' => 'application/x-cpio',
 135:         'cpt' => 'application/mac-compactpro',
 136:         'csh' => 'application/x-csh',
 137:         'csv' => ['text/csv', 'application/vnd.ms-excel'],
 138:         'dcr' => 'application/x-director',
 139:         'dir' => 'application/x-director',
 140:         'dms' => 'application/octet-stream',
 141:         'doc' => 'application/msword',
 142:         'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
 143:         'drw' => 'application/drafting',
 144:         'dvi' => 'application/x-dvi',
 145:         'dwg' => 'application/acad',
 146:         'dxf' => 'application/dxf',
 147:         'dxr' => 'application/x-director',
 148:         'eot' => 'application/vnd.ms-fontobject',
 149:         'eps' => 'application/postscript',
 150:         'exe' => 'application/octet-stream',
 151:         'ez' => 'application/andrew-inset',
 152:         'flv' => 'video/x-flv',
 153:         'gtar' => 'application/x-gtar',
 154:         'gz' => 'application/x-gzip',
 155:         'bz2' => 'application/x-bzip',
 156:         '7z' => 'application/x-7z-compressed',
 157:         'hdf' => 'application/x-hdf',
 158:         'hqx' => 'application/mac-binhex40',
 159:         'ico' => 'image/x-icon',
 160:         'ips' => 'application/x-ipscript',
 161:         'ipx' => 'application/x-ipix',
 162:         'js' => 'application/javascript',
 163:         'jsonapi' => 'application/vnd.api+json',
 164:         'latex' => 'application/x-latex',
 165:         'lha' => 'application/octet-stream',
 166:         'lsp' => 'application/x-lisp',
 167:         'lzh' => 'application/octet-stream',
 168:         'man' => 'application/x-troff-man',
 169:         'me' => 'application/x-troff-me',
 170:         'mif' => 'application/vnd.mif',
 171:         'ms' => 'application/x-troff-ms',
 172:         'nc' => 'application/x-netcdf',
 173:         'oda' => 'application/oda',
 174:         'otf' => 'font/otf',
 175:         'pdf' => 'application/pdf',
 176:         'pgn' => 'application/x-chess-pgn',
 177:         'pot' => 'application/vnd.ms-powerpoint',
 178:         'pps' => 'application/vnd.ms-powerpoint',
 179:         'ppt' => 'application/vnd.ms-powerpoint',
 180:         'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
 181:         'ppz' => 'application/vnd.ms-powerpoint',
 182:         'pre' => 'application/x-freelance',
 183:         'prt' => 'application/pro_eng',
 184:         'ps' => 'application/postscript',
 185:         'roff' => 'application/x-troff',
 186:         'scm' => 'application/x-lotusscreencam',
 187:         'set' => 'application/set',
 188:         'sh' => 'application/x-sh',
 189:         'shar' => 'application/x-shar',
 190:         'sit' => 'application/x-stuffit',
 191:         'skd' => 'application/x-koan',
 192:         'skm' => 'application/x-koan',
 193:         'skp' => 'application/x-koan',
 194:         'skt' => 'application/x-koan',
 195:         'smi' => 'application/smil',
 196:         'smil' => 'application/smil',
 197:         'sol' => 'application/solids',
 198:         'spl' => 'application/x-futuresplash',
 199:         'src' => 'application/x-wais-source',
 200:         'step' => 'application/STEP',
 201:         'stl' => 'application/SLA',
 202:         'stp' => 'application/STEP',
 203:         'sv4cpio' => 'application/x-sv4cpio',
 204:         'sv4crc' => 'application/x-sv4crc',
 205:         'svg' => 'image/svg+xml',
 206:         'svgz' => 'image/svg+xml',
 207:         'swf' => 'application/x-shockwave-flash',
 208:         't' => 'application/x-troff',
 209:         'tar' => 'application/x-tar',
 210:         'tcl' => 'application/x-tcl',
 211:         'tex' => 'application/x-tex',
 212:         'texi' => 'application/x-texinfo',
 213:         'texinfo' => 'application/x-texinfo',
 214:         'tr' => 'application/x-troff',
 215:         'tsp' => 'application/dsptype',
 216:         'ttc' => 'font/ttf',
 217:         'ttf' => 'font/ttf',
 218:         'unv' => 'application/i-deas',
 219:         'ustar' => 'application/x-ustar',
 220:         'vcd' => 'application/x-cdlink',
 221:         'vda' => 'application/vda',
 222:         'xlc' => 'application/vnd.ms-excel',
 223:         'xll' => 'application/vnd.ms-excel',
 224:         'xlm' => 'application/vnd.ms-excel',
 225:         'xls' => 'application/vnd.ms-excel',
 226:         'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
 227:         'xlw' => 'application/vnd.ms-excel',
 228:         'zip' => 'application/zip',
 229:         'aif' => 'audio/x-aiff',
 230:         'aifc' => 'audio/x-aiff',
 231:         'aiff' => 'audio/x-aiff',
 232:         'au' => 'audio/basic',
 233:         'kar' => 'audio/midi',
 234:         'mid' => 'audio/midi',
 235:         'midi' => 'audio/midi',
 236:         'mp2' => 'audio/mpeg',
 237:         'mp3' => 'audio/mpeg',
 238:         'mpga' => 'audio/mpeg',
 239:         'ogg' => 'audio/ogg',
 240:         'oga' => 'audio/ogg',
 241:         'spx' => 'audio/ogg',
 242:         'ra' => 'audio/x-realaudio',
 243:         'ram' => 'audio/x-pn-realaudio',
 244:         'rm' => 'audio/x-pn-realaudio',
 245:         'rpm' => 'audio/x-pn-realaudio-plugin',
 246:         'snd' => 'audio/basic',
 247:         'tsi' => 'audio/TSP-audio',
 248:         'wav' => 'audio/x-wav',
 249:         'aac' => 'audio/aac',
 250:         'asc' => 'text/plain',
 251:         'c' => 'text/plain',
 252:         'cc' => 'text/plain',
 253:         'css' => 'text/css',
 254:         'etx' => 'text/x-setext',
 255:         'f' => 'text/plain',
 256:         'f90' => 'text/plain',
 257:         'h' => 'text/plain',
 258:         'hh' => 'text/plain',
 259:         'htm' => ['text/html', '*/*'],
 260:         'ics' => 'text/calendar',
 261:         'm' => 'text/plain',
 262:         'rtf' => 'text/rtf',
 263:         'rtx' => 'text/richtext',
 264:         'sgm' => 'text/sgml',
 265:         'sgml' => 'text/sgml',
 266:         'tsv' => 'text/tab-separated-values',
 267:         'tpl' => 'text/template',
 268:         'txt' => 'text/plain',
 269:         'text' => 'text/plain',
 270:         'avi' => 'video/x-msvideo',
 271:         'fli' => 'video/x-fli',
 272:         'mov' => 'video/quicktime',
 273:         'movie' => 'video/x-sgi-movie',
 274:         'mpe' => 'video/mpeg',
 275:         'mpeg' => 'video/mpeg',
 276:         'mpg' => 'video/mpeg',
 277:         'qt' => 'video/quicktime',
 278:         'viv' => 'video/vnd.vivo',
 279:         'vivo' => 'video/vnd.vivo',
 280:         'ogv' => 'video/ogg',
 281:         'webm' => 'video/webm',
 282:         'mp4' => 'video/mp4',
 283:         'm4v' => 'video/mp4',
 284:         'f4v' => 'video/mp4',
 285:         'f4p' => 'video/mp4',
 286:         'm4a' => 'audio/mp4',
 287:         'f4a' => 'audio/mp4',
 288:         'f4b' => 'audio/mp4',
 289:         'gif' => 'image/gif',
 290:         'ief' => 'image/ief',
 291:         'jpg' => 'image/jpeg',
 292:         'jpeg' => 'image/jpeg',
 293:         'jpe' => 'image/jpeg',
 294:         'pbm' => 'image/x-portable-bitmap',
 295:         'pgm' => 'image/x-portable-graymap',
 296:         'png' => 'image/png',
 297:         'pnm' => 'image/x-portable-anymap',
 298:         'ppm' => 'image/x-portable-pixmap',
 299:         'ras' => 'image/cmu-raster',
 300:         'rgb' => 'image/x-rgb',
 301:         'tif' => 'image/tiff',
 302:         'tiff' => 'image/tiff',
 303:         'xbm' => 'image/x-xbitmap',
 304:         'xpm' => 'image/x-xpixmap',
 305:         'xwd' => 'image/x-xwindowdump',
 306:         'psd' => ['application/photoshop', 'application/psd', 'image/psd', 'image/x-photoshop', 'image/photoshop', 'zz-application/zz-winassoc-psd'],
 307:         'ice' => 'x-conference/x-cooltalk',
 308:         'iges' => 'model/iges',
 309:         'igs' => 'model/iges',
 310:         'mesh' => 'model/mesh',
 311:         'msh' => 'model/mesh',
 312:         'silo' => 'model/mesh',
 313:         'vrml' => 'model/vrml',
 314:         'wrl' => 'model/vrml',
 315:         'mime' => 'www/mime',
 316:         'pdb' => 'chemical/x-pdb',
 317:         'xyz' => 'chemical/x-pdb',
 318:         'javascript' => 'application/javascript',
 319:         'form' => 'application/x-www-form-urlencoded',
 320:         'file' => 'multipart/form-data',
 321:         'xhtml-mobile' => 'application/vnd.wap.xhtml+xml',
 322:         'atom' => 'application/atom+xml',
 323:         'amf' => 'application/x-amf',
 324:         'wap' => ['text/vnd.wap.wml', 'text/vnd.wap.wmlscript', 'image/vnd.wap.wbmp'],
 325:         'wml' => 'text/vnd.wap.wml',
 326:         'wmlscript' => 'text/vnd.wap.wmlscript',
 327:         'wbmp' => 'image/vnd.wap.wbmp',
 328:         'woff' => 'application/x-font-woff',
 329:         'appcache' => 'text/cache-manifest',
 330:         'manifest' => 'text/cache-manifest',
 331:         'htc' => 'text/x-component',
 332:         'rdf' => 'application/xml',
 333:         'crx' => 'application/x-chrome-extension',
 334:         'oex' => 'application/x-opera-extension',
 335:         'xpi' => 'application/x-xpinstall',
 336:         'safariextz' => 'application/octet-stream',
 337:         'webapp' => 'application/x-web-app-manifest+json',
 338:         'vcf' => 'text/x-vcard',
 339:         'vtt' => 'text/vtt',
 340:         'mkv' => 'video/x-matroska',
 341:         'pkpass' => 'application/vnd.apple.pkpass',
 342:         'ajax' => 'text/html',
 343:         'bmp' => 'image/bmp'
 344:     ];
 345: 
 346:     /**
 347:      * Protocol header to send to the client
 348:      *
 349:      * @var string
 350:      */
 351:     protected $_protocol = 'HTTP/1.1';
 352: 
 353:     /**
 354:      * Status code to send to the client
 355:      *
 356:      * @var int
 357:      */
 358:     protected $_status = 200;
 359: 
 360:     /**
 361:      * Content type to send. This can be an 'extension' that will be transformed using the $_mimetypes array
 362:      * or a complete mime-type
 363:      *
 364:      * @var string
 365:      */
 366:     protected $_contentType = 'text/html';
 367: 
 368:     /**
 369:      * File object for file to be read out as response
 370:      *
 371:      * @var \Cake\Filesystem\File|null
 372:      */
 373:     protected $_file;
 374: 
 375:     /**
 376:      * File range. Used for requesting ranges of files.
 377:      *
 378:      * @var array
 379:      */
 380:     protected $_fileRange = [];
 381: 
 382:     /**
 383:      * The charset the response body is encoded with
 384:      *
 385:      * @var string
 386:      */
 387:     protected $_charset = 'UTF-8';
 388: 
 389:     /**
 390:      * Holds all the cache directives that will be converted
 391:      * into headers when sending the request
 392:      *
 393:      * @var array
 394:      */
 395:     protected $_cacheDirectives = [];
 396: 
 397:     /**
 398:      * Collection of cookies to send to the client
 399:      *
 400:      * @var \Cake\Http\Cookie\CookieCollection
 401:      */
 402:     protected $_cookies = null;
 403: 
 404:     /**
 405:      * Reason Phrase
 406:      *
 407:      * @var string
 408:      */
 409:     protected $_reasonPhrase = 'OK';
 410: 
 411:     /**
 412:      * Stream mode options.
 413:      *
 414:      * @var string
 415:      */
 416:     protected $_streamMode = 'wb+';
 417: 
 418:     /**
 419:      * Stream target or resource object.
 420:      *
 421:      * @var string|resource
 422:      */
 423:     protected $_streamTarget = 'php://memory';
 424: 
 425:     /**
 426:      * Constructor
 427:      *
 428:      * @param array $options list of parameters to setup the response. Possible values are:
 429:      *  - body: the response text that should be sent to the client
 430:      *  - statusCodes: additional allowable response codes
 431:      *  - status: the HTTP status code to respond with
 432:      *  - type: a complete mime-type string or an extension mapped in this class
 433:      *  - charset: the charset for the response body
 434:      */
 435:     public function __construct(array $options = [])
 436:     {
 437:         if (isset($options['streamTarget'])) {
 438:             $this->_streamTarget = $options['streamTarget'];
 439:         }
 440:         if (isset($options['streamMode'])) {
 441:             $this->_streamMode = $options['streamMode'];
 442:         }
 443:         if (isset($options['stream'])) {
 444:             if (!$options['stream'] instanceof StreamInterface) {
 445:                 throw new InvalidArgumentException('Stream option must be an object that implements StreamInterface');
 446:             }
 447:             $this->stream = $options['stream'];
 448:         } else {
 449:             $this->_createStream();
 450:         }
 451:         if (isset($options['body'])) {
 452:             $this->stream->write($options['body']);
 453:         }
 454:         if (isset($options['statusCodes'])) {
 455:             $this->httpCodes($options['statusCodes']);
 456:         }
 457:         if (isset($options['status'])) {
 458:             $this->_setStatus($options['status']);
 459:         }
 460:         if (!isset($options['charset'])) {
 461:             $options['charset'] = Configure::read('App.encoding');
 462:         }
 463:         $this->_charset = $options['charset'];
 464:         if (isset($options['type'])) {
 465:             $this->_contentType = $this->resolveType($options['type']);
 466:         }
 467:         $this->_setContentType();
 468:         $this->_cookies = new CookieCollection();
 469:     }
 470: 
 471:     /**
 472:      * Creates the stream object.
 473:      *
 474:      * @return void
 475:      */
 476:     protected function _createStream()
 477:     {
 478:         $this->stream = new Stream($this->_streamTarget, $this->_streamMode);
 479:     }
 480: 
 481:     /**
 482:      * Sends the complete response to the client including headers and message body.
 483:      * Will echo out the content in the response body.
 484:      *
 485:      * @return void
 486:      * @deprecated 3.4.0 Will be removed in 4.0.0
 487:      */
 488:     public function send()
 489:     {
 490:         deprecationWarning('Response::send() will be removed in 4.0.0');
 491: 
 492:         if ($this->hasHeader('Location') && $this->_status === 200) {
 493:             $this->statusCode(302);
 494:         }
 495: 
 496:         $this->_setContent();
 497:         $this->sendHeaders();
 498: 
 499:         if ($this->_file) {
 500:             $this->_sendFile($this->_file, $this->_fileRange);
 501:             $this->_file = null;
 502:             $this->_fileRange = [];
 503:         } else {
 504:             $this->_sendContent($this->body());
 505:         }
 506: 
 507:         if (function_exists('fastcgi_finish_request')) {
 508:             fastcgi_finish_request();
 509:         }
 510:     }
 511: 
 512:     /**
 513:      * Sends the HTTP headers and cookies.
 514:      *
 515:      * @return void
 516:      * @deprecated 3.4.0 Will be removed in 4.0.0
 517:      */
 518:     public function sendHeaders()
 519:     {
 520:         deprecationWarning(
 521:             'Will be removed in 4.0.0'
 522:         );
 523: 
 524:         $file = $line = null;
 525:         if (headers_sent($file, $line)) {
 526:             Log::warning("Headers already sent in {$file}:{$line}");
 527: 
 528:             return;
 529:         }
 530: 
 531:         $codeMessage = $this->_statusCodes[$this->_status];
 532:         $this->_setCookies();
 533:         $this->_sendHeader("{$this->_protocol} {$this->_status} {$codeMessage}");
 534:         $this->_setContentType();
 535: 
 536:         foreach ($this->headers as $header => $values) {
 537:             foreach ((array)$values as $value) {
 538:                 $this->_sendHeader($header, $value);
 539:             }
 540:         }
 541:     }
 542: 
 543:     /**
 544:      * Sets the cookies that have been added via Cake\Http\Response::cookie() before any
 545:      * other output is sent to the client. Will set the cookies in the order they
 546:      * have been set.
 547:      *
 548:      * @return void
 549:      * @deprecated 3.4.0 Will be removed in 4.0.0
 550:      */
 551:     protected function _setCookies()
 552:     {
 553:         deprecationWarning(
 554:             'Will be removed in 4.0.0'
 555:         );
 556: 
 557:         foreach ($this->_cookies as $cookie) {
 558:             setcookie(
 559:                 $cookie->getName(),
 560:                 $cookie->getValue(),
 561:                 $cookie->getExpiresTimestamp(),
 562:                 $cookie->getPath(),
 563:                 $cookie->getDomain(),
 564:                 $cookie->isSecure(),
 565:                 $cookie->isHttpOnly()
 566:             );
 567:         }
 568:     }
 569: 
 570:     /**
 571:      * Formats the Content-Type header based on the configured contentType and charset
 572:      * the charset will only be set in the header if the response is of type text/*
 573:      *
 574:      * @return void
 575:      */
 576:     protected function _setContentType()
 577:     {
 578:         if (in_array($this->_status, [304, 204])) {
 579:             $this->_clearHeader('Content-Type');
 580: 
 581:             return;
 582:         }
 583:         $whitelist = [
 584:             'application/javascript', 'application/xml', 'application/rss+xml'
 585:         ];
 586: 
 587:         $charset = false;
 588:         if ($this->_charset &&
 589:             (strpos($this->_contentType, 'text/') === 0 || in_array($this->_contentType, $whitelist))
 590:         ) {
 591:             $charset = true;
 592:         }
 593: 
 594:         if ($charset) {
 595:             $this->_setHeader('Content-Type', "{$this->_contentType}; charset={$this->_charset}");
 596:         } else {
 597:             $this->_setHeader('Content-Type', (string)$this->_contentType);
 598:         }
 599:     }
 600: 
 601:     /**
 602:      * Sets the response body to an empty text if the status code is 204 or 304
 603:      *
 604:      * @return void
 605:      * @deprecated 3.4.0 Will be removed in 4.0.0
 606:      */
 607:     protected function _setContent()
 608:     {
 609:         deprecationWarning(
 610:             'Will be removed in 4.0.0'
 611:         );
 612: 
 613:         if (in_array($this->_status, [304, 204])) {
 614:             $this->body('');
 615:         }
 616:     }
 617: 
 618:     /**
 619:      * Sends a header to the client.
 620:      *
 621:      * @param string $name the header name
 622:      * @param string|null $value the header value
 623:      * @return void
 624:      * @deprecated 3.4.0 Will be removed in 4.0.0
 625:      */
 626:     protected function _sendHeader($name, $value = null)
 627:     {
 628:         deprecationWarning(
 629:             'Will be removed in 4.0.0'
 630:         );
 631: 
 632:         if ($value === null) {
 633:             header($name);
 634:         } else {
 635:             header("{$name}: {$value}");
 636:         }
 637:     }
 638: 
 639:     /**
 640:      * Sends a content string to the client.
 641:      *
 642:      * If the content is a callable, it is invoked. The callable should either
 643:      * return a string or output content directly and have no return value.
 644:      *
 645:      * @param string|callable $content String to send as response body or callable
 646:      *  which returns/outputs content.
 647:      * @return void
 648:      * @deprecated 3.4.0 Will be removed in 4.0.0
 649:      */
 650:     protected function _sendContent($content)
 651:     {
 652:         deprecationWarning(
 653:             'Will be removed in 4.0.0'
 654:         );
 655: 
 656:         if (!is_string($content) && is_callable($content)) {
 657:             $content = $content();
 658:         }
 659: 
 660:         echo $content;
 661:     }
 662: 
 663:     /**
 664:      * Buffers a header string to be sent
 665:      * Returns the complete list of buffered headers
 666:      *
 667:      * ### Single header
 668:      * ```
 669:      * header('Location', 'http://example.com');
 670:      * ```
 671:      *
 672:      * ### Multiple headers
 673:      * ```
 674:      * header(['Location' => 'http://example.com', 'X-Extra' => 'My header']);
 675:      * ```
 676:      *
 677:      * ### String header
 678:      * ```
 679:      * header('WWW-Authenticate: Negotiate');
 680:      * ```
 681:      *
 682:      * ### Array of string headers
 683:      * ```
 684:      * header(['WWW-Authenticate: Negotiate', 'Content-type: application/pdf']);
 685:      * ```
 686:      *
 687:      * Multiple calls for setting the same header name will have the same effect as setting the header once
 688:      * with the last value sent for it
 689:      * ```
 690:      * header('WWW-Authenticate: Negotiate');
 691:      * header('WWW-Authenticate: Not-Negotiate');
 692:      * ```
 693:      * will have the same effect as only doing
 694:      * ```
 695:      * header('WWW-Authenticate: Not-Negotiate');
 696:      * ```
 697:      *
 698:      * @param string|array|null $header An array of header strings or a single header string
 699:      *  - an associative array of "header name" => "header value" is also accepted
 700:      *  - an array of string headers is also accepted
 701:      * @param string|array|null $value The header value(s)
 702:      * @return array List of headers to be sent
 703:      * @deprecated 3.4.0 Use `withHeader()`, `getHeaderLine()` and `getHeaders()` instead.
 704:      */
 705:     public function header($header = null, $value = null)
 706:     {
 707:         deprecationWarning(
 708:             'Response::header() is deprecated. ' .
 709:             'Use `withHeader()`, `getHeaderLine()` and `getHeaders()` instead.'
 710:         );
 711: 
 712:         if ($header === null) {
 713:             return $this->getSimpleHeaders();
 714:         }
 715: 
 716:         $headers = is_array($header) ? $header : [$header => $value];
 717:         foreach ($headers as $header => $value) {
 718:             if (is_numeric($header)) {
 719:                 list($header, $value) = [$value, null];
 720:             }
 721:             if ($value === null) {
 722:                 list($header, $value) = explode(':', $header, 2);
 723:             }
 724: 
 725:             $lower = strtolower($header);
 726:             if (array_key_exists($lower, $this->headerNames)) {
 727:                 $header = $this->headerNames[$lower];
 728:             } else {
 729:                 $this->headerNames[$lower] = $header;
 730:             }
 731: 
 732:             $this->headers[$header] = is_array($value) ? array_map('trim', $value) : [trim($value)];
 733:         }
 734: 
 735:         return $this->getSimpleHeaders();
 736:     }
 737: 
 738:     /**
 739:      * Backwards compatibility helper for getting flattened headers.
 740:      *
 741:      * Previously CakePHP would store headers as a simple dictionary, now that
 742:      * we're supporting PSR7, the internal storage has each header as an array.
 743:      *
 744:      * @return array
 745:      */
 746:     protected function getSimpleHeaders()
 747:     {
 748:         $out = [];
 749:         foreach ($this->headers as $key => $values) {
 750:             $header = $this->headerNames[strtolower($key)];
 751:             if (count($values) === 1) {
 752:                 $values = $values[0];
 753:             }
 754:             $out[$header] = $values;
 755:         }
 756: 
 757:         return $out;
 758:     }
 759: 
 760:     /**
 761:      * Accessor for the location header.
 762:      *
 763:      * Get/Set the Location header value.
 764:      *
 765:      * @param null|string $url Either null to get the current location, or a string to set one.
 766:      * @return string|null When setting the location null will be returned. When reading the location
 767:      *   a string of the current location header value (if any) will be returned.
 768:      * @deprecated 3.4.0 Mutable responses are deprecated. Use `withLocation()` and `getHeaderLine()`
 769:      *   instead.
 770:      */
 771:     public function location($url = null)
 772:     {
 773:         deprecationWarning(
 774:             'Response::location() is deprecated. ' .
 775:             'Mutable responses are deprecated. Use `withLocation()` and `getHeaderLine()` instead.'
 776:         );
 777: 
 778:         if ($url === null) {
 779:             $result = $this->getHeaderLine('Location');
 780:             if (!$result) {
 781:                 return null;
 782:             }
 783: 
 784:             return $result;
 785:         }
 786:         if ($this->_status === 200) {
 787:             $this->_status = 302;
 788:         }
 789:         $this->_setHeader('Location', $url);
 790: 
 791:         return null;
 792:     }
 793: 
 794:     /**
 795:      * Return an instance with an updated location header.
 796:      *
 797:      * If the current status code is 200, it will be replaced
 798:      * with 302.
 799:      *
 800:      * @param string $url The location to redirect to.
 801:      * @return static A new response with the Location header set.
 802:      */
 803:     public function withLocation($url)
 804:     {
 805:         $new = $this->withHeader('Location', $url);
 806:         if ($new->_status === 200) {
 807:             $new->_status = 302;
 808:         }
 809: 
 810:         return $new;
 811:     }
 812: 
 813:     /**
 814:      * Sets a header.
 815:      *
 816:      * @param string $header Header key.
 817:      * @param string $value Header value.
 818:      * @return void
 819:      */
 820:     protected function _setHeader($header, $value)
 821:     {
 822:         $normalized = strtolower($header);
 823:         $this->headerNames[$normalized] = $header;
 824:         $this->headers[$header] = [$value];
 825:     }
 826: 
 827:     /**
 828:      * Clear header
 829:      *
 830:      * @param string $header Header key.
 831:      * @return void
 832:      */
 833:     protected function _clearHeader($header)
 834:     {
 835:         $normalized = strtolower($header);
 836:         if (!isset($this->headerNames[$normalized])) {
 837:             return;
 838:         }
 839:         $original = $this->headerNames[$normalized];
 840:         unset($this->headerNames[$normalized], $this->headers[$original]);
 841:     }
 842: 
 843:     /**
 844:      * Buffers the response message to be sent
 845:      * if $content is null the current buffer is returned
 846:      *
 847:      * @param string|callable|null $content the string or callable message to be sent
 848:      * @return string|null Current message buffer if $content param is passed as null
 849:      * @deprecated 3.4.0 Mutable response methods are deprecated. Use `withBody()`/`withStringBody()` and `getBody()` instead.
 850:      */
 851:     public function body($content = null)
 852:     {
 853:         deprecationWarning(
 854:             'Response::body() is deprecated. ' .
 855:             'Mutable response methods are deprecated. Use `withBody()` and `getBody()` instead.'
 856:         );
 857: 
 858:         if ($content === null) {
 859:             if ($this->stream->isSeekable()) {
 860:                 $this->stream->rewind();
 861:             }
 862:             $result = $this->stream->getContents();
 863:             if (strlen($result) === 0) {
 864:                 return null;
 865:             }
 866: 
 867:             return $result;
 868:         }
 869: 
 870:         // Compatibility with closure/streaming responses
 871:         if (!is_string($content) && is_callable($content)) {
 872:             $this->stream = new CallbackStream($content);
 873:         } else {
 874:             $this->_createStream();
 875:             $this->stream->write($content);
 876:         }
 877: 
 878:         return $content;
 879:     }
 880: 
 881:     /**
 882:      * Handles the callable body for backward compatibility reasons.
 883:      *
 884:      * @param callable $content Callable content.
 885:      * @return string
 886:      */
 887:     protected function _handleCallableBody(callable $content)
 888:     {
 889:         ob_start();
 890:         $result1 = $content();
 891:         $result2 = ob_get_contents();
 892:         ob_get_clean();
 893: 
 894:         if ($result1) {
 895:             return $result1;
 896:         }
 897: 
 898:         return $result2;
 899:     }
 900: 
 901:     /**
 902:      * Sets the HTTP status code to be sent
 903:      * if $code is null the current code is returned
 904:      *
 905:      * If the status code is 304 or 204, the existing Content-Type header
 906:      * will be cleared, as these response codes have no body.
 907:      *
 908:      * @param int|null $code the HTTP status code
 909:      * @return int Current status code
 910:      * @throws \InvalidArgumentException When an unknown status code is reached.
 911:      * @deprecated 3.4.0 Use `getStatusCode()` and `withStatus()` instead.
 912:      */
 913:     public function statusCode($code = null)
 914:     {
 915:         deprecationWarning(
 916:             'Response::statusCode() is deprecated. ' .
 917:             'Use `getStatusCode()` and `withStatus()` instead.'
 918:         );
 919: 
 920:         if ($code === null) {
 921:             return $this->_status;
 922:         }
 923:         if (!isset($this->_statusCodes[$code])) {
 924:             throw new InvalidArgumentException('Unknown status code');
 925:         }
 926:         $this->_setStatus($code);
 927: 
 928:         return $code;
 929:     }
 930: 
 931:     /**
 932:      * Gets the response status code.
 933:      *
 934:      * The status code is a 3-digit integer result code of the server's attempt
 935:      * to understand and satisfy the request.
 936:      *
 937:      * @return int Status code.
 938:      */
 939:     public function getStatusCode()
 940:     {
 941:         return $this->_status;
 942:     }
 943: 
 944:     /**
 945:      * Return an instance with the specified status code and, optionally, reason phrase.
 946:      *
 947:      * If no reason phrase is specified, implementations MAY choose to default
 948:      * to the RFC 7231 or IANA recommended reason phrase for the response's
 949:      * status code.
 950:      *
 951:      * This method MUST be implemented in such a way as to retain the
 952:      * immutability of the message, and MUST return an instance that has the
 953:      * updated status and reason phrase.
 954:      *
 955:      * If the status code is 304 or 204, the existing Content-Type header
 956:      * will be cleared, as these response codes have no body.
 957:      *
 958:      * @link https://tools.ietf.org/html/rfc7231#section-6
 959:      * @link https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
 960:      * @param int $code The 3-digit integer result code to set.
 961:      * @param string $reasonPhrase The reason phrase to use with the
 962:      *     provided status code; if none is provided, implementations MAY
 963:      *     use the defaults as suggested in the HTTP specification.
 964:      * @return static
 965:      * @throws \InvalidArgumentException For invalid status code arguments.
 966:      */
 967:     public function withStatus($code, $reasonPhrase = '')
 968:     {
 969:         $new = clone $this;
 970:         $new->_setStatus($code, $reasonPhrase);
 971: 
 972:         return $new;
 973:     }
 974: 
 975:     /**
 976:      * Modifier for response status
 977:      *
 978:      * @param int $code The code to set.
 979:      * @param string $reasonPhrase The response reason phrase.
 980:      * @return void
 981:      * @throws \InvalidArgumentException For invalid status code arguments.
 982:      */
 983:     protected function _setStatus($code, $reasonPhrase = '')
 984:     {
 985:         if (!isset($this->_statusCodes[$code])) {
 986:             throw new InvalidArgumentException(sprintf(
 987:                 'Invalid status code: %s. Use a valid HTTP status code in range 1xx - 5xx.',
 988:                 $code
 989:             ));
 990:         }
 991: 
 992:         $this->_status = $code;
 993:         if (empty($reasonPhrase)) {
 994:             $reasonPhrase = $this->_statusCodes[$code];
 995:         }
 996:         $this->_reasonPhrase = $reasonPhrase;
 997:         $this->_setContentType();
 998:     }
 999: 
1000:     /**
1001:      * Gets the response reason phrase associated with the status code.
1002:      *
1003:      * Because a reason phrase is not a required element in a response
1004:      * status line, the reason phrase value MAY be null. Implementations MAY
1005:      * choose to return the default RFC 7231 recommended reason phrase (or those
1006:      * listed in the IANA HTTP Status Code Registry) for the response's
1007:      * status code.
1008:      *
1009:      * @link https://tools.ietf.org/html/rfc7231#section-6
1010:      * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
1011:      * @return string Reason phrase; must return an empty string if none present.
1012:      */
1013:     public function getReasonPhrase()
1014:     {
1015:         return $this->_reasonPhrase;
1016:     }
1017: 
1018:     /**
1019:      * Queries & sets valid HTTP response codes & messages.
1020:      *
1021:      * @param int|array|null $code If $code is an integer, then the corresponding code/message is
1022:      *        returned if it exists, null if it does not exist. If $code is an array, then the
1023:      *        keys are used as codes and the values as messages to add to the default HTTP
1024:      *        codes. The codes must be integers greater than 99 and less than 1000. Keep in
1025:      *        mind that the HTTP specification outlines that status codes begin with a digit
1026:      *        between 1 and 5, which defines the class of response the client is to expect.
1027:      *        Example:
1028:      *
1029:      *        httpCodes(404); // returns [404 => 'Not Found']
1030:      *
1031:      *        httpCodes([
1032:      *            381 => 'Unicorn Moved',
1033:      *            555 => 'Unexpected Minotaur'
1034:      *        ]); // sets these new values, and returns true
1035:      *
1036:      *        httpCodes([
1037:      *            0 => 'Nothing Here',
1038:      *            -1 => 'Reverse Infinity',
1039:      *            12345 => 'Universal Password',
1040:      *            'Hello' => 'World'
1041:      *        ]); // throws an exception due to invalid codes
1042:      *
1043:      *        For more on HTTP status codes see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1
1044:      *
1045:      * @return mixed Associative array of the HTTP codes as keys, and the message
1046:      *    strings as values, or null of the given $code does not exist.
1047:      * @throws \InvalidArgumentException If an attempt is made to add an invalid status code
1048:      * @deprecated 3.4.0 Will be removed in 4.0.0
1049:      */
1050:     public function httpCodes($code = null)
1051:     {
1052:         deprecationWarning('Response::httpCodes(). Will be removed in 4.0.0');
1053: 
1054:         if (empty($code)) {
1055:             return $this->_statusCodes;
1056:         }
1057:         if (is_array($code)) {
1058:             $codes = array_keys($code);
1059:             $min = min($codes);
1060:             if (!is_int($min) || $min < 100 || max($codes) > 999) {
1061:                 throw new InvalidArgumentException('Invalid status code');
1062:             }
1063:             $this->_statusCodes = $code + $this->_statusCodes;
1064: 
1065:             return true;
1066:         }
1067:         if (!isset($this->_statusCodes[$code])) {
1068:             return null;
1069:         }
1070: 
1071:         return [$code => $this->_statusCodes[$code]];
1072:     }
1073: 
1074:     /**
1075:      * Sets the response content type. It can be either a file extension
1076:      * which will be mapped internally to a mime-type or a string representing a mime-type
1077:      * if $contentType is null the current content type is returned
1078:      * if $contentType is an associative array, content type definitions will be stored/replaced
1079:      *
1080:      * ### Setting the content type
1081:      *
1082:      * ```
1083:      * type('jpg');
1084:      * ```
1085:      *
1086:      * If you attempt to set the type on a 304 or 204 status code response, the
1087:      * content type will not take effect as these status codes do not have content-types.
1088:      *
1089:      * ### Returning the current content type
1090:      *
1091:      * ```
1092:      * type();
1093:      * ```
1094:      *
1095:      * ### Storing content type definitions
1096:      *
1097:      * ```
1098:      * type(['keynote' => 'application/keynote', 'bat' => 'application/bat']);
1099:      * ```
1100:      *
1101:      * ### Replacing a content type definition
1102:      *
1103:      * ```
1104:      * type(['jpg' => 'text/plain']);
1105:      * ```
1106:      *
1107:      * @param string|null $contentType Content type key.
1108:      * @return mixed Current content type or false if supplied an invalid content type.
1109:      * @deprecated 3.5.5 Use getType() or withType() instead.
1110:      */
1111:     public function type($contentType = null)
1112:     {
1113:         deprecationWarning(
1114:             'Response::type() is deprecated. ' .
1115:             'Use setTypeMap(), getType() or withType() instead.'
1116:         );
1117: 
1118:         if ($contentType === null) {
1119:             return $this->getType();
1120:         }
1121:         if (is_array($contentType)) {
1122:             foreach ($contentType as $type => $definition) {
1123:                 $this->_mimeTypes[$type] = $definition;
1124:             }
1125: 
1126:             return $this->getType();
1127:         }
1128:         if (isset($this->_mimeTypes[$contentType])) {
1129:             $contentType = $this->_mimeTypes[$contentType];
1130:             $contentType = is_array($contentType) ? current($contentType) : $contentType;
1131:         }
1132:         if (strpos($contentType, '/') === false) {
1133:             return false;
1134:         }
1135:         $this->_contentType = $contentType;
1136:         $this->_setContentType();
1137: 
1138:         return $contentType;
1139:     }
1140: 
1141:     /**
1142:      * Sets a content type definition into the map.
1143:      *
1144:      * E.g.: setTypeMap('xhtml', ['application/xhtml+xml', 'application/xhtml'])
1145:      *
1146:      * This is needed for RequestHandlerComponent and recognition of types.
1147:      *
1148:      * @param string $type Content type.
1149:      * @param string|array $mimeType Definition of the mime type.
1150:      * @return void
1151:      */
1152:     public function setTypeMap($type, $mimeType)
1153:     {
1154:         $this->_mimeTypes[$type] = $mimeType;
1155:     }
1156: 
1157:     /**
1158:      * Returns the current content type.
1159:      *
1160:      * @return string
1161:      */
1162:     public function getType()
1163:     {
1164:         return $this->_contentType;
1165:     }
1166: 
1167:     /**
1168:      * Get an updated response with the content type set.
1169:      *
1170:      * If you attempt to set the type on a 304 or 204 status code response, the
1171:      * content type will not take effect as these status codes do not have content-types.
1172:      *
1173:      * @param string $contentType Either a file extension which will be mapped to a mime-type or a concrete mime-type.
1174:      * @return static
1175:      */
1176:     public function withType($contentType)
1177:     {
1178:         $mappedType = $this->resolveType($contentType);
1179:         $new = clone $this;
1180:         $new->_contentType = $mappedType;
1181:         $new->_setContentType();
1182: 
1183:         return $new;
1184:     }
1185: 
1186:     /**
1187:      * Translate and validate content-types.
1188:      *
1189:      * @param string $contentType The content-type or type alias.
1190:      * @return string The resolved content-type
1191:      * @throws \InvalidArgumentException When an invalid content-type or alias is used.
1192:      */
1193:     protected function resolveType($contentType)
1194:     {
1195:         $mapped = $this->getMimeType($contentType);
1196:         if ($mapped) {
1197:             return is_array($mapped) ? current($mapped) : $mapped;
1198:         }
1199:         if (strpos($contentType, '/') === false) {
1200:             throw new InvalidArgumentException(sprintf('"%s" is an invalid content type.', $contentType));
1201:         }
1202: 
1203:         return $contentType;
1204:     }
1205: 
1206:     /**
1207:      * Returns the mime type definition for an alias
1208:      *
1209:      * e.g `getMimeType('pdf'); // returns 'application/pdf'`
1210:      *
1211:      * @param string $alias the content type alias to map
1212:      * @return mixed String mapped mime type or false if $alias is not mapped
1213:      */
1214:     public function getMimeType($alias)
1215:     {
1216:         if (isset($this->_mimeTypes[$alias])) {
1217:             return $this->_mimeTypes[$alias];
1218:         }
1219: 
1220:         return false;
1221:     }
1222: 
1223:     /**
1224:      * Maps a content-type back to an alias
1225:      *
1226:      * e.g `mapType('application/pdf'); // returns 'pdf'`
1227:      *
1228:      * @param string|array $ctype Either a string content type to map, or an array of types.
1229:      * @return string|array|null Aliases for the types provided.
1230:      */
1231:     public function mapType($ctype)
1232:     {
1233:         if (is_array($ctype)) {
1234:             return array_map([$this, 'mapType'], $ctype);
1235:         }
1236: 
1237:         foreach ($this->_mimeTypes as $alias => $types) {
1238:             if (in_array($ctype, (array)$types)) {
1239:                 return $alias;
1240:             }
1241:         }
1242: 
1243:         return null;
1244:     }
1245: 
1246:     /**
1247:      * Sets the response charset
1248:      * if $charset is null the current charset is returned
1249:      *
1250:      * @param string|null $charset Character set string.
1251:      * @return string Current charset
1252:      * @deprecated 3.5.0 Use getCharset()/withCharset() instead.
1253:      */
1254:     public function charset($charset = null)
1255:     {
1256:         deprecationWarning(
1257:             'Response::charset() is deprecated. ' .
1258:             'Use getCharset()/withCharset() instead.'
1259:         );
1260: 
1261:         if ($charset === null) {
1262:             return $this->_charset;
1263:         }
1264:         $this->_charset = $charset;
1265:         $this->_setContentType();
1266: 
1267:         return $this->_charset;
1268:     }
1269: 
1270:     /**
1271:      * Returns the current charset.
1272:      *
1273:      * @return string
1274:      */
1275:     public function getCharset()
1276:     {
1277:         return $this->_charset;
1278:     }
1279: 
1280:     /**
1281:      * Get a new instance with an updated charset.
1282:      *
1283:      * @param string $charset Character set string.
1284:      * @return static
1285:      */
1286:     public function withCharset($charset)
1287:     {
1288:         $new = clone $this;
1289:         $new->_charset = $charset;
1290:         $new->_setContentType();
1291: 
1292:         return $new;
1293:     }
1294: 
1295:     /**
1296:      * Sets the correct headers to instruct the client to not cache the response
1297:      *
1298:      * @return void
1299:      * @deprecated 3.4.0 Use withDisabledCache() instead.
1300:      */
1301:     public function disableCache()
1302:     {
1303:         deprecationWarning(
1304:             'Response::disableCache() is deprecated. ' .
1305:             'Use withDisabledCache() instead.'
1306:         );
1307: 
1308:         $this->_setHeader('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT');
1309:         $this->_setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT');
1310:         $this->_setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
1311:     }
1312: 
1313:     /**
1314:      * Create a new instance with headers to instruct the client to not cache the response
1315:      *
1316:      * @return static
1317:      */
1318:     public function withDisabledCache()
1319:     {
1320:         return $this->withHeader('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT')
1321:             ->withHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT')
1322:             ->withHeader('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
1323:     }
1324: 
1325:     /**
1326:      * Sets the correct headers to instruct the client to cache the response.
1327:      *
1328:      * @param string $since a valid time since the response text has not been modified
1329:      * @param string $time a valid time for cache expiry
1330:      * @return void
1331:      * @deprecated 3.4.0 Use withCache() instead.
1332:      */
1333:     public function cache($since, $time = '+1 day')
1334:     {
1335:         deprecationWarning(
1336:             'Response::cache() is deprecated. ' .
1337:             'Use withCache() instead.'
1338:         );
1339: 
1340:         if (!is_int($time)) {
1341:             $time = strtotime($time);
1342:         }
1343: 
1344:         $this->_setHeader('Date', gmdate('D, j M Y G:i:s ', time()) . 'GMT');
1345: 
1346:         $this->modified($since);
1347:         $this->expires($time);
1348:         $this->sharable(true);
1349:         $this->maxAge($time - time());
1350:     }
1351: 
1352:     /**
1353:      * Create a new instance with the headers to enable client caching.
1354:      *
1355:      * @param string $since a valid time since the response text has not been modified
1356:      * @param string $time a valid time for cache expiry
1357:      * @return static
1358:      */
1359:     public function withCache($since, $time = '+1 day')
1360:     {
1361:         if (!is_int($time)) {
1362:             $time = strtotime($time);
1363:         }
1364: 
1365:         return $this->withHeader('Date', gmdate('D, j M Y G:i:s ', time()) . 'GMT')
1366:             ->withModified($since)
1367:             ->withExpires($time)
1368:             ->withSharable(true)
1369:             ->withMaxAge($time - time());
1370:     }
1371: 
1372:     /**
1373:      * Sets whether a response is eligible to be cached by intermediate proxies
1374:      * This method controls the `public` or `private` directive in the Cache-Control
1375:      * header
1376:      *
1377:      * @param bool|null $public If set to true, the Cache-Control header will be set as public
1378:      *   if set to false, the response will be set to private
1379:      *   if no value is provided, it will return whether the response is sharable or not
1380:      * @param int|null $time time in seconds after which the response should no longer be considered fresh
1381:      * @return bool|null
1382:      */
1383:     public function sharable($public = null, $time = null)
1384:     {
1385:         deprecationWarning(
1386:             'Response::sharable() is deprecated. ' .
1387:             'Use withSharable() instead.'
1388:         );
1389:         if ($public === null) {
1390:             $public = array_key_exists('public', $this->_cacheDirectives);
1391:             $private = array_key_exists('private', $this->_cacheDirectives);
1392:             $noCache = array_key_exists('no-cache', $this->_cacheDirectives);
1393:             if (!$public && !$private && !$noCache) {
1394:                 return null;
1395:             }
1396: 
1397:             return $public || !($private || $noCache);
1398:         }
1399:         if ($public) {
1400:             $this->_cacheDirectives['public'] = true;
1401:             unset($this->_cacheDirectives['private']);
1402:         } else {
1403:             $this->_cacheDirectives['private'] = true;
1404:             unset($this->_cacheDirectives['public']);
1405:         }
1406: 
1407:         $this->maxAge($time);
1408:         if (!$time) {
1409:             $this->_setCacheControl();
1410:         }
1411: 
1412:         return (bool)$public;
1413:     }
1414: 
1415:     /**
1416:      * Create a new instace with the public/private Cache-Control directive set.
1417:      *
1418:      * @param bool $public If set to true, the Cache-Control header will be set as public
1419:      *   if set to false, the response will be set to private.
1420:      * @param int|null $time time in seconds after which the response should no longer be considered fresh.
1421:      * @return static
1422:      */
1423:     public function withSharable($public, $time = null)
1424:     {
1425:         $new = clone $this;
1426:         unset($new->_cacheDirectives['private'], $new->_cacheDirectives['public']);
1427: 
1428:         $key = $public ? 'public' : 'private';
1429:         $new->_cacheDirectives[$key] = true;
1430: 
1431:         if ($time !== null) {
1432:             $new->_cacheDirectives['max-age'] = $time;
1433:         }
1434:         $new->_setCacheControl();
1435: 
1436:         return $new;
1437:     }
1438: 
1439:     /**
1440:      * Sets the Cache-Control s-maxage directive.
1441:      *
1442:      * The max-age is the number of seconds after which the response should no longer be considered
1443:      * a good candidate to be fetched from a shared cache (like in a proxy server).
1444:      * If called with no parameters, this function will return the current max-age value if any
1445:      *
1446:      * @deprecated 3.6.5 Use withSharedMaxAge() instead.
1447:      * @param int|null $seconds if null, the method will return the current s-maxage value
1448:      * @return int|null
1449:      */
1450:     public function sharedMaxAge($seconds = null)
1451:     {
1452:         deprecationWarning(
1453:             'Response::sharedMaxAge() is deprecated. ' .
1454:             'Use withSharedMaxAge() instead.'
1455:         );
1456:         if ($seconds !== null) {
1457:             $this->_cacheDirectives['s-maxage'] = $seconds;
1458:             $this->_setCacheControl();
1459:         }
1460:         if (isset($this->_cacheDirectives['s-maxage'])) {
1461:             return $this->_cacheDirectives['s-maxage'];
1462:         }
1463: 
1464:         return null;
1465:     }
1466: 
1467:     /**
1468:      * Create a new instance with the Cache-Control s-maxage directive.
1469:      *
1470:      * The max-age is the number of seconds after which the response should no longer be considered
1471:      * a good candidate to be fetched from a shared cache (like in a proxy server).
1472:      *
1473:      * @param int $seconds The number of seconds for shared max-age
1474:      * @return static
1475:      */
1476:     public function withSharedMaxAge($seconds)
1477:     {
1478:         $new = clone $this;
1479:         $new->_cacheDirectives['s-maxage'] = $seconds;
1480:         $new->_setCacheControl();
1481: 
1482:         return $new;
1483:     }
1484: 
1485:     /**
1486:      * Sets the Cache-Control max-age directive.
1487:      * The max-age is the number of seconds after which the response should no longer be considered
1488:      * a good candidate to be fetched from the local (client) cache.
1489:      * If called with no parameters, this function will return the current max-age value if any
1490:      *
1491:      * @deprecated 3.6.5 Use withMaxAge() instead.
1492:      * @param int|null $seconds if null, the method will return the current max-age value
1493:      * @return int|null
1494:      */
1495:     public function maxAge($seconds = null)
1496:     {
1497:         deprecationWarning(
1498:             'Response::maxAge() is deprecated. ' .
1499:             'Use withMaxAge() instead.'
1500:         );
1501:         if ($seconds !== null) {
1502:             $this->_cacheDirectives['max-age'] = $seconds;
1503:             $this->_setCacheControl();
1504:         }
1505:         if (isset($this->_cacheDirectives['max-age'])) {
1506:             return $this->_cacheDirectives['max-age'];
1507:         }
1508: 
1509:         return null;
1510:     }
1511: 
1512:     /**
1513:      * Create an instance with Cache-Control max-age directive set.
1514:      *
1515:      * The max-age is the number of seconds after which the response should no longer be considered
1516:      * a good candidate to be fetched from the local (client) cache.
1517:      *
1518:      * @param int $seconds The seconds a cached response can be considered valid
1519:      * @return static
1520:      */
1521:     public function withMaxAge($seconds)
1522:     {
1523:         $new = clone $this;
1524:         $new->_cacheDirectives['max-age'] = $seconds;
1525:         $new->_setCacheControl();
1526: 
1527:         return $new;
1528:     }
1529: 
1530:     /**
1531:      * Sets the Cache-Control must-revalidate directive.
1532:      * must-revalidate indicates that the response should not be served
1533:      * stale by a cache under any circumstance without first revalidating
1534:      * with the origin.
1535:      * If called with no parameters, this function will return whether must-revalidate is present.
1536:      *
1537:      * @param bool|null $enable if null, the method will return the current
1538:      *   must-revalidate value. If boolean sets or unsets the directive.
1539:      * @return bool
1540:      * @deprecated 3.4.0 Use withMustRevalidate() instead.
1541:      */
1542:     public function mustRevalidate($enable = null)
1543:     {
1544:         deprecationWarning(
1545:             'Response::mustRevalidate() is deprecated. ' .
1546:             'Use withMustRevalidate() instead.'
1547:         );
1548: 
1549:         if ($enable !== null) {
1550:             if ($enable) {
1551:                 $this->_cacheDirectives['must-revalidate'] = true;
1552:             } else {
1553:                 unset($this->_cacheDirectives['must-revalidate']);
1554:             }
1555:             $this->_setCacheControl();
1556:         }
1557: 
1558:         return array_key_exists('must-revalidate', $this->_cacheDirectives);
1559:     }
1560: 
1561:     /**
1562:      * Create an instance with Cache-Control must-revalidate directive set.
1563:      *
1564:      * Sets the Cache-Control must-revalidate directive.
1565:      * must-revalidate indicates that the response should not be served
1566:      * stale by a cache under any circumstance without first revalidating
1567:      * with the origin.
1568:      *
1569:      * @param bool $enable If boolean sets or unsets the directive.
1570:      * @return static
1571:      */
1572:     public function withMustRevalidate($enable)
1573:     {
1574:         $new = clone $this;
1575:         if ($enable) {
1576:             $new->_cacheDirectives['must-revalidate'] = true;
1577:         } else {
1578:             unset($new->_cacheDirectives['must-revalidate']);
1579:         }
1580:         $new->_setCacheControl();
1581: 
1582:         return $new;
1583:     }
1584: 
1585:     /**
1586:      * Helper method to generate a valid Cache-Control header from the options set
1587:      * in other methods
1588:      *
1589:      * @return void
1590:      */
1591:     protected function _setCacheControl()
1592:     {
1593:         $control = '';
1594:         foreach ($this->_cacheDirectives as $key => $val) {
1595:             $control .= $val === true ? $key : sprintf('%s=%s', $key, $val);
1596:             $control .= ', ';
1597:         }
1598:         $control = rtrim($control, ', ');
1599:         $this->_setHeader('Cache-Control', $control);
1600:     }
1601: 
1602:     /**
1603:      * Sets the Expires header for the response by taking an expiration time
1604:      * If called with no parameters it will return the current Expires value
1605:      *
1606:      * ### Examples:
1607:      *
1608:      * `$response->expires('now')` Will Expire the response cache now
1609:      * `$response->expires(new DateTime('+1 day'))` Will set the expiration in next 24 hours
1610:      * `$response->expires()` Will return the current expiration header value
1611:      *
1612:      * @param string|\DateTimeInterface|null $time Valid time string or \DateTime instance.
1613:      * @return string|null
1614:      * @deprecated 3.4.0 Use withExpires() instead.
1615:      */
1616:     public function expires($time = null)
1617:     {
1618:         deprecationWarning(
1619:             'Response::expires() is deprecated. ' .
1620:             'Use withExpires() instead.'
1621:         );
1622: 
1623:         if ($time !== null) {
1624:             $date = $this->_getUTCDate($time);
1625:             $this->_setHeader('Expires', $date->format('D, j M Y H:i:s') . ' GMT');
1626:         }
1627: 
1628:         if ($this->hasHeader('Expires')) {
1629:             return $this->getHeaderLine('Expires');
1630:         }
1631: 
1632:         return null;
1633:     }
1634: 
1635:     /**
1636:      * Create a new instance with the Expires header set.
1637:      *
1638:      * ### Examples:
1639:      *
1640:      * ```
1641:      * // Will Expire the response cache now
1642:      * $response->withExpires('now')
1643:      *
1644:      * // Will set the expiration in next 24 hours
1645:      * $response->withExpires(new DateTime('+1 day'))
1646:      * ```
1647:      *
1648:      * @param string|\DateTimeInterface $time Valid time string or \DateTime instance.
1649:      * @return static
1650:      */
1651:     public function withExpires($time)
1652:     {
1653:         $date = $this->_getUTCDate($time);
1654: 
1655:         return $this->withHeader('Expires', $date->format('D, j M Y H:i:s') . ' GMT');
1656:     }
1657: 
1658:     /**
1659:      * Sets the Last-Modified header for the response by taking a modification time
1660:      * If called with no parameters it will return the current Last-Modified value
1661:      *
1662:      * ### Examples:
1663:      *
1664:      * `$response->modified('now')` Will set the Last-Modified to the current time
1665:      * `$response->modified(new DateTime('+1 day'))` Will set the modification date in the past 24 hours
1666:      * `$response->modified()` Will return the current Last-Modified header value
1667:      *
1668:      * @param string|\DateTimeInterface|null $time Valid time string or \DateTime instance.
1669:      * @return string|null
1670:      * @deprecated 3.4.0 Use withModified() instead.
1671:      */
1672:     public function modified($time = null)
1673:     {
1674:         deprecationWarning(
1675:             'Response::modified() is deprecated. ' .
1676:             'Use withModified() or getHeaderLine("Last-Modified") instead.'
1677:         );
1678: 
1679:         if ($time !== null) {
1680:             $date = $this->_getUTCDate($time);
1681:             $this->_setHeader('Last-Modified', $date->format('D, j M Y H:i:s') . ' GMT');
1682:         }
1683: 
1684:         if ($this->hasHeader('Last-Modified')) {
1685:             return $this->getHeaderLine('Last-Modified');
1686:         }
1687: 
1688:         return null;
1689:     }
1690: 
1691:     /**
1692:      * Create a new instance with the Last-Modified header set.
1693:      *
1694:      * ### Examples:
1695:      *
1696:      * ```
1697:      * // Will Expire the response cache now
1698:      * $response->withModified('now')
1699:      *
1700:      * // Will set the expiration in next 24 hours
1701:      * $response->withModified(new DateTime('+1 day'))
1702:      * ```
1703:      *
1704:      * @param string|\DateTimeInterface $time Valid time string or \DateTimeInterface instance.
1705:      * @return static
1706:      */
1707:     public function withModified($time)
1708:     {
1709:         $date = $this->_getUTCDate($time);
1710: 
1711:         return $this->withHeader('Last-Modified', $date->format('D, j M Y H:i:s') . ' GMT');
1712:     }
1713: 
1714:     /**
1715:      * Sets the response as Not Modified by removing any body contents
1716:      * setting the status code to "304 Not Modified" and removing all
1717:      * conflicting headers
1718:      *
1719:      * *Warning* This method mutates the response in-place and should be avoided.
1720:      *
1721:      * @return void
1722:      */
1723:     public function notModified()
1724:     {
1725:         $this->_createStream();
1726:         $this->_setStatus(304);
1727: 
1728:         $remove = [
1729:             'Allow',
1730:             'Content-Encoding',
1731:             'Content-Language',
1732:             'Content-Length',
1733:             'Content-MD5',
1734:             'Content-Type',
1735:             'Last-Modified'
1736:         ];
1737:         foreach ($remove as $header) {
1738:             $this->_clearHeader($header);
1739:         }
1740:     }
1741: 
1742:     /**
1743:      * Create a new instance as 'not modified'
1744:      *
1745:      * This will remove any body contents set the status code
1746:      * to "304" and removing headers that describe
1747:      * a response body.
1748:      *
1749:      * @return static
1750:      */
1751:     public function withNotModified()
1752:     {
1753:         $new = $this->withStatus(304);
1754:         $new->_createStream();
1755:         $remove = [
1756:             'Allow',
1757:             'Content-Encoding',
1758:             'Content-Language',
1759:             'Content-Length',
1760:             'Content-MD5',
1761:             'Content-Type',
1762:             'Last-Modified'
1763:         ];
1764:         foreach ($remove as $header) {
1765:             $new = $new->withoutHeader($header);
1766:         }
1767: 
1768:         return $new;
1769:     }
1770: 
1771:     /**
1772:      * Sets the Vary header for the response, if an array is passed,
1773:      * values will be imploded into a comma separated string. If no
1774:      * parameters are passed, then an array with the current Vary header
1775:      * value is returned
1776:      *
1777:      * @param string|array|null $cacheVariances A single Vary string or an array
1778:      *   containing the list for variances.
1779:      * @return array|null
1780:      * @deprecated 3.4.0 Use withVary() instead.
1781:      */
1782:     public function vary($cacheVariances = null)
1783:     {
1784:         deprecationWarning(
1785:             'Response::vary() is deprecated. ' .
1786:             'Use withVary() instead.'
1787:         );
1788: 
1789:         if ($cacheVariances !== null) {
1790:             $cacheVariances = (array)$cacheVariances;
1791:             $this->_setHeader('Vary', implode(', ', $cacheVariances));
1792:         }
1793: 
1794:         if ($this->hasHeader('Vary')) {
1795:             return explode(', ', $this->getHeaderLine('Vary'));
1796:         }
1797: 
1798:         return null;
1799:     }
1800: 
1801:     /**
1802:      * Create a new instance with the Vary header set.
1803:      *
1804:      * If an array is passed values will be imploded into a comma
1805:      * separated string. If no parameters are passed, then an
1806:      * array with the current Vary header value is returned
1807:      *
1808:      * @param string|array $cacheVariances A single Vary string or an array
1809:      *   containing the list for variances.
1810:      * @return static
1811:      */
1812:     public function withVary($cacheVariances)
1813:     {
1814:         return $this->withHeader('Vary', (array)$cacheVariances);
1815:     }
1816: 
1817:     /**
1818:      * Sets the response Etag, Etags are a strong indicative that a response
1819:      * can be cached by a HTTP client. A bad way of generating Etags is
1820:      * creating a hash of the response output, instead generate a unique
1821:      * hash of the unique components that identifies a request, such as a
1822:      * modification time, a resource Id, and anything else you consider it
1823:      * makes it unique.
1824:      *
1825:      * Second parameter is used to instruct clients that the content has
1826:      * changed, but semantically, it can be used as the same thing. Think
1827:      * for instance of a page with a hit counter, two different page views
1828:      * are equivalent, but they differ by a few bytes. This leaves off to
1829:      * the Client the decision of using or not the cached page.
1830:      *
1831:      * If no parameters are passed, current Etag header is returned.
1832:      *
1833:      * @param string|null $hash The unique hash that identifies this response
1834:      * @param bool $weak Whether the response is semantically the same as
1835:      *   other with the same hash or not
1836:      * @return string|null
1837:      * @deprecated 3.4.0 Use withEtag() instead.
1838:      */
1839:     public function etag($hash = null, $weak = false)
1840:     {
1841:         deprecationWarning(
1842:             'Response::etag() is deprecated. ' .
1843:             'Use withEtag() or getHeaderLine("Etag") instead.'
1844:         );
1845: 
1846:         if ($hash !== null) {
1847:             $this->_setHeader('Etag', sprintf('%s"%s"', $weak ? 'W/' : null, $hash));
1848:         }
1849: 
1850:         if ($this->hasHeader('Etag')) {
1851:             return $this->getHeaderLine('Etag');
1852:         }
1853: 
1854:         return null;
1855:     }
1856: 
1857:     /**
1858:      * Create a new instance with the Etag header set.
1859:      *
1860:      * Etags are a strong indicative that a response can be cached by a
1861:      * HTTP client. A bad way of generating Etags is creating a hash of
1862:      * the response output, instead generate a unique hash of the
1863:      * unique components that identifies a request, such as a
1864:      * modification time, a resource Id, and anything else you consider it
1865:      * that makes the response unique.
1866:      *
1867:      * The second parameter is used to inform clients that the content has
1868:      * changed, but semantically it is equivalent to existing cached values. Consider
1869:      * a page with a hit counter, two different page views are equivalent, but
1870:      * they differ by a few bytes. This permits the Client to decide whether they should
1871:      * use the cached data.
1872:      *
1873:      * @param string $hash The unique hash that identifies this response
1874:      * @param bool $weak Whether the response is semantically the same as
1875:      *   other with the same hash or not. Defaults to false
1876:      * @return static
1877:      */
1878:     public function withEtag($hash, $weak = false)
1879:     {
1880:         $hash = sprintf('%s"%s"', $weak ? 'W/' : null, $hash);
1881: 
1882:         return $this->withHeader('Etag', $hash);
1883:     }
1884: 
1885:     /**
1886:      * Returns a DateTime object initialized at the $time param and using UTC
1887:      * as timezone
1888:      *
1889:      * @param string|int|\DateTimeInterface|null $time Valid time string or \DateTimeInterface instance.
1890:      * @return \DateTimeInterface
1891:      */
1892:     protected function _getUTCDate($time = null)
1893:     {
1894:         if ($time instanceof DateTimeInterface) {
1895:             $result = clone $time;
1896:         } elseif (is_int($time)) {
1897:             $result = new DateTime(date('Y-m-d H:i:s', $time));
1898:         } else {
1899:             $result = new DateTime($time);
1900:         }
1901: 
1902:         return $result->setTimezone(new DateTimeZone('UTC'));
1903:     }
1904: 
1905:     /**
1906:      * Sets the correct output buffering handler to send a compressed response. Responses will
1907:      * be compressed with zlib, if the extension is available.
1908:      *
1909:      * @return bool false if client does not accept compressed responses or no handler is available, true otherwise
1910:      */
1911:     public function compress()
1912:     {
1913:         $compressionEnabled = ini_get('zlib.output_compression') !== '1' &&
1914:             extension_loaded('zlib') &&
1915:             (strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false);
1916: 
1917:         return $compressionEnabled && ob_start('ob_gzhandler');
1918:     }
1919: 
1920:     /**
1921:      * Returns whether the resulting output will be compressed by PHP
1922:      *
1923:      * @return bool
1924:      */
1925:     public function outputCompressed()
1926:     {
1927:         return strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false
1928:             && (ini_get('zlib.output_compression') === '1' || in_array('ob_gzhandler', ob_list_handlers()));
1929:     }
1930: 
1931:     /**
1932:      * Sets the correct headers to instruct the browser to download the response as a file.
1933:      *
1934:      * @param string $filename The name of the file as the browser will download the response
1935:      * @return void
1936:      * @deprecated 3.4.0 Use withDownload() instead.
1937:      */
1938:     public function download($filename)
1939:     {
1940:         deprecationWarning(
1941:             'Response::download() is deprecated. ' .
1942:             'Use withDownload() instead.'
1943:         );
1944: 
1945:         $this->header('Content-Disposition', 'attachment; filename="' . $filename . '"');
1946:     }
1947: 
1948:     /**
1949:      * Create a new instance with the Content-Disposition header set.
1950:      *
1951:      * @param string $filename The name of the file as the browser will download the response
1952:      * @return static
1953:      */
1954:     public function withDownload($filename)
1955:     {
1956:         return $this->withHeader('Content-Disposition', 'attachment; filename="' . $filename . '"');
1957:     }
1958: 
1959:     /**
1960:      * Sets the protocol to be used when sending the response. Defaults to HTTP/1.1
1961:      * If called with no arguments, it will return the current configured protocol
1962:      *
1963:      * @param string|null $protocol Protocol to be used for sending response.
1964:      * @return string Protocol currently set
1965:      * @deprecated 3.4.0 Use getProtocolVersion() instead.
1966:      */
1967:     public function protocol($protocol = null)
1968:     {
1969:         deprecationWarning(
1970:             'Response::protocol() is deprecated. ' .
1971:             'Use getProtocolVersion() instead.'
1972:         );
1973: 
1974:         if ($protocol !== null) {
1975:             $this->_protocol = $protocol;
1976:         }
1977: 
1978:         return $this->_protocol;
1979:     }
1980: 
1981:     /**
1982:      * Sets the Content-Length header for the response
1983:      * If called with no arguments returns the last Content-Length set
1984:      *
1985:      * @param int|null $bytes Number of bytes
1986:      * @return string|null
1987:      * @deprecated 3.4.0 Use withLength() to set length instead.
1988:      */
1989:     public function length($bytes = null)
1990:     {
1991:         deprecationWarning(
1992:             'Response::length() is deprecated. ' .
1993:             'Use withLength() instead.'
1994:         );
1995: 
1996:         if ($bytes !== null) {
1997:             $this->_setHeader('Content-Length', $bytes);
1998:         }
1999: 
2000:         if ($this->hasHeader('Content-Length')) {
2001:             return $this->getHeaderLine('Content-Length');
2002:         }
2003: 
2004:         return null;
2005:     }
2006: 
2007:     /**
2008:      * Create a new response with the Content-Length header set.
2009:      *
2010:      * @param int|string $bytes Number of bytes
2011:      * @return static
2012:      */
2013:     public function withLength($bytes)
2014:     {
2015:         return $this->withHeader('Content-Length', (string)$bytes);
2016:     }
2017: 
2018:     /**
2019:      * Create a new response with the Link header set.
2020:      *
2021:      * ### Examples
2022:      *
2023:      * ```
2024:      * $response = $response->withAddedLink('http://example.com?page=1', ['rel' => 'prev'])
2025:      *     ->withAddedLink('http://example.com?page=3', ['rel' => 'next']);
2026:      * ```
2027:      *
2028:      * Will generate:
2029:      *
2030:      * ```
2031:      * Link: <http://example.com?page=1>; rel="prev"
2032:      * Link: <http://example.com?page=3>; rel="next"
2033:      * ```
2034:      *
2035:      * @param string $url The LinkHeader url.
2036:      * @param array $options The LinkHeader params.
2037:      * @return static
2038:      * @since 3.6.0
2039:      */
2040:     public function withAddedLink($url, $options = [])
2041:     {
2042:         $params = [];
2043:         foreach ($options as $key => $option) {
2044:             $params[] = $key . '="' . $option . '"';
2045:         }
2046: 
2047:         $param = '';
2048:         if ($params) {
2049:             $param = '; ' . implode('; ', $params);
2050:         }
2051: 
2052:         return $this->withAddedHeader('Link', '<' . $url . '>' . $param);
2053:     }
2054: 
2055:     /**
2056:      * Checks whether a response has not been modified according to the 'If-None-Match'
2057:      * (Etags) and 'If-Modified-Since' (last modification date) request
2058:      * headers. If the response is detected to be not modified, it
2059:      * is marked as so accordingly so the client can be informed of that.
2060:      *
2061:      * In order to mark a response as not modified, you need to set at least
2062:      * the Last-Modified etag response header before calling this method. Otherwise
2063:      * a comparison will not be possible.
2064:      *
2065:      * *Warning* This method mutates the response in-place and should be avoided.
2066:      *
2067:      * @param \Cake\Http\ServerRequest $request Request object
2068:      * @return bool Whether the response was marked as not modified or not.
2069:      */
2070:     public function checkNotModified(ServerRequest $request)
2071:     {
2072:         $etags = preg_split('/\s*,\s*/', (string)$request->getHeaderLine('If-None-Match'), 0, PREG_SPLIT_NO_EMPTY);
2073:         $responseTag = $this->getHeaderLine('Etag');
2074:         $etagMatches = null;
2075:         if ($responseTag) {
2076:             $etagMatches = in_array('*', $etags) || in_array($responseTag, $etags);
2077:         }
2078: 
2079:         $modifiedSince = $request->getHeaderLine('If-Modified-Since');
2080:         $timeMatches = null;
2081:         if ($modifiedSince && $this->hasHeader('Last-Modified')) {
2082:             $timeMatches = strtotime($this->getHeaderLine('Last-Modified')) === strtotime($modifiedSince);
2083:         }
2084:         if ($etagMatches === null && $timeMatches === null) {
2085:             return false;
2086:         }
2087:         $notModified = $etagMatches !== false && $timeMatches !== false;
2088:         if ($notModified) {
2089:             $this->notModified();
2090:         }
2091: 
2092:         return $notModified;
2093:     }
2094: 
2095:     /**
2096:      * String conversion. Fetches the response body as a string.
2097:      * Does *not* send headers.
2098:      * If body is a callable, a blank string is returned.
2099:      *
2100:      * @return string
2101:      */
2102:     public function __toString()
2103:     {
2104:         $this->stream->rewind();
2105: 
2106:         return (string)$this->stream->getContents();
2107:     }
2108: 
2109:     /**
2110:      * Getter/Setter for cookie configs
2111:      *
2112:      * This method acts as a setter/getter depending on the type of the argument.
2113:      * If the method is called with no arguments, it returns all configurations.
2114:      *
2115:      * If the method is called with a string as argument, it returns either the
2116:      * given configuration if it is set, or null, if it's not set.
2117:      *
2118:      * If the method is called with an array as argument, it will set the cookie
2119:      * configuration to the cookie container.
2120:      *
2121:      *  ### Options (when setting a configuration)
2122:      *  - name: The Cookie name
2123:      *  - value: Value of the cookie
2124:      *  - expire: Time the cookie expires in
2125:      *  - path: Path the cookie applies to
2126:      *  - domain: Domain the cookie is for.
2127:      *  - secure: Is the cookie https?
2128:      *  - httpOnly: Is the cookie available in the client?
2129:      *
2130:      * ### Examples
2131:      *
2132:      * ### Getting all cookies
2133:      *
2134:      * `$this->cookie()`
2135:      *
2136:      * ### Getting a certain cookie configuration
2137:      *
2138:      * `$this->cookie('MyCookie')`
2139:      *
2140:      * ### Setting a cookie configuration
2141:      *
2142:      * `$this->cookie((array) $options)`
2143:      *
2144:      * @param array|null $options Either null to get all cookies, string for a specific cookie
2145:      *  or array to set cookie.
2146:      * @return mixed
2147:      * @deprecated 3.4.0 Use getCookie(), getCookies() and withCookie() instead.
2148:      */
2149:     public function cookie($options = null)
2150:     {
2151:         deprecationWarning(
2152:             'Response::cookie() is deprecated. ' .
2153:             'Use getCookie(), getCookies() and withCookie() instead.'
2154:         );
2155: 
2156:         if ($options === null) {
2157:             return $this->getCookies();
2158:         }
2159: 
2160:         if (is_string($options)) {
2161:             if (!$this->_cookies->has($options)) {
2162:                 return null;
2163:             }
2164: 
2165:             $cookie = $this->_cookies->get($options);
2166: 
2167:             return $this->convertCookieToArray($cookie);
2168:         }
2169: 
2170:         $options += [
2171:             'name' => 'CakeCookie[default]',
2172:             'value' => '',
2173:             'expire' => 0,
2174:             'path' => '/',
2175:             'domain' => '',
2176:             'secure' => false,
2177:             'httpOnly' => false
2178:         ];
2179:         $expires = $options['expire'] ? new DateTime('@' . $options['expire']) : null;
2180:         $cookie = new Cookie(
2181:             $options['name'],
2182:             $options['value'],
2183:             $expires,
2184:             $options['path'],
2185:             $options['domain'],
2186:             $options['secure'],
2187:             $options['httpOnly']
2188:         );
2189:         $this->_cookies = $this->_cookies->add($cookie);
2190:     }
2191: 
2192:     /**
2193:      * Create a new response with a cookie set.
2194:      *
2195:      * ### Data
2196:      *
2197:      * - `value`: Value of the cookie
2198:      * - `expire`: Time the cookie expires in
2199:      * - `path`: Path the cookie applies to
2200:      * - `domain`: Domain the cookie is for.
2201:      * - `secure`: Is the cookie https?
2202:      * - `httpOnly`: Is the cookie available in the client?
2203:      *
2204:      * ### Examples
2205:      *
2206:      * ```
2207:      * // set scalar value with defaults
2208:      * $response = $response->withCookie('remember_me', 1);
2209:      *
2210:      * // customize cookie attributes
2211:      * $response = $response->withCookie('remember_me', ['path' => '/login']);
2212:      *
2213:      * // add a cookie object
2214:      * $response = $response->withCookie(new Cookie('remember_me', 1));
2215:      * ```
2216:      *
2217:      * @param string|\Cake\Http\Cookie\Cookie $name The name of the cookie to set, or a cookie object
2218:      * @param array|string $data Either a string value, or an array of cookie options.
2219:      * @return static
2220:      */
2221:     public function withCookie($name, $data = '')
2222:     {
2223:         if ($name instanceof Cookie) {
2224:             $cookie = $name;
2225:         } else {
2226:             deprecationWarning(
2227:                 get_called_class() . '::withCookie(string $name, array $data) is deprecated. ' .
2228:                 'Pass an instance of \Cake\Http\Cookie\Cookie instead.'
2229:             );
2230: 
2231:             if (!is_array($data)) {
2232:                 $data = ['value' => $data];
2233:             }
2234:             $data += [
2235:                 'value' => '',
2236:                 'expire' => 0,
2237:                 'path' => '/',
2238:                 'domain' => '',
2239:                 'secure' => false,
2240:                 'httpOnly' => false
2241:             ];
2242:             $expires = $data['expire'] ? new DateTime('@' . $data['expire']) : null;
2243:             $cookie = new Cookie(
2244:                 $name,
2245:                 $data['value'],
2246:                 $expires,
2247:                 $data['path'],
2248:                 $data['domain'],
2249:                 $data['secure'],
2250:                 $data['httpOnly']
2251:             );
2252:         }
2253: 
2254:         $new = clone $this;
2255:         $new->_cookies = $new->_cookies->add($cookie);
2256: 
2257:         return $new;
2258:     }
2259: 
2260:     /**
2261:      * Create a new response with an expired cookie set.
2262:      *
2263:      * ### Options
2264:      *
2265:      * - `path`: Path the cookie applies to
2266:      * - `domain`: Domain the cookie is for.
2267:      * - `secure`: Is the cookie https?
2268:      * - `httpOnly`: Is the cookie available in the client?
2269:      *
2270:      * ### Examples
2271:      *
2272:      * ```
2273:      * // set scalar value with defaults
2274:      * $response = $response->withExpiredCookie('remember_me');
2275:      *
2276:      * // customize cookie attributes
2277:      * $response = $response->withExpiredCookie('remember_me', ['path' => '/login']);
2278:      *
2279:      * // add a cookie object
2280:      * $response = $response->withExpiredCookie(new Cookie('remember_me'));
2281:      * ```
2282:      *
2283:      * @param string|\Cake\Http\Cookie\CookieInterface $name The name of the cookie to expire, or a cookie object
2284:      * @param array $options An array of cookie options.
2285:      * @return static
2286:      */
2287:     public function withExpiredCookie($name, $options = [])
2288:     {
2289:         if ($name instanceof CookieInterface) {
2290:             $cookie = $name->withExpired();
2291:         } else {
2292:             deprecationWarning(
2293:                 get_called_class() . '::withExpiredCookie(string $name, array $data) is deprecated. ' .
2294:                 'Pass an instance of \Cake\Http\Cookie\Cookie instead.'
2295:             );
2296: 
2297:             $options += [
2298:                 'path' => '/',
2299:                 'domain' => '',
2300:                 'secure' => false,
2301:                 'httpOnly' => false
2302:             ];
2303: 
2304:             $cookie = new Cookie(
2305:                 $name,
2306:                 '',
2307:                 DateTime::createFromFormat('U', 1),
2308:                 $options['path'],
2309:                 $options['domain'],
2310:                 $options['secure'],
2311:                 $options['httpOnly']
2312:             );
2313:         }
2314: 
2315:         $new = clone $this;
2316:         $new->_cookies = $new->_cookies->add($cookie);
2317: 
2318:         return $new;
2319:     }
2320: 
2321:     /**
2322:      * Read a single cookie from the response.
2323:      *
2324:      * This method provides read access to pending cookies. It will
2325:      * not read the `Set-Cookie` header if set.
2326:      *
2327:      * @param string $name The cookie name you want to read.
2328:      * @return array|null Either the cookie data or null
2329:      */
2330:     public function getCookie($name)
2331:     {
2332:         if (!$this->_cookies->has($name)) {
2333:             return null;
2334:         }
2335: 
2336:         $cookie = $this->_cookies->get($name);
2337: 
2338:         return $this->convertCookieToArray($cookie);
2339:     }
2340: 
2341:     /**
2342:      * Get all cookies in the response.
2343:      *
2344:      * Returns an associative array of cookie name => cookie data.
2345:      *
2346:      * @return array
2347:      */
2348:     public function getCookies()
2349:     {
2350:         $out = [];
2351:         foreach ($this->_cookies as $cookie) {
2352:             $out[$cookie->getName()] = $this->convertCookieToArray($cookie);
2353:         }
2354: 
2355:         return $out;
2356:     }
2357: 
2358:     /**
2359:      * Convert the cookie into an array of its properties.
2360:      *
2361:      * This method is compatible with the historical behavior of Cake\Http\Response,
2362:      * where `httponly` is `httpOnly` and `expires` is `expire`
2363:      *
2364:      * @param \Cake\Http\Cookie\CookieInterface $cookie Cookie object.
2365:      * @return array
2366:      */
2367:     protected function convertCookieToArray(CookieInterface $cookie)
2368:     {
2369:         return [
2370:             'name' => $cookie->getName(),
2371:             'value' => $cookie->getStringValue(),
2372:             'path' => $cookie->getPath(),
2373:             'domain' => $cookie->getDomain(),
2374:             'secure' => $cookie->isSecure(),
2375:             'httpOnly' => $cookie->isHttpOnly(),
2376:             'expire' => $cookie->getExpiresTimestamp()
2377:         ];
2378:     }
2379: 
2380:     /**
2381:      * Get the CookieCollection from the response
2382:      *
2383:      * @return \Cake\Http\Cookie\CookieCollection
2384:      */
2385:     public function getCookieCollection()
2386:     {
2387:         return $this->_cookies;
2388:     }
2389: 
2390:     /**
2391:      * Setup access for origin and methods on cross origin requests
2392:      *
2393:      * This method allow multiple ways to setup the domains, see the examples
2394:      *
2395:      * ### Full URI
2396:      * ```
2397:      * cors($request, 'https://www.cakephp.org');
2398:      * ```
2399:      *
2400:      * ### URI with wildcard
2401:      * ```
2402:      * cors($request, 'https://*.cakephp.org');
2403:      * ```
2404:      *
2405:      * ### Ignoring the requested protocol
2406:      * ```
2407:      * cors($request, 'www.cakephp.org');
2408:      * ```
2409:      *
2410:      * ### Any URI
2411:      * ```
2412:      * cors($request, '*');
2413:      * ```
2414:      *
2415:      * ### Whitelist of URIs
2416:      * ```
2417:      * cors($request, ['http://www.cakephp.org', '*.google.com', 'https://myproject.github.io']);
2418:      * ```
2419:      *
2420:      * *Note* The `$allowedDomains`, `$allowedMethods`, `$allowedHeaders` parameters are deprecated.
2421:      * Instead the builder object should be used.
2422:      *
2423:      * @param \Cake\Http\ServerRequest $request Request object
2424:      * @param string|array $allowedDomains List of allowed domains, see method description for more details
2425:      * @param string|array $allowedMethods List of HTTP verbs allowed
2426:      * @param string|array $allowedHeaders List of HTTP headers allowed
2427:      * @return \Cake\Http\CorsBuilder A builder object the provides a fluent interface for defining
2428:      *   additional CORS headers.
2429:      */
2430:     public function cors(ServerRequest $request, $allowedDomains = [], $allowedMethods = [], $allowedHeaders = [])
2431:     {
2432:         $origin = $request->getHeaderLine('Origin');
2433:         $ssl = $request->is('ssl');
2434:         $builder = new CorsBuilder($this, $origin, $ssl);
2435:         if (!$origin) {
2436:             return $builder;
2437:         }
2438:         if (empty($allowedDomains) && empty($allowedMethods) && empty($allowedHeaders)) {
2439:             return $builder;
2440:         }
2441:         deprecationWarning(
2442:             'The $allowedDomains, $allowedMethods, and $allowedHeaders parameters of Response::cors() ' .
2443:             'are deprecated. Instead you should use the builder methods on the return of cors().'
2444:         );
2445: 
2446:         $updated = $builder->allowOrigin($allowedDomains)
2447:             ->allowMethods((array)$allowedMethods)
2448:             ->allowHeaders((array)$allowedHeaders)
2449:             ->build();
2450: 
2451:         // If $updated is a new instance, mutate this object in-place
2452:         // to retain existing behavior.
2453:         if ($updated !== $this) {
2454:             foreach ($updated->getHeaders() as $name => $values) {
2455:                 if (!$this->hasHeader($name)) {
2456:                     $this->_setHeader($name, $values[0]);
2457:                 }
2458:             }
2459:         }
2460: 
2461:         return $builder;
2462:     }
2463: 
2464:     /**
2465:      * Setup for display or download the given file.
2466:      *
2467:      * If $_SERVER['HTTP_RANGE'] is set a slice of the file will be
2468:      * returned instead of the entire file.
2469:      *
2470:      * ### Options keys
2471:      *
2472:      * - name: Alternate download name
2473:      * - download: If `true` sets download header and forces file to be downloaded rather than displayed in browser
2474:      *
2475:      * @param string $path Path to file. If the path is not an absolute path that resolves
2476:      *   to a file, `APP` will be prepended to the path (this behavior is deprecated).
2477:      * @param array $options Options See above.
2478:      * @return void
2479:      * @throws \Cake\Http\Exception\NotFoundException
2480:      * @deprecated 3.4.0 Use withFile() instead.
2481:      */
2482:     public function file($path, array $options = [])
2483:     {
2484:         deprecationWarning(
2485:             'Response::file() is deprecated. ' .
2486:             'Use withFile() instead.'
2487:         );
2488: 
2489:         $file = $this->validateFile($path);
2490:         $options += [
2491:             'name' => null,
2492:             'download' => null
2493:         ];
2494: 
2495:         $extension = strtolower($file->ext());
2496:         $download = $options['download'];
2497:         if ((!$extension || $this->type($extension) === false) && $download === null) {
2498:             $download = true;
2499:         }
2500: 
2501:         $fileSize = $file->size();
2502:         if ($download) {
2503:             $agent = env('HTTP_USER_AGENT');
2504: 
2505:             if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent)) {
2506:                 $contentType = 'application/octet-stream';
2507:             } elseif (preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
2508:                 $contentType = 'application/force-download';
2509:             }
2510: 
2511:             if (!empty($contentType)) {
2512:                 $this->type($contentType);
2513:             }
2514:             if ($options['name'] === null) {
2515:                 $name = $file->name;
2516:             } else {
2517:                 $name = $options['name'];
2518:             }
2519:             $this->download($name);
2520:             $this->header('Content-Transfer-Encoding', 'binary');
2521:         }
2522: 
2523:         $this->header('Accept-Ranges', 'bytes');
2524:         $httpRange = env('HTTP_RANGE');
2525:         if (isset($httpRange)) {
2526:             $this->_fileRange($file, $httpRange);
2527:         } else {
2528:             $this->header('Content-Length', $fileSize);
2529:         }
2530: 
2531:         $this->_file = $file;
2532:         $this->stream = new Stream($file->path, 'rb');
2533:     }
2534: 
2535:     /**
2536:      * Create a new instance that is based on a file.
2537:      *
2538:      * This method will augment both the body and a number of related headers.
2539:      *
2540:      * If `$_SERVER['HTTP_RANGE']` is set, a slice of the file will be
2541:      * returned instead of the entire file.
2542:      *
2543:      * ### Options keys
2544:      *
2545:      * - name: Alternate download name
2546:      * - download: If `true` sets download header and forces file to
2547:      *   be downloaded rather than displayed inline.
2548:      *
2549:      * @param string $path Path to file. If the path is not an absolute path that resolves
2550:      *   to a file, `APP` will be prepended to the path (this behavior is deprecated).
2551:      * @param array $options Options See above.
2552:      * @return static
2553:      * @throws \Cake\Http\Exception\NotFoundException
2554:      */
2555:     public function withFile($path, array $options = [])
2556:     {
2557:         $file = $this->validateFile($path);
2558:         $options += [
2559:             'name' => null,
2560:             'download' => null
2561:         ];
2562: 
2563:         $extension = strtolower($file->ext());
2564:         $mapped = $this->getMimeType($extension);
2565:         if ((!$extension || !$mapped) && $options['download'] === null) {
2566:             $options['download'] = true;
2567:         }
2568: 
2569:         $new = clone $this;
2570:         if ($mapped) {
2571:             $new = $new->withType($extension);
2572:         }
2573: 
2574:         $fileSize = $file->size();
2575:         if ($options['download']) {
2576:             $agent = env('HTTP_USER_AGENT');
2577: 
2578:             if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent)) {
2579:                 $contentType = 'application/octet-stream';
2580:             } elseif (preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
2581:                 $contentType = 'application/force-download';
2582:             }
2583: 
2584:             if (isset($contentType)) {
2585:                 $new = $new->withType($contentType);
2586:             }
2587:             $name = $options['name'] ?: $file->name;
2588:             $new = $new->withDownload($name)
2589:                 ->withHeader('Content-Transfer-Encoding', 'binary');
2590:         }
2591: 
2592:         $new = $new->withHeader('Accept-Ranges', 'bytes');
2593:         $httpRange = env('HTTP_RANGE');
2594:         if (isset($httpRange)) {
2595:             $new->_fileRange($file, $httpRange);
2596:         } else {
2597:             $new = $new->withHeader('Content-Length', (string)$fileSize);
2598:         }
2599:         $new->_file = $file;
2600:         $new->stream = new Stream($file->path, 'rb');
2601: 
2602:         return $new;
2603:     }
2604: 
2605:     /**
2606:      * Convenience method to set a string into the response body
2607:      *
2608:      * @param string $string The string to be sent
2609:      * @return static
2610:      */
2611:     public function withStringBody($string)
2612:     {
2613:         $new = clone $this;
2614:         $new->_createStream();
2615:         $new->stream->write((string)$string);
2616: 
2617:         return $new;
2618:     }
2619: 
2620:     /**
2621:      * Validate a file path is a valid response body.
2622:      *
2623:      * @param string $path The path to the file.
2624:      * @throws \Cake\Http\Exception\NotFoundException
2625:      * @return \Cake\Filesystem\File
2626:      */
2627:     protected function validateFile($path)
2628:     {
2629:         if (strpos($path, '../') !== false || strpos($path, '..\\') !== false) {
2630:             throw new NotFoundException(__d('cake', 'The requested file contains `..` and will not be read.'));
2631:         }
2632:         if (!is_file($path)) {
2633:             deprecationWarning(
2634:                 'Automatic prefixing of paths with `APP` by `Response::file()` and `withFile()` is deprecated. ' .
2635:                 'Use absolute paths instead.'
2636:             );
2637:             $path = APP . $path;
2638:         }
2639:         if (!Folder::isAbsolute($path)) {
2640:             deprecationWarning(
2641:                 'Serving files via `file()` or `withFile()` using relative paths is deprecated.' .
2642:                 'Use an absolute path instead.'
2643:             );
2644:         }
2645: 
2646:         $file = new File($path);
2647:         if (!$file->exists() || !$file->readable()) {
2648:             if (Configure::read('debug')) {
2649:                 throw new NotFoundException(sprintf('The requested file %s was not found or not readable', $path));
2650:             }
2651:             throw new NotFoundException(__d('cake', 'The requested file was not found'));
2652:         }
2653: 
2654:         return $file;
2655:     }
2656: 
2657:     /**
2658:      * Get the current file if one exists.
2659:      *
2660:      * @return \Cake\Filesystem\File|null The file to use in the response or null
2661:      */
2662:     public function getFile()
2663:     {
2664:         return $this->_file;
2665:     }
2666: 
2667:     /**
2668:      * Apply a file range to a file and set the end offset.
2669:      *
2670:      * If an invalid range is requested a 416 Status code will be used
2671:      * in the response.
2672:      *
2673:      * @param \Cake\Filesystem\File $file The file to set a range on.
2674:      * @param string $httpRange The range to use.
2675:      * @return void
2676:      * @deprecated 3.4.0 Long term this needs to be refactored to follow immutable paradigms.
2677:      *   However for now, it is simpler to leave this alone.
2678:      */
2679:     protected function _fileRange($file, $httpRange)
2680:     {
2681:         $fileSize = $file->size();
2682:         $lastByte = $fileSize - 1;
2683:         $start = 0;
2684:         $end = $lastByte;
2685: 
2686:         preg_match('/^bytes\s*=\s*(\d+)?\s*-\s*(\d+)?$/', $httpRange, $matches);
2687:         if ($matches) {
2688:             $start = $matches[1];
2689:             $end = isset($matches[2]) ? $matches[2] : '';
2690:         }
2691: 
2692:         if ($start === '') {
2693:             $start = $fileSize - $end;
2694:             $end = $lastByte;
2695:         }
2696:         if ($end === '') {
2697:             $end = $lastByte;
2698:         }
2699: 
2700:         if ($start > $end || $end > $lastByte || $start > $lastByte) {
2701:             $this->_setStatus(416);
2702:             $this->_setHeader('Content-Range', 'bytes 0-' . $lastByte . '/' . $fileSize);
2703: 
2704:             return;
2705:         }
2706: 
2707:         $this->_setHeader('Content-Length', $end - $start + 1);
2708:         $this->_setHeader('Content-Range', 'bytes ' . $start . '-' . $end . '/' . $fileSize);
2709:         $this->_setStatus(206);
2710:         $this->_fileRange = [$start, $end];
2711:     }
2712: 
2713:     /**
2714:      * Reads out a file, and echos the content to the client.
2715:      *
2716:      * @param \Cake\Filesystem\File $file File object
2717:      * @param array $range The range to read out of the file.
2718:      * @return bool True is whole file is echoed successfully or false if client connection is lost in between
2719:      * @deprecated 3.4.0 Will be removed in 4.0.0
2720:      */
2721:     protected function _sendFile($file, $range)
2722:     {
2723:         deprecationWarning('Will be removed in 4.0.0');
2724: 
2725:         ob_implicit_flush(true);
2726: 
2727:         $file->open('rb');
2728: 
2729:         $end = $start = false;
2730:         if ($range) {
2731:             list($start, $end) = $range;
2732:         }
2733:         if ($start !== false) {
2734:             $file->offset($start);
2735:         }
2736: 
2737:         $bufferSize = 8192;
2738:         if (strpos(ini_get('disable_functions'), 'set_time_limit') === false) {
2739:             set_time_limit(0);
2740:         }
2741:         session_write_close();
2742:         while (!feof($file->handle)) {
2743:             if (!$this->_isActive()) {
2744:                 $file->close();
2745: 
2746:                 return false;
2747:             }
2748:             $offset = $file->offset();
2749:             if ($end && $offset >= $end) {
2750:                 break;
2751:             }
2752:             if ($end && $offset + $bufferSize >= $end) {
2753:                 $bufferSize = $end - $offset + 1;
2754:             }
2755:             echo fread($file->handle, $bufferSize);
2756:         }
2757:         $file->close();
2758: 
2759:         return true;
2760:     }
2761: 
2762:     /**
2763:      * Returns true if connection is still active
2764:      *
2765:      * @return bool
2766:      * @deprecated 3.4.0 Will be removed in 4.0.0
2767:      */
2768:     protected function _isActive()
2769:     {
2770:         deprecationWarning('Will be removed in 4.0.0');
2771: 
2772:         return connection_status() === CONNECTION_NORMAL && !connection_aborted();
2773:     }
2774: 
2775:     /**
2776:      * Clears the contents of the topmost output buffer and discards them
2777:      *
2778:      * @return bool
2779:      * @deprecated 3.2.4 This function is not needed anymore
2780:      */
2781:     protected function _clearBuffer()
2782:     {
2783:         deprecationWarning(
2784:             'This function is not needed anymore and will be removed.'
2785:         );
2786: 
2787:         //@codingStandardsIgnoreStart
2788:         return @ob_end_clean();
2789:         //@codingStandardsIgnoreEnd
2790:     }
2791: 
2792:     /**
2793:      * Flushes the contents of the output buffer
2794:      *
2795:      * @return void
2796:      * @deprecated 3.2.4 This function is not needed anymore
2797:      */
2798:     protected function _flushBuffer()
2799:     {
2800:         deprecationWarning(
2801:             'This function is not needed anymore and will be removed.'
2802:         );
2803: 
2804:         //@codingStandardsIgnoreStart
2805:         @flush();
2806:         if (ob_get_level()) {
2807:             @ob_flush();
2808:         }
2809:         //@codingStandardsIgnoreEnd
2810:     }
2811: 
2812:     /**
2813:      * Stop execution of the current script. Wraps exit() making
2814:      * testing easier.
2815:      *
2816:      * @param int|string $status See https://secure.php.net/exit for values
2817:      * @return void
2818:      * @deprecated 3.4.0 Will be removed in 4.0.0
2819:      */
2820:     public function stop($status = 0)
2821:     {
2822:         deprecationWarning('Will be removed in 4.0.0');
2823: 
2824:         exit($status);
2825:     }
2826: 
2827:     /**
2828:      * Returns an array that can be used to describe the internal state of this
2829:      * object.
2830:      *
2831:      * @return array
2832:      */
2833:     public function __debugInfo()
2834:     {
2835:         return [
2836:             'status' => $this->_status,
2837:             'contentType' => $this->_contentType,
2838:             'headers' => $this->headers,
2839:             'file' => $this->_file,
2840:             'fileRange' => $this->_fileRange,
2841:             'cookies' => $this->_cookies,
2842:             'cacheDirectives' => $this->_cacheDirectives,
2843:             'body' => (string)$this->getBody(),
2844:         ];
2845:     }
2846: }
2847: 
2848: // @deprecated 3.4.0 Add backwards compat alias.
2849: class_alias('Cake\Http\Response', 'Cake\Network\Response');
2850: 
Follow @CakePHP
#IRC
OpenHub
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Logos & Trademarks
  • Community
  • Team
  • Issues (Github)
  • YouTube Channel
  • Get Involved
  • Bakery
  • Featured Resources
  • Newsletter
  • Certification
  • My CakePHP
  • CakeFest
  • Facebook
  • Twitter
  • Help & Support
  • Forum
  • Stack Overflow
  • IRC
  • Slack
  • Paid Support

Generated using CakePHP API Docs