Login   Register  
PHP Classes
elePHPant
Icontem

File: index.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Kristo Vaher  >  Wave Framework  >  index.php  >  Download  
File: index.php
Role: Application script
Content type: text/plain
Description: Index Gateway
Class: Wave Framework
MVC framework for building Web sites and APIs
Author: By
Last change: Made session starting configurations possible to be changed at runtime.
Updated documentation with indexes and anchors.
Added jQuery 2.0 as an alternative - to use the new version of jQuery that ignores Internet Explorer up to and including 8, then change jquery.js file to jquery2.js in the View Controller.
Developer tools Debugger and Log Reader for internal log messages are now browser IP and session-based, allowing to separate messages from one another based on client that makes the request.
Added a warning to developer tools when using the default password.
Fixed bug with tester script that did not properly autoload certain PHP scripts.
Implemented an improved getState() method that allows to return data better from nested arrays.
Implemented an improved setState() that allows to set nested array values in State more easily.
PNG images will also now get a background, if RGB setting is set in the dynamic image loading.
Date: 2013-05-21 01:44
Size: 17,839 bytes
 

Contents

Class file image Download
<?php

/**
 * Wave Framework <http://www.waveframework.com>
 * Index Gateway
 *
 * Index Gateway is an index/bootstrap file of Wave Framework that will serve almost every HTTP 
 * request made to the system built on Wave Framework. It analyzes the HTTP request, loads Logger 
 * and configuration as well as HTTP request Limiter, overwrites error handler of PHP and then 
 * executes the command through one of the request handlers that are stored in 
 * /engine/handler.[handler-type].php files.
 *
 * @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/gateway.htm
 * @since      1.0.0
 * @version    3.6.0
 */

// SOLVING THE HTTP REQUEST

	// For performance logging
	$microTime=microtime(true);

	// Custom error-handling and reporting is used
	// Make sure to check for errors with /tools/debugger.php script (especially when you encounter a critical error page)
	error_reporting(0);

	// Getting resource without GET string
	$tmp=explode('?',$_SERVER['REQUEST_URI']);
	$resourceAddress=array_shift($tmp);

	// Stopping all direct requests to Index Gateway
	if($resourceAddress==$_SERVER['SCRIPT_NAME']){
		header('HTTP/1.1 403 Forbidden');
		die();
	}

	// Currently known location of the file in filesystem
	// Double replacement occurs since some environments give document root with the slash in the end, some don't (like Windows)
	$resourceRequest=str_replace(DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR,DIRECTORY_SEPARATOR,$_SERVER['DOCUMENT_ROOT'].$resourceAddress);
	
	// If requested URL does not point to a directory, then request is possibly made to a file
	if(!is_dir($resourceRequest)){
		// Getting directory, filename and extension information about current resource address
		$resourceInfo=pathinfo($resourceRequest);
		// Solving the folder that user agent is loading resource from
		$resourceFolder=$resourceInfo['dirname'].DIRECTORY_SEPARATOR;
		// Assigning file information
		$resourceFile=$resourceInfo['basename'];
		// If extension was detected then this too is used
		if(isset($resourceInfo['extension'])){
			$resourceExtension=$resourceInfo['extension'];
		}
	}
	
// LOADING CONFIGURATION

	// Defining root directory, this is required by handlers in /engine/ subfolder
	define('__ROOT__',__DIR__.DIRECTORY_SEPARATOR);

	//Configuration is stored in this array, it has to be defined even if no configuration is loaded
	$config=array();

	// Including the configuration
	if(!file_exists(__ROOT__.'filesystem'.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'config.tmp') || filemtime(__ROOT__.'config.ini')>filemtime(__ROOT__.'filesystem'.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'config.tmp')){
		
		// Configuration is parsed from INI file in the root of the system
		$config=parse_ini_file(__ROOT__.'config.ini',false,INI_SCANNER_RAW);
		
		// List of logger IP's
		if(isset($config['logger-ip'])){
			$config['logger-ip']=explode(',',$config['logger-ip']);
		}
		
		// List of languages
		if(isset($config['languages'])){
			$config['languages']=explode(',',$config['languages']);
		}
		
		// Internal logging flags
		if(isset($config['internal-logging'])){
			$config['internal-logging']=explode(',',$config['internal-logging']);
		}
		
		// API logging settings
		if(isset($config['api-logging'])){
			$config['api-logging']=explode(',',$config['api-logging']);
		}
		
		// File extensions and defaults
		if(isset($config['image-extensions'])){
			$config['image-extensions']=explode(',',$config['image-extensions']);
		} else {
			$config['image-extensions']=array('jpeg','jpg','png');
		}
		if(isset($config['resource-extensions'])){
			$config['resource-extensions']=explode(',',$config['resource-extensions']);
		} else {
			$config['resource-extensions']=array('css','js','txt','csv','xml','html','htm','rss','vcard','appcache');
		}
		if(isset($config['forbidden-extensions'])){
			$config['forbidden-extensions']=explode(',',$config['forbidden-extensions']);
		} else {
			$config['forbidden-extensions']=array('tmp','log','ht','htaccess','pem','crt','db','sql','version','conf','ini','empty');
		}
	
		// Required timezone setting
		if(!isset($config['timezone'])){
			// Setting GMT as the default timezone
			$config['timezone']='Europe/London';
		}
		
		// If version number is not set
		if(!isset($config['version'])){
			$config['version']='1.0.0';
		}
	
		// Trusted proxies and IP address
		if(isset($config['trusted-proxies'])){
			$config['trusted-proxies']=explode(',',$config['trusted-proxies']);
		} else {
			$config['trusted-proxies']=array('*');
		}
	
		// Trusted proxies and IP address
		if(isset($config['session-fingerprint'])){
			$config['session-fingerprint']=explode(',',$config['session-fingerprint']);
		}
		
		// Cache of parsed INI file is stored for later use
		if(!file_put_contents(__ROOT__.'filesystem'.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'config.tmp',serialize($config))){
			header('HTTP/1.1 500 Internal Server Error');
			echo '<h1>HTTP/1.1 500 Internal Server Error</h1>';
			echo '<p>Cannot write cache in filesystem, please make sure filesystem folders are writable.</p>';
		}
		
	} else {
		// Since INI file has not been changed, configuration is loaded from cache
		$config=unserialize(file_get_contents(__ROOT__.'filesystem'.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'config.tmp'));
	}
	
	// Setting the timezone
	date_default_timezone_set($config['timezone']);
	
	// IP may be forwarded (such as when website is used through a proxy), this can check for such an occasion
	if(isset($_SERVER['HTTP_CLIENT_IP']) && (in_array('*',$config['trusted-proxies']) || in_array($_SERVER['REMOTE_ADDR'],$config['trusted-proxies']))){
		$tmp=explode(',',$_SERVER['HTTP_CLIENT_IP']);
		define('__IP__',trim(array_pop($tmp)));
	} elseif(isset($_SERVER['HTTP_X_FORWARDED_FOR']) && (in_array('*',$config['trusted-proxies']) || in_array($_SERVER['REMOTE_ADDR'],$config['trusted-proxies']))){
		$tmp=explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']);
		define('__IP__',trim(array_pop($tmp)));
	} else {
		define('__IP__',$_SERVER['REMOTE_ADDR']);
	}
	
// LOADING LOGGER

	// Logger file is used for performance logging for later review
	// Configuration file can set what type of logging is used
	if(isset($config['logger']) && $config['logger']!=false && (!isset($config['logger-ip']) || in_array('*',$config['logger-ip']) || in_array(__IP__,$config['logger-ip'])) && ($config['logger']=='*' || preg_match($config['logger'],$_SERVER['REQUEST_URI']))){
		require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'class.www-logger.php');
		$logger=new WWW_Logger(__ROOT__.'filesystem'.DIRECTORY_SEPARATOR.'logs'.DIRECTORY_SEPARATOR,$microTime);
	}
	
// LOADING HTTP REQUEST LIMITER

	// If limiter is configured to be used
	if(isset($config['limiter']) && $config['limiter']){

		// Limiter is used to block requests under specific conditions, like DOS attacks or when server load is too high
		require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'class.www-limiter.php');
		$limiter=new WWW_Limiter(__ROOT__.'filesystem'.DIRECTORY_SEPARATOR.'limiter'.DIRECTORY_SEPARATOR);
		
		// Assigning logger to Limiter
		// Logger is used to output log data in case Limiter stops the script pre-maturely
		if(isset($logger)){
			$limiter->logger=$logger;
		}
		// Load limiter blocks access if server load is detected to be too high at the moment of request
		if(isset($config['limiter-load']) && $config['limiter-load']){
			$limiter->limitServerLoad($config['limiter-load']);
		}
		// Load limiter allows access for certain IP's or blocks access to specific blacklist of IP's
		if(isset($config['limiter-whitelist']) && $config['limiter-whitelist']){
			$limiter->limitWhitelisted($config['limiter-whitelist']);
		} elseif(isset($config['limiter-blacklist']) && $config['limiter-blacklist']){
			$limiter->limitBlacklisted($config['limiter-blacklist']);
		}
		// If HTTPS limiter is used, the ststem returns a 401 error if the user agent attempts to access the site without HTTPS
		if(isset($config['limiter-https']) && $config['limiter-https']){
			$limiter->limitNonSecureRequests(); // By default the user agent is redirected to HTTPS address of the same request
		}
		// If HTTP authentication is turned on, the system checks for credentials and returns 401 if failed
		if(isset($config['limiter-authentication']) && $config['limiter-authentication']){
			$limiter->limitUnauthorized($config['http-authentication-username'],$config['http-authentication-password'],((isset($config['http-authentication-ip']))?$config['http-authentication-ip']:'*'));
		}
		// Request limiter keeps track of how many requests per minute are allowed on IP.
		// If limit is exceeded, then IP is blocked for an hour.
		if(isset($config['limiter-request']) && $config['limiter-request']){
			$limiter->limitRequestCount($config['limiter-request']);
		}
		// Referrer limiter checks if the Referrer URL is allowed or not
		if(isset($config['limiter-referrer']) && $config['limiter-referrer']){
			$limiter->limitReferrer($config['limiter-referrer']);
		}

	}
	
// ERROR AND EXIT HANDLING

	// This error handler replaces default PHP error handler and is tied to Exception class
	function WWW_exitHandler($type=false,$message=false,$file=false,$line=false){
	
		// if this is called through error handler
		if($message){
			$errorCheck=array();
			$errorCheck['type']=$type;
			$errorCheck['message']=$message;
			$errorCheck['file']=$file;
			$errorCheck['line']=$line;
		} else {
			// Getting the last thrown error, if any
			$errorCheck=error_get_last();
		}
		
		// If there is an error and it is not a deprecated Line 0 error (which sporadically is thrown in PHP 5.3.4)
		if($errorCheck && ($errorCheck['line']!=0 || $errorCheck['type']<E_DEPRECATED)){
		
			global $config;
		
			// Detecting if error is fatal - thus if error message should be shown to the user
			$fatalError=false;
			if(in_array($errorCheck['type'],array(E_ERROR,E_USER_ERROR,E_CORE_ERROR,E_PARSE))){
				$fatalError=true;
			}
					
			// Error report will be stored in this array
			$errorReport=array();
			// Setting GMT as the error-reporting timezone
			$errorReport[]=date('d.m.Y H:i:s');
			
			// Input data to error report
			$error=array();
			if(!empty($_GET)){
				$error['get']=$_GET;
			}
			if(!empty($_POST)){
				$error['post']=$_POST;
			}
			if(!empty($_FILES)){
				$error['files']=$_FILES;
			}
			if(!empty($_COOKIE)){
				$error['cookies']=$_COOKIE;
			}
			if(!empty($_SERVER)){
				$error['server']=$_SERVER;
			}
			// Add to error array
			$errorReport[]=$error;
			
			//Adding backtrace
			if(isset($config['trace-errors']) && $config['trace-errors']==1){
				$errorReport[]=debug_backtrace();
			}
			
			// Writing current error and file to the array as well
			$error=array();
			$error['url']=$_SERVER['REQUEST_URI'];
			$error['type']=$errorCheck['type'];
			$error['file']=$errorCheck['file'];
			$error['line']=$errorCheck['line'];
			$error['message']=$errorCheck['message'];
			$errorReport[]=$error;
			
			// This is the signature used for storing developer sessions
			$signatureFolder=md5($_SERVER['REMOTE_ADDR'].' '.$_SERVER['HTTP_USER_AGENT']);
			
			// If error folder does not yet exist
			if(!is_dir(__ROOT__.'filesystem'.DIRECTORY_SEPARATOR.'errors'.DIRECTORY_SEPARATOR.$signatureFolder.DIRECTORY_SEPARATOR)){
				if(!mkdir(__ROOT__.'filesystem'.DIRECTORY_SEPARATOR.'errors'.DIRECTORY_SEPARATOR.$signatureFolder.DIRECTORY_SEPARATOR,0755)){
					trigger_error('Cannot create error folder',E_USER_ERROR);
				}
				file_put_contents(__ROOT__.'filesystem'.DIRECTORY_SEPARATOR.'errors'.DIRECTORY_SEPARATOR.$signatureFolder.DIRECTORY_SEPARATOR.'signature.tmp',$_SERVER['REMOTE_ADDR'].' '.$_SERVER['HTTP_USER_AGENT']);
			}
			
			// Logging the error, the error filename is calculated from current error message (this makes sure there are no duplicates, if the error message is the same).
			file_put_contents(__ROOT__.'filesystem'.DIRECTORY_SEPARATOR.'errors'.DIRECTORY_SEPARATOR.$signatureFolder.DIRECTORY_SEPARATOR.md5($error['file'].$error['message']).'.tmp',json_encode($errorReport)."\n",FILE_APPEND);

			// As long as the error level is set to display errors of this type				
			if($fatalError){
			
				// Cleaning output buffer, if it exists
				if(ob_get_level()>=1){
					ob_end_clean();
				}

				// There was an error in code
				// System returns 500 header even as a server error (possible bug in code)
				header('HTTP/1.1 500 Internal Server Error');
				
				// Regular users will be shown a friendly error message
				if(isset($config['verbose-errors']) && $config['verbose-errors']==1){
					echo '<div style="font:18px Tahoma; text-align:center;padding:100px 50px 10px 50px;">CRITICAL ERROR ENCOUNTERED</div>';
					echo '<div style="font:12px Tahoma;width:500px;margin:auto;padding:5px 50px 5px 50px;"><b>TYPE</b>: '.htmlspecialchars($errorCheck['type']).'</div>';
					echo '<div style="font:12px Tahoma;width:500px;margin:auto;padding:5px 50px 5px 50px;"><b>FILE</b>: '.htmlspecialchars($errorCheck['file']).'</div>';
					echo '<div style="font:12px Tahoma;width:500px;margin:auto;padding:5px 50px 5px 50px;"><b>LINE</b>: '.htmlspecialchars($errorCheck['line']).'</div>';
					echo '<div style="font:12px Tahoma;width:500px;margin:auto;padding:5px 50px 5px 50px;"><b>MESSAGE</b>: '.htmlspecialchars($errorCheck['message']).'</div>';
					if(isset($config['trace-errors']) && $config['trace-errors']==1){
						echo '<div style="font:14px Tahoma; text-align:center;padding:10px 50px 100px 50px;">FULL STACK TRACE AVAILABLE FROM DEBUGGER SCRIPT</div>';
					} else {
						echo '<div style="font:14px Tahoma; text-align:center;padding:10px 50px 100px 50px;">FULL STACK TRACE IS NOT LOGGED BY DEBUGGER</div>';
					}
				} else {
					echo '<div style="font:18px Tahoma; text-align:center;padding:100px 50px 10px 50px;">WE ARE CURRENTLY EXPERIENCING A PROBLEM WITH YOUR REQUEST</div>';
					echo '<div style="font:14px Tahoma; text-align:center;padding:10px 50px 100px 50px;">ERROR HAS BEEN LOGGED FOR FURTHER INVESTIGATION</div>';
				}
				
				// Closing the entire request
				die();
				
			}
			
		}
		
	}
	
	// Setting the error handler
	set_error_handler('WWW_exitHandler',E_ALL);
	register_shutdown_function('WWW_exitHandler'); 
	
// LOADING HANDLERS

	// Index Gateway works differently based on what file is being requested
	// Handlers for all different modes are stored under /engine/ subfolder

	// request has a file extension, then system will attempt to use another handler
	if(isset($resourceExtension)){

		// Handler is detected based on requested file extension
		if(in_array($resourceExtension,$config['image-extensions'])){
		
			// Image Handler allows for things such as dynamic image loading
			require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'handler.image.php');
			
		} elseif(in_array($resourceExtension,$config['resource-extensions'])){
		
			// Text-based resources are handled by Resource Handler, except for two special cases (robots.txt and sitemap.xml)
			if($resourceFile=='sitemap.xml'){
				// Sitemap is dynamically generated from sitemap files in /resource/ subfolder
				require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'handler.sitemap.php');
			} elseif($resourceFile=='robots.txt'){
				// Robots file is dynamically generated based on 'robots' configuration in config.php file
				require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'handler.robots.php');
			} elseif($resourceExtension=='appcache'){
				// Appcache settings can be dynamically generated
				require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'handler.appcache.php');
			} else {
				// In every other case the system loads text based resources with additional options, such as compressions and minifying, with Resource Handler
				require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'handler.resource.php');
			}
			
		} elseif(in_array($resourceExtension,$config['forbidden-extensions'])){
		
			// These file extensions are not allowed, thus 403 error is returned
			// Log category is 'file' due to it being a file with an extension
			if(isset($logger)){
				$logger->setCustomLogData(array('response-code'=>403,'category'=>'file'));
				$logger->writeLog();
			}
			
			// Returning 403 header
			header('HTTP/1.1 403 Forbidden');
			die();
			
		} elseif($resourceExtension=='api'){
			
			// Replacing the extension in the request to find handler filename
			$apiHandler=str_replace('.api','',$resourceFile);
			
			// If the file exists then system loads the new API, otherwise 404 is returned
			if(file_exists(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'handler.api-'.$apiHandler.'.php')){
									
				// Custom API files need to be placed in engine subfolder
				// To see how the default API is built, take a look at handler.api.php
				require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'handler.api-'.$apiHandler.'.php');
				
			} else {
			
				// This allows API filename to define what type of data should be returned
				if($apiHandler=='www'){
					if(!isset($_GET['www-return-type'])){
						$_GET['www-return-type']='php';
					}
				} elseif($apiHandler!='json' && $apiHandler!='www'){
					$_GET['www-return-type']=$apiHandler;
				}
				require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'handler.api.php');
			
			}
			
		} else {
			// File Handler is loaded for every other file request case
			require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'handler.file.php');
		}
		
	} else {
		// Every other request is handled by Data Handler, which loads URL and View controllers for website views
		require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'handler.data.php');
	}

?>