PHP Classes

File: engine/class.www-state.php

Recommend this page to a friend!
  Classes of Kristo Vaher   Wave Framework   engine/class.www-state.php   Download  
File: engine/class.www-state.php
Role: Class source
Content type: text/plain
Description: State Class
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: 10 days ago
Size: 67,694 bytes
 

Contents

Class file image Download
<?php /** * Wave Framework <http://github.com/kristovaher/Wave-Framework> * State Class * * State is always required by Wave Framework. It is used by API and some handlers. State is used * to keep track of system state, configuration and its changes, such as relevant PHP settings. * It allows changing these settings, and thus affecting API or PHP configuration. State also * includes functionality for State Messenger, sessions, cookies, translations and sitemap data. * State is assigned in API and is accessible in MVC objects through Factory wrapper methods. * Multiple different states can be used by the same request, but usually just one is used per * request. State is only kept for the duration of the request processing and is not stored * beyond its use in the request. * * @package State * @author Kristo Vaher <kristo@waher.net> * @copyright Copyright (c) 2012, Kristo Vaher * @license GNU Lesser General Public License Version 3 * @tutorial /doc/pages/state.htm * @since 1.0.0 * @version 3.7.0 */ class WWW_State { /** * This is the primary variable of State class and it carries representation of the system * configuration, both loaded from /config.ini file as well as initialized from environmental * server variables. */ public $data=array(); /** * This should hold Database class and connection data, if used. */ public $databaseConnection=false; /** * This holds the session handler for session management. This is a * WWW_Sessions class from /resources/sessions.php file. */ public $sessionHandler=false; /** * This is an internal variable that State uses to check if it has already validated sessions * or not. */ private $sessionStarted=false; /** * This holds the 'keyword' or 'passkey' of currently used State messenger. */ private $messenger=false; /** * This holds state messenger data as an array. */ private $messengerData=array(); /** * This holds system Tool Class object, if it exists. */ private $tools=false; /** * Construction of State object initializes the defaults for $data variable. A lot of the * data is either loaded from /config.ini file or initialized based on server environment * variables. Fingerprint string is also created during construction as well as input data * loaded from XML or JSON strings, if sent with POST directly. * * @param array $config configuration array * @return WWW_State */ final public function __construct($config=array()){ // PRE-DEFINED STATE VALUES // Required constants if(!defined('__IP__')){ define('__IP__',$_SERVER['REMOTE_ADDR']); } if(!defined('__ROOT__')){ define('__ROOT__',__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR); } // A lot of default State variables are loaded from PHP settings, others are simply pre-defined // Every setting from core configuration file is also listed here $this->data=array( '404-image-placeholder'=>true, '404-view'=>'404', 'access-control'=>false, 'apc'=>0, 'api-logging'=>array('*','!public'), 'api-permissions'=>array('*'), 'api-profile'=>'public', 'api-public-profile'=>'public', 'api-public-token'=>false, 'api-versions'=>array('v1'), 'cache-database'=>false, 'cache-database-address-column'=>'address', 'cache-database-data-column'=>'data', 'cache-database-errors'=>true, 'cache-database-host'=>'localhost', 'cache-database-name'=>'', 'cache-database-password'=>'', 'cache-database-persistent'=>false, 'cache-database-table-name'=>'cache', 'cache-database-timestamp-column'=>'timestamp', 'cache-database-type'=>false, 'cache-database-username'=>'', 'client-ip'=>__IP__, 'client-user-agent'=>((isset($_SERVER['HTTP_USER_AGENT']))?$_SERVER['HTTP_USER_AGENT']:''), 'content-security-policy'=>false, 'database-errors'=>true, 'database-host'=>'localhost', 'database-name'=>'', 'database-password'=>'', 'database-persistent'=>false, 'database-type'=>'mysql', 'database-username'=>'', 'developer'=>false, 'developer-ip'=>'', 'developer-user-agent'=>'', 'directory-data'=>false, 'directory-filesystem'=>false, 'directory-keys'=>false, 'directory-static'=>false, 'directory-system'=>str_replace('engine'.DIRECTORY_SEPARATOR.'class.www-state.php','',__FILE__), 'directory-tmp'=>false, 'directory-user'=>false, 'dynamic-color-whitelist'=>'', 'dynamic-filter-whitelist'=>'', 'dynamic-image-filters'=>true, 'dynamic-image-loading'=>true, 'dynamic-max-size'=>1000, 'dynamic-position-whitelist'=>'', 'dynamic-quality-whitelist'=>'', 'dynamic-resource-loading'=>true, 'dynamic-size-whitelist'=>'', 'enforce-first-language-url'=>true, 'enforce-url-end-slash'=>true, 'errors-reporting'=>'full', 'errors-trace'=>false, 'errors-verbose'=>false, 'file-robots'=>'noindex,nocache,nofollow,noarchive,noimageindex,nosnippet', 'fingerprint'=>false, 'forbidden-extensions'=>array('tmp','log','ht','htaccess','pem','crt','db','sql','version','conf','ini','empty'), 'frame-permissions'=>'', 'headers-set'=>array(), 'headers-unset'=>array(), 'home-view'=>'home', 'http-accept'=>((isset($_SERVER['HTTP_ACCEPT']))?explode(',',$_SERVER['HTTP_ACCEPT']):''), 'http-accept-charset'=>((isset($_SERVER['HTTP_ACCEPT_CHARSET']))?explode(',',$_SERVER['HTTP_ACCEPT_CHARSET']):array()), 'http-accept-encoding'=>((isset($_SERVER['HTTP_ACCEPT_ENCODING']))?explode(',',$_SERVER['HTTP_ACCEPT_ENCODING']):array()), 'http-accept-language'=>((isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))?explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']):array()), 'http-authentication-ip'=>'*', 'http-authentication-password'=>'', 'http-authentication-username'=>'', 'http-do-not-track'=>((isset($_SERVER['HTTP_DNT']) && $_SERVER['HTTP_DNT']==1)?true:false), 'http-content-length'=>((isset($_SERVER['CONTENT_LENGTH']))?$_SERVER['CONTENT_LENGTH']:false), 'http-content-type'=>((isset($_SERVER['CONTENT_TYPE']))?$_SERVER['CONTENT_TYPE']:false), 'http-host'=>$_SERVER['HTTP_HOST'], 'http-if-modified-since'=>false, 'http-input'=>false, 'http-referrer'=>((isset($_SERVER['HTTP_REFERER']))?$_SERVER['HTTP_REFERER']:false), 'http-request-method'=>$_SERVER['REQUEST_METHOD'], 'https-mode'=>(((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS']==1 || $_SERVER['HTTPS']=='on')) || (isset($_SERVER['SCRIPT_URI']) && strpos($_SERVER['SCRIPT_URI'],'https://')!==false))?true:false), 'image-extensions'=>array('jpeg','jpg','png'), 'image-robots'=>'noindex,nocache,nofollow,noarchive,noimageindex,nosnippet', 'index-url-cache-timeout'=>0, 'index-view-cache-timeout'=>0, 'internal-logging'=>array('*','!input-data','!output-data'), 'language'=>false, 'languages'=>array('en'), 'limiter'=>false, 'limiter-authentication'=>false, 'limiter-blacklist'=>false, 'limiter-https'=>false, 'limiter-load'=>false, 'limiter-referrer'=>'*', 'limiter-request'=>false, 'limiter-whitelist'=>false, 'locale'=>setlocale(LC_ALL,0), 'memcache'=>false, 'memcache-host'=>'localhost', 'memcache-port'=>11211, 'memory-limit'=>false, 'output-compression'=>'deflate', 'project-author'=>false, 'project-copyright'=>false, 'project-title'=>false, 'request-id'=>((isset($_SERVER['UNIQUE_ID']))?$_SERVER['UNIQUE_ID']:false), 'request-time'=>$_SERVER['REQUEST_TIME'], 'request-true'=>false, 'request-uri'=>$_SERVER['REQUEST_URI'], 'resource-cache-timeout'=>31536000, 'resource-extensions'=>array('css','js','txt','csv','xml','html','htm','rss','vcard'), 'resource-robots'=>'noindex,nocache,nofollow,noarchive,noimageindex,nosnippet', 'robots'=>'noindex,nocache,nofollow,noarchive,noimageindex,nosnippet', 'robots-cache-timeout'=>14400, 'server-ip'=>((isset($_SERVER['SERVER_ADDR']))?$_SERVER['SERVER_ADDR']:false), 'server-name'=>((isset($_SERVER['SERVER_NAME']))?$_SERVER['SERVER_NAME']:false), 'server-port'=>((isset($_SERVER['SERVER_PORT']))?$_SERVER['SERVER_PORT']:false), 'session-data'=>array(), 'session-domain'=>false, 'session-fingerprint'=>array(), 'session-fingerprint-key'=>'www-fingerprint', 'session-http-only'=>true, 'session-id'=>false, 'session-lifetime'=>0, 'session-name'=>'WWW'.crc32(__ROOT__), 'session-original-data'=>array(), 'session-path'=>false, 'session-permissions-key'=>'www-permissions', 'session-regenerate'=>0, 'session-secure'=>false, 'session-timestamp-key'=>'www-timestamp', 'session-token-key'=>'www-public-token', 'session-user-key'=>'www-user', 'sitemap'=>array(), 'sitemap-cache-timeout'=>14400, 'sitemap-raw'=>array(), 'storage'=>array(), 'test-database-host'=>false, 'test-database-name'=>false, 'test-database-password'=>false, 'test-database-type'=>false, 'test-database-username'=>false, 'testing'=>false, 'time-limit'=>false, 'timezone'=>false, 'translations'=>array(), 'trusted-proxies'=>array('*'), 'url-base'=>false, 'url-web'=>str_replace('index.php','',$_SERVER['SCRIPT_NAME']), 'user-data'=>false, 'user-permissions'=>false, 'version-api'=>'v1', 'version-system'=>'1.0.0', 'version-www'=>'1.0.0', 'view'=>array(), 'view-headers'=>array() ); // ASSIGNING STATE FROM CONFIGURATION FILE // If array of configuration data is set during object creation, it is used // This loops over all the configuration options from /config.ini file through setState() function // That function has key-specific functionality that can be tied to some internal commands and PHP functions if(!empty($config)){ $this->setState(array($config)); } // CHECKING FOR SERVER OR PHP SPECIFIC CONFIGURATION OPTIONS AND ASSIGNING UNSET CONFIGURATIONS // Removing full stop from the beginning of both directory URL's if($this->data['url-web'][0]=='.'){ $this->data['url-web'][0]=''; } // If request ID is not set if(!$this->data['request-id']){ $this->data['request-id']=md5(uniqid('www',true).microtime().$this->data['request-uri'].$this->data['request-time'].$this->data['client-user-agent'].$this->data['client-ip']); } // If default session cookie domain is not set if(!$this->data['session-domain']){ $this->data['session-domain']=$this->data['http-host']; } // If default session cookie path is not set if(!$this->data['session-path']){ $this->data['session-path']=$this->data['url-web']; } // Finding base URL if(!$this->data['url-base']){ $this->data['url-base']=(($this->data['https-mode'])?'https://':'http://').$this->data['http-host'].$this->data['url-web']; } // Defining default user root folder if(!$this->data['directory-filesystem']){ $this->data['directory-filesystem']=$this->data['directory-system'].'filesystem'.DIRECTORY_SEPARATOR; } // Defining default user root folder if(!$this->data['directory-user']){ $this->data['directory-user']=$this->data['directory-filesystem'].'userdata'.DIRECTORY_SEPARATOR; } // Defining default user root folder if(!$this->data['directory-data']){ $this->data['directory-data']=$this->data['directory-filesystem'].'data'.DIRECTORY_SEPARATOR; } // Defining default static folder if(!$this->data['directory-static']){ $this->data['directory-static']=$this->data['directory-filesystem'].'static'.DIRECTORY_SEPARATOR; } // Defining temporary files root folder if(!$this->data['directory-tmp']){ $this->data['directory-tmp']=$this->data['directory-filesystem'].'tmp'.DIRECTORY_SEPARATOR; } // Defining certificates and keys folder if(!$this->data['directory-keys']){ $this->data['directory-keys']=$this->data['directory-filesystem'].'keys'.DIRECTORY_SEPARATOR; } // If timezone is still set to false, then system attempts to set the currently set timezone // Some systems throw deprecated warning if this value is not set if($this->data['timezone']==false){ // Some systems throw a deprecated warning without implicitly re-setting default timezone date_default_timezone_set('Europe/London'); $this->data['timezone']='Europe/London'; } // Setting if modified since, if it happens to be set if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $this->data['http-if-modified-since']==false){ $this->data['http-if-modified-since']=strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']); } // If default API profile has been changed by configuration, we assign current API profile to default profile as well if($this->data['api-public-profile']!='public'){ $this->data['api-profile']=$this->data['api-public-profile']; } // If first language is not defined then first node from languages array is used if($this->data['language']==false){ $this->data['language']=$this->data['languages'][0]; } // Making sure that boundary is not part of the content type definition if($this->data['http-content-type']){ $tmp=explode(';',$this->data['http-content-type']); $this->data['http-content-type']=array_shift($tmp); } // Compressed output is turned off if the requesting user agent does not support it // This is also turned off if PHP does not support Zlib compressions if($this->data['output-compression']!=false){ if(!in_array($this->data['output-compression'],$this->data['http-accept-encoding']) || !extension_loaded('Zlib')){ $this->data['output-compression']=false; } } // If configuration has not sent a request string then State solves it using request-uri if(!$this->data['request-true']){ // If install is at www.example.com/w/ subfolder and user requests www.example.com/w/en/page/ then this would be parsed to 'en/page/' $this->data['request-true']=preg_replace('/(^'.preg_quote($this->data['url-web'],'/').')/ui','',$this->data['request-uri']); } // This sets the developer flag to either true or false depending on configuration if($this->data['developer-ip']!='' && $this->data['developer-user-agent']!=''){ // IP addresses can be comma-separated list $developers=explode(',',$this->data['developer-ip']); // User agent is matched by finding user agent string within the configuration if(in_array($this->data['client-ip'],$developers) && strpos(str_replace(';','',$this->data['client-user-agent']),$this->data['developer-user-agent'])!==false){ $this->data['developer']=true; } } // FINGERPRINTING // Fingerprint is created based on data sent by user agent, this can be useful for light detection without cookies $fingerprint=''; // If IP is set for session fingerprinting if(in_array('ip',$this->data['session-fingerprint'])){ $fingerprint.=$this->data['client-ip']; } // If browser is used for session fingerprinting if(in_array('browser',$this->data['session-fingerprint'])){ $fingerprint.=$this->data['client-user-agent']; $fingerprint.=(isset($_SERVER['HTTP_ACCEPT']))?$_SERVER['HTTP_ACCEPT']:''; $fingerprint.=(isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))?$_SERVER['HTTP_ACCEPT_LANGUAGE']:''; $fingerprint.=(isset($_SERVER['HTTP_ACCEPT_ENCODING']))?$_SERVER['HTTP_ACCEPT_ENCODING']:''; $fingerprint.=(isset($_SERVER['HTTP_ACCEPT_CHARSET']))?$_SERVER['HTTP_ACCEPT_CHARSET']:''; $fingerprint.=(isset($_SERVER['HTTP_KEEP_ALIVE']))?$_SERVER['HTTP_KEEP_ALIVE']:''; $fingerprint.=(isset($_SERVER['HTTP_CONNECTION']))?$_SERVER['HTTP_CONNECTION']:''; } // If fingerprint is not an empty string then it overwrites the State value if($fingerprint!=''){ // Fingerprint is hashed with MD5 using session name for salt $this->data['fingerprint']=md5($this->data['session-name'].$fingerprint); } // JSON OR XML BASED INPUT // PHP Input data is ignored for input if form submit is done if(!in_array($this->data['http-content-type'],array(false,'','application/x-www-form-urlencoded','multipart/form-data'))){ // Gather sent input $phpInput=file_get_contents('php://input'); // For custom content types, when data is sent as an XML or JSON string if($phpInput!=''){ // Parsing method depends on content type header if($this->data['http-content-type']=='application/json'){ // JSON string is converted to associative array $this->data['http-input']=json_decode($phpInput,true); } elseif(extension_loaded('SimpleXML') && ($this->data['http-content-type']=='application/xml')){ // This is not supported in earlier versions of LibXML if(defined('LIBXML_PARSEHUGE')){ $tmp=simplexml_load_string($phpInput,'SimpleXMLElement',LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_ERR_NONE | LIBXML_PARSEHUGE); } else { $tmp=simplexml_load_string($phpInput,'SimpleXMLElement',LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_ERR_NONE); } // Data is converted to array only if an object was created if($tmp){ $this->data['http-input']=json_decode(json_encode($tmp),true); } } else { // Storing the entire stream directly $this->data['http-input']=$phpInput; } } } // If special input file is set as XML if(isset($_FILES['www-xml']) || isset($_REQUEST['www-xml'])){ // If this is a file upload or not if(isset($_FILES['www-xml'])){ // This is not supported in earlier versions of LibXML if(defined('LIBXML_PARSEHUGE')){ $tmp=simplexml_load_file($_FILES['www-xml']['tmp_name'],'SimpleXMLElement',LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_ERR_NONE | LIBXML_PARSEHUGE); } else { $tmp=simplexml_load_file($_FILES['www-xml']['tmp_name'],'SimpleXMLElement',LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_ERR_NONE); } } else { // This is not supported in earlier versions of LibXML if(defined('LIBXML_PARSEHUGE')){ $tmp=simplexml_load_string($_REQUEST['www-xml'],'SimpleXMLElement',LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_ERR_NONE | LIBXML_PARSEHUGE); } else { $tmp=simplexml_load_string($_REQUEST['www-xml'],'SimpleXMLElement',LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_ERR_NONE); } } // Data is converted to array only if an object was created if($tmp){ $this->data['http-input']=json_decode(json_encode($tmp),true); } } // If special input file is set as JSON if(isset($_FILES['www-json']) || isset($_REQUEST['www-json'])){ if(isset($_FILES['www-json'])){ // JSON string is converted to associative array $this->data['http-input']=json_decode(file_get_contents($_FILES['www-json']['tmp_name']),true); } else { // JSON string is converted to associative array $this->data['http-input']=json_decode($_REQUEST['www-json'],true); } } } /** * When State class is not used anymore, then state messenger data - if set - is written * to filesystem based on the State messenger key. This method also deletes session cookie, * if sessions have been used but the session variable itself is empty. * * @return null */ final public function __destruct(){ // Only applies if request messenger actually holds data if($this->messenger){ // This stores messenger data in filesystem $this->storeMessenger(); } // This will commit session to the session storage if(!headers_sent()){ $this->commitHeaders(); } } // STATE MANIPULATION /** * This is the basic call to return a State variable from the object. If this method is * called without any variables, then the entire State array is returned. You can send * one or more key variables to this method to return a specific key from State. If you * send multiple parameters then this method attempts to find keys of a key in case the * State variable is an array itself. $input variable is only used within State class * itself. Method returns null for keys that have not been set. * * @param array $input input variables sent to the function * @return mixed */ final public function getState($input){ // Returning the entire array, if entire state was requested if(!$input){ return $this->data; } // Finding out the specific requested variable $return=&$this->data; // Looping over each of the input parameters sent to the function foreach($input as $key){ if(!isset($return[$key])){ trigger_error('State variable '.implode(',',$input).' does not exist',E_USER_NOTICE); return null; } $return=&$return[$key]; } return $return; } /** * This method is used to set a $data variable value in State object. $variable can * also be an array of keys and values, in which case multiple variables are set at * once. This method uses stateChanged() for variables that carry additional * functionality, such as setting timezone. $input variable is only used within * State class itself. * * @param mixed $input input variables sent to the function * @return boolean */ final public function setState($input){ // If an array is sent as key-value pair if($input && count($input)==1 && is_array($input[0])){ foreach($input[0] as $key=>$val){ // Setting the state variable (note that the stateChanged may change this) $this->data[$key]=$val; // Checking for system specific changes due to the changed State $this->stateChanged($key,$val); } } elseif(count($input)>1){ // The last element in the sent parameters is the value to be set $value=array_pop($input); // Setting the proper address of State to the newly set variable $pointer=&$this->data; foreach($input as $key){ $pointer=&$pointer[$key]; } $pointer=$value; // Checking for system specific changes due to the changed State return $this->stateChanged($input[0],$value); } else { // Only one parameter was sent, meaning that the setting fails trigger_error('setState() method requires two variables if not sending an array',E_USER_NOTICE); return false; } // State value has been set return true; } /** * This is a private method used internally whenever configuration is changed. It has * checks for cases when a variable is changed that carries additional functionality * such as when changing the timezone or output compression. For example, if output * compression is set, but not supported by user agent that is making the request, * then output supression is turned off. * * @param string $variable variable name that is changed * @param mixed $value new value of the variable * @return boolean */ final private function stateChanged($variable,$value=true){ // Certain variables are checked that might change system flags switch ($variable) { case 'timezone': // Attempting to set default timezone if(!date_default_timezone_set($value)){ trigger_error('Cannot set timezone to '.$value,E_USER_WARNING); return false; } break; case 'memory-limit': if($value){ if(!function_exists('ini_set') || !ini_set('memory_limit',$value)){ trigger_error('Cannot set memory limit to '.$value,E_USER_WARNING); return false; } } break; case 'time-limit': if($value){ set_time_limit($value); } break; case 'locale': if($value){ if(!setlocale(LC_ALL,$value.'.UTF-8')){ trigger_error('Cannot set locale to '.$value.'.UTF-8',E_USER_WARNING); return false; } } break; case 'output-compression': // If user agent does not expect compressed data and PHP extension is not loaded, then this value cannot be turned on if($value==false || !in_array($value,$this->data['http-accept-encoding']) || !extension_loaded('Zlib')){ $this->data[$variable]=false; return false; } break; } // State has been changed return true; } /** * This function is called before output is pushed to browser by the API or when State * object is not used anymore. This method is not accessible to Factory class, but it * is not private. * * @return boolean */ final public function commitHeaders(){ // If sessions have been started if($this->sessionStarted){ // If session data has changed in any way or session is assigned to be regenerated if($this->sessionHandler->regenerateId || $this->data['session-original-data']!=$this->data['session-data']){ // Checking if there are more data in sessions than just internal keys $sessionSize=count($this->data['session-data']); if($sessionSize==0 || ($sessionSize==1 && isset($this->data['session-data'][$this->data['session-timestamp-key']])) || ($sessionSize==1 && isset($this->data['session-data'][$this->data['session-fingerprint-key']])) || ($sessionSize==2 && isset($this->data['session-data'][$this->data['session-timestamp-key']]) && isset($this->data['session-data'][$this->data['session-fingerprint-key']]))){ // This will make the session handler destroy the session $this->data['session-data']=array(); } // Sending State session data to session handler $this->sessionHandler->sessionData=$this->data['session-data']; // Closing sessions $this->sessionHandler->sessionCommit(); } } // Commiting headers for added headers if(!empty($this->data['headers-set'])){ foreach($this->data['headers-set'] as $header=>$replace){ header($header,$replace); } } // Removing headers if set for removal if(!empty($this->data['headers-unset'])){ foreach($this->data['headers-unset'] as $header){ header_remove($header); } } // Headers have been commited return true; } // SITEMAP AND TRANSLATIONS /** * This method returns an array of currently active translations, or for a language set * with $language variable. If $keyword is also set, then it returns a specific translation * with that keyword from $language translations. If $keyword is an array, then $subkeyword * can be used to return specific translation from that keyword. * * @param boolean|string $language language keyword, if this is not set then returns current language translations * @param boolean|string $keyword if only single keyword needs to be returned * @param boolean|string $subkeyword if the $keyword is an array, then $subkeyword is the actual translation that is requested * @return array, string or false if failed */ final public function getTranslations($language=false,$keyword=false,$subkeyword=false){ // If language is not set, then assuming current language if(!$language){ $language=$this->data['language']; } // If translations data is already stored in state if(!isset($this->data['translations'][$language])){ // Translations can be loaded from overrides folder as well if(file_exists($this->data['directory-system'].'overrides'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.$language.'.translations.ini')){ $sourceUrl=$this->data['directory-system'].'overrides'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.$language.'.translations.ini'; } elseif(file_exists($this->data['directory-system'].'resources'.DIRECTORY_SEPARATOR.$language.'.translations.ini')){ $sourceUrl=$this->data['directory-system'].'resources'.DIRECTORY_SEPARATOR.$language.'.translations.ini'; } else { return false; } // This data can also be stored in cache $cacheUrl=$this->data['directory-system'].'filesystem'.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.$language.'.translations.tmp'; // Including the translations file if(!file_exists($cacheUrl) || filemtime($sourceUrl)>filemtime($cacheUrl)){ // Translations are parsed from INI file in the resources folder $this->data['translations'][$language]=parse_ini_file($sourceUrl,true,INI_SCANNER_RAW); if(!$this->data['translations'][$language]){ trigger_error('Cannot parse INI file: '.$sourceUrl,E_USER_ERROR); } // Cache of parsed INI file is stored for later use if(!file_put_contents($cacheUrl,serialize($this->data['translations'][$language]))){ trigger_error('Cannot store INI file cache at '.$cacheUrl,E_USER_ERROR); } } else { // Since INI file has not been changed, translations are loaded from cache $this->data['translations'][$language]=unserialize(file_get_contents($cacheUrl)); } } // Returning keyword, if it is requested if($keyword){ // If $keyword is an array and subkeyword is requested if($subkeyword){ if(isset($this->data['translations'][$language][$keyword],$this->data['translations'][$language][$keyword][$subkeyword])){ return $this->data['translations'][$language][$keyword][$subkeyword]; } else { return false; } } else { if(isset($this->data['translations'][$language][$keyword])){ return $this->data['translations'][$language][$keyword]; } else { return false; } } } else { // If keyword was not set, then returning entire array return $this->data['translations'][$language]; } } /** * This method includes or reads in a file from '/resources/content/' folder $name is the * modified filename that can also include subfolders, but without the language prefix and * without extension in the filename itself. If $language is not defined then currently * active language is used. * * @param string $name filename without language prefix * @param boolean|string $language language keyword * @return string */ final public function getContent($name,$language=false){ // If language is not defined then default language is used if(!$language){ $language=$this->data['language']; } // Initial content folder $fileDestination='./resources/content/'; // Folder can be defined in the name $components=explode('/',$name); $count=count($components); // If a folder is defined if($count>1){ for($i=0;$i<$count;$i++){ // If the last node is used if($count==($i+1)){ $fileDestination.=$language.'.'.$components[$i]; } else { $fileDestination.=$components[$i].'/'; } } } else { $fileDestination.=$language.'.'.$name; } // Adding the extension $fileDestination.='.htm'; // Making sure that the file itself exists if(file_exists($fileDestination)){ // Echoing out the contents to output buffer return file_get_contents($fileDestination); } else { trigger_error('Cannot find a file in '.$fileDestination,E_USER_WARNING); return false; } } /** * This method returns an array of currently active sitemap, or a sitemap for a language * set with $language variable. If $keyword is also set, then it returns a specific * sitemap node with that keyword from $language sitemap file. This method returns the * original, non-modified sitemap that has not been parsed for use with URL controller. * * @param boolean|string $language language keyword, if this is not set then returns current language sitemap * @param boolean|string $keyword if only a single URL node needs to be returned * @return array or false if failed */ final public function getSitemapRaw($language=false,$keyword=false){ // If language is not set, then assuming current language if(!$language){ $language=$this->data['language']; } // If translations data is already stored in state if(!isset($this->data['sitemap-raw'][$language])){ // Translations can be loaded from overrides folder as well if(file_exists($this->data['directory-system'].'overrides'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.$language.'.sitemap.ini')){ $sourceUrl=$this->data['directory-system'].'overrides'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.$language.'.sitemap.ini'; } elseif(file_exists($this->data['directory-system'].'resources'.DIRECTORY_SEPARATOR.$language.'.sitemap.ini')){ $sourceUrl=$this->data['directory-system'].'resources'.DIRECTORY_SEPARATOR.$language.'.sitemap.ini'; } else { return false; } // This data can also be stored in cache $cacheUrl=$this->data['directory-system'].'filesystem'.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.$language.'.sitemap.tmp'; // Including the sitemap file if(!file_exists($cacheUrl) || filemtime($sourceUrl)>filemtime($cacheUrl)){ // Sitemap is parsed from INI file in the resources folder $this->data['sitemap-raw'][$language]=parse_ini_file($sourceUrl,true,INI_SCANNER_RAW); if(!$this->data['sitemap-raw'][$language]){ trigger_error('Cannot parse INI file: '.$sourceUrl,E_USER_ERROR); } // Parsing the sitemap foreach($this->data['sitemap-raw'][$language] as $key=>$settings){ $this->data['sitemap-raw'][$language][$key]['nodes']=explode('/',$key); } // Cache of parsed INI file is stored for later use if(!file_put_contents($cacheUrl,serialize($this->data['sitemap-raw'][$language]))){ trigger_error('Cannot store INI file cache at '.$cacheUrl,E_USER_ERROR); } } else { // Since INI file has not been changed, translations are loaded from cache $this->data['sitemap-raw'][$language]=unserialize(file_get_contents($cacheUrl)); } } // Returning keyword, if it is requested if($keyword){ if(isset($this->data['sitemap-raw'][$language][$keyword])){ return $this->data['sitemap-raw'][$language][$keyword]; } else { return false; } } else { // If keyword was not set, then returning entire array return $this->data['sitemap-raw'][$language]; } } /** * This returns sitemap array that is modified for use with View controller and other * parts of the system. It returns sitemap for current language or a language set with * $language variable and can return a specific sitemap node based on $keyword. * * @param boolean|string $language language keyword, if this is not set then returns current language sitemap * @param boolean|string $keyword if only a single URL node needs to be returned * @return array or false if failed */ final public function getSitemap($language=false,$keyword=false){ // If language is not set, then assuming current language if(!$language){ $language=$this->data['language']; } // If translations data is already stored in state if(!isset($this->data['sitemap'][$language])){ // Getting raw sitemap data $siteMapRaw=$this->getSitemapRaw($language); if(!$siteMapRaw){ return false; } // This is output array $this->data['sitemap'][$language]=array(); // System builds usable URL map for views foreach($siteMapRaw as $key=>$node){ // Only sitemap nodes with set view will be assigned to reference if(isset($node['view'])){ // Since the same view can be referenced in multiple locations if(isset($node['subview'])){ $node['view']=$node['view'].'/'.$node['subview']; } // This is used only if view has not yet been defined if(!isset($this->data['sitemap'][$language][$node['view']])){ $this->data['sitemap'][$language][$node['view']]=$key; } // Home views do not need a URL node if($node['view']!=$this->data['home-view']){ if(strpos($key,':')!==false){ $url=''; $count=0; $bits=explode('/',$key); foreach($bits as $b){ if($b[0]!=':'){ $url.=$b.'/'; } else { $url.=':'.$count.':/'; $count++; } } } else { $url=$key.'/'; } } else { $url=''; } // Storing data from Sitemap file $this->data['sitemap'][$language][$node['view']]=$siteMapRaw[$key]; // If first language URL is not enforced, then this is taken into account if($language==$this->data['languages'][0] && $this->data['enforce-first-language-url']==false){ $this->data['sitemap'][$language][$node['view']]['url']=$this->data['url-web'].$url; } else { $this->data['sitemap'][$language][$node['view']]['url']=$this->data['url-web'].$language.'/'.$url; } } } } // Returning keyword, if it is requested if($keyword){ if(isset($this->data['sitemap'][$language][$keyword])){ return $this->data['sitemap'][$language][$keyword]; } else { return false; } } else { // If keyword was not set, then returning entire array return $this->data['sitemap'][$language]; } } // STATE MESSENGER /** * This method initializes State messenger by giving it an address and assigning the file * that State messenger will be stored under. If the file already exists and $overwrite is * not turned on, then it automatically loads contents of that file from filesystem. * * @param string $address key that messenger data will be saved under * @param boolean $overwrite if this is set then existing state messenger file will be overwritten * @return boolean */ final public function stateMessenger($address,$overwrite=false){ // If messenger is already set if($this->messenger){ // This stores data to filesystem $this->storeMessenger(); // New messenger data will be empty $this->messengerData=array(); } // New messenger address is hashed $this->messenger=md5($address); // Messenger data is stored in a filesystem subfolder $dataAddress=$this->data['directory-system'].'filesystem'.DIRECTORY_SEPARATOR.'messenger'.DIRECTORY_SEPARATOR.substr($this->messenger,0,2).DIRECTORY_SEPARATOR.$this->messenger.'.tmp'; // If this state messenger address already stores data, then it is loaded as the base data if(!$overwrite && file_exists($dataAddress)){ $this->messengerData=unserialize(file_get_contents($dataAddress)); } // Messenger is always started return true; } /** * This writes data to State messenger. $data is the key and $value is the value of the * key. $data can also be an array of keys and values, in which case multiple values are * set at the same time. * * @param array|string $data key or data array * @param mixed $value value, if data is a key * @return boolean */ final public function setMessengerData($data,$value=false){ // If messenger address is set if($this->messenger){ // If data is an array, then it adds data recursively if(is_array($data)){ foreach($data as $key=>$value){ // Setting messenger data $this->messengerData[$key]=$value; } } else { // Setting messenger data $this->messengerData[$data]=$value; } return true; } else { return false; } } /** * This method removes key from State messenger based on value of $key. If $key is not * set, then the entire State messenger data is cleared. * * @param boolean|string $key key that will be removed, if set to false then removes the entire data * @return boolean */ final public function unsetMessengerData($key=false){ // If messenger address is set if($this->messenger && $key){ if(isset($this->messengerData[$key])){ unset($this->messengerData[$key]); return true; } else { return false; } } elseif($this->messenger){ $this->messengerData=array(); return true; } else { return false; } } /** * This method returns data from State messenger. It either returns all the data from * initialized state messenger, or just a $key from it. If $remove is set, then data is * also set for deletion after it has been accessed. * * @param boolean|string $key data keyword * @param boolean $remove true or false flag whether to delete the request data after returning it * @return mixed or false if failed */ final public function getMessengerData($key=false,$remove=true){ // If messenger address is set if($this->messenger && $key){ if(isset($this->messengerData[$key])){ $return=$this->messengerData[$key]; // If key is set for removal, then unsetting it if($remove){ unset($this->messengerData[$key]); } return $return; } else { return false; } } elseif($this->messenger){ $return=$this->messengerData; // If data is set for removal, then removing the whole data array if($remove){ $this->messengerData=array(); } return $return; } else { return false; } } /** * This method simply stores messenger data in filesystem, if there is data to store. * * @return boolean */ final private function storeMessenger(){ // Data is only stored if messenger is set if($this->messenger){ // Finding data folder $dataFolder=$this->data['directory-system'].'filesystem'.DIRECTORY_SEPARATOR.'messenger'.DIRECTORY_SEPARATOR.substr($this->messenger,0,2).DIRECTORY_SEPARATOR; // If there is data to store in messenger if(!empty($this->messengerData)){ // Testing if state messenger folder exists if(!is_dir($dataFolder)){ if(!mkdir($dataFolder,0755)){ trigger_error('Cannot create messenger folder',E_USER_ERROR); } } // Writing messenger data to file if(!file_put_contents($dataFolder.$this->messenger.'.tmp',serialize($this->messengerData))){ trigger_error('Cannot write messenger data',E_USER_ERROR); } } elseif(file_exists($dataFolder.$this->messenger.'.tmp')){ unlink($dataFolder.$this->messenger.'.tmp'); } // Processing complete return true; } else { // Messenger was not set return false; } } // SESSION USER AND PERMISSIONS /** * This method sets user data array in session. This is a simple helper function used * for holding user-specific data for a web service. $data is an array of user data. * * @param boolean|array $data data array set to user * @return boolean */ final public function setUser($data=false){ // If user is not being unset if($data){ // Setting the session $this->setSession($this->data['session-user-key'],$data); // Setting the state variable $this->data['user-data']=$data; return true; } else { // Unsetting the user return $this->unsetUser(); } } /** * This either returns the entire user data array or just a specific $key of user data * from the session. * * @param boolean|string $key element returned from user data, if not set then returns the entire user data * @return mixed */ final public function getUser($key=false){ // Testing if permissions state has been populated or not if(!$this->data['user-data']){ $this->data['user-data']=$this->getSession($this->data['session-user-key']); // If this session key did not exist, then returning false if(!$this->data['user-data']){ return false; } } // If key is set if($key){ if(isset($this->data['user-data'][$key])){ // Returning key data return $this->data['user-data'][$key]; } else { return false; } } else { // Returning entire user array return $this->data['user-data']; } } /** * This unsets user data and removes the session of user data. * * @return boolean */ final public function unsetUser(){ // Unsetting the session $this->unsetSession($this->data['session-user-key']); // Regenerating session cookie $this->regenerateSession(true,true); // Unsetting the state variable $this->data['user-data']=false; return true; } /** * This method sets an array of $permissions or a comma-separated string of permissions * for the current user permissions session. * * @param boolean|array|string $permissions an array or a string of permissions * @return boolean */ final public function setPermissions($permissions=false){ // If permissions are set if($permissions){ // If permissions were sent as a string if(!is_array($permissions)){ $permissions=explode(',',$permissions); } // Setting the session variable $this->setSession($this->data['session-permissions-key'],$permissions); // Setting the state variable $this->data['user-permissions']=$permissions; return true; } else { // Unsetting permissions return $this->unsetPermissions(); } } /** * This method returns an array of currently set user permissions from the session. * * @return array */ final public function getPermissions(){ // Testing if permissions state has been populated or not if(!$this->data['user-permissions']){ $this->data['user-permissions']=$this->getSession($this->data['session-permissions-key']); } return $this->data['user-permissions']; } /** * This checks for an existence of permissions in the user permissions session array * or in the API profile permissions setting. $permissions is either a comma-separated * string of permissions to be checked, or an array. This method returns false when one * of those permission keys is not set in the permissions session. Method returns true, * if $permissions exist in the permissions session array. * * @param string|array $permissions comma-separated string or an array that is checked against permissions array * @return boolean */ final public function checkPermissions($permissions){ // Public profile permissions are stored in the session if($this->data['api-profile']==$this->data['api-public-profile']){ // Testing if permissions state has been populated or not if(!$this->data['user-permissions']){ $this->data['user-permissions']=$this->getSession($this->data['session-permissions-key']); // If this session key did not exist, then returning false if(!$this->data['user-permissions']){ return false; } } $permissionsMatch=$this->data['user-permissions']; } else { // Private API profile permissions are stored separately $permissionsMatch=$this->data['api-permissions']; } // Permissions are handled as an array if(!is_array($permissions)){ $permissions=explode(',',$permissions); } // If all permissions are set, then permissions will not be separately validated and true is assumed if(!in_array('*',$permissionsMatch)){ foreach($permissions as $p){ // Returning true or false depending on whether this key exists or not if(!in_array($p,$permissionsMatch)){ return false; } } } else { foreach($permissions as $p){ // Returning false, if one of the permissions is disallowed if(in_array('!'.$p,$permissionsMatch)){ return false; } } } // Permission check passed return true; } /** * This unsets permissions data from session similarly to how unsetUser() method unsets * user data from session. * * @return boolean */ final public function unsetPermissions(){ // Unsetting the session $this->unsetSession($this->data['session-permissions-key']); // Regenerating session cookie $this->regenerateSession(true,true); // Unsetting the state variable $this->data['user-permissions']=false; return true; } /** * This method returns the currently active public token that is used to increase security * against cross-site-request-forgery attacks. This method returns false if user session * is not populated, in which case public token is not needed. $regenerate sets if the token * should be regenerated if it already exists, this invalidates forms when Back button is * used after submitting data, but is more secure. $forced is used to force token generation * even if no user session is active. * * @param boolean $regenerate if public token should be regenerated * @param boolean $forced if token is generated even when there is no actual user session active * @return string or boolean if no user session active */ final public function getPublicToken($regenerate=false,$forced=false){ // This is only required to protect users with active sessions if($forced || $this->getUser()){ // Public token is stored in the session $token=$this->getSession('www-public-token'); if($token && $token!='' && !$regenerate){ // Returning a token return $token; } else { // Generating a new token $token=sha1($this->data['client-ip'].$this->data['client-user-agent'].$this->data['request-id'].microtime()); $this->setSession($this->data['session-token-key'],$token); // Setting it also in sessions $this->data['api-public-token']=$token; // Returning a token return $token; } } else { return false; } } /** * This method is useful when 'api-public-token' setting is off in configuration, but you * still want to protect your API method from public API requests from XSS and other attacks. * This returns false if the provided public API token is incorrect. * * @return boolean */ final public function checkPublicToken(){ // This is only checked if the token actually exists and public API profile is used if($this->data['api-public-token'] && $this->data['api-public-profile']==$this->data['api-profile']){ if($this->data['api-public-token']==$this->getSession($this->data['session-token-key'])){ return true; } else { return false; } } else { return true; } } // SESSION AND COOKIES /** * This method validates current session data and checks for lifetime as well as * session fingerprint. It notifies session handler if any changes have to be made. * Configuration array can have these keys: * 'name' - session cookie name * 'lifetime' - session cookie lifetime * 'path' - URL path for session cookie * 'domain' - domain for session cookie * 'secure' - whether session cookie is for secure connections only * 'http-only' - whether session cookie is for HTTP requests only and unavailable for scripts * 'user-key' - session key for storing user data * 'permissions-key' - session key for storing user permissions * 'fingerprint-key' - session key for storing session fingerprint * 'timestamp-key' - session key for storing session timestamp * 'token-key' - session key for public request tokens for protecting against replay attacks * 'fingerprint' - session key for session fingerprinting for protecting against replay attacks * * @param boolean|array $configuration list of session configuration options * @return boolean */ final public function startSession($configuration=false){ // If configuration options are sent if($configuration && is_array($configuration) && !empty($configuration)){ // Making sure that only session variables are overwritten $sessionConfigurationKeys=array('name','lifetime','path','domain','secure','http-only','user-key','permissions-key','fingerprint-key','timestamp-key','token-key','fingerprint'); foreach($sessionConfigurationKeys as $key){ if(isset($configuration[$key])){ $this->data['session-'.$key]=$configuration[$key]; } } } // Starting sessions if session ID does not exist if(!$this->sessionHandler->sessionId){ // Opening sessions and initializing session data $this->sessionHandler->sessionOpen((isset($configuration['session-name']))?$configuration['session-name']:$this->data['session-name']); } // Setting session cookie variables from Configuration $this->sessionHandler->sessionCookie(array( 'lifetime'=>$this->data['session-lifetime'], 'path'=>$this->data['session-path'], 'domain'=>$this->data['session-domain'], 'secure'=>$this->data['session-secure'], 'http-only'=>$this->data['session-http-only'], )); // This can regenerate the session ID, if enough time has passed if($this->data['session-regenerate']){ if(!isset($this->data['session-data'][$this->data['session-timestamp-key']])){ // Storing a session creation time in sessions $this->data['session-data'][$this->data['session-timestamp-key']]=$this->data['request-time']; } elseif($this->data['request-time']>($this->data['session-data'][$this->data['session-timestamp-key']]+$this->data['session-regenerate'])){ // Regenerating the session ID $this->sessionHandler->regenerateId=true; $this->sessionHandler->regenerateRemoveOld=true; // Storing a session creation time in sessions $this->data['session-data'][$this->data['session-timestamp-key']]=$this->data['request-time']; } } // If session fingerprinting is used if($this->data['session-fingerprint'] && $this->data['fingerprint']){ if(!isset($this->data['session-data'][$this->data['session-fingerprint-key']])){ // Storing session fingerprint in sessions $this->data['session-data'][$this->data['session-fingerprint-key']]=$this->data['fingerprint']; } elseif($this->data['session-data'][$this->data['session-fingerprint-key']]!=$this->data['fingerprint']){ // Regenerating the session ID $this->sessionHandler->regenerateId=true; $this->sessionHandler->regenerateRemoveOld=false; // Emptying the session array $this->data['session-data']=array(); } } // Session validation complete $this->sessionStarted=true; return true; } /** * This method notifies the session handler that the session data should be * stored under a new ID. * * @param boolean $regenerate to regenerate or not * @param boolean $deleteOld deletes the previous one, if set to true * @return boolean */ final public function regenerateSession($regenerate=true,$deleteOld=true){ // Making sure that sessions have been started if(!$this->sessionStarted){ $this->startSession(); } $this->sessionHandler->regenerateId=$regenerate; $this->sessionHandler->regenerateRemoveOld=$deleteOld; return true; } /** * This method sets a session variable $key with a $value. If $key is an array of * keys and values, then multiple session variables are set at once. * * @param boolean|array|string $key key of the variable or an array of keys and values * @param mixed $value value to be set * @return boolean */ final public function setSession($key=false,$value=false){ // Making sure that sessions have been started if(!$this->sessionStarted){ $this->startSession(); } // Multiple values can be set if key is an array if(is_array($key)){ foreach($key as $k=>$v){ // setting value based on key $this->data['session-data'][$k]=$v; } } elseif($key){ // Setting value based on key $this->data['session-data'][$key]=$value; } else { return false; } return true; } /** * This method returns $key value from session data. If $key is an array of keys, then * it can return multiple variables from session at once. If $key is not set, then entire * session array is returned. Method returns null for keys that have not been set. * * @param boolean|string|array $key key to return or an array of keys * @return mixed */ final public function getSession($key=false){ // Making sure that sessions have been started if(!$this->sessionStarted){ $this->startSession(); } // Multiple keys can be returned if(is_array($key)){ // This array will hold multiple values $return=array(); foreach($key as $val){ // Getting value based on key if(isset($this->data['session-data'][$val])){ $return[$val]=$this->data['session-data'][$val]; } else { $return[$val]=null; } } return $return; } elseif($key){ // Return data from specific key if(isset($this->data['session-data'][$key])){ return $this->data['session-data'][$key]; } else { return null; } } else { // Return entire session data, if key was not set return $this->data['session-data']; } } /** * This method unsets $key value from current session. If $key is an array of keys, then * multiple variables can be unset at once. If $key is not set at all, then this simply * destroys the entire session. * * @param boolean|string|array $key key of the value to be unset, or an array of keys * @return boolean */ final public function unsetSession($key=false){ // Making sure that sessions have been started if(!$this->sessionStarted){ $this->startSession(); } // Can unset multiple values if(is_array($key)){ foreach($key as $value){ unset($this->data['session-data'][$value]); } } elseif($key){ unset($this->data['session-data'][$key]); } else { // Entire session data is unset $this->data['session-data']=array(); // Session cookie will be regenerated if new data is stored $this->sessionHandler->regenerateId=true; $this->sessionHandler->regenerateRemoveOld=true; } return true; } /** * This method sets a cookie with $key and a $value. $configuration is an array of * cookie parameters that can be set. * * @param string|array $key key of the variable, or an array of keys and values * @param string|array $value value to be set, can also be an array * @param array $configuration cookie configuration options, list of keys below * 'expire' - timestamp when the cookie is set to expire * 'timeout' - alternative to 'expire', this sets how long the cookie can survive * 'path' - cookie limited to URL path * 'domain' - cookie limited to domain * 'secure' - whether cookie is for secure connections only * 'http-only' - if the cookie is only available for HTTP requests * @return boolean */ final public function setCookie($key,$value,$configuration=array()){ // Checking for configuration options if(!isset($configuration['expire'])){ if(isset($configuration['timeout'])){ $configuration['expire']=$this->data['request-time']+$configuration['timeout']; } else { $configuration['expire']=2147483647; } } if(!isset($configuration['path'])){ $configuration['path']=$this->data['url-web']; } if(!isset($configuration['domain'])){ $configuration['domain']=$this->data['http-host']; } if(!isset($configuration['secure'])){ $configuration['secure']=false; } if(!isset($configuration['http-only'])){ $configuration['http-only']=true; } // Can set multiple values if(is_array($key)){ // Value can act as a configuration if(is_array($value)){ $configuration=$value; } foreach($key as $k=>$v){ // Value can be an array, in which case the values set will be an array if(is_array($v)){ foreach($v as $index=>$val){ setcookie($k.'['.$index.']',$val,$configuration['expire'],$configuration['path'],$configuration['domain'],$configuration['secure'],$configuration['http-only']); // Cookie values can be accessed immediately after they are set $_COOKIE[$k][$index]=$val; } } else { // Setting the cookie setcookie($k,$v,$configuration['expire'],$configuration['path'],$configuration['domain'],$configuration['secure'],$configuration['http-only']); // Cookie values can be accessed immediately after they are set $_COOKIE[$k]=$v; } } } else { // Value can be an array, in which case the values set will be an array if(is_array($value)){ foreach($value as $index=>$val){ setcookie($key.'['.$index.']',$val,$configuration['expire'],$configuration['path'],$configuration['domain'],$configuration['secure'],$configuration['http-only']); // Cookie values can be accessed immediately after they are set $_COOKIE[$key][$index]=$val; } } else { // Setting the cookie setcookie($key,$value,$configuration['expire'],$configuration['path'],$configuration['domain'],$configuration['secure'],$configuration['http-only']); // Cookie values can be accessed immediately after they are set $_COOKIE[$key]=$value; } } } /** * This method returns a cookie value with the set $key. $key can also be an array of * keys, in which case multiple cookie values are returned in an array. Method returns null * for keys that have not been set. * * @param string $key key of the value to be returned, can be an array * @return mixed */ final public function getCookie($key){ // Multiple keys can be returned if(is_array($key)){ // This array will hold multiple values $return=array(); foreach($key as $val){ if(isset($_COOKIE[$val])){ $return[$val]=$_COOKIE[$val]; } else { $return[$val]=false; } } return $return; } else { // Returning single cookie value if(isset($_COOKIE[$key])){ return $_COOKIE[$key]; } else { return null; } } } /** * This method unsets a cookie with the set key of $key. If $key is an array, then * it can remove multiple cookies at once. * * @param string|array $key key of the value to be unset or an array of keys * @return boolean */ final public function unsetCookie($key){ // Can set multiple values if(is_array($key)){ foreach($key as $value){ if(isset($_COOKIE[$value])){ // Removes cookie by setting its duration to 0 setcookie($value,'',($this->data['request-time']-3600)); // Unsets the cookie value unset($_COOKIE[$value]); } } } else { if(isset($_COOKIE[$key])){ // Removes cookie by setting its duration to 0 setcookie($key,'',($this->data['request-time']-3600)); // Unsets the cookie value unset($_COOKIE[$value]); } else { return false; } } return true; } // HEADERS /** * This method adds a header to the array of headers to be added before data is pushed * to the client, when headers are sent. $header is the header string to add and $replace * is a true/false setting for whether previously sent header like this is replaced or not. * * @param string|array $header header string to add or an array of header strings * @param boolean $replace whether the header should be replaced, if previously set * @return boolean */ final public function setHeader($header,$replace=true){ // Multiple headers can be set at once if(is_array($header)){ foreach($header as $h){ if($h!=''){ // Removing the header from unset array unset($this->data['headers-unset'][$h]); // Assigning the setting to headers array $this->data['headers-set'][$h]=$replace; } } } else { if($header!=''){ // Removing the header from unset array unset($this->data['headers-unset'][$header]); // Assigning the setting to headers array $this->data['headers-set'][$header]=$replace; } } return true; } /** * This method adds a header to the array of headers to be removed before data is pushed * to the client, when headers are sent. $header is the header string to remove. * * @param string|array $header header string to add or an array of header strings * @return boolean */ final public function unsetHeader($header){ // Multiple headers can be unset at once if(is_array($header)){ foreach($header as $h){ if($h!=''){ // Removing the header from unset array unset($this->data['headers-set'][$h]); // Assigning the setting to headers array $this->data['headers-unset'][$h]=true; } } } else { if($header!=''){ // Unsetting the header, if previously set unset($this->data['headers-set'][$header]); // Assigning the setting to headers array $this->data['headers-unset'][$header]=true; } } return true; } // TOOLS /** * This method loads tools object if it doesn't exist yet and then allows to * call various methods of the tool. You can call filesystem cleaner, indexer * or a file-size calculator (and each work recursively). * * @param string $type the type of tool to be loaded * @param mixed $arg1 additional parameter for the tool * @param mixed $arg2 additional parameter for the tool * @return mixed based on the tool */ final public function callTool($type,$arg1=false,$arg2=false){ // If tool does not exist yet if(!$this->tools){ // Loading the Imager class if(!class_exists('WWW_Tools',false)){ require($this->data['directory-system'].'engine'.DIRECTORY_SEPARATOR.'class.www-tools.php'); } // Creating a new tool object $this->tools=new WWW_Tools(); } // Calling the method based on selected tool switch($type){ case 'cleaner': // This variable defines the cut-off time of the cleaner if($arg2){ return $this->tools->cleaner($arg1,$arg2); } else { return $this->tools->cleaner($arg1); } break; case 'indexer': // This variable defines the mode of the index method if($arg2){ return $this->tools->indexer($arg1,$arg2); } else { return $this->tools->indexer($arg1); } break; case 'sizer': // Takes just the directory variable return $this->tools->sizer($arg1); break; case 'counter': // Takes just the directory variable return $this->tools->counter($arg1); break; default: trigger_error('This tool does not exist: '.$type,E_USER_WARNING); return false; break; } } // TERMINAL /** * This method is wrapper function for making terminal calls. It attempts to detect * what terminal is available on the system, if any, and then execute the call and * return the results of the call. * * @param string $command command to be executed * @return mixed */ final public function terminal($command){ // Status variable $status=1; // Checking all possible terminal functions if(function_exists('system')){ ob_start(); system($command,$status); $output=ob_get_contents(); ob_end_clean(); } elseif(function_exists('passthru')){ ob_start(); passthru($command,$status); $output=ob_get_contents(); ob_end_clean(); } elseif(function_exists('exec')){ exec($command,$output,$status); $output=implode("\n",$output); } elseif(function_exists('shell_exec')){ $output=shell_exec($command); } else { // No function was available, returning false return false; } // Returning result return array('output'=>$output,'status'=>$status); } } ?>