PHP Classes

File: engine/handler.file.php

Recommend this page to a friend!
  Classes of Kristo Vaher   Wave Framework   engine/handler.file.php   Download  
File: engine/handler.file.php
Role: Application script
Content type: text/plain
Description: File Handler
Class: Wave Framework
MVC framework for building Web sites and APIs
Author: By
Last change: Fixed tiny errors as a result of PHP version updates
Date: 14 days ago
Size: 8,714 bytes
 

Contents

Class file image Download
<?php
/**
 * Wave Framework <http://github.com/kristovaher/Wave-Framework>
 * File Handler
 *
 * File Handler is used for returning a file from web server. It is loaded by Index Controller
 * when a request is made to a file that has an extension that has not already been served by
 * another Handler (such as Resource and Image handlers). File Handler is usually considered
 * for returning documents, videos and audio from the web server. It also allows to return a
 * file within a byte range, like in the case of file streams.
 *
 * @package Index Gateway
 * @author Kristo Vaher <kristo@waher.net>
 * @copyright Copyright (c) 2012, Kristo Vaher
 * @license GNU Lesser General Public License Version 3
 * @tutorial /doc/pages/handler_file.htm
 * @since 1.5.0
 * @version 3.6.4
 */
// INITIALIZATION
// Stopping all requests that did not come from Index Gateway
   
if(!isset($resourceAddress)){
       
header('HTTP/1.1 403 Forbidden');
        die();
    }
   
   
// If access control header is set in configuration
   
if(isset($config['access-control'])){
       
header('Access-Control-Allow-Origin: '.$config['access-control']);
    }
// If filename includes & symbol, then system assumes it should be dynamically generated
   
$parameters=array_unique(explode('&',$resourceFile));
// Getting the downloadable file name
   
$resourceFile=array_pop($parameters);
// The amount of non-filenames in the request
   
$parameterCount=count($parameters);
   
   
// Range of bytes to return
    // This allows user agent to request only part of the file
   
if(isset($_SERVER['HTTP_RANGE'])){
       
$tmp=explode('=',$_SERVER['HTTP_RANGE']);
       
$bytesData=explode('-',array_pop($tmp));
        if(isset(
$bytesData) && is_numeric($bytesData[0]) && is_numeric($bytesData[1])){
           
$bytesFrom=$bytesData[0];
           
$bytesTo=$bytesData[1];
        }
    }
// No cache flag
   
$noCache=array_search('nocache',$parameters);
    if(
$noCache!==false){
       
// Unsetting the key for nocache parameter
       
unset($parameters[$noCache]);
    }
   
   
// Web root is the subfolder on public site
   
$webRoot=str_replace('index.php','',$_SERVER['SCRIPT_NAME']);
// Checking if the file might be loaded from overrides folder
   
if(preg_match('/^'.str_replace('/','\/',$webRoot).'resources\//',$_SERVER['REQUEST_URI'])){
       
// Solving possible overrides folder
       
$overridesFolder=str_replace($webRoot.'resources'.DIRECTORY_SEPARATOR,$webRoot.'overrides'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR,$resourceFolder);
        if(
file_exists($overridesFolder.$resourceFile)){
           
$resourceFolder=$overridesFolder;
        }
    }
// Default cache timeout of one month, unless timeout is set
   
if(!isset($config['resource-cache-timeout'])){
       
$config['resource-cache-timeout']=31536000; // A year
   
}
// CHECK FOR PARAMETER SUPPORT
// If more than one parameter is set, it returns 404
    // 404 is also returned if file does not actually exist
   
if($parameterCount>1 || ($parameterCount==1 && !$noCache) || !file_exists($resourceFolder.$resourceFile)){
       
// Adding log entry
       
if(isset($logger)){
           
// Assigning custom log data to logger
           
$logger->setCustomLogData(array('category'=>'file','response-code'=>'404'));
           
// Writing log entry
           
$logger->writeLog();
        }
       
// Returning 404 header
       
header('HTTP/1.1 404 Not Found');
        die();
    }
// Last-modified date
   
$lastModified=filemtime($resourceFolder.$resourceFile);
// NOT MODIFIED CHECK
// Checking if file has been modified or not
   
if(!$noCache){
       
// If the request timestamp is exactly the same, then we let the browser know of this
       
if((isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])==$lastModified) || (isset($_SERVER['HTTP_IF_RANGE']) && strtotime($_SERVER['HTTP_IF_RANGE'])==$lastModified)){
           
// Adding log entry
           
if(isset($logger)){
               
// Assigning custom log data to logger
               
$logger->setCustomLogData(array('cache-used'=>true,'category'=>'image','response-code'=>'304'));
               
// Writing log entry
               
$logger->writeLog();
            }
           
// Cache headers (Last modified is never sent with 304 header)
           
header('Cache-Control: public,max-age='.$config['resource-cache-timeout']);
           
header('Expires: '.gmdate('D, d M Y H:i:s',($_SERVER['REQUEST_TIME']+$config['resource-cache-timeout'])).' GMT');
           
// Returning 304 header
           
header('HTTP/1.1 304 Not Modified');
            die();
        }
    }
// DETECTING MIME TYPE
// Currently assumed MIME type
   
$mimeType='';
   
// Finding the proper MIME type
   
if(extension_loaded('fileinfo')){
       
// This opens MIME type 'magic' resource for use
       
if($fileInfo=finfo_open(FILEINFO_MIME_TYPE)){
           
// Finding MIME type with magic resource
           
$mimeType=finfo_file($fileInfo,$resourceFolder.$resourceFile);
           
// Resourse is not needed further, so it is closed
           
finfo_close($fileInfo);
        }
    } else {
       
// Since Fileinfo was not available, we use extension-based detection as fallback
       
if(isset($resourceExtension)){
            switch(
$resourceExtension){
                case
'ico':
                   
$mimeType='image/vnd.microsoft.icon;';
                    break;
                case
'zip':
                   
$mimeType='application/zip';
                    break;
                case
'pdf':
                   
$mimeType='application/pdf';
                    break;
                case
'mp3':
                   
$mimeType='audio/mpeg';
                    break;
                case
'gif':
                   
$mimeType='image/gif';
                    break;
                case
'tif':
                   
$mimeType='image/tiff';
                    break;
            }
        }
    }
   
// HEADERS
// Assigning MIME type if it was found
   
if($mimeType && $mimeType!=''){
       
// Detected mime type is set as content-type header
       
header('Content-Type: '.$mimeType.';');
    } else {
       
// Octet stream is a general-use unknown resource, and browsers will often attempt to 'download' such a file
       
header('Content-Type: application/octet-stream;');
       
header('Content-Disposition: attachment; filename='.$resourceFile);
    }
       
   
// If cache is used, then proper headers will be sent
   
if($noCache){
       
// User agent is told to cache these results for set duration
       
header('Cache-Control: no-cache,no-store');
       
header('Expires: '.gmdate('D, d M Y H:i:s',$_SERVER['REQUEST_TIME']).' GMT');
       
header('Last-Modified: '.gmdate('D, d M Y H:i:s',$lastModified).' GMT');
    } else {
       
// User agent is told to cache these results for set duration
       
header('Cache-Control: public,max-age='.$config['resource-cache-timeout']);
       
header('Expires: '.gmdate('D, d M Y H:i:s',($_SERVER['REQUEST_TIME']+$config['resource-cache-timeout'])).' GMT');
       
header('Last-Modified: '.gmdate('D, d M Y H:i:s',$lastModified).' GMT');
    }
// Robots header
   
if(isset($config['file-robots'])){
       
// If file-specific robots setting is defined
       
header('Robots-Tag: '.$config['file-robots'],true);
    } elseif(isset(
$config['robots'])){
       
// This sets general robots setting, if it is defined in configuration file
       
header('Robots-Tag: '.$config['robots'],true);
    } else {
       
// If robots setting is not configured, system tells user agent not to cache the file
       
header('Robots-Tag: noindex,nocache,nofollow,noarchive,noimageindex,nosnippet',true);
    }
   
// OUTPUT
// If user agent only requested part of the file to be returned
   
if(isset($bytesFrom,$bytesTo)){
       
// Getting current output length
       
$contentLength=filesize($resourceFolder.$resourceFile);
        if(
$bytesTo<=$contentLength){
           
// Required for range response
           
header('HTTP/1.1 206 Partial Content');
           
header('Content-Range: bytes '.$bytesFrom.'-'.$bytesTo.'/'.$contentLength);
           
// Content length is defined that can speed up website requests, letting user agent to determine file size
           
header('Content-Length: '.($bytesTo-$bytesFrom));
           
// Returning part of the file
           
$fileHandle=fopen($resourceFolder.$resourceFile,'r');
           
fseek($fileHandle,$bytesFrom);
           
// Returning the data to user agent
           
echo fread($fileHandle,($bytesTo-$bytesFrom));
        } else {
           
header('HTTP/1.1 416 Requested Range Not Satisfiable');
        }
    } else {
       
// Getting current output length
       
$contentLength=filesize($resourceFolder.$resourceFile);
       
// Content length is defined that can speed up website requests, letting user agent to determine file size
       
header('Content-Length: '.$contentLength);
       
// Returning the file to user agent
       
readfile($resourceFolder.$resourceFile);
    }
   
// WRITING TO LOG
// If Logger is defined then request is logged and can be used for performance review later
   
if(isset($logger)){
       
// Assigning custom log data to logger
       
$logger->setCustomLogData(array('cache-used'=>false,'category'=>'file','content-length-used'=>$contentLength));
       
// Writing log entry
       
$logger->writeLog();
    }
?>