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 }