File indexing completed on 2024-12-22 05:36:33
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Controller 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 /** 0023 * Zend_Controller_Response_Abstract 0024 * 0025 * Base class for Zend_Controller responses 0026 * 0027 * @package Zend_Controller 0028 * @subpackage Response 0029 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0030 * @license http://framework.zend.com/license/new-bsd New BSD License 0031 */ 0032 abstract class Zend_Controller_Response_Abstract 0033 { 0034 /** 0035 * Body content 0036 * @var array 0037 */ 0038 protected $_body = array(); 0039 0040 /** 0041 * Exception stack 0042 * @var Exception 0043 */ 0044 protected $_exceptions = array(); 0045 0046 /** 0047 * Array of headers. Each header is an array with keys 'name' and 'value' 0048 * @var array 0049 */ 0050 protected $_headers = array(); 0051 0052 /** 0053 * Array of raw headers. Each header is a single string, the entire header to emit 0054 * @var array 0055 */ 0056 protected $_headersRaw = array(); 0057 0058 /** 0059 * HTTP response code to use in headers 0060 * @var int 0061 */ 0062 protected $_httpResponseCode = 200; 0063 0064 /** 0065 * Flag; is this response a redirect? 0066 * @var boolean 0067 */ 0068 protected $_isRedirect = false; 0069 0070 /** 0071 * Whether or not to render exceptions; off by default 0072 * @var boolean 0073 */ 0074 protected $_renderExceptions = false; 0075 0076 /** 0077 * Flag; if true, when header operations are called after headers have been 0078 * sent, an exception will be raised; otherwise, processing will continue 0079 * as normal. Defaults to true. 0080 * 0081 * @see canSendHeaders() 0082 * @var boolean 0083 */ 0084 public $headersSentThrowsException = true; 0085 0086 /** 0087 * Normalize a header name 0088 * 0089 * Normalizes a header name to X-Capitalized-Names 0090 * 0091 * @param string $name 0092 * @return string 0093 */ 0094 protected function _normalizeHeader($name) 0095 { 0096 $filtered = str_replace(array('-', '_'), ' ', (string) $name); 0097 $filtered = ucwords(strtolower($filtered)); 0098 $filtered = str_replace(' ', '-', $filtered); 0099 return $filtered; 0100 } 0101 0102 /** 0103 * Set a header 0104 * 0105 * If $replace is true, replaces any headers already defined with that 0106 * $name. 0107 * 0108 * @param string $name 0109 * @param string $value 0110 * @param boolean $replace 0111 * @return Zend_Controller_Response_Abstract 0112 */ 0113 public function setHeader($name, $value, $replace = false) 0114 { 0115 $this->canSendHeaders(true); 0116 $name = $this->_normalizeHeader($name); 0117 $value = (string) $value; 0118 0119 if ($replace) { 0120 foreach ($this->_headers as $key => $header) { 0121 if ($name == $header['name']) { 0122 unset($this->_headers[$key]); 0123 } 0124 } 0125 } 0126 0127 $this->_headers[] = array( 0128 'name' => $name, 0129 'value' => $value, 0130 'replace' => $replace 0131 ); 0132 0133 return $this; 0134 } 0135 0136 /** 0137 * Set redirect URL 0138 * 0139 * Sets Location header and response code. Forces replacement of any prior 0140 * redirects. 0141 * 0142 * @param string $url 0143 * @param int $code 0144 * @return Zend_Controller_Response_Abstract 0145 */ 0146 public function setRedirect($url, $code = 302) 0147 { 0148 $this->canSendHeaders(true); 0149 $this->setHeader('Location', $url, true) 0150 ->setHttpResponseCode($code); 0151 0152 return $this; 0153 } 0154 0155 /** 0156 * Is this a redirect? 0157 * 0158 * @return boolean 0159 */ 0160 public function isRedirect() 0161 { 0162 return $this->_isRedirect; 0163 } 0164 0165 /** 0166 * Return array of headers; see {@link $_headers} for format 0167 * 0168 * @return array 0169 */ 0170 public function getHeaders() 0171 { 0172 return $this->_headers; 0173 } 0174 0175 /** 0176 * Clear headers 0177 * 0178 * @return Zend_Controller_Response_Abstract 0179 */ 0180 public function clearHeaders() 0181 { 0182 $this->_headers = array(); 0183 0184 return $this; 0185 } 0186 0187 /** 0188 * Clears the specified HTTP header 0189 * 0190 * @param string $name 0191 * @return Zend_Controller_Response_Abstract 0192 */ 0193 public function clearHeader($name) 0194 { 0195 if (! count($this->_headers)) { 0196 return $this; 0197 } 0198 0199 foreach ($this->_headers as $index => $header) { 0200 if ($name == $header['name']) { 0201 unset($this->_headers[$index]); 0202 } 0203 } 0204 0205 return $this; 0206 } 0207 0208 /** 0209 * Set raw HTTP header 0210 * 0211 * Allows setting non key => value headers, such as status codes 0212 * 0213 * @param string $value 0214 * @return Zend_Controller_Response_Abstract 0215 */ 0216 public function setRawHeader($value) 0217 { 0218 $this->canSendHeaders(true); 0219 if ('Location' == substr($value, 0, 8)) { 0220 $this->_isRedirect = true; 0221 } 0222 $this->_headersRaw[] = (string) $value; 0223 return $this; 0224 } 0225 0226 /** 0227 * Retrieve all {@link setRawHeader() raw HTTP headers} 0228 * 0229 * @return array 0230 */ 0231 public function getRawHeaders() 0232 { 0233 return $this->_headersRaw; 0234 } 0235 0236 /** 0237 * Clear all {@link setRawHeader() raw HTTP headers} 0238 * 0239 * @return Zend_Controller_Response_Abstract 0240 */ 0241 public function clearRawHeaders() 0242 { 0243 $this->_headersRaw = array(); 0244 return $this; 0245 } 0246 0247 /** 0248 * Clears the specified raw HTTP header 0249 * 0250 * @param string $headerRaw 0251 * @return Zend_Controller_Response_Abstract 0252 */ 0253 public function clearRawHeader($headerRaw) 0254 { 0255 if (! count($this->_headersRaw)) { 0256 return $this; 0257 } 0258 0259 $key = array_search($headerRaw, $this->_headersRaw); 0260 if ($key !== false) { 0261 unset($this->_headersRaw[$key]); 0262 } 0263 0264 return $this; 0265 } 0266 0267 /** 0268 * Clear all headers, normal and raw 0269 * 0270 * @return Zend_Controller_Response_Abstract 0271 */ 0272 public function clearAllHeaders() 0273 { 0274 return $this->clearHeaders() 0275 ->clearRawHeaders(); 0276 } 0277 0278 /** 0279 * Set HTTP response code to use with headers 0280 * 0281 * @param int $code 0282 * @return Zend_Controller_Response_Abstract 0283 */ 0284 public function setHttpResponseCode($code) 0285 { 0286 if (!is_int($code) || (100 > $code) || (599 < $code)) { 0287 // require_once 'Zend/Controller/Response/Exception.php'; 0288 throw new Zend_Controller_Response_Exception('Invalid HTTP response code'); 0289 } 0290 0291 if ((300 <= $code) && (307 >= $code)) { 0292 $this->_isRedirect = true; 0293 } else { 0294 $this->_isRedirect = false; 0295 } 0296 0297 $this->_httpResponseCode = $code; 0298 return $this; 0299 } 0300 0301 /** 0302 * Retrieve HTTP response code 0303 * 0304 * @return int 0305 */ 0306 public function getHttpResponseCode() 0307 { 0308 return $this->_httpResponseCode; 0309 } 0310 0311 /** 0312 * Can we send headers? 0313 * 0314 * @param boolean $throw Whether or not to throw an exception if headers have been sent; defaults to false 0315 * @return boolean 0316 * @throws Zend_Controller_Response_Exception 0317 */ 0318 public function canSendHeaders($throw = false) 0319 { 0320 $ok = headers_sent($file, $line); 0321 if ($ok && $throw && $this->headersSentThrowsException) { 0322 // require_once 'Zend/Controller/Response/Exception.php'; 0323 throw new Zend_Controller_Response_Exception('Cannot send headers; headers already sent in ' . $file . ', line ' . $line); 0324 } 0325 0326 return !$ok; 0327 } 0328 0329 /** 0330 * Send all headers 0331 * 0332 * Sends any headers specified. If an {@link setHttpResponseCode() HTTP response code} 0333 * has been specified, it is sent with the first header. 0334 * 0335 * @return Zend_Controller_Response_Abstract 0336 */ 0337 public function sendHeaders() 0338 { 0339 // Only check if we can send headers if we have headers to send 0340 if (count($this->_headersRaw) || count($this->_headers) || (200 != $this->_httpResponseCode)) { 0341 $this->canSendHeaders(true); 0342 } elseif (200 == $this->_httpResponseCode) { 0343 // Haven't changed the response code, and we have no headers 0344 return $this; 0345 } 0346 0347 $httpCodeSent = false; 0348 0349 foreach ($this->_headersRaw as $header) { 0350 if (!$httpCodeSent && $this->_httpResponseCode) { 0351 header($header, true, $this->_httpResponseCode); 0352 $httpCodeSent = true; 0353 } else { 0354 header($header); 0355 } 0356 } 0357 0358 foreach ($this->_headers as $header) { 0359 if (!$httpCodeSent && $this->_httpResponseCode) { 0360 header($header['name'] . ': ' . $header['value'], $header['replace'], $this->_httpResponseCode); 0361 $httpCodeSent = true; 0362 } else { 0363 header($header['name'] . ': ' . $header['value'], $header['replace']); 0364 } 0365 } 0366 0367 if (!$httpCodeSent) { 0368 header('HTTP/1.1 ' . $this->_httpResponseCode); 0369 $httpCodeSent = true; 0370 } 0371 0372 return $this; 0373 } 0374 0375 /** 0376 * Set body content 0377 * 0378 * If $name is not passed, or is not a string, resets the entire body and 0379 * sets the 'default' key to $content. 0380 * 0381 * If $name is a string, sets the named segment in the body array to 0382 * $content. 0383 * 0384 * @param string $content 0385 * @param null|string $name 0386 * @return Zend_Controller_Response_Abstract 0387 */ 0388 public function setBody($content, $name = null) 0389 { 0390 if ((null === $name) || !is_string($name)) { 0391 $this->_body = array('default' => (string) $content); 0392 } else { 0393 $this->_body[$name] = (string) $content; 0394 } 0395 0396 return $this; 0397 } 0398 0399 /** 0400 * Append content to the body content 0401 * 0402 * @param string $content 0403 * @param null|string $name 0404 * @return Zend_Controller_Response_Abstract 0405 */ 0406 public function appendBody($content, $name = null) 0407 { 0408 if ((null === $name) || !is_string($name)) { 0409 if (isset($this->_body['default'])) { 0410 $this->_body['default'] .= (string) $content; 0411 } else { 0412 return $this->append('default', $content); 0413 } 0414 } elseif (isset($this->_body[$name])) { 0415 $this->_body[$name] .= (string) $content; 0416 } else { 0417 return $this->append($name, $content); 0418 } 0419 0420 return $this; 0421 } 0422 0423 /** 0424 * Clear body array 0425 * 0426 * With no arguments, clears the entire body array. Given a $name, clears 0427 * just that named segment; if no segment matching $name exists, returns 0428 * false to indicate an error. 0429 * 0430 * @param string $name Named segment to clear 0431 * @return boolean 0432 */ 0433 public function clearBody($name = null) 0434 { 0435 if (null !== $name) { 0436 $name = (string) $name; 0437 if (isset($this->_body[$name])) { 0438 unset($this->_body[$name]); 0439 return true; 0440 } 0441 0442 return false; 0443 } 0444 0445 $this->_body = array(); 0446 return true; 0447 } 0448 0449 /** 0450 * Return the body content 0451 * 0452 * If $spec is false, returns the concatenated values of the body content 0453 * array. If $spec is boolean true, returns the body content array. If 0454 * $spec is a string and matches a named segment, returns the contents of 0455 * that segment; otherwise, returns null. 0456 * 0457 * @param boolean $spec 0458 * @return string|array|null 0459 */ 0460 public function getBody($spec = false) 0461 { 0462 if (false === $spec) { 0463 ob_start(); 0464 $this->outputBody(); 0465 return ob_get_clean(); 0466 } elseif (true === $spec) { 0467 return $this->_body; 0468 } elseif (is_string($spec) && isset($this->_body[$spec])) { 0469 return $this->_body[$spec]; 0470 } 0471 0472 return null; 0473 } 0474 0475 /** 0476 * Append a named body segment to the body content array 0477 * 0478 * If segment already exists, replaces with $content and places at end of 0479 * array. 0480 * 0481 * @param string $name 0482 * @param string $content 0483 * @return Zend_Controller_Response_Abstract 0484 */ 0485 public function append($name, $content) 0486 { 0487 if (!is_string($name)) { 0488 // require_once 'Zend/Controller/Response/Exception.php'; 0489 throw new Zend_Controller_Response_Exception('Invalid body segment key ("' . gettype($name) . '")'); 0490 } 0491 0492 if (isset($this->_body[$name])) { 0493 unset($this->_body[$name]); 0494 } 0495 $this->_body[$name] = (string) $content; 0496 return $this; 0497 } 0498 0499 /** 0500 * Prepend a named body segment to the body content array 0501 * 0502 * If segment already exists, replaces with $content and places at top of 0503 * array. 0504 * 0505 * @param string $name 0506 * @param string $content 0507 * @return void 0508 */ 0509 public function prepend($name, $content) 0510 { 0511 if (!is_string($name)) { 0512 // require_once 'Zend/Controller/Response/Exception.php'; 0513 throw new Zend_Controller_Response_Exception('Invalid body segment key ("' . gettype($name) . '")'); 0514 } 0515 0516 if (isset($this->_body[$name])) { 0517 unset($this->_body[$name]); 0518 } 0519 0520 $new = array($name => (string) $content); 0521 $this->_body = $new + $this->_body; 0522 0523 return $this; 0524 } 0525 0526 /** 0527 * Insert a named segment into the body content array 0528 * 0529 * @param string $name 0530 * @param string $content 0531 * @param string $parent 0532 * @param boolean $before Whether to insert the new segment before or 0533 * after the parent. Defaults to false (after) 0534 * @return Zend_Controller_Response_Abstract 0535 */ 0536 public function insert($name, $content, $parent = null, $before = false) 0537 { 0538 if (!is_string($name)) { 0539 // require_once 'Zend/Controller/Response/Exception.php'; 0540 throw new Zend_Controller_Response_Exception('Invalid body segment key ("' . gettype($name) . '")'); 0541 } 0542 0543 if ((null !== $parent) && !is_string($parent)) { 0544 // require_once 'Zend/Controller/Response/Exception.php'; 0545 throw new Zend_Controller_Response_Exception('Invalid body segment parent key ("' . gettype($parent) . '")'); 0546 } 0547 0548 if (isset($this->_body[$name])) { 0549 unset($this->_body[$name]); 0550 } 0551 0552 if ((null === $parent) || !isset($this->_body[$parent])) { 0553 return $this->append($name, $content); 0554 } 0555 0556 $ins = array($name => (string) $content); 0557 $keys = array_keys($this->_body); 0558 $loc = array_search($parent, $keys); 0559 if (!$before) { 0560 // Increment location if not inserting before 0561 ++$loc; 0562 } 0563 0564 if (0 === $loc) { 0565 // If location of key is 0, we're prepending 0566 $this->_body = $ins + $this->_body; 0567 } elseif ($loc >= (count($this->_body))) { 0568 // If location of key is maximal, we're appending 0569 $this->_body = $this->_body + $ins; 0570 } else { 0571 // Otherwise, insert at location specified 0572 $pre = array_slice($this->_body, 0, $loc); 0573 $post = array_slice($this->_body, $loc); 0574 $this->_body = $pre + $ins + $post; 0575 } 0576 0577 return $this; 0578 } 0579 0580 /** 0581 * Echo the body segments 0582 * 0583 * @return void 0584 */ 0585 public function outputBody() 0586 { 0587 $body = implode('', $this->_body); 0588 echo $body; 0589 } 0590 0591 /** 0592 * Register an exception with the response 0593 * 0594 * @param Exception $e 0595 * @return Zend_Controller_Response_Abstract 0596 */ 0597 public function setException(Exception $e) 0598 { 0599 $this->_exceptions[] = $e; 0600 return $this; 0601 } 0602 0603 /** 0604 * Retrieve the exception stack 0605 * 0606 * @return array 0607 */ 0608 public function getException() 0609 { 0610 return $this->_exceptions; 0611 } 0612 0613 /** 0614 * Has an exception been registered with the response? 0615 * 0616 * @return boolean 0617 */ 0618 public function isException() 0619 { 0620 return !empty($this->_exceptions); 0621 } 0622 0623 /** 0624 * Does the response object contain an exception of a given type? 0625 * 0626 * @param string $type 0627 * @return boolean 0628 */ 0629 public function hasExceptionOfType($type) 0630 { 0631 foreach ($this->_exceptions as $e) { 0632 if ($e instanceof $type) { 0633 return true; 0634 } 0635 } 0636 0637 return false; 0638 } 0639 0640 /** 0641 * Does the response object contain an exception with a given message? 0642 * 0643 * @param string $message 0644 * @return boolean 0645 */ 0646 public function hasExceptionOfMessage($message) 0647 { 0648 foreach ($this->_exceptions as $e) { 0649 if ($message == $e->getMessage()) { 0650 return true; 0651 } 0652 } 0653 0654 return false; 0655 } 0656 0657 /** 0658 * Does the response object contain an exception with a given code? 0659 * 0660 * @param int $code 0661 * @return boolean 0662 */ 0663 public function hasExceptionOfCode($code) 0664 { 0665 $code = (int) $code; 0666 foreach ($this->_exceptions as $e) { 0667 if ($code == $e->getCode()) { 0668 return true; 0669 } 0670 } 0671 0672 return false; 0673 } 0674 0675 /** 0676 * Retrieve all exceptions of a given type 0677 * 0678 * @param string $type 0679 * @return false|array 0680 */ 0681 public function getExceptionByType($type) 0682 { 0683 $exceptions = array(); 0684 foreach ($this->_exceptions as $e) { 0685 if ($e instanceof $type) { 0686 $exceptions[] = $e; 0687 } 0688 } 0689 0690 if (empty($exceptions)) { 0691 $exceptions = false; 0692 } 0693 0694 return $exceptions; 0695 } 0696 0697 /** 0698 * Retrieve all exceptions of a given message 0699 * 0700 * @param string $message 0701 * @return false|array 0702 */ 0703 public function getExceptionByMessage($message) 0704 { 0705 $exceptions = array(); 0706 foreach ($this->_exceptions as $e) { 0707 if ($message == $e->getMessage()) { 0708 $exceptions[] = $e; 0709 } 0710 } 0711 0712 if (empty($exceptions)) { 0713 $exceptions = false; 0714 } 0715 0716 return $exceptions; 0717 } 0718 0719 /** 0720 * Retrieve all exceptions of a given code 0721 * 0722 * @param mixed $code 0723 * @return void 0724 */ 0725 public function getExceptionByCode($code) 0726 { 0727 $code = (int) $code; 0728 $exceptions = array(); 0729 foreach ($this->_exceptions as $e) { 0730 if ($code == $e->getCode()) { 0731 $exceptions[] = $e; 0732 } 0733 } 0734 0735 if (empty($exceptions)) { 0736 $exceptions = false; 0737 } 0738 0739 return $exceptions; 0740 } 0741 0742 /** 0743 * Whether or not to render exceptions (off by default) 0744 * 0745 * If called with no arguments or a null argument, returns the value of the 0746 * flag; otherwise, sets it and returns the current value. 0747 * 0748 * @param boolean $flag Optional 0749 * @return boolean 0750 */ 0751 public function renderExceptions($flag = null) 0752 { 0753 if (null !== $flag) { 0754 $this->_renderExceptions = $flag ? true : false; 0755 } 0756 0757 return $this->_renderExceptions; 0758 } 0759 0760 /** 0761 * Send the response, including all headers, rendering exceptions if so 0762 * requested. 0763 * 0764 * @return void 0765 */ 0766 public function sendResponse() 0767 { 0768 $this->sendHeaders(); 0769 0770 if ($this->isException() && $this->renderExceptions()) { 0771 $exceptions = ''; 0772 foreach ($this->getException() as $e) { 0773 $exceptions .= $e->__toString() . "\n"; 0774 } 0775 echo $exceptions; 0776 return; 0777 } 0778 0779 $this->outputBody(); 0780 } 0781 0782 /** 0783 * Magic __toString functionality 0784 * 0785 * Proxies to {@link sendResponse()} and returns response value as string 0786 * using output buffering. 0787 * 0788 * @return string 0789 */ 0790 public function __toString() 0791 { 0792 ob_start(); 0793 $this->sendResponse(); 0794 return ob_get_clean(); 0795 } 0796 }