. */ /** * This file contains the Request class. * * @author Dinu Florin * @package Core */ /** * Request class. * * @author Dinu Florin * @package Core */ class Response extends Pt { private static $instance = null; private static $protocol = null; private static $status = null; /** * Signal emitted before sending the response to the client. * * @param string $content the buffer content. * @signal */ protected static function sending($content) {} /** * Signal emitted before a redirect. * * @param string $url the URL for the redirect. * @signal */ protected static function redirecting($url) {} /** * Get the response headers as an array. * * @return array. */ public static function getHeaders() { $headers = headers_list(); $ret = array(); foreach($headers as $header) { $h = explode(':', $header); $ret[HTTP::makeStdHeaderName($h[0])] = trim($h[1]); } return $ret; } /** * Get a single header. * * @param string $name The header name. * @return string The header value. * @see Response::getHeaders() */ public static function getHeader($name) { $headers = self::getHeaders(); $name = HTTP::makeStdHeaderName($name); if(isset($headers[$name])) { return $headers[$name]; } else { return null; } } /** * Add a HTTP header to the response. It will not replace the existing headers. * * @param string $name The header to add. * @param string $value The header value. */ public static function addHeader($name, $value) { $header = HTTP::makeStdHeaderName($name).': '.$value; if(headers_sent($file, $line)) throw new GenericException("Headers are already sent in {$file}, line {$line}"); header($header, false); } /** * Add a HTTP header to the response. It will replace any existing headers with the same type. * * @param string $name The header to add. * @param string $value The header value. */ public static function setHeader($name, $value) { $header = HTTP::makeStdHeaderName($name).': '.$value; if(headers_sent($file, $line)) throw new GenericException("Headers are already sent in {$file}, line {$line}"); header($header, true); } /** * Set the HTTP status for the response. * * @param number $status a HTTP status. */ public static function setStatus($status) { assert('is_numeric($status)'); $text = HTTP::getStatusMessage($status); $protocol = self::getProtocol(); self::$status = $status; header("{$protocol} {$status} {$text}", true); } /** * Get the HTTP status. * * @return string */ public static function getStatus() { return self::$status; } /** * Get the protocol. This can be HTTP1_0 or HTTP1_1. */ public static function getProtocol() { if(is_null(self::$protocol)) { switch($_SERVER['SERVER_PROTOCOL']) { case 'HTTP/1.0': self::$protocol = HTTP::HTTP1_0; break; case 'HTTP/1.1': self::$protocol = HTTP::HTTP1_1; break; default: self::setStatus(HTTP::HTTP_VERSION_NOT_SUPPORTED); throw new GenericException('Unsuported protocol'); } } return self::$protocol; } /** * Set the protocol. * * @param string $protocol */ public static function setProtocol($protocol) { self::$protocol = $protocol; self::setStatus(self::$status); } /** * Performs a HTTP redirect and exits. * * @param string $url The URL to redirect to. * @param boolean $permanent The redirect is permanent (301) or temporary (302). This only works if a redirect is done with HTTP headers, the scripted one ignores it. **/ public static function redirect($url, $permanent = false) { assert('is_string($url)'); assert('is_bool($permanent)'); self::emit('Response', 'redirecting', $url); self::clearContent(); echo "Moved"; if($permanent) { self::setStatus(HTTP::MOVED_PERMANENTLY); } else { self::setStatus(HTTP::FOUND); } self::setHeader('Location', $url); self::flushContent(); exit; } /** * Don't call this, it will throw an exception, this class is a Singleton. */ public static function initialize() { static $initialized = false; if($initialized) return; $initialized = true; if(!ob_start(array('Response', 'onBufferFlush'), 0)) { trigger_error('Failed to start output buffering', E_USER_WARNING); } $oldPBHeader = self::getHeader('X-Powered-By'); self::setHeader('X-Powered-By', 'PHP Toolkit'.(($oldPBHeader)?' ('.$oldPBHeader.')':'')); self::setHeader('X-Pt-Version', PT_VERSION); set_error_handler(array('Response', 'setErrorStatus'), E_ERROR|E_USER_ERROR); // set_exception_handler(array('Response', '_setErrorStatus')); self::setStatus(HTTP::OK); self::registerSignal('Response', 'sending'); self::registerSignal('Response', 'redirecting'); self::registerSlot('Response', 'cleanup'); self::connect('Core', 'shutdown', 'Response', 'cleanup'); } /** * Cleanup slot, it's connected to Core::shutdown. * * @slot * @ignore */ public static function cleanup() { ob_end_flush(); } /** * Clear the output buffer. */ public static function clearContent() { ob_clean(); } /** * Get the content of the output buffer. * * @return string. */ public static function getContent() { return ob_get_contents(); } /** * Set the content of the output buffer. * * @param string $content */ public static function setContent($content) { self::clearContent(); echo $content; } /** * Send the contents of the output buffer to the browser. * The buffer will be empty after calling this method. */ public static function flushContent() { ob_flush(); ob_clean(); } /** * Callback to set the HTTP status to 500 on error. * @ignore */ public static function setErrorStatus() { self::setStatus(HTTP::INTERNAL_SERVER_ERROR); return false; } /** * Callback passed to ob_start(). * * @ignore * @return mixed. */ public static function onBufferFlush($content) { self::emit('Response', 'sending', $content); if(Request::getMethod() === HTTP::HEAD) { return ''; } else { return false; } } } ?>