mirror of
				https://github.com/Mibew/design.git
				synced 2025-11-04 12:25:19 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			434 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			434 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?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.
 | 
						|
 */
 | 
						|
 | 
						|
//require_once('mibew_api_interaction.php');
 | 
						|
 | 
						|
/**
 | 
						|
 * Implements Mibew API specification version 1.0
 | 
						|
 *
 | 
						|
 * @todo May be use regular methods instead of static one
 | 
						|
 */
 | 
						|
Class MibewAPI {
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Version of the MIBEW API protocol implemented by the class
 | 
						|
	 */
 | 
						|
	const PROTOCOL_VERSION = '1.0';
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Array of MibewAPI objects
 | 
						|
	 * @var array
 | 
						|
	 */
 | 
						|
	protected static $interactions = array();
 | 
						|
 | 
						|
	/**
 | 
						|
	 * An object that encapsulates type of the interaction
 | 
						|
	 *
 | 
						|
	 * @var MibewAPIInteraction
 | 
						|
	 */
 | 
						|
	protected $interaction = NULL;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Returns MibewAPI object
 | 
						|
	 *
 | 
						|
	 * @param string $class_name A name of the interaction type class
 | 
						|
	 * @return MibeAPI object
 | 
						|
	 * @throws MibewAPIException
 | 
						|
	 */
 | 
						|
	public static function getAPI($class_name) {
 | 
						|
		if (! class_exists($class_name)) {
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"Wrong interaction type",
 | 
						|
				MibewAPIException::WRONG_INTERACTION_TYPE
 | 
						|
			);
 | 
						|
		}
 | 
						|
		if (empty(self::$interactions[$class_name])) {
 | 
						|
			self::$interactions[$class_name] = new self(new $class_name());
 | 
						|
		}
 | 
						|
		return self::$interactions[$class_name];
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Class constructor
 | 
						|
	 *
 | 
						|
	 * @param MibewAPIInteraction $interaction Interaction type object
 | 
						|
	 */
 | 
						|
	protected function __construct(MibewAPIInteraction $interaction) {
 | 
						|
		$this->interaction = $interaction;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Validate package
 | 
						|
	 *
 | 
						|
	 * @param array $package Package array. See Mibew API for details.
 | 
						|
	 * @param array $trusted_signatures Array of trusted signatures.
 | 
						|
	 * @throws MibewAPIException
 | 
						|
	 */
 | 
						|
	public function checkPackage($package, $trusted_signatures) {
 | 
						|
		// Check signature
 | 
						|
		if (! isset($package['signature'])) {
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"Package signature is empty",
 | 
						|
				MibewAPIException::EMPTY_SIGNATURE
 | 
						|
			);
 | 
						|
		}
 | 
						|
		if (! in_array($package['signature'], $trusted_signatures)) {
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"Package signed with untrusted signature",
 | 
						|
				MibewAPIException::UNTRUSTED_SIGNATURE
 | 
						|
			);
 | 
						|
		}
 | 
						|
 | 
						|
		// Check protocol
 | 
						|
		if (empty($package['proto'])) {
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"Package protocol is empty",
 | 
						|
				MibewAPIException::EMPTY_PROTOCOL
 | 
						|
			);
 | 
						|
		}
 | 
						|
		if ($package['proto'] != self::PROTOCOL_VERSION) {
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"Wrong package protocol version '{$package['proto']}'",
 | 
						|
				MibewAPIException::WRONG_PROTOCOL_VERSION
 | 
						|
			);
 | 
						|
		}
 | 
						|
 | 
						|
		// Check async flag
 | 
						|
		if (! isset($package['async'])) {
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"'async' flag is missed",
 | 
						|
				MibewAPIException::ASYNC_FLAG_MISSED
 | 
						|
			);
 | 
						|
		}
 | 
						|
		if (! is_bool($package['async'])) {
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"Wrong 'async' flag value",
 | 
						|
				MibewAPIException::WRONG_ASYNC_FLAG_VALUE
 | 
						|
			);
 | 
						|
		}
 | 
						|
 | 
						|
		// Package must have at least one request
 | 
						|
		if (empty($package['requests'])) {
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"Empty requests set",
 | 
						|
				MibewAPIException::EMPTY_REQUESTS
 | 
						|
			);
 | 
						|
		}
 | 
						|
		// Check requests in package
 | 
						|
		foreach ($package['requests'] as $request) {
 | 
						|
			$this->checkRequest($request);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Validate request
 | 
						|
	 *
 | 
						|
	 * @param array $request Request array. See Mibew API for details.
 | 
						|
	 * @throws MibewAPIException
 | 
						|
	 */
 | 
						|
	public function checkRequest($request) {
 | 
						|
		// Check token
 | 
						|
		if (empty($request['token'])) {
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"Empty request token",
 | 
						|
				MibewAPIException::EMPTY_TOKEN
 | 
						|
			);
 | 
						|
		}
 | 
						|
		// Request must have at least one function
 | 
						|
		if (empty($request['functions'])) {
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"Empty functions set",
 | 
						|
				MibewAPIException::EMPTY_FUNCTIONS
 | 
						|
			);
 | 
						|
		}
 | 
						|
		// Check functions in request
 | 
						|
		foreach ($request['functions'] as $function) {
 | 
						|
			$this->checkFunction($function);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Validate function
 | 
						|
	 *
 | 
						|
	 * @param array $function Function array. See Mibew API for details.
 | 
						|
	 * @param boolean $filter_reserved_functions Determine if function name must not be in
 | 
						|
	 * reserved list
 | 
						|
	 * @throws MibewAPIException
 | 
						|
	 */
 | 
						|
	public function checkFunction($function, $filter_reserved_functions = false) {
 | 
						|
		// Check function name
 | 
						|
		if (empty($function['function'])) {
 | 
						|
			throw new MibewAPIException(
 | 
						|
				'Cannot call for function with empty name',
 | 
						|
				MibewAPIException::EMPTY_FUNCTION_NAME
 | 
						|
			);
 | 
						|
		}
 | 
						|
		if ($filter_reserved_functions) {
 | 
						|
			if (in_array(
 | 
						|
				$function['function'],
 | 
						|
				$this->interaction->reservedFunctionNames
 | 
						|
			)) {
 | 
						|
				throw new MibewAPIException(
 | 
						|
					"'{$function['function']}' is reserved function name",
 | 
						|
					MibewAPIException::FUNCTION_NAME_RESERVED
 | 
						|
				);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		// Check function's arguments
 | 
						|
		if (empty($function['arguments'])) {
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"There are no arguments in '{$function['function']}' function",
 | 
						|
				MibewAPIException::EMPTY_ARGUMENTS
 | 
						|
			);
 | 
						|
		}
 | 
						|
		if (! is_array($function['arguments'])) {
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"Arguments must be an array",
 | 
						|
				MibewAPIException::WRONG_ARGUMENTS_TYPE
 | 
						|
			);
 | 
						|
		}
 | 
						|
		$unset_arguments = array_diff(
 | 
						|
			$this->interaction->getObligatoryArguments($function['function']),
 | 
						|
			array_keys($function['arguments'])
 | 
						|
		);
 | 
						|
		if (! empty($unset_arguments)) {
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"Arguments '" . implode("', '", $unset_arguments) . "' must be set",
 | 
						|
				MibewAPIException::OBLIGATORY_ARGUMENTS_MISSED
 | 
						|
			);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Encodes package
 | 
						|
	 *
 | 
						|
	 * @param array $requests Requests array. See Mibew API for details.
 | 
						|
	 * @param string $signature Sender signature.
 | 
						|
	 * @param boolean $async true for asynchronous request and false for synchronous request
 | 
						|
	 * @return string Ready for transfer encoded package
 | 
						|
	 */
 | 
						|
	public function encodePackage($requests, $signature, $async) {
 | 
						|
		$package = array();
 | 
						|
		$package['signature'] = $signature;
 | 
						|
		$package['proto'] = self::PROTOCOL_VERSION;
 | 
						|
		$package['async'] = $async;
 | 
						|
		$package['requests'] = $requests;
 | 
						|
		return urlencode(json_encode($package));
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Decodes package and validate package structure
 | 
						|
	 *
 | 
						|
	 * @param string $package Encoded package
 | 
						|
	 * @param array $trusted_signatures List of trusted signatures
 | 
						|
	 * @return array Decoded package array. See Mibew API for details.
 | 
						|
	 * @throws MibewAPIException
 | 
						|
	 */
 | 
						|
	public function decodePackage($package, $trusted_signatures) {
 | 
						|
		$decoded_package = urldecode($package);
 | 
						|
		// JSON regular expression
 | 
						|
		$pcre_regex = '/
 | 
						|
		(?(DEFINE)
 | 
						|
		(?<number>   -? (?= [1-9]|0(?!\d) ) \d+ (\.\d+)? ([eE] [+-]? \d+)? )
 | 
						|
		(?<boolean>   true | false | null )
 | 
						|
		(?<string>    " ([^"\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9a-f]{4} )* " )
 | 
						|
		(?<array>     \[  (?:  (?&json)  (?: , (?&json)  )*  )?  \s* \] )
 | 
						|
		(?<pair>      \s* (?&string) \s* : (?&json)  )
 | 
						|
		(?<object>    \{  (?:  (?&pair)  (?: , (?&pair)  )*  )?  \s* \} )
 | 
						|
		(?<json>   \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) ) \s* )
 | 
						|
		)
 | 
						|
		\A (?&json) \Z
 | 
						|
		/six';
 | 
						|
		// Check JSON
 | 
						|
		if (!preg_match($pcre_regex, $decoded_package)) {
 | 
						|
			// Not valid JSON
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"Package have not valid json structure",
 | 
						|
				MibewAPIException::NOT_VALID_JSON
 | 
						|
			);
 | 
						|
		}
 | 
						|
		$decoded_package = json_decode($decoded_package, true);
 | 
						|
		$this->checkPackage($decoded_package, $trusted_signatures);
 | 
						|
		return $decoded_package;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Builds result package
 | 
						|
	 *
 | 
						|
	 * @param string $token Token of the result package
 | 
						|
	 * @param array $result_arguments Arguments of result function
 | 
						|
	 * @return array Result package
 | 
						|
	 */
 | 
						|
	public function buildResult($token, $result_arguments) {
 | 
						|
		$arguments = $result_arguments + $this->interaction->getObligatoryArgumentsDefaults('result');
 | 
						|
		$package = array(
 | 
						|
			'token' => $token,
 | 
						|
			'functions' => array(
 | 
						|
				array(
 | 
						|
					'function' => 'result',
 | 
						|
					'arguments' => $arguments
 | 
						|
				)
 | 
						|
			)
 | 
						|
		);
 | 
						|
		return $package;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Search 'result' function in $function_list. If request contains more than one result
 | 
						|
	 * functions throws an MibewAPIException
 | 
						|
	 *
 | 
						|
	 * @param array $functions_list Array of functions. See MibewAPI for function structure
 | 
						|
	 * details
 | 
						|
	 * @param mixed $existance 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 mixed Function array if 'result' function found and NULL otherwise
 | 
						|
	 * @throws MibewAPIException
 | 
						|
	 */
 | 
						|
	public function getResultFunction ($functions_list, $existence = null) {
 | 
						|
		$result_function = null;
 | 
						|
		// Try to find 'result' function
 | 
						|
		foreach ($functions_list as $function) {
 | 
						|
			if ($function['function'] == 'result') {
 | 
						|
				if (! is_null($result_function)) {
 | 
						|
					// Another 'result' function found
 | 
						|
					throw new MibewAPIException(
 | 
						|
						"Function 'result' already exists in request",
 | 
						|
						MibewAPIException::RESULT_FUNCTION_ALREADY_EXISTS
 | 
						|
					);
 | 
						|
				}
 | 
						|
				// First 'result' function found
 | 
						|
				$result_function = $function;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if ($existence === true && is_null($result_function)) {
 | 
						|
			// 'result' function must present in request
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"There is no 'result' function in request",
 | 
						|
				MibewAPIException::NO_RESULT_FUNCTION
 | 
						|
			);
 | 
						|
		}
 | 
						|
		if ($existence === false && !is_null($result_function)) {
 | 
						|
			// 'result' function must not present in request
 | 
						|
			throw new MibewAPIException(
 | 
						|
				"There is 'result' function in request",
 | 
						|
				MibewAPIException::RESULT_FUNCTION_EXISTS
 | 
						|
			);
 | 
						|
		}
 | 
						|
		return $result_function;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Mibew API Exception class.
 | 
						|
 */
 | 
						|
class MibewAPIException extends Exception {
 | 
						|
	/**
 | 
						|
	 * Async flag is missed.
 | 
						|
	 */
 | 
						|
	const ASYNC_FLAG_MISSED = 1;
 | 
						|
	/**
 | 
						|
	 * There are no arguments in function
 | 
						|
	 */
 | 
						|
	const EMPTY_ARGUMENTS = 2;
 | 
						|
	/**
 | 
						|
	 * Cannot call for function with empty name
 | 
						|
	 */
 | 
						|
	const EMPTY_FUNCTION_NAME = 3;
 | 
						|
	/**
 | 
						|
	 * Functions set is empty
 | 
						|
	 */
 | 
						|
	const EMPTY_FUNCTIONS = 4;
 | 
						|
	/**
 | 
						|
	 * Package protocol is empty
 | 
						|
	 */
 | 
						|
	const EMPTY_PROTOCOL = 5;
 | 
						|
	/**
 | 
						|
	 * Requests set is empty
 | 
						|
	 */
 | 
						|
	const EMPTY_REQUESTS = 6;
 | 
						|
	/**
 | 
						|
	 * Package signature is empty
 | 
						|
	 */
 | 
						|
	const EMPTY_SIGNATURE = 7;
 | 
						|
	/**
 | 
						|
	 * Request token is empty
 | 
						|
	 */
 | 
						|
	const EMPTY_TOKEN = 8;
 | 
						|
	/**
 | 
						|
	 * Wrong reference. Reference variable is empty
 | 
						|
	 */
 | 
						|
	const EMPTY_VARIABLE_IN_REFERENCE = 9;
 | 
						|
	/**
 | 
						|
	 * This function name is reserved
 | 
						|
	 */
 | 
						|
	const FUNCTION_NAME_RESERVED = 10;
 | 
						|
	/**
 | 
						|
	 * There is no result function
 | 
						|
	 */
 | 
						|
	const NO_RESULT_FUNCTION = 11;
 | 
						|
	/**
 | 
						|
	 * Package have not valid JSON structure
 | 
						|
	 */
 | 
						|
	const NOT_VALID_JSON = 12;
 | 
						|
	/**
 | 
						|
	 * Some of the function's obligatory arguments are missed
 | 
						|
	 */
 | 
						|
	const OBLIGATORY_ARGUMENTS_MISSED = 13;
 | 
						|
	/**
 | 
						|
	 * Request contains more than one result functions
 | 
						|
	 */
 | 
						|
	const RESULT_FUNCTION_ALREADY_EXISTS = 14;
 | 
						|
	/**
 | 
						|
	 * There is 'result' function in request
 | 
						|
	 */
 | 
						|
	const RESULT_FUNCTION_EXISTS = 15;
 | 
						|
	/**
 | 
						|
	 * Package signed with untrusted signature
 | 
						|
	 */
 | 
						|
	const UNTRUSTED_SIGNATURE = 16;
 | 
						|
	/**
 | 
						|
	 * Wrong reference. Variable is undefined in functions results
 | 
						|
	 */
 | 
						|
	const VARIABLE_IS_UNDEFINED_IN_REFERENCE = 17;
 | 
						|
	/**
 | 
						|
	 * Variable is undefined in function's results
 | 
						|
	 */
 | 
						|
	const VARIABLE_IS_UNDEFINED_IN_RESULT = 18;
 | 
						|
	/**
 | 
						|
	 * Arguments must be an array
 | 
						|
	 */
 | 
						|
	const WRONG_ARGUMENTS_TYPE = 19;
 | 
						|
	/**
 | 
						|
	 * Async flag value is wrong
 | 
						|
	 */
 | 
						|
	const WRONG_ASYNC_FLAG_VALUE = 20;
 | 
						|
	/**
 | 
						|
	 * Wrong reference. Function with this number does not call yet
 | 
						|
	 */
 | 
						|
	const WRONG_FUNCTION_NUM_IN_REFERENCE = 21;
 | 
						|
	/**
 | 
						|
	 * Wrong interaction type
 | 
						|
	 */
 | 
						|
	const WRONG_INTERACTION_TYPE = 22;
 | 
						|
	/**
 | 
						|
	 * Wrong package protocol version
 | 
						|
	 */
 | 
						|
	const WRONG_PROTOCOL_VERSION = 23;
 | 
						|
}
 | 
						|
 | 
						|
?>
 |