00001 <?php
00010 class response_http extends response_abstract {
00011
00017 protected $headers;
00018
00024 protected $beforeOut = array();
00025
00026 protected function afterInit() {
00027 $this->headers = $this->cfg->headers;
00028 }
00029
00036 public function getStatus($name = false) {
00037 if ($name)
00038 return $this->cfg->statusCfg[$this->cfg->status];
00039 else
00040 return $this->cfg->status;
00041 }
00042
00049 public function setStatus($status) {
00050 if (array_key_exists($status, $this->cfg->statusCfg)) {
00051 $this->cfg->status = $status;
00052 return true;
00053 }
00054 return false;
00055 }
00056
00062 public function getCompress() {
00063 return $this->cfg->compress;
00064 }
00065
00071 public function setCompress($compress) {
00072 $this->cfg->compress = (boolean) $compress;
00073 }
00074
00080 public function getLayout() {
00081 return $this->cfg->layout;
00082 }
00083
00089 public function setlayout($layout) {
00090 $this->cfg->layout = $layout;
00091 }
00092
00098 public function getAjaxLayout() {
00099 return $this->cfg->ajaxLayout;
00100 }
00101
00107 public function setAjaxlayout($ajaxLayout) {
00108 $this->cfg->ajaxLayout = $ajaxLayout;
00109 }
00110
00119 public function addHeader($name, $value, $replace = true) {
00120 if ($name == 'Content-Type')
00121 return $this->setContentType($value, $replace);
00122 if ($replace || !$this->hasHeader($name)) {
00123 $this->headers[$name] = $value;
00124 return true;
00125 }
00126 return false;
00127 }
00128
00137 public function setContentType($value, $replace = true) {
00138 if ($replace || !$this->hasHeader('Content-Type')) {
00139 if (array_key_exists($value, $this->cfg->contentTypeCfg))
00140 $value = $this->cfg->getInArray('contentTypeCfg', $value);
00141 else if (strpos($value, '/') === false)
00142 $value = 'text/'.$value;
00143 $this->headers['Content-Type'] = $value.'; charset='.$this->cfg->charset;
00144 return true;
00145 }
00146 return false;
00147 }
00148
00152 public function neverExpire() {
00153 $this->addHeader('Expires', gmdate('D, j M Y H:i:s', strtotime('+32 days')).' GMT');
00154 }
00155
00162 public function getHeader($name = null) {
00163 if (is_null($name))
00164 return $this->headers;
00165 if ($this->hasHeader($name))
00166 return $this->headers[$name];
00167 return null;
00168 }
00169
00176 public function hasHeader($name) {
00177 return array_key_exists($name, $this->headers);
00178 }
00179
00183 public function clearHeaders() {
00184 $this->headers = $this->cfg->headers;
00185 }
00186
00190 public function sendHeaders() {
00191 if (!headers_sent()) {
00192 header('HTTP/1.0 '.$this->getStatus().' '.$this->getStatus(true));
00193 foreach($this->headers as $name=>$value) {
00194 header($name.': '.$value);
00195 }
00196 }
00197 }
00198
00204 public function addBeforeOut($callback) {
00205 $this->beforeOut[] = $callback;
00206 }
00207
00211 protected function beforeOut() {
00212 foreach($this->beforeOut as $bo)
00213 call_user_func($bo);
00214
00215 if ($this->cfg->compress && !ob_get_length())
00216 ob_start('ob_gzhandler');
00217 }
00218
00224 public function send($headerOnly = false) {
00225 if (!headers_sent()) {
00226 $this->sendHeaders();
00227 $this->beforeOut();
00228 if ($headerOnly)
00229 exit(0);
00230 }
00231
00232 $layout = request::isAjax()? $this->cfg->ajaxLayout : $this->cfg->layout;
00233 if (!$layout) {
00234 return $this->content;
00235 } else {
00236 $tpl = factory::get('tpl', array(
00237 'module'=>'out',
00238 'action'=>$layout,
00239 'default'=>'layout',
00240 'layout'=>false,
00241 'cache'=>array('auto'=>false)
00242 ));
00243 $tpl->set('content', $this->content);
00244 return $tpl->fetch();
00245 }
00246 }
00247
00253 public function sendText($text) {
00254 $this->sendHeaders();
00255 $this->beforeOut();
00256 echo $text;
00257 exit(0);
00258 }
00259
00267 public function sendFile($file, $name = null, $delete = false) {
00268 $name = $name? $name : basename($file);
00269 if (file::exists($file))
00270 $this->mediaDownload($file, true, $name, $delete);
00271 else
00272 $this->error();
00273 }
00274
00281 public function sendFileAsString($file, $name) {
00282 $this->cfg->compress = false;
00283 $this->neverExpire();
00284 $this->addHeader('Expires', '0');
00285 $this->addHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0');
00286 $this->addHeader('Content-Type', 'application/force-download');
00287 $this->addHeader('Content-Disposition', 'attachment; filename='.$name.'');
00288 $this->addHeader('Last-Modified', gmdate('D, j M Y H:i:s').' GMT', true);
00289 $this->addHeader('Pragma', null, false);
00290 $this->sendText($file);
00291 }
00292
00298 public function showFile($file) {
00299 if (file::exists($file)) {
00300 $type = file::getType($file);
00301 if (strpos($type, 'audio') === 0 || strpos($type, 'video') === 0) {
00302 $this->mediaDownload($file);
00303 } else {
00304 $this->cfg->compress = false;
00305 $this->neverExpire();
00306 $this->addHeader('Last-Modified', gmdate('D, j M Y H:i:s', filemtime($file)).' GMT', true);
00307 $this->addHeader('Content-Type', $type, true);
00308 $this->addHeader('Cache-Control', 'public', false);
00309 $this->addHeader('Pragma', null, false);
00310 $this->addHeader('Content-length', file::size($file), true);
00311 $this->sendText(file::read($file));
00312 }
00313 }
00314 }
00315
00324 protected function mediaDownload($file, $forceDownload = false, $fileName = null, $delete = false) {
00325 $fileName = $fileName ? $fileName : basename($file);
00326 if(strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE'))
00327 $fileName = preg_replace('/\./', '%2e', $fileName, substr_count($fileName, '.') - 1);
00328 $fileModified = filemtime($file);
00329 $fileSize = filesize($file);
00330 $fileType = file::getType($file);
00331 $audio = strpos($fileType, 'audio') === 0;
00332 $video = strpos($fileType, 'video') === 0;
00333
00334 $seekStart = 0;
00335 $seekEnd = -1;
00336 $bufferSize = 8*1024;
00337 $partialDownload = false;
00338 $httpRangeDownload = false;
00339
00340 if (isset($_SERVER['HTTP_RANGE'])) {
00341 $range = explode('-', substr($_SERVER['HTTP_RANGE'], strlen('bytes=')));
00342 if($range[0] > 0)
00343 $seekStart = intval($range[0]);
00344 $seekEnd = $range[1] > 0 ? intval($range[1]) : -1;
00345 $partialDownload = true;
00346 $httpRangeDownload = true;
00347 } else if ($audio && request::isMobile()) {
00348 $partialDownload = true;
00349 $httpRangeDownload = true;
00350 }
00351
00352 if ($seekEnd < $seekStart)
00353 $seekEnd = $fileSize - 1;
00354
00355 $contentLength = $seekEnd - $seekStart + 1;
00356
00357 if(!$fileHandle = fopen($file, 'rb'))
00358 $this->error();
00359
00360
00361 header('Pragma: public');
00362 if ($forceDownload) {
00363 if (ini_get('zlib.output_compression'))
00364 ini_set('zlib.output_compression', 'Off');
00365
00366 header('Expires: 0');
00367 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
00368 header('Cache-Control: private', false);
00369
00370 header('Content-Type: application/force-download');
00371 header('Content-Type: application/octet-stream');
00372 header('Content-Type: application/download');
00373 header('Content-type: '.$fileType);
00374 header('Content-Disposition: attachment; filename='.$fileName.'');
00375 } else {
00376 header('Cache-Control: public');
00377 header('Content-type: '.$fileType);
00378 header('Content-Disposition: inline; filename='.$fileName.'');
00379 }
00380 header('Last-Modified: '.date('D, d M Y H:i:s \G\M\T', $fileModified));
00381 header("Content-Transfer-Encoding: binary\n");
00382 if ($httpRangeDownload) {
00383 header('HTTP/1.0 206 Partial Content');
00384 header('Status: 206 Partial Content');
00385 header('Accept-Ranges: bytes');
00386 header('Content-Range: bytes '.$seekStart.'-'.$seekEnd.'/'.$fileSize);
00387 }
00388 header('Content-Length: '.$contentLength);
00389
00390 if ($seekStart > 0) {
00391 $partialDownload = true;
00392 fseek($fileHandle, $seekStart);
00393 if ($fileType == 'video/x-flv')
00394 echo 'FLV', pack('C', 1), pack('C', 1), pack('N', 9), pack('N', 9);
00395 }
00396
00397 $this->setCompress(false);
00398 $this->beforeOut();
00399
00400 $speed = 0;
00401 $bytesSent = 0;
00402 $chunk = 1;
00403 $throttle = $video ? 320 : ($audio ? 84 : 0);
00404 $burst = 1024 * ($video ? 500 : ($audio ? 120 : 0));
00405 while (!(connection_aborted() || connection_status() == 1) && $bytesSent < $contentLength) {
00406
00407 if ($bytesSent >= $burst)
00408 $speed = $throttle;
00409
00410
00411 if ($bytesSent + $bufferSize > $contentLength)
00412 $bufferSize = $contentLength - $bytesSent;
00413
00414
00415 echo fread($fileHandle, $bufferSize);
00416 $bytesSent+= $bufferSize;
00417
00418
00419 flush();
00420
00421
00422 if($speed && ($bytesSent - $burst > $speed * $chunk*1024)) {
00423 sleep(1);
00424 $chunk++;
00425 }
00426 }
00427
00428 fclose($fileHandle);
00429
00430 if ($delete)
00431 file::delete($file);
00432 exit;
00433 }
00434
00441 public function comment($comment) {
00442 $tmp = '';
00443 switch(request::get('out')) {
00444 case 'js':
00445 case 'json':
00446 case 'css':
00447 $tmp = "/*\n[comment]\n*/\n";
00448 break;
00449 case 'html':
00450 case 'xml':
00451 $tmp = "<!--\n[comment]\n-->\n";
00452 break;
00453 }
00454 return str_replace('[comment]', $comment, $tmp);
00455 }
00456
00463 public function redirect($url, $status = 301) {
00464 $this->clearHeaders();
00465 $this->setStatus($status);
00466 $this->addHeader('Location', $url);
00467 $this->sendHeaders();
00468 $this->beforeOut();
00469 exit(0);
00470 }
00471
00478 public function error($url = null, $number = 404) {
00479 $this->redirect(request::uri($url? $url : '/'.$number), $number);
00480 }
00481
00482 }