PHP Classes

File: engine/class.www-wrapper.php

Recommend this page to a friend!
  Classes of Kristo Vaher  >  Wave Framework  >  engine/class.www-wrapper.php  >  Download  
File: engine/class.www-wrapper.php
Role: Class source
Content type: text/plain
Description: PHP Wrapper
Class: Wave Framework
MVC framework for building Web sites and APIs
Author: By
Last change: Fixed a problem where access-control header settings were not set properly. Removed code that was used for testing purposes and accidentally submitted to live. Added JSONP support for JavaScript wrapper as well as JSONV support (which is like JSONP, except it defines a variable instead of callback). Added a new 'file-extensions' configuration setting that is used when routing a request to File Handler. This allows to use '.' symbol in regular website URL's, since File Handler requests are whitelisted with configuration now. Methods getState(), getCookie() and getSession() now return 'null' values, if they have not been set, instead of false.
Date: 8 years ago
Size: 53,315 bytes
 

Contents

Class file image Download
<?php

/**
 * Wave Framework <http://www.waveframework.com>
 * PHP API Wrapper Class
 *
 * Main purpose of an API Wrapper is to make it easier to make API requests over HTTP to a system 
 * built on Wave Framework. API Wrapper class does everything for the developer without requiring 
 * the developer to learn the ins and outs of technical details about how to build an API request. 
 * Wave Framework comes with two separate API authentication methods, one more secure than the 
 * other, both which are handled by this Wrapper class.
 *
 * @package    API
 * @author     Kristo Vaher <kristo@waher.net>
 * @copyright  Copyright (c) 2012, Kristo Vaher
 * @license    GNU Lesser General Public License Version 3
 * @tutorial   /doc/pages/wrapper_php.htm
 * @since      2.0.0
 * @version    3.6.9
 */

class WWW_Wrapper {

	/**
	 * This is the address and URL of the API that the Wrapper will connect to. The API address 
	 * must be for Wave Framework API. This value is set either in object creation or when 
	 * setting 'www-address' input variable.
	 */
	private $apiAddress;
	
	/**
	 * This holds the current language of the API, it can be useful if the API commands return 
	 * language-specific responses and translations from the API. This variable is set by sending 
	 * 'www-language' input variable.
	 */
	private $apiLanguage=false;
	
	/**
	 * This holds information about current API state, such as profile name, secret key and 
	 * various API-related flags for callbacks, asyncrhonous status and more. This variable is 
	 * passed around per each API call.
	 */
	private $apiState=array(
		'apiProfile'=>false,
		'apiSecretKey'=>false,
		'apiToken'=>false,
		'apiPublicToken'=>false,
		'apiHashValidation'=>true,
		'apiStateKey'=>false,
		'apiVersion'=>false,
		'headers'=>false,
		'returnHash'=>false,
		'returnTimestamp'=>false,
		'trueCallback'=>false,
		'falseCallback'=>false,
		'errorCallback'=>false,
		'trueCallbackParameters'=>false,
		'falseCallbackParameters'=>false,
		'requestTimeout'=>10,
		'timestampDuration'=>60,
		'unserialize'=>true,
		'lastModified'=>false
	);
	
	/**
	 * This variable holds the last known error message returned from the API.
	 */
	public $errorMessage=false;
	
	/**
	 * This variable holds the last known response code returned from the API.
	 */
	public $responseCode=false;
	
	/**
	 * Input data is a variable that stores all the plain-text input sent with the API request, 
	 * it's a key-value pair of variables and their values for the API.
	 */
	private $inputData=array();
	
	/**
	 * Crypted input is an array of keys and values that holds data that will be encrypted 
	 * prior to be sent to API. This will be encrypted with the session token of the API in 
	 * serialized form.
	 */
	private $cryptedData=array();
	
	/**
	 * This array stores keys and values for files that will be sent to API. Key is the 'input 
	 * file name' and value is the location of the file in filesystem.
	 */
	private $inputFiles=array();
	
	/**
	 * This flag holds state about support for cURL. cURL will be used to make requests unless 
	 * it is not enabled on the server.
	 */
	private $curlEnabled=false;
	
	/**
	 * This is an array that gathers log information about the requests made through the API 
	 * that can be used for debugging purposes should something go wrong.
	 */
	private $log=array();
	
	/**
	 * This is a flag that halts the entire functionality of the Wrapper object, if it is set. 
	 * Once this happens you should check the log to see what went wrong.
	 */
	private $criticalError=false;
	
	/**
	 * This variable holds the address for the file that is used as a cookie container in the 
	 * file system. This allows Wrapper to use cookies when making API requests.
	 */
	private $cookieContainer=false;
	
	/**
	 * This variable holds the address for the file that is used as a certificate container in the 
	 * file system. When this is set, then certificates are validated when they are used.
	 */
	private $certificateContainer=false;
	
	/**
	 * This is the user-agent string of the API Wrapper and it is sent by the Wrapper when making 
	 * cURL requests. It is useful later on to determine where the requests come from. Note that 
	 * when cURL is not supported and file_get_contents() makes the request, then user agent is 
	 * not sent with the request.
	 */
	private $userAgent='WaveFramework/3.6.9 (PHP)';
	
	/**
	 * This is the GET string maximum length. Most servers should easily be able to deal with 
	 * 2048 bytes of request string length, but this value can be changed by submitting a 
	 * different length with 'www-get-length' input value.
	 */
	private $getLimit=2048;
	
	/**
	 * If this value is set, then API log will be reset after each API request. This value can 
	 * be sent with 'www-reset-log' keyword sent to Wrapper.
	 */
	private $resetLog=true;

	/**
	 * Wrapper object creation requires an 'address', which is the address that Wrapper will make 
	 * API requests to. If this is not defined, then 'address' assumes that the system it makes 
	 * requests to is the same where the API is loaded from. 'language' is a language keyword from 
	 * the system that API makes a connection with and is used whenever language-specific results 
	 * are returned from API.
	 * 
	 * @param boolean|string $address API address, default value is current domain presumed API address
	 * @param boolean|string $language language keyword, default value is current document language
	 * @return WWW_Wrapper
	 */
	public function __construct($address=false,$language=false){
	
		// For cases when the API address is not set
		if(!$address){
			$address=((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS']==1 || $_SERVER['HTTPS']=='on'))?'https://':'http://').$_SERVER['HTTP_HOST'].'/json.api';
		}
		
		// If language is set, then this language is used across API
		if($language){
			$this->apiLanguage=$language;
		}
		
		// This should be URL to API of Wave Framework
		$this->apiAddress=$address;
		
		// This checks for cURL support, which is required for making POST requests
		// cURL is also faster than file_get_contents() method
		if(extension_loaded('curl')){
			// Flag is checked during request creation
			$this->curlEnabled=true;
		} elseif(!function_exists('ini_get') || ini_get('allow_url_fopen')!=1){
			// This means that requests cannot be made at all
			$this->criticalError=true;
			// If cURL is enabled, then file_get_contents() requires PHP setting to make requests to URL's
			$this->responseCode=213;
			$this->errorMessage='Cannot make URL requests: Cannot detect if PHP can make URL requests, please enable allow_url_fopen setting or ini_get() function';
			$this->log[]=$this->errorMessage;
		}
		
		// JSON is required
		if(!function_exists('json_encode')){
			// This means that requests cannot be made at all
			$this->criticalError=true;
			$this->responseCode=214;
			$this->errorMessage='Cannot serialize data: JSON is required for API requests to work properly';
			$this->log[]=$this->errorMessage;
		}
		
		// Log entry
		$this->log[]='Wave API Wrapper object created with API address: '.$address;
		
	}
	
	// SETTINGS
		
		/**
		 * This method returns current log of the API wrapper. If $implode is set, then the 
		 * value of $implode is used as a character to implode the log with. Otherwise the 
		 * log is returned as an array.
		 * 
		 * @param boolean|string $implode string to implode the log with
         * @return array/string depending if imploded
		 */
		public function returnLog($implode=false){
		
			// Log entry for log access
			$this->log[]='Returning log';
			// Imploding, if requested
			if(!$implode){
				return $this->log;
			} else {
				return implode($implode,$this->log);
			}
			
		}
		
		/**
		 * This method clears the API log. This method can be called manually or is called 
		 * automatically if log is assigned to be reset with each new API request made by 
		 * the object.
		 * 
		 * @return boolean
		 */
		public function clearLog(){
			$this->log=array();
			$this->log[]='Log cleared';
			return true;
		}
		
		/**
		 * This method allows to set cookie container for cURL calls. If this is set to false, 
		 * then cookies are not used at all. $location is the file that is used for cookie 
		 * container, it is automatically created if the file does not exist.
		 *
		 * @param boolean|string $location cookie container file location in filesystem
		 * @return boolean
		 */
		public function setCookieContainer($location=false){
		
			// If value is anything but false
			if($location){
				// Testing if file exists or attempting to create that file
				if(file_exists($location) && is_writable($location)){
					$this->cookieContainer=$location;
					$this->log[]='Cookie container set to: '.$location;
					return true;
				} elseif(file_put_contents($location,'')){
					$this->cookieContainer=$location;
					$this->log[]='Cookie container set to: '.$location;
					return true;
				} else {
					// Cookie container is not accessible
					$this->cookieContainer=false;
					$this->log[]='Cannot set cookie container to: '.$location;
					return false;
				}
			} else {
				$this->cookieContainer=false;
				$this->log[]='Cookies are turned off';
				return true;
			}
			
		}
		
		/**
		 * This sets the CA Cert container file. This file can be gotten from various sources, 
		 * like http://curl.haxx.se/docs/caextract.html and can be used to verify host and 
		 * peer data with cURL requests.
		 *
		 * @param boolean|string $location cookie container file location in filesystem
		 * @return boolean
		 */
		public function setCertificateContainer($location=false){
		
			// If value is anything but false
			if($location){
				// Testing if file exists or attempting to create that file
				if(file_exists($location)){
					$this->certificateContainer=$location;
					$this->log[]='Certificate container set to: '.$location;
					return true;
				} else {
					// Certificate container is not found
					$this->certificateContainer=false;
					$this->log[]='Cannot set certificate container to: '.$location;
					return false;
				}
			} else {
				$this->certificateContainer=false;
				$this->log[]='Certificate container is not used';
				return true;
			}
			
		}
		
		/**
		 * This method will clear and delete all cookies stored in cookie container defined 
		 * in $location or in general. Warning, this method technically removes the contents 
		 * of any writable file if set in $location. If $location is not set, then it attempts 
		 * to use the previously defined cookie container.
		 *
		 * @param boolean|string $location location of cookies file, if this is not set then uses current one
		 * @return boolean
		 */
		public function clearCookieContainer($location=false){
		
			// If location was not sent with the request, then using the existing one
			if(!$location){
				$location=$this->cookieContainer;
			}
			if($location){
				if(file_exists($location) && is_writable($location) && file_put_contents($location,'')){
					$this->log[]='Cookies cleared';
					return true;
				} else {
					$this->log[]='Cannot clear cookies';
					return false;
				}
			} else {
				return false;
			}
			
		}
		
		/**
		 * This method returns currently used token, if one exists. This can be stored for 
		 * subsequent requests with Wrapper (or manually over HTTP).
		 *
		 * @return string or false if token does not exist
		 */
		public function getToken(){
			// Returning from the state array
			return $this->apiState['apiToken'];
		}
		
	// INPUT
		
		/**
		 * This method is used to set an input value in the API Wrapper. $input is the key 
		 * to set and $value is the value of the input key. $input can also be an array, 
		 * in which case multiple input values will be set in the same call. This method 
		 * calls private inputSetter() function that checks the input value for any internal 
		 * flags that might not actually be sent as an input to the API.
		 * 
		 * @param string|array $input key of the input data, or an array of keys and values
		 * @param boolean|string $value value of the input data
		 * @return boolean
		 */
		public function setInput($input,$value=false){
		
			// If this is an array then it populates input array recursively
			if(is_array($input)){
				foreach($input as $key=>$val){
					// Value is filtered through inputSetter function
					$this->inputSetter($key,$val);
				}
			} else {
				// Value is filtered through inputSetter function
				$this->inputSetter($input,$value);
			}
			return true;
			
		}
		
		/**
		 * This is a helper function that setInput() method uses to actually assign $value 
		 * to the $input keyword. A lot of the keywords set carry additional functionality 
		 * that may entirely be API Wrapper specific. This method also creates a log entry 
		 * for any value that is changed or set.
		 * 
		 * @param string $input input data key
		 * @param string $value input data value
		 * @return boolean
		 */
		private function inputSetter($input,$value){
		
			// Different input keys carry additional functionality
			switch($input){
				case 'www-api':
					$this->apiAddress=$value;
					$this->log[]='API address changed to: '.$value;
					break;
				case 'www-hash-validation':
					$this->apiState['apiHashValidation']=$value;
					if($value){
						$this->log[]='API hash validation is used';
					} else {
						$this->log[]='API hash validation is not used';
					}
					break;
				case 'www-secret-key':
					$this->apiState['apiSecretKey']=$value;
					$this->log[]='API secret key set to: '.$value;
					break;
				case 'www-token':
					$this->apiState['apiToken']=$value;
					$this->log[]='API session token set to: '.$value;
					break;
				case 'www-profile':
					$this->apiState['apiProfile']=$value;
					$this->log[]='API profile set to: '.$value;
					break;
				case 'www-version':
					$this->apiState['apiVersion']=$value;
					$this->log[]='API version set to: '.$value;
					break;
				case 'www-state':
					$this->apiState['apiStateKey']=$value;
					$this->log[]='API state check key set to: '.$value;
					break;
				case 'www-headers':
					$this->apiState['headers']=$value;
					if($value){
						$this->log[]='System specific parameters will be returned as headers';
					} else {
						$this->log[]='System specific parameters will be returned as part of response';
					}
					break;
				case 'www-return-hash':
					$this->apiState['returnHash']=$value;
					if($value){
						$this->log[]='API request will require hash validation';
					} else {
						$this->log[]='API request will not require hash validation';
					}
					break;
				case 'www-return-timestamp':
					$this->apiState['returnTimestamp']=$value;
					if($value){
						$this->log[]='API request will require timestamp validation';
					} else {
						$this->log[]='API request will not require timestamp validation';
					}
					break;
				case 'www-public-token':
					$this->apiState['apiPublicToken']=$value;
					if($value){
						$this->log[]='API public token set to: '.$value;
					} else {
						$this->log[]='API public token unset';
					}
					break;
				case 'www-return-type':
					$this->inputData[$input]=$value;
					$this->log[]='Input value of "'.$input.'" set to: '.$value;
					if($value!='json' && $value!='serializedarray' && $value!='querystring'){
						$this->apiState['unserialize']=false;
						$this->log[]='API result cannot be unserialized, setting unserialize flag to false';
					}
					break;
				case 'www-request-timeout':
					$this->apiState['requestTimeout']=$value;
					$this->log[]='API request timeout set to: '.$value;
					break;
				case 'www-unserialize':
					$this->apiState['unserialize']=$value;
					$this->log[]='API serialization value set to: '.$value;
					break;
				case 'www-true-callback':
					$this->apiState['trueCallback']=$value;
					if($value){
						if(gettype($value)!=='object'){
							$this->log[]='API return true/success callback set to: '.$value.'()';
						} else {
							$this->log[]='API return true/success callback uses an anonymous function';
						}
					}
					break;
				case 'www-false-callback':
					$this->apiState['falseCallback']=$value;
					if($value){
						if(gettype($value)!=='object'){
							$this->log[]='API return false/failure callback set to: '.$value.'()';
						} else {
							$this->log[]='API return false/failure callback uses an anonymous function';
						}
					}
					break;
				case 'www-error-callback':
					$this->apiState['errorCallback']=$value;
					if($value){
						if(gettype($value)!=='object'){
							$this->log[]='API return error callback set to: '.$value.'()';
						} else {
							$this->log[]='API return error callback uses an anonymous function';
						}
					}
					break;
				case 'www-true-callback-parameters':
					$this->apiState['trueCallbackParameters']=$value;
					if($value){
						$this->log[]='API return true/success callback parameters set';
					}
					break;
				case 'www-false-callback-parameters':
					$this->apiState['falseCallbackParameters']=$value;
					if($value){
						$this->log[]='API return false/failure callback parameters set';
					}
					break;
				case 'www-last-modified':
					$this->apiState['lastModified']=$value;
					$this->log[]='API last-modified request time set to: '.$value;
					break;
				case 'www-language':
					$this->apiLanguage=$value;
					if($value){
						$this->log[]='API result language set to: '.$value;
					} else {
						$this->log[]='API result language uninitialized';
					}
					break;
				case 'www-get-limit':
					$this->getLimit=$value;
					$this->log[]='Maximum GET string length is set to: '.$value;
					break;
				case 'www-reset-log':
					$this->resetLog=$value;
					if($value){
						$this->log[]='Log is reset after each new request';
					} else {
						$this->log[]='Log is kept for multiple requests';
					}
					break;
				case 'www-timestamp-duration':
					$this->apiState['timestampDuration']=$value;
					$this->log[]='API valid timestamp duration set to: '.$value;
					break;
				case 'www-output':
					$this->log[]='Ignoring www-output setting, wrapper always requires output to be set to true';
					break;
				case 'www-cookie-container':
					return $this->setCookieContainer($value);
					break;
				case 'www-certificate-container':
					return $this->setcertificateContainer($value);
					break;
				default:
					// True/false conversions for input strings
					if($value===true){
						$value=1;
					} elseif($value===false){
						$value=0;
					}
					$this->inputData[$input]=$value;
					$this->log[]='Input value of "'.$input.'" set to: '.$value;
					break;				
			}
			return true;
			
		}
		
		/**
		 * This method sets a crypted input data that will be encrypted with secret key or a 
		 * token prior to making the HTTP request. This allows to transmit secure data across 
		 * servers. Note that crypted input should not be used when hash validation is not used 
		 * for making a request, since the token or secret key would also be sent with the 
		 * request. $input is the keyword and $value is the value. $input can also be an array 
		 * of keys and values.
		 *
		 * @param string|array $input input data key or an array of keys and values
		 * @param boolean|string $value input data value
		 * @return boolean
		 */
		public function setCryptedInput($input,$value=false){
		
			// If this is an array then it populates input array recursively
			if(is_array($input)){
				foreach($input as $key=>$val){
					// Value is converted to string to make sure that json_encode() includes quotes in hash calculations
					$this->cryptedData[$key]=$val;
					$this->log[]='Crypted input value of "'.$key.'" set to: '.$val;
				}
			} else {
				// Value is simply added to inputData array
				$this->cryptedData[$input]=$value;
				$this->log[]='Crypted input value of "'.$input.'" set to: '.$value;
			}
			return true;
			
		}
		
		/**
		 * This method sets files that will be uploaded with the API request. $file is the name
		 * of the file and $location is the address of the file in filesystem. Multiple files
		 * can be attached at once by sending $file as an array of filenames and locations. This 
		 * method also checks if the file actually exists.
		 *
		 * @param string|array $file file keyword or an array of keywords and file locations
		 * @param boolean|string $location file location in filesystem
		 * @return boolean/error depending on whether file exists or not
		 */
		public function setFile($file,$location=false){
		
			// If this is an array then it populates input array recursively
			if(is_array($file)){
				foreach($file as $key=>$loc){
					// File needs to exist in filesystem
					if($loc && file_exists($loc)){
						$this->inputFiles[$key]=$loc;
						$this->log[]='Input file "'.$key.'" location set to: '.$loc;
					} else {
						trigger_error('File location not defined or file does not exist in that location: '.$loc,E_USER_ERROR);
					}
				}
			} else {
				// File needs to exist in filesystem
				if($location && file_exists($location)){
					$this->inputFiles[$file]=$location;
					$this->log[]='Input file "'.$file.'" location set to: '.$location;
				} else {
					trigger_error('File location not defined or file does not exist in that location: '.$location,E_USER_ERROR);
				}
			}
			return true;
			
		}
		
		/**
		 * This method resets the state of API. It is called after each API request with 
		 * $reset set to false. To entirely reset the state of API $reset should be 
		 * set to true and this will reset everything except the log file.
		 * 
		 * @param boolean $reset whether to also reset authentication and other state data
		 * @return boolean
		 */
		public function clearInput($reset=false){
		
			// If authentication should also be cleared
			if($reset){
				$this->apiState['apiProfile']=false;
				$this->apiState['apiSecretKey']=false;
				$this->apiState['apiToken']=false;
				$this->apiState['apiPublicToken']=false;
				$this->apiState['apiHashValidation']=true;
				$this->apiState['apiVersion']=false;
				$this->apiState['headers']=false;
				$this->apiState['returnHash']=false;
				$this->apiState['returnTimestamp']=false;
				$this->apiState['requestTimeout']=10;
				$this->apiState['timestampDuration']=60;
			}
			// Resetting the API state test key
			$this->apiState['apiStateKey']=false;
			// Neutralizing state settings
			$this->apiState['unserialize']=true;
			$this->apiState['lastModified']=false;
			// Neutralizing callbacks
			$this->apiState['trueCallback']=false;
			$this->apiState['falseCallback']=false;
			$this->apiState['errorCallback']=false;
			$this->apiState['trueCallbackParameters']=false;
			$this->apiState['falseCallbackParameters']=false;
			// Input data
			$this->inputData=array();
			$this->cryptedData=array();
			$this->inputFiles=array();
			// Log entry
			$this->log[]='Input data, crypted input and file data is unset';
			return true;
			
		}
		
	// SENDING REQUEST
		
		/**
		 * This method executes the API request by building the request based on set input 
		 * data and sending it to API using cURL or file_get_contents() methods. It also 
		 * builds all validations as well as validates the returned response from the server 
		 * and calls callback functions, if they are set. It is possible to send input 
		 * variables directly with a single call by supplying $variables, $fileVariables and 
		 * $cryptedVariables arrays.
		 * 
		 * @param boolean|array $variables array of input variables
		 * @param boolean|array $fileVariables array of filenames and locations to upload
		 * @param boolean|array $cryptedVariables array of input data to be encrypted
		 * @return array/string depending on what is requested
		 */
		public function sendRequest($variables=false,$fileVariables=false,$cryptedVariables=false){
		
			// If log is assigned to be reset with each new API request
			if($this->resetLog){
				$this->clearLog();
			}
		
			// In case variables have been sent with a single request
			if($variables && is_array($variables)){
				foreach($variables as $key=>$value){
					// Setting variable through input setter
					$this->setInput($key,$value);
				}
			}
			if($fileVariables && is_array($fileVariables)){
				foreach($fileVariables as $key=>$value){
					// Setting variable through input setter
					$this->setFile($key,$value);
				}
			}
			if($cryptedVariables && is_array($cryptedVariables)){
				foreach($cryptedVariables as $key=>$value){
					// Setting variable through input setter
					$this->setCryptedInput($key,$value);
				}
			}
		
			// This is the input data used
			$thisInputData=$this->inputData;
			$thisCryptedData=$this->cryptedData;
			
			// Current state settings
			$thisApiState=$this->apiState;
			
			// Assigning authentication options that are sent with the request
			if($thisApiState['apiProfile']!=false){
				$thisInputData['www-profile']=$thisApiState['apiProfile'];
			}
			// Assigning API version, if it is set
			if($thisApiState['apiVersion']!=false){
				$thisInputData['www-version']=$thisApiState['apiVersion'];
			}
			// Assigning the state check key
			if($thisApiState['apiStateKey']!=false){
				$thisInputData['www-state']=$thisApiState['apiStateKey'];
			}
			// Notifying API to return www-* prefix data in headers
			if($thisApiState['headers']!=false){
				$thisInputData['www-headers']=1;
			}
			// Assigning return-timestamp flag to request
			if($thisApiState['returnTimestamp']==true || $thisApiState['returnTimestamp']==1){
				$thisInputData['www-return-timestamp']=1;
			}
			// Assigning return-hash flag to request
			if($thisApiState['returnHash']==true || $thisApiState['returnHash']==1){
				$thisInputData['www-return-hash']=1;
			}
			// Assigning public API token as part of the request
			if($thisApiState['apiPublicToken']){
				$thisInputData['www-public-token']=$thisApiState['apiPublicToken'];
			}
			
			// If language is set
			if($this->apiLanguage){
				$thisInputData['www-language']=$this->apiLanguage;
			}
			
			// Clears the source input data
			$this->clearInput();
		
			// Returns false if there is an existing critical error
			if($this->criticalError){
				return $this->errorHandler($thisInputData,$this->responseCode,$this->errorMessage,$thisApiState['errorCallback']);
			}
			
			// Log entry
			$this->log[]='Starting to build request';
		
			// Correct request requires command to be set
			if(!isset($thisInputData['www-command'])){
				return $this->errorHandler($thisInputData,201,'API command is not set, this is required',$thisApiState['errorCallback']);
			}
		
			// If default value is set, then it is removed
			if(isset($thisInputData['www-return-type']) && $thisInputData['www-return-type']=='json'){
				$this->log[]='Since www-return-type is set to default value, it is removed from input data';
				unset($thisInputData['www-return-type']);
			}
			// If default value is set, then it is removed
			if(isset($thisInputData['www-cache-timeout']) && $thisInputData['www-cache-timeout']==0){
				$this->log[]='Since www-cache-timeout is set to default value, it is removed from input data';
				unset($thisInputData['www-cache-timeout']);
			}
			// If default value is set, then it is removed
			if(isset($thisInputData['www-minify']) && $thisInputData['www-minify']==false){
				$this->log[]='Since www-minify is set to default value, it is removed from input data';
				unset($thisInputData['www-minify']);
			}
			
			
			// If encryption key is set, then this is sent together with crypted data
			if($thisApiState['apiProfile'] && isset($thisApiState['apiSecretKey'],$thisInputData['www-crypt-output'])){
				$this->log[]='Crypt output key was set as regular input for non-public profile API request, it is moved to crypted input instead';
				$thisCryptedData['www-crypt-output']=$thisInputData['www-crypt-output'];
				unset($thisInputData['www-crypt-output']);
			}
			
			// If profile is used, then timestamp will also be sent with the request
			if($thisApiState['apiProfile']){
				// Timestamp is required in API requests since it will be used for request validation and replay attack protection
				if(!isset($thisInputData['www-timestamp'])){
					$thisInputData['www-timestamp']=time();
				}
			}
		
			// If API secret key is set, then wrapper assumes that non-public profile is used, thus hash and timestamp have to be included
			if($thisApiState['apiSecretKey']){
			
				// Log entry
				$this->log[]='API secret key set, hash authentication will be used';
				// If crypted data array is populated, then this data is encrypted in www-crypt-input key
				if(!isset($thisInputData['www-crypt-input']) && !empty($thisCryptedData)){
					// This is only possible if API token is set
					if($thisApiState['apiSecretKey']){
						// Mcrypt extension is required
						if(extension_loaded('mcrypt')){
							// Data is encrypted with Rijndael 256bit encryption
							if($thisApiState['apiToken']){
								$thisInputData['www-crypt-input']=base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256,md5($thisApiState['apiToken']),json_encode($thisCryptedData),MCRYPT_MODE_CBC,md5($thisApiState['apiSecretKey'])));
							} else {
								$thisInputData['www-crypt-input']=base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256,md5($thisApiState['apiSecretKey']),json_encode($thisCryptedData),MCRYPT_MODE_ECB));
							}
							$this->log[]='Crypted input created using JSON encoded input data, token and secret key';
						} else {
							return $this->errorHandler($thisInputData,203,'Unable to encrypt data, server configuration problem',$thisApiState['errorCallback']);
						}
					} else {
						return $this->errorHandler($thisInputData,202,'Crypted input can only be used with a set secret key',$thisApiState['errorCallback']);
					}
				}
				
				// If API hash validation is used
				if($thisApiState['apiHashValidation']){
					// Validation hash is generated based on current serialization option
					if(!isset($thisInputData['www-hash'])){
						// Calculating validation hash
						if($thisApiState['apiToken'] && $thisInputData['www-command']!='www-create-session'){
							$thisInputData['www-hash']=$this->validationHash($thisInputData,$thisApiState['apiToken'].$thisApiState['apiSecretKey']);
						} else {
							$thisInputData['www-hash']=$this->validationHash($thisInputData,$thisApiState['apiSecretKey']);
						}
					}
					// Log entry
					if($thisApiState['apiToken']){
						$this->log[]='Validation hash created using JSON encoded input data, API token and secret key';
					} else {
						$this->log[]='Validation hash created using JSON encoded input data and secret key';
					}
				} else {
					// Attaching secret key or token to the request
					if($thisInputData['www-command']=='www-create-session' && $thisApiState['apiSecretKey']){
						$thisInputData['www-secret-key']=$thisApiState['apiSecretKey'];
						$this->log[]='Validation will be secret key based';
					} elseif($thisApiState['apiToken']){
						$thisInputData['www-token']=$thisApiState['apiToken'];
						$this->log[]='Validation will be session token based';
					}
				}
				
			} else {
			
				// Token-only validation means that token will be sent to the server, but data itself will not be hashed. This works like a cookie.
				if($thisApiState['apiToken']){
					// Adding token to input if it is set
					$thisInputData['www-token']=$thisApiState['apiToken'];
					// Log entry
					$this->log[]='Using token-only validation';
				} else {
					// Log entry
					$this->log[]='API secret key is not set, hash validation will not be used';
				}
				
			}
			
			// MAKING A REQUEST
			
				// Building the request URL
				$requestURL=$this->apiAddress;
				$requestData=http_build_query($thisInputData);
				$getRequestLength=strlen($requestURL.'?'.$requestData);
				
				// Get request is made if the URL is shorter than 2048 bytes (2KB).
				// While servers can easily handle 8KB of data, servers are recommended to be vary if the GET request is longer than 2KB
				if($getRequestLength<=$this->getLimit && empty($this->inputFiles)){
				
					// cURL is used unless it is not supported on the server
					if($this->curlEnabled){
					
						// Log entry
						$this->log[]='Making GET request to API using cURL to URL: '.$requestURL.'?'.$requestData;
						
						// Initializing cURL object
						$cURL=curl_init();
						// Setting cURL options
						$requestOptions=array(
							CURLOPT_URL=>$requestURL.'?'.$requestData,
							CURLOPT_REFERER=>((!isset($_SERVER['HTTPS']) || ($_SERVER['HTTPS']!=1 && $_SERVER['HTTPS']!='on'))?'http://':'https://').$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'],
							CURLOPT_HTTPGET=>true,
							CURLOPT_TIMEOUT=>$thisApiState['requestTimeout'],
							CURLOPT_USERAGENT=>$this->userAgent,
							CURLOPT_HEADER=>true,
							CURLOPT_RETURNTRANSFER=>true,
							CURLOPT_FOLLOWLOCATION=>false
						);
						
						// Cookies
						if($this->cookieContainer){
							curl_setopt_array($cURL,array(CURLOPT_COOKIESESSION=>false,CURLOPT_COOKIEFILE=>$this->cookieContainer,CURLOPT_COOKIEJAR=>$this->cookieContainer));
						} else {
							curl_setopt($cURL,CURLOPT_COOKIESESSION,true);
						}
						
						// Certificates
						if($this->certificateContainer){
							curl_setopt_array($cURL,array(CURLOPT_SSL_VERIFYPEER=>true,CURLOPT_SSL_VERIFYHOST=>2,CURLOPT_CAINFO=>$this->certificateContainer));
						} else {
							curl_setopt_array($cURL,array(CURLOPT_SSL_VERIFYPEER=>false,CURLOPT_SSL_VERIFYHOST=>false));
						}
						
						// If last modified header is sent
						if($thisApiState['lastModified']){
							curl_setopt($cURL,CURLOPT_HTTPHEADER,array('If-Modified-Since: '.gmdate('D, d M Y H:i:s',$thisApiState['lastModified']).' GMT'));
						}
						
						// Assigning options to cURL object
						curl_setopt_array($cURL,$requestOptions);
						
						// Executing the request
						$resultData=curl_exec($cURL);
						list($resultHeaders,$resultData)=explode("\r\n\r\n",$resultData,2);
						$resultHeaders=explode("\n",$resultHeaders);
						
						// Returning false if the request failed
						if(!$resultData){
							if($thisApiState['lastModified'] && curl_getinfo($cURL,CURLINFO_HTTP_CODE)==304){
								// Closing the resource
								curl_close($cURL);
								return $this->errorHandler($thisInputData,214,'Not modified',$thisApiState['errorCallback']);
							} else {
								$error='POST request failed: cURL error '.curl_getinfo($cURL,CURLINFO_HTTP_CODE).' - '.curl_error($cURL);
								// Closing the resource
								curl_close($cURL);
								return $this->errorHandler($thisInputData,204,$error,$thisApiState['errorCallback']);
							}
						} else {
							$this->log[]='GET request successful: '.curl_getinfo($cURL,CURLINFO_HTTP_CODE);
						}
						
						// Closing the resource
						curl_close($cURL);
						
					} else {
					
						// Log entry
						$this->log[]='Making GET request to API using file-get-contents to URL: '.$requestURL;
						// GET request an also be made by file_get_contents()
						if(!$resultData=file_get_contents($requestURL.'?'.$requestData)){
							return $this->errorHandler($thisInputData,204,'GET request failed: file_get_contents() failed',$thisApiState['errorCallback']);
						}
						// It is not possible to get headers with file_get_contents()
						$resultHeaders=array();
						
					}
					
				} else {
				
					// cURL is used unless it is not supported on the server
					if($this->curlEnabled){
					
						// If there are no input files, then the entire data stream is added as a POST variable stream
						if(!empty($this->inputFiles)){
						
							// This is the variable that carries POST variables that will be sent to cURL
							$postData=$thisInputData;
							// Regular input variables can be sent over GET if they are not too long
							if($getRequestLength<=$this->getLimit){
								// Changes the request URL and clears input array from data
								$requestURL=$this->apiAddress.'?'.$requestData;
								$postData=array();
							} else {
								// This stores input variables that include @ symbol that cURL interprets for file upload
								$securityInput=array();
								// Escaping possible security hole in cURL by escaping the @ sign
								foreach($postData as $key=>$val){
									// If the first character is @
									if($val[0]=='@'){
										// Adding the variable to separate array and clearing original input array
										$securityInput[$key]=$val;
										unset($postData[$key]);
										// Log entry
										$this->log[]='Variable '.$key.' has a value that starts with @, sending it as GET variable';
									} else {
										// Log entry
										$this->log[]='Attaching variable to request: '.$key.'='.$val;
									}
								}
								// If unsecure variables are part of the POST request
								// Please note that if this data is too long for GET then the request will fail entirely
								if(!empty($securityInput)){
									$requestURL=$requestURL.'?'.http_build_query($securityInput);
								}
							}
							
							// Attaching files to the request
							foreach($this->inputFiles as $file=>$location){
								$this->log[]='Attaching a file to request: '.$location;
								$postData[$file]='@'.$location;
							}
							
						} else {
							// This sends all the variables to cURL as a string
							$postData=$requestData;
						}
							
						// Log entry
						$this->log[]='Making POST request to API using cURL to URL: '.$requestURL;
						
						// If the request GET URL is too long, then request is not made
						if(strlen($requestURL)>$this->getLimit){
							return $this->errorHandler($thisInputData,205,'POST request failed: Request URL is too long',$thisApiState['errorCallback']);
						}

						// Initializing cURL object
						$cURL=curl_init();
						// Setting cURL options
						$requestOptions=array(
							CURLOPT_URL=>$requestURL,
							CURLOPT_REFERER=>((!isset($_SERVER['HTTPS']) || ($_SERVER['HTTPS']!=1 && $_SERVER['HTTPS']!='on'))?'http://':'https://').$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'],
							CURLOPT_POST=>true,
							CURLOPT_POSTFIELDS=>$postData,
							CURLOPT_TIMEOUT=>$thisApiState['requestTimeout'],
							CURLOPT_USERAGENT=>$this->userAgent,
							CURLOPT_HEADER=>true,
							CURLOPT_RETURNTRANSFER=>true,
							CURLOPT_FOLLOWLOCATION=>false
						);
						
						// Cookies
						if($this->cookieContainer){
							curl_setopt_array($cURL,array(CURLOPT_COOKIESESSION=>false,CURLOPT_COOKIEFILE=>$this->cookieContainer,CURLOPT_COOKIEJAR=>$this->cookieContainer));
						} else {
							curl_setopt($cURL,CURLOPT_COOKIESESSION,true);
						}
						
						// Certificates
						if($this->certificateContainer){
							curl_setopt_array($cURL,array(CURLOPT_SSL_VERIFYPEER=>true,CURLOPT_SSL_VERIFYHOST=>2,CURLOPT_CAINFO=>$this->certificateContainer));
						} else {
							curl_setopt_array($cURL,array(CURLOPT_SSL_VERIFYPEER=>false,CURLOPT_SSL_VERIFYHOST=>false));
						}
						
						// If last modified header is sent
						if($thisApiState['lastModified']){
							curl_setopt($cURL,CURLOPT_HTTPHEADER,array('If-Modified-Since: '.gmdate('D, d M Y H:i:s',$thisApiState['lastModified']).' GMT'));
						}
						
						// Assigning options to cURL object
						curl_setopt_array($cURL,$requestOptions);
						
						// Executing the request
						$resultData=curl_exec($cURL);
						list($resultHeaders,$resultData)=explode("\r\n\r\n",$resultData,2);
						$resultHeaders=explode("\n",$resultHeaders);
					
						// Log entry
						$this->log[]='Making POST request to API using cURL to URL: '.$this->apiAddress;
						
						// Returning false if the request failed
						if(!$resultData){
							if($thisApiState['lastModified'] && curl_getinfo($cURL,CURLINFO_HTTP_CODE)==304){
								// Closing the resource
								curl_close($cURL);
								return $this->errorHandler($thisInputData,214,'Not modified',$thisApiState['errorCallback']);
							} else {
								$error='POST request failed: cURL error '.curl_getinfo($cURL,CURLINFO_HTTP_CODE).' - '.curl_error($cURL);
								// Closing the resource
								curl_close($cURL);
								return $this->errorHandler($thisInputData,205,$error,$thisApiState['errorCallback']);
							}
						} else {
							$this->log[]='POST request successful: '.curl_getinfo($cURL,CURLINFO_HTTP_CODE);
						}
						
						// Closing the resource
						curl_close($cURL);
					
					} else {
						return $this->errorHandler($thisInputData,205,'POST request failed: cURL is not supported',$thisApiState['errorCallback']);
					}
				}
				
				// Log entry
				$this->log[]='Result of request: '.$resultData;
				$this->log[]='Headers of request: '.implode(',',$resultHeaders);
				
			// DECRYPTION
				
				// If requested data was encrypted, then this attempts to decrypt the data
				// This also checks to make sure that a serialized data was not returned (which usually means an error)
				if(strpos($resultData,'{')===false && strpos($resultData,'[')===false && isset($thisCryptedData['www-crypt-output']) || isset($thisInputData['www-crypt-output'])){
					// Decryption is different based on whether secret key was used or not
					if($thisApiState['apiSecretKey']){
						// If secret key was set, then decryption uses the secret key for initialization vector
						$resultData=mcrypt_decrypt(MCRYPT_RIJNDAEL_256,md5($thisApiState['apiToken']),base64_decode($resultData),MCRYPT_MODE_CBC,md5($thisApiState['apiSecretKey']));
					} else {
						// Without secret key the system assumes that public profile is used and decryption is done in ECB mode
						$resultData=mcrypt_decrypt(MCRYPT_RIJNDAEL_256,md5($thisApiState['apiToken']),base64_decode($resultData),MCRYPT_MODE_ECB);
					}
					// If decryption was a success
					if($resultData){
						$resultData=trim($resultData);
						$this->log[]='Result of decrypted request: '.$resultData;
					} else {
						return $this->errorHandler($thisInputData,206,'Output decryption has failed',$thisApiState['errorCallback']);
					}
				}
				
			// Returning the result directly if the result is not intended to be unserialized
			if(!$thisApiState['unserialize']){
			
				// Log entry for returning data
				$this->log[]='Returning result without unserializing';
				// Data is simply returned if serialization was not requested
				return $resultData;
				
			} else {
				
				// PARSING REQUEST RESULT
				
					// If unserialize command was set and the data type was JSON or serialized array, then it is returned as serialized
					if(!isset($thisInputData['www-return-type']) || $thisInputData['www-return-type']=='json'){
						// JSON support is required
						$resultData=json_decode($resultData,true);
					} else if($thisInputData['www-return-type']=='serializedarray'){
						// Return data is unserialized
						$resultData=unserialize($resultData,true);
						if(!$resultData){
							return $this->errorHandler($thisInputData,207,'Cannot unserialize returned data: unserialize() failed',$thisApiState['errorCallback']);
						} else {
							$this->log[]='Returning unserialized result';
						}
					} else if($thisInputData['www-return-type']=='querystring'){
						// Return data is filtered through string parsing and url decoding to create return array
						parse_str(urldecode($resultData),$resultData);
						if(!$resultData){
							return $this->errorHandler($thisInputData,207,'Cannot unserialize returned data: Cannot parse query data string',$thisApiState['errorCallback']);
						} else {
							$this->log[]='Returning parsed query string result';
						}
					}
					
					// Default response code and message
					$responseCode=500;
					$responseMessage='OK';
					
					// Checking if the result had differend response code and message
					if(isset($resultData['www-response-code'])){
						$responseCode=$resultData['www-response-code'];
						if(isset($resultData['www-message'])){
							$responseMessage=$resultData['www-message'];
						}
					}
					
				// WAVE HEADER RESPONSES
			
					// If headers are assigned to contain www-* response values
					if($thisApiState['headers']){
						if(isset($resultHeaders['www-response-code'])){
							$responseCode=$resultHeaders['www-response-code'];
						}
						if(isset($resultHeaders['www-message'])){
							$responseMessage=$resultHeaders['www-message'];
						}
					}
					
				// ERRORS
				
					if($responseCode<400){
						if(isset($responseMessage)){
							return $this->errorHandler($thisInputData,$responseCode,$responseMessage,$thisApiState['errorCallback']);
						} else {
							return $this->errorHandler($thisInputData,$responseCode,'Error',$thisApiState['errorCallback']);
						}
					}
					
				// RESULT VALIDATION
				
					// Result validation only applies to non-public profiles
					if($thisApiState['apiProfile'] && ($thisApiState['returnHash'] || $thisApiState['returnTimestamp'])){
						
						// If it was requested that validation timestamp is returned
						if($thisApiState['returnTimestamp']){
							if(isset($resultData['www-timestamp'])){
								// Making sure that the returned result is within accepted time limit
								if((time()-$thisApiState['timestampDuration'])>$resultData['www-timestamp']){
									return $this->errorHandler($thisInputData,209,'Validation timestamp is too old',$thisApiState['errorCallback']);
								}
							} else {
								return $this->errorHandler($thisInputData,208,'Validation data missing: Timestamp was not returned',$thisApiState['errorCallback']);
							}
						}
					
						// If it was requested that validation timestamp is returned
						if($thisApiState['apiStateKey']){
							if(!isset($resultData['www-state']) || $resultData['www-state']!=$thisApiState['apiStateKey']){
								return $this->errorHandler($thisInputData,210,'Validation state keys do not match',$thisApiState['errorCallback']);
							}
						}
						
						// If it was requested that validation hash is returned
						if($thisApiState['returnHash']){
							// Hash and timestamp have to be defined in response
							if(isset($resultData['www-hash'])){
								// Assigning returned array to hash validation array
								$validationData=$resultData;
								// Hash itself is removed from validation
								unset($validationData['www-hash']);
								// Validation depends on whether session creation or destruction commands were called
								if($thisInputData['www-command']=='www-create-session'){
									$hash=$this->validationHash($validationData,$thisApiState['apiSecretKey']);
								} else {
									$hash=$this->validationHash($validationData,$thisApiState['apiToken'].$thisApiState['apiSecretKey']);
								}
								// Unsetting the validation hash since it is not used
								unset($validationData);
								// If sent hash is the same as calculated hash
								if($hash==$resultData['www-hash']){
									$this->log[]='Hash validation successful';
								} else {
									return $this->errorHandler($thisInputData,210,'Hash validation failed',$thisApiState['errorCallback']);
								}
							} else {
								return $this->errorHandler($thisInputData,208,'Validation data missing: Hash was not returned',$thisApiState['errorCallback']);
							}
						}
					
					}
				
				// Resetting the error variables
				$this->responseCode=false;
				$this->errorMessage=false;
				
				// If this command was to create a token
				if($thisInputData['www-command']=='www-create-session' && isset($resultData['www-token'])){
					$this->apiState['apiToken']=$resultData['www-token'];
					$this->log[]='Session token was found in reply, API session token set to: '.$resultData['www-token'];
				} elseif($thisInputData['www-command']=='www-destroy-session'){
					$this->apiState['apiToken']=false;
					$this->log[]='Session has been destroyed';
				}
				
				// If callback has been defined
				if($thisApiState['trueCallback'] && $responseCode>=500){
					// If the callback is a function name and not a function itself
					if(gettype($thisApiState['trueCallback'])!=='object'){
						// Calling user function
						if(function_exists($thisApiState['trueCallback'])){
							$this->log[]='Sending data to callback: '.$thisApiState['trueCallback'].'()';
							// Callback execution
							if($thisApiState['trueCallbackParameters']!=false){
								return call_user_func($thisApiState['trueCallback'],$resultData,$thisApiState['trueCallbackParameters']);
							} else {
								return call_user_func($thisApiState['trueCallback'],$resultData);
							}
						} else {
							return $this->errorHandler($thisInputData,216,'Callback method not found: '.$thisApiState['trueCallback'].'()',$thisApiState['errorCallback']);
						}
					} else {
						// Returning data from callback
						if($thisApiState['trueCallbackParameters']!=false){
							return $thisApiState['trueCallback']($resultData,$thisApiState['trueCallbackParameters']);
						} else {
							return $thisApiState['trueCallback']($resultData);
						}
					}
				} elseif($thisApiState['falseCallback'] && $responseCode<500){
					// If the callback is a function name and not a function itself
					if(gettype($thisApiState['falseCallback'])!=='object'){
						// Calling user function
						if(function_exists($thisApiState['falseCallback'])){
							$this->log[]='Sending data to callback: '.$thisApiState['falseCallback'].'()';
							// Callback execution
							if($thisApiState['falseCallbackParameters']!=false){
								return call_user_func($thisApiState['falseCallback'],$resultData,$thisApiState['falseCallbackParameters']);
							} else {
								return call_user_func($thisApiState['falseCallback'],$resultData);
							}
						} else {
							return $this->errorHandler($thisInputData,216,'Callback method not found: '.$thisApiState['falseCallback'].'()',$thisApiState['falseCallback']);
						}
					} else {
						// Returning data from callback
						if($thisApiState['falseCallbackParameters']!=false){
							return $thisApiState['falseCallback']($resultData,$thisApiState['falseCallbackParameters']);
						} else {
							return $thisApiState['falseCallback']($resultData);
						}
					}
				} else {
					// Returning request result
					return $resultData;
				}
			
			}
			
		}
		
	// REQUIRED FUNCTIONS
	
		/**
		 * This method is used to build an input data validation hash string for authenticating 
		 * API requests. The entire input array of $validationData is serialized and hashed 
		 * with SHA-1 and a salt string set in $postFix. This is used for all API requests where 
		 * input has to be validated.
		 *
		 * @param array $validationData array to build a hash from
		 * @param string $postFix string that is used to salt the hash
		 * @return string
		 */
		private function validationHash($validationData,$postFix){
		
			// Sorting and encoding the output data
			$validationData=$this->ksortArray($validationData);
			// Returning validation hash
			return sha1(http_build_query($validationData).$postFix);
			
		}
		
		/**
		 * This is a helper function used by validationHash() function to serialize an array 
		 * recursively. It applies ksort() to main method as well as to all sub-arrays. $data 
		 * is the array to be sorted.
		 * 
		 * @param array|mixed $data variable to be sorted
		 * @return array/mixed
		 */
		private function ksortArray($data){
		
			// Method is based on the current data type
			if(is_array($data)){
				// Sorting the current array
				ksort($data);
				// Sorting every sub-array, if it is one
				$keys=array_keys($data);
				$keySize=sizeOf($keys);
				for($i=0;$i<$keySize;$i++){
					$data[$keys[$i]]=$this->ksortArray($data[$keys[$i]]);
				}
			}
			return $data;
			
		}
		
		/**
		 * This method is simply meant for returning a result if there was an error in the 
		 * sent request.
		 * 
		 * @param array $inputData data that was sent as input
		 * @param string $responseCode response code of the error
		 * @param string $errorMessage verbose error message
		 * @param string|function $errorCallback anonymous function or function name to call
		 * @return boolean/mixed depending on if callback function was used
		 */
		private function errorHandler($inputData,$responseCode,$errorMessage,$errorCallback){
		
			// Assigning error details to object state
			$this->responseCode=$responseCode;
			$this->errorMessage=$errorMessage;
			$this->log[]=$errorMessage;
			// If failure callback has been defined
			if($errorCallback){
				// If the callback is a function name and not a function itself
				if(gettype($errorCallback)!=='object'){
					// Looking for function of that name
					if(function_exists($errorCallback)){
						$this->log[]='Sending failure data to callback: '.$errorCallback.'()';
						// Callback execution
						return call_user_func($errorCallback,array('www-input'=>$inputData,'www-response-code'=>$responseCode,'www-message'=>$errorMessage));
					} else {
						$this->responseCode=216;
						$this->errorMessage='Callback method not found: '.$errorCallback.'()';
						$this->log[]=$this->errorMessage;
						return false;
					}
				} else {
					// Returning data from callback
					return $errorCallback($inputData);
				}
			} else {
				return false;
			}
			
		}

}

?>
For more information send a message to info at phpclasses dot org.