PHP Classes

File: engine/handler.resource.php

Recommend this page to a friend!
  Classes of Kristo Vaher   Wave Framework   engine/handler.resource.php   Download  
File: engine/handler.resource.php
Role: Application script
Content type: text/plain
Description: Resource 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: 17,017 bytes
 

Contents

Class file image Download
<?php /** * Wave Framework <http://github.com/kristovaher/Wave-Framework> * Resource Handler * * Resource Handler is used to return files that are considered web resources that are not * media. This includes things like JavaScript, CSS stylesheets, XML, HTML and other file * formats (this is based on configuration). Resource Handler uses Wave Frameworks on-demand * resource loader, which allows to combine multiple files to a single resource file or * minify contents of the scripts. It also checks for files from overrides folder, which * can be returned instead of the actual file. * * @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_resource.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']); } // Dynamic resource loading can be turned off in configuration if(!isset($config['dynamic-resource-loading']) || $config['dynamic-resource-loading']==true){ // Comma separated filenames will mean that the result will be unified $parameters=array_unique(explode('&',$resourceFile)); } else { // If dynamic resource loading was turned off, then the entire 'first parameter' is considered to be the full string for parsing purposes $parameters=array(); $parameters[0]=$resourceFile; } // Storing last modified time here $lastModified=false; // This flag stores whether cache was used $cacheUsed=false; // If cache is not defined in configuration file then pre-set is used if(!isset($config['resource-cache-timeout'])){ $config['resource-cache-timeout']=31536000; // A year } // Web root is the subfolder on public site $webRoot=str_replace('index.php','',$_SERVER['SCRIPT_NAME']); // Files from /resources/content/ cannot be accessed directly if(preg_match('/^'.str_replace('/','\/',$webRoot).'resources\/content\//',$_SERVER['REQUEST_URI'])){ header('HTTP/1.1 403 Forbidden'); die(); } // If minification is used for CSS and JS $minify=false; // Default cache flag $noCache=false; // Default BASE64 flag $base64=false; // Checking if the file might be loaded from overrides folder $overridesFolder=false; 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); } // GETTING RESOURCE CONTENTS // If file does not carry any additional parameters, then there is no need to dynamically generate it if(!isset($parameters[1])){ // FILE EXISTENCE CHECK // Testing file name $fileName=explode('.',$parameters[0]); // If file does not exist in regular nor overrides folder if(file_exists($overridesFolder.$parameters[0]) && in_array(array_pop($fileName),$config['resource-extensions'])){ // Getting the last modified time from overrides folder $lastModified=filemtime($overridesFolder.$parameters[0]); // This is the loaded resource $parameters[0]=$overridesFolder.$resourceFile; } elseif((file_exists($resourceFolder.$parameters[0]) && in_array(array_pop($fileName),$config['resource-extensions'])) || $parameters[0]=='class.www-factory.js' || $parameters[0]=='class.www-wrapper.js'){ // Wave Framework files can be loaded unified together with files from /resources/ folders if($parameters[0]=='class.www-factory.js' || $parameters[0]=='class.www-wrapper.js'){ // Wave Framework specific file is stored in engine folder $parameters[0]=__ROOT__.'engine'.DIRECTORY_SEPARATOR.$parameters[0]; // Last modified time of the file stored in overrides folder $lastModified=filemtime($parameters[0]); } else { // This is the loaded resource $parameters[0]=$resourceFolder.$parameters[0]; // Getting the last modified time from the expected resource folder $lastModified=filemtime($parameters[0]); } } else { // Adding log entry if(isset($logger)){ $logger->setCustomLogData(array('category'=>'resource','response-code'=>'404')); $logger->writeLog(); } // Returning 404 header header('HTTP/1.1 404 Not Found'); die(); } // NOT MODIFIED CHECK // 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){ // Adding log entry if(isset($logger)){ $logger->setCustomLogData(array('category'=>'resource','cache-used'=>true,'response-code'=>'304')); $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(); } } else { // Newest last-modified file is considered for the last modified time foreach($parameters as $key=>$parameter){ // PARAMETER CONDITIONS // Cache can be turned off and minification can be turned on with parameters. // Full stop including parameters are also entirely ignored for parameter considerations if($parameter!='nocache' && $parameter!='minify' && $parameter!='base64'){ // MULTIPLE REQUESTED FILES // Possible version number if(is_numeric($parameter)){ // This is possibly a version number unset($parameters[$key]); } else { // Making sure that parent folders are not requested $parameters[$key]=str_replace('..','',$parameter); // Testing file name $fileName=explode('.',$parameters[$key]); // Overrides can be used if file with the same name is stored in same folder under /overrides/ folder if($overridesFolder && file_exists($overridesFolder.$parameter) && in_array(array_pop($fileName),$config['resource-extensions'])){ // File was found and the filename will be replaced by file location for later processing $parameters[$key]=$overridesFolder.$parameter; // Last modified time of the file stored in overrides folder $thisLastModified=filemtime($overridesFolder.$parameter); // Only the newest last modified time will be used for output headers if($lastModified==false || $lastModified<$thisLastModified){ $lastModified=$thisLastModified; } } elseif((file_exists($resourceFolder.$parameter) && in_array(array_pop($fileName),$config['resource-extensions'])) || $parameter=='class.www-factory.js' || $parameter=='class.www-wrapper.js'){ if($parameter=='class.www-factory.js' || $parameter=='class.www-wrapper.js'){ // Wave Framework specific file is stored in engine folder $parameters[$key]=__ROOT__.'engine'.DIRECTORY_SEPARATOR.$parameter; // Last modified time of the file stored in overrides folder $thisLastModified=filemtime($parameters[$key]); } else { // File was found and the filename will be replaced by file location for later processing $parameters[$key]=$resourceFolder.$parameter; // Last modified time of the file stored in overrides folder $thisLastModified=filemtime($parameters[$key]); } // Only the newest last modified time will be used for output headers if($lastModified==false || $lastModified<$thisLastModified){ $lastModified=$thisLastModified; } } else { // Adding log entry if(isset($logger)){ $logger->setCustomLogData(array('category'=>'resource','response-code'=>'404')); $logger->writeLog(); } // Returning 404 header header('HTTP/1.1 404 Not Found'); die(); } } } elseif($parameter=='minify'){ // This will use minify for CSS and JS files $minify=true; // Unsetting the parameter as it will not be used later unset($parameters[$key]); } elseif($parameter=='nocache'){ // Caching $noCache=true; // Unsetting the parameter as it will not be used later unset($parameters[$key]); } elseif($parameter=='base64'){ // Caching $base64=true; // Unsetting the parameter as it will not be used later unset($parameters[$key]); } else { // Adding log entry if(isset($logger)){ $logger->setCustomLogData(array('category'=>'resource','response-code'=>'404')); $logger->writeLog(); } // Returning 404 header header('HTTP/1.1 404 Not Found'); die(); } } } // COMPRESSION SETTINGS // This stores currently used compression mode $compression=''; // If output compression is turned on then the content is compressed if((isset($config['output-compression']) && $config['output-compression']!=false) && extension_loaded('Zlib')){ // Different compression options can be used switch($config['output-compression']){ case 'deflate': $compression='deflate'; break; case 'gzip': $compression='gzip'; break; } } elseif(extension_loaded('Zlib')){ // User agent accepted methods are checked when compression is not set in configuration itself if(isset($_SERVER['HTTP_ACCEPT_ENCODING'])){ if(in_array('deflate',explode(',',$_SERVER['HTTP_ACCEPT_ENCODING']))){ $compression='deflate'; } elseif(in_array('gzip',explode(',',$_SERVER['HTTP_ACCEPT_ENCODING']))){ $compression='gzip'; } } } // CACHE AND NOT MODIFIED SETTINGS // Solving cache folders and directory $cacheFilename=md5($lastModified.'&'.$config['version-system'].'&'.$config['version-api'].'&'.$_SERVER['REQUEST_URI']).(($compression!='')?'_'.$compression:'').'.tmp'; $cacheDirectory=__ROOT__.'filesystem'.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.substr($cacheFilename,0,2).DIRECTORY_SEPARATOR; // If cache file exists then cache modified is considered that time if(file_exists($cacheDirectory.$cacheFilename)){ $lastModified=filemtime($cacheDirectory.$cacheFilename); } else { // Otherwise it is server request time $lastModified=$_SERVER['REQUEST_TIME']; } // 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){ // Adding log entry if(isset($logger)){ $logger->setCustomLogData(array('category'=>'resource','cache-used'=>true,'response-code'=>'304')); $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(); } // GENERATING RESOURCE // If resource cannot be found from cache, it is generated if($noCache || ($lastModified==$_SERVER['REQUEST_TIME'] || $lastModified<($_SERVER['REQUEST_TIME']-$config['resource-cache-timeout']))){ // LOADING CONTENTS // Resource data is stored as a string $data=''; // All requested files are appended foreach($parameters as $parameter){ // Loading data into string. $data.=file_get_contents($parameter)."\n"; } // MINIFICATION AND COMPRESSION // Minification of data for smaller filesize and less clutter. if($minify){ // Including minification class require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'class.www-minifier.php'); // Minification is based on the type of class switch($resourceExtension){ case 'js': $data=WWW_Minifier::minifyJS($data); break; case 'css': $data=WWW_Minifier::minifyCSS($data); break; case 'xml': $data=WWW_Minifier::minifyXML($data); break; case 'htm': $data=WWW_Minifier::minifyHTML($data); break; case 'html': $data=WWW_Minifier::minifyHTML($data); break; case 'rss': $data=WWW_Minifier::minifyXML($data); break; } } // If data is requested in BASE64 encoding if($base64){ $data=base64_encode($data); } // Data is compressed based on current compression settings switch($compression){ case 'deflate': $data=gzdeflate($data,9); break; case 'gzip': $data=gzencode($data,9); break; } // STORING IN CACHE // Resource cache is cached in subdirectories, if directory does not exist then it is created if(!is_dir($cacheDirectory)){ if(!mkdir($cacheDirectory,0755)){ trigger_error('Cannot create cache folder',E_USER_ERROR); } } // Data is written to cache file if(!file_put_contents($cacheDirectory.$cacheFilename,$data)){ trigger_error('Cannot create resource cache',E_USER_ERROR); } // Unsetting the variable due to memory reasons unset($data); } else { // Logger is notified that cache was used $cacheUsed=true; } // HEADERS // Serving up the correct content type header if($base64){ // BASE64 text string header('Content-Type: application/octet-stream'); header('Content-Transfer-Encoding: base64'); } else { // System returns proper content type based on file extension if(isset($resourceExtension)){ switch($resourceExtension){ case 'js': header('Content-Type: application/javascript;charset=utf-8;'); break; case 'css': header('Content-Type: text/css;charset=utf-8;'); break; case 'xml': header('Content-Type: text/xml;charset=utf-8;'); break; case 'txt': header('Content-Type: text/plain;charset=utf-8;'); break; case 'csv': header('Content-Type: text/csv;charset=utf-8;'); break; case 'html': header('Content-Type: text/html;charset=utf-8;'); break; case 'htm': header('Content-Type: text/html;charset=utf-8;'); break; case 'rss': header('Content-Type: application/rss+xml;charset=utf-8;'); break; case 'vcard': header('Content-Type: text/vcard;charset=utf-8;'); break; default: header('Content-Type: text/plain;charset=utf-8;'); break; } } } // 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'); } // This tells proxies to store both compressed and uncompressed version header('Vary: Accept-Encoding'); // Proper compression header if($compression!=''){ header('Content-Encoding: '.$compression); } // Robots header if(isset($config['resource-robots'])){ // If resource-specific robots setting is defined header('Robots-Tag: '.$config['resource-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 // Getting current output length $contentLength=filesize($cacheDirectory.$cacheFilename); // Content length is defined that can speed up website requests, letting user agent to determine file size header('Content-Length: '.$contentLength); // Returning the file contents to user agent readfile($cacheDirectory.$cacheFilename); // File is deleted if cache was requested to be off if($noCache){ unlink($cacheDirectory.$cacheFilename); } // 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'=>$cacheUsed,'category'=>'resource','content-length'=>$contentLength)); // Writing log entry $logger->writeLog(); } ?>