mirror of
				https://github.com/Mibew/mibew.git
				synced 2025-10-26 00:06:55 +03:00 
			
		
		
		
	Created RequestProcessor class
This commit is contained in:
		
							parent
							
								
									e708282a8f
								
							
						
					
					
						commit
						b2a0a6cebd
					
				
							
								
								
									
										444
									
								
								src/messenger/webim/libs/classes/request_processor.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										444
									
								
								src/messenger/webim/libs/classes/request_processor.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,444 @@ | ||||
| <?php | ||||
| /* | ||||
|  * Copyright 2005-2013 the original author or authors. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| /** | ||||
|  * Implements abstract class for request processing | ||||
|  * | ||||
|  * Register events (see RequestProcessor::registerEvents() for details): | ||||
|  *  - <eventPrefix>RequestReceived | ||||
|  *  - <eventPrefix>ReceiveRequestError | ||||
|  *  - <eventPrefix>ResponseReceived | ||||
|  *  - <eventPrefix>CallError | ||||
|  *  - <eventPrefix>FunctionCall | ||||
|  * | ||||
|  * <eventPrefix> variable specifies in RequestProcessor::__construct() | ||||
|  * | ||||
|  * @see RequestProcessor::__construct() | ||||
|  * @see RequestProcessor::registerEvents() | ||||
|  */ | ||||
| abstract class RequestProcessor { | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Instance of the MibewAPI class | ||||
| 	 * @var type | ||||
| 	 */ | ||||
| 	protected $mibewAPI = null; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Prefix that uses for all registered by the class events. | ||||
| 	 * @var string | ||||
| 	 */ | ||||
| 	protected $eventPrefix = ''; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Array of the responses packages | ||||
| 	 * @var array | ||||
| 	 */ | ||||
| 	protected $responses = array(); | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Array of configurations | ||||
| 	 * @var array | ||||
| 	 */ | ||||
| 	protected $config = array(); | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Class constructor | ||||
| 	 * | ||||
| 	 * @param type $config Configuration data. | ||||
| 	 * It must contains following keys: | ||||
| 	 *  - 'signature': Use for verification sender | ||||
| 	 *  - 'trusted_signatures': array of trusted signatures. Uses for identify another | ||||
| 	 *    side of interaction. | ||||
| 	 * And may contains following (if not default values will be used) | ||||
| 	 *  - 'event_prefixport': prefix that uses for all registered by the class events. The default value is the class | ||||
| 	 *    name with first character in lower case | ||||
| 	 * @todo think about errors' level | ||||
| 	 */ | ||||
| 	public function __construct($config) { | ||||
| 		// Check signature
 | ||||
| 		if (! isset($config['signature'])) { | ||||
| 			trigger_error("Signature is not specified", E_USER_ERROR); | ||||
| 		} | ||||
| 
 | ||||
| 		// Check trusted signatures
 | ||||
| 		if (! isset($config['trusted_signatures'])) { | ||||
| 			trigger_error("Trusted signatures is not specified", E_USER_ERROR); | ||||
| 		} | ||||
| 
 | ||||
| 		// Get an instance of the MibewAPI class
 | ||||
| 		$this->mibewAPI = $this->getMibewAPIInstance(); | ||||
| 
 | ||||
| 		// Get class name and prefix for events and etc.
 | ||||
| 		$class_name = get_class($this); | ||||
| 		$this->eventPrefix = empty($config['event_prefix']) | ||||
| 			? strtolower(substr($class_name, 0, 1)) . substr($class_name, 1) | ||||
| 			: $config['event_prefix']; | ||||
| 
 | ||||
| 		// Store config
 | ||||
| 		$this->config = $config; | ||||
| 
 | ||||
| 		// Register Events
 | ||||
| 		$this->registerEvents(); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Proccess received packages | ||||
| 	 * | ||||
| 	 * On any error function returns only boolean false. To handle error add listener to the | ||||
| 	 * "<eventPrefix>ReceiveRequestError" event. | ||||
| 	 * | ||||
| 	 * @param string $package Encoded package | ||||
| 	 * @return boolean true if request processed succussfully or false on failure | ||||
| 	 */ | ||||
| 	public function receiveRequest($package){ | ||||
| 		$dispatcher = EventDispatcher::getInstance(); | ||||
| 		// Try to handle request
 | ||||
| 		try { | ||||
| 			// Decode package
 | ||||
| 			$request_package = $this->mibewAPI->decodePackage( | ||||
| 				$package, | ||||
| 				$this->config['trusted_signatures'] | ||||
| 			); | ||||
| 
 | ||||
| 			// Trigger request received event
 | ||||
| 			$vars = array('package' => $request_package); | ||||
| 			$dispatcher->triggerEvent( | ||||
| 				$this->eventPrefix . 'RequestReceived', | ||||
| 				$vars | ||||
| 			); | ||||
| 			$package = $vars['package']; | ||||
| 
 | ||||
| 			// Process requests in package
 | ||||
| 			// Clear responses
 | ||||
| 			$this->responses = array(); | ||||
| 			foreach ($package['requests'] as $request) { | ||||
| 				// Try to load callback function for this token
 | ||||
| 				$callback = $this->loadCallback($request['token']); | ||||
| 				$need_result = ! is_null($callback); | ||||
| 				$arguments = $this->processRequest($request, $need_result); | ||||
| 
 | ||||
| 				if ($need_result) { | ||||
| 					// There is callback function
 | ||||
| 					// TODO: Think about callback functions nature
 | ||||
| 					$object = $callback['object']; | ||||
| 					$method = $callback['method']; | ||||
| 					$object->$method($arguments); | ||||
| 				} else { | ||||
| 					// There is no callback function
 | ||||
| 					$this->responses[] = $this->mibewAPI->buildResult( | ||||
| 						$request['token'], | ||||
| 						$arguments | ||||
| 					); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			if ($request_package['async']) { | ||||
| 				$this->sendAsyncResponses($this->responses); | ||||
| 			} else { | ||||
| 				$this->sendSyncResponses($this->responses); | ||||
| 			} | ||||
| 
 | ||||
| 			// Output response
 | ||||
| 		} catch (Exception $e) { | ||||
| 			// Something went wrong. Trigger error event
 | ||||
| 			$vars = array('exception' => $e); | ||||
| 			$dispatcher->triggerEvent($this->eventPrefix . 'RequestError', $vars); | ||||
| 			return false; | ||||
| 		} | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Call functions at the other side | ||||
| 	 * | ||||
| 	 * On any error function returns only boolean false. To handle error add listener to the | ||||
| 	 * "<eventPrefix>CallError" event. | ||||
| 	 * | ||||
| 	 * @param array $functions Array of functions. See Mibew API for details. | ||||
| 	 * @param boolean $async True for asynchronous requests and false for synchronous request | ||||
| 	 * @param mixed $callback callback array or null for synchronous requests. | ||||
| 	 * @return mixed request result or boolean false on failure. | ||||
| 	 */ | ||||
| 	public function call($functions, $async, $callback = null) { | ||||
| 		// Get an instance of the EventDispatcher class
 | ||||
| 		$dispatcher = EventDispatcher::getInstance(); | ||||
| 		// Try to call function at Other side
 | ||||
| 		try { | ||||
| 			// Check functions to call
 | ||||
| 			if (! is_array($functions)) { | ||||
| 				throw new RequestProcessorException( | ||||
| 					'#1 argument must be an array!', | ||||
| 					RequestProcessorException::WRONG_ARGUMENTS | ||||
| 				); | ||||
| 			} | ||||
| 			foreach ($functions as $function) { | ||||
| 				$this->mibewAPI->checkFunction($function, true); | ||||
| 			} | ||||
| 
 | ||||
| 			// Create request
 | ||||
| 			$token = md5(microtime() . rand()); | ||||
| 			$request = array( | ||||
| 				'token' => $token, | ||||
| 				'functions' => $functions | ||||
| 			); | ||||
| 
 | ||||
| 			if ($async) { | ||||
| 				// TODO: Think about callbacks
 | ||||
| 				// TODO: May be add exception if $callback = null
 | ||||
| 
 | ||||
| 				// Store callback
 | ||||
| 				$this->storeCallback($callback); | ||||
| 
 | ||||
| 				// Send asynchronous request
 | ||||
| 				$this->sendAsyncRequest($request); | ||||
| 				return true; | ||||
| 			} | ||||
| 
 | ||||
| 			// Send synchronous request
 | ||||
| 			$response_package = $this->sendSyncRequest($request); | ||||
| 
 | ||||
| 			// Trigger response received event
 | ||||
| 			$vars = array('package' => $response_package); | ||||
| 			$dispatcher->triggerEvent($this->eventPrefix . 'ResponseReceived', $vars); | ||||
| 
 | ||||
| 			// Process requests in response
 | ||||
| 			$result = null; | ||||
| 			foreach ($response_package['requests'] as $request) { | ||||
| 				// Use only response with token equals to request token. Ignore other packages.
 | ||||
| 				// TODO: May be not ignore other packages
 | ||||
| 				if ($request['token'] == $token) { | ||||
| 					$result = $this->processRequest($request, true); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			if (is_null($result)) { | ||||
| 				throw new RequestProcessorException( | ||||
| 					"There is no 'result' function in response", | ||||
| 					RequestProcessorException::NO_RESULT_FUNCTION | ||||
| 				); | ||||
| 			} | ||||
| 		} catch (Exception $e) { | ||||
| 			// Trigger error event
 | ||||
| 			$vars = array('exception' => $e); | ||||
| 			$dispatcher->triggerEvent($this->eventPrefix . "CallError", $vars); | ||||
| 			return false; | ||||
| 		} | ||||
| 		return $result; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Register events | ||||
| 	 * | ||||
| 	 * Registered Events: | ||||
| 	 * | ||||
| 	 * 1. "<eventPrefix>RequestReceived" - triggers when request decoded and validate | ||||
| 	 * successfully, before execution functions from request. | ||||
| 	 * | ||||
| 	 * An associative array passed to event handler have following keys: | ||||
| 	 *  - 'package' : decoded and validated package array. See Mibew API for details of the | ||||
| 	 *    package structure | ||||
| 	 * | ||||
| 	 * | ||||
| 	 * 2. "<eventPrefix>ReceiveRequestError" - triggers when error occurs during received | ||||
| 	 * request processing. | ||||
| 	 * | ||||
| 	 * An associative array passed to event handler have following keys: | ||||
| 	 *  - 'exception' : an object of Exception (or inherited) class related to occurred error. | ||||
| 	 * | ||||
| 	 * | ||||
| 	 * 3. "<eventPrefix>ResponseReceived" - triggers when request sent successfully, and | ||||
| 	 * response received. | ||||
| 	 * | ||||
| 	 * An associative array passed to event handler have following keys: | ||||
| 	 *  - 'package' : decoded and validated response package array. See Mibew API for details of | ||||
| 	 *    the package structure. | ||||
| 	 * | ||||
| 	 * | ||||
| 	 * 4. "<eventPrefix>CallError" - triggers when error occurs in | ||||
| 	 * call() method. | ||||
| 	 * | ||||
| 	 * An associative array passed to event handler have following keys: | ||||
| 	 *  - 'exception' : an object of Exception (or inherited) class related to occurred error. | ||||
| 	 * | ||||
| 	 * | ||||
| 	 * 5. "<eventPrefix>FunctionCall" - triggers when function from request calls. | ||||
| 	 * | ||||
| 	 * An associative array passed to event handler is 'function' array. See Mibew API for | ||||
| 	 * detail of the 'function' array structure. | ||||
| 	 * | ||||
| 	 * If function wants to return some results, it should add results to the 'results' element | ||||
| 	 * of the function array. | ||||
| 	 * | ||||
| 	 * Example of the event handler: | ||||
| 	 * <code> | ||||
| 	 * public function callHandler(&$function) { | ||||
| 	 *	if ($function['function'] == 'microtime') { | ||||
| 	 *		$as_float = empty($function['arguments']['as_float']) | ||||
| 	 *			? false | ||||
| 	 *			: $function['arguments']['as_float']; | ||||
| 	 *		$function['results']['time'] = microtime($as_float); | ||||
| 	 *	} | ||||
| 	 * } | ||||
| 	 * </code> | ||||
| 	 */ | ||||
| 	protected function registerEvents() { | ||||
| 		$dispatcher = EventDispatcher::getInstance(); | ||||
| 		$dispatcher->registerEvent($this->eventPrefix . 'RequestReceived'); | ||||
| 		$dispatcher->registerEvent($this->eventPrefix . 'RequestError'); | ||||
| 		$dispatcher->registerEvent($this->eventPrefix . 'ResponseReceived'); | ||||
| 		$dispatcher->registerEvent($this->eventPrefix . 'CallError'); | ||||
| 		$dispatcher->registerEvent($this->eventPrefix . 'FunctionCall'); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Process request | ||||
| 	 * | ||||
| 	 * @param array $request 'Requests' array. See Mibew API for details. | ||||
| 	 * @param mixed $result_function Control existance of the 'result' function in request. | ||||
| 	 * Use boolean true if 'result' function must exists in request, boolean false if must not | ||||
| 	 * and null if it doesn't matter. | ||||
| 	 * @return array Array of requests results. | ||||
| 	 */ | ||||
| 	protected function processRequest($request, $result_function = null) { | ||||
| 		$context = new MibewAPIExecutionContext(); | ||||
| 
 | ||||
| 		// Get result functions
 | ||||
| 		$result_function = $this->mibewAPI->getResultFunction( | ||||
| 			$request['functions'], | ||||
| 			$result_function | ||||
| 		); | ||||
| 
 | ||||
| 		// Request contains not only result function
 | ||||
| 		if (! is_null($result_function) && count($request['functions']) > 1) { | ||||
| 			trigger_error( | ||||
| 				'Request contains not only result function', | ||||
| 				E_USER_WARNING | ||||
| 			); | ||||
| 		} | ||||
| 
 | ||||
| 		if (is_null($result_function)) { | ||||
| 			// Execute functions
 | ||||
| 			foreach ($request['functions'] as $function) { | ||||
| 				$this->processFunction($function, $context); | ||||
| 			} | ||||
| 			return $context->getResults(); | ||||
| 		} else { | ||||
| 			// Return result
 | ||||
| 			return $result_function['arguments']; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Process function
 | ||||
| 	 * | ||||
| 	 * @param array $function 'Function' array. See Mibew API for details | ||||
| 	 * @param MibewAPIExecutionContext Execution context | ||||
| 	 * @return array Result of function call | ||||
| 	 */ | ||||
| 	protected function processFunction($function, MibewAPIExecutionContext &$context) { | ||||
| 		// Get function arguments with replaced references
 | ||||
| 		$arguments = $context->getArgumentsList($function); | ||||
| 
 | ||||
| 		$call_vars = array( | ||||
| 			'function' => $function['function'], | ||||
| 			'arguments' => $arguments, | ||||
| 			'results' => array() | ||||
| 		); | ||||
| 
 | ||||
| 		// Call processor function
 | ||||
| 		$this->processorCall($call_vars); | ||||
| 
 | ||||
| 		// Trigger FunctionCall event
 | ||||
| 		$dispatcher = EventDispatcher::getInstance(); | ||||
| 		$dispatcher->triggerEvent($this->eventPrefix . 'FunctionCall', $call_vars); | ||||
| 
 | ||||
| 		// Get results
 | ||||
| 		$results = $call_vars['results']; | ||||
| 
 | ||||
| 		// Add function results to execution context
 | ||||
| 		$context->storeFunctionResults($function, $results); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Creates and returns an instance of the MibewAPI class. | ||||
| 	 * | ||||
| 	 * @return MibewAPI | ||||
| 	 */ | ||||
| 	protected abstract function getMibewAPIInstance(); | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Loads callback function
 | ||||
| 	 * | ||||
| 	 * @param string $token Token of the request related to callback function
 | ||||
| 	 * @return mixed callback function array or null if callback function not exists | ||||
| 	 */ | ||||
| 	protected abstract function loadCallback($token); | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Dispatcher of the functions, provided by the RequestProcessor (or inherited) classes as an external API. | ||||
| 	 * | ||||
| 	 * It calls before '<eventPrefix>FunctionCall' event triggers. | ||||
| 	 * | ||||
| 	 * @param array &$func Function array equals to array, passed to the '<eventPrefix>FunctionCall' event. | ||||
| 	 * @see RequestProcessor::registerEvents() | ||||
| 	 */ | ||||
| 	protected abstract function processorCall(&$func); | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Sends synchronous request | ||||
| 	 * | ||||
| 	 * @param array $request The 'request' array. See Mibew API for details | ||||
| 	 * @return mixed response array or boolean false on failure | ||||
| 	 */ | ||||
| 	protected abstract function sendSyncRequest($request); | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Sends asynchronous request | ||||
| 	 * | ||||
| 	 * @param array $request The 'request' array. See Mibew API for details | ||||
| 	 * @return boolean true on success or false on failure | ||||
| 	 */ | ||||
| 	protected abstract function sendAsyncRequest($request); | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Sends synchronous responses | ||||
| 	 * | ||||
| 	 * @param array $responses An array of the 'Request' arrays. See Mibew API for details | ||||
| 	 */ | ||||
| 	protected abstract function sendSyncResponses($responses); | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Sends asynchronous responses | ||||
| 	 * | ||||
| 	 * @param array $responses An array of the 'Request' arrays. See Mibew API for details | ||||
| 	 */ | ||||
| 	protected abstract function sendAsyncResponses($responses); | ||||
| } | ||||
| 
 | ||||
| class RequestProcessorException extends Exception { | ||||
| 	/** | ||||
| 	 * Result function is absent | ||||
| 	 */ | ||||
| 	const NO_RESULT_FUNCTION = 1; | ||||
| 	/** | ||||
| 	 * Wrong function arguments | ||||
| 	 */ | ||||
| 	const WRONG_ARGUMENTS = 2; | ||||
| } | ||||
| 
 | ||||
| ?>
 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user