diff --git a/ktwebservice/KTWebService.php b/ktwebservice/KTWebService.php new file mode 100644 index 0000000..7d1df33 --- /dev/null +++ b/ktwebservice/KTWebService.php @@ -0,0 +1,622 @@ +. + * + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, + * California 94120-7775, or email info@knowledgetree.com. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU General Public License version 3. + * + * In accordance with Section 7(b) of the GNU General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "Powered by + * KnowledgeTree" logo and retain the original copyright notice. If the display of the + * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices + * must display the words "Powered by KnowledgeTree" and retain the original + * copyright notice. + * Contributor( s): ______________________________________ + * + */ + +require_once('../config/dmsDefaults.php'); +require_once('../ktapi/ktapi.inc.php'); + +/** + * Base class for encoding the request response + * + * @author KnowledgeTree Team + * @package KTWebService + * @version Version 0.9 + */ +class EncoderBase +{ + /** + * Constructor for the base encoder + * + * @author KnowledgeTree Team + * @access public + */ + public function __construct() + { + } + + /** + * Gets the encoder based on type - XML or JSON + * + * @author KnowledgeTree Team + * @access public + * @static + * @param string $type The type of encoder - xml|json + * @return EncoderBase + */ + public static function getEncoder($type) + { + $encoder = ucwords(strtolower($type)) . 'Encoder'; + return new $encoder(); + } + + /** + * Performs the encoding + * + * @author KnowledgeTree Team + * @access public + * @param mixed $input The response to be encoded + * @return string + */ + public function encode($input) + { + return $input; + } + + /** + * Returns the headers associated with the encoding + * + * @author KnowledgeTree Team + * @access public + * @return array + */ + public function getHeaders() + { + return array(); + } +} + +/** + * Encodes the response output using json + * + * @author KnowledgeTree Team + * @package KTWebService + * @version Version 0.9 + */ +class JsonEncoder extends EncoderBase +{ + /** + * Serialises the response using json + * + * @author KnowledgeTree Team + * @access public + * @param mixed $input The response to be serialised + * @return string The JSON string + */ + public function encode($input) + { + return json_encode($input); + } +} + +/** + * Encodes the response output in XML + * + * @author KnowledgeTree Team + * @package KTWebService + * @version Version 0.9 + */ +class XmlEncoder extends EncoderBase +{ + /** + * Formats the output as XML + * + * @author KnowledgeTree Team + * @access public + * @param array $input The response to be formatted + * @return string The XML + */ + public function encode($input) + { + if(is_string($input)){ + $input = array($input); + } + + if(!is_array($input)){ + // throw exception + return false; + } + + $xml = ''."\n"; + $xml .= ''."\n"; + + $xml .= XmlEncoder::createXmlFromArray($input); + + return $xml; + } + + /** + * Creates an XML string from an array + * + * @author KnowledgeTree Team + * @access private + * @param array $input The array to be formatted + * @return string The XML + */ + private static function createXmlFromArray($input) + { + $xml = ''; + foreach ($input as $key => $value) { + + if(is_array($value)){ + $value = XmlEncoder::createXmlFromArray($value); + } + + $xml .= "<$key>$value"; + } + return $xml; + } + + /** + * Returns the xml headers + * + * @author KnowledgeTree Team + * @access public + * @return array The headers as an array + */ + public function getHeaders() + { + $headers = array(); + $headers['Content-type'] = 'text/xml'; + return $headers; + } +} + +/** + * Base class for the request response + * + * @author KnowledgeTree Team + * @package KTWebService + * @version Version 0.9 + */ +class ResponseBase +{ + /** + * The formatting to be used in the response + * + * @access protected + * @var string xml|json + */ + protected $responseType; + + /** + * Create the response and set the type. + * + * @author KnowledgeTree Team + * @access public + * @param string $type xml|json + */ + public function __construct($type = 'xml') + { + $this->responseType = $type; + } + + /** + * Dispatch function for calling the appropriate request method + * + * @author KnowledgeTree Team + * @access public + * @param string $method The request method - get|post|put|delete + * @param array $args The request arguments / parameters + * @return bool FALSE on fail + */ + public function _dispatch($method, $args) + { + $dispatch_method = "_{$method}"; + if(!is_callable(array($this, $dispatch_method))) { + $this->_respondError("501 Not Implemented"); + return false; + } + + $this->{$dispatch_method}($args); + } + + /** + * returns the protocol used in the request + * + * @author KnowledgeTree Team + * @access protected + * @return string + */ + protected function _getProtocol() + { + $protocol = "HTTP/1.1"; + if(isset($_SERVER['SERVER_PROTOCOL'])) { + $protocol = $_SERVER['SERVER_PROTOCOL']; + } + return $protocol; + } + + /** + * Sets the error header + * + * @author KnowledgeTree Team + * @access protected + * @param string $error + */ + protected function _respondError($error) + { + $protocol = $this->_getProtocol(); + header("$protocol $error"); + } + + /** + * Sets the response headers and body + * + * @author KnowledgeTree Team + * @access protected + * @param string $body The body of the response + * @param array $headers The headers for the response + */ + protected function _respond($body, $headers = array()) + { + foreach($headers AS $header => $value) { + header("{$header}: {$value}"); + } + echo $body; + } +} + +/** + * Request response class - takes the input request and creates the response + * + * @author KnowledgeTree Team + * @package KTWebService + * @version Version 0.9 + */ +class Response extends ResponseBase +{ + protected $output; + protected $headers; + + protected $error; + protected $error_code; + + protected $class; + protected $method; + + protected $ktapi; + + /** + * Creates the response with the given type + * + * @author KnowledgeTree Team + * @access public + * @param string $type xml|json + */ + public function __construct($type) + { + parent::__construct($type); + + $this->class = 'KTAPI'; + } + + /** + * Parses the arguments for the method and parameters. + * Uses reflection to determine the order of parameters and then runs the requested method. + * + * @author KnowledgeTree Team + * @access protected + * @param array $args The request arguments + * @return array The result of the method + */ + protected function callMethod($args) + { + $method = (isset($args['method'])) ? $args['method'] : ''; + $session_id = (isset($args['session_id'])) ? $args['session_id'] : NULL; + unset($args['method']); + unset($args['session_id']); + + // Get the available methods in KTAPI + $reflect = new ReflectionClass($this->class); + $methods = $reflect->getMethods(); + + // Check that the method exists + $exists = false; + foreach ($methods as $var){ + if($var->getName() == $method){ + $exists = true; + break; + } + } + + if(!$exists){ + $this->error = 'Method does not exist in the API: '.$method; + $this->error_code = 404; + return false; + } + + $this->method = $method; + + // Get method parameters + $reflectMethod = new ReflectionMethod($this->class, $method); + $methodParams = $reflectMethod->getParameters(); + + $orderedParams = array(); + // map parameters to supplied arguments and determine order + foreach ($methodParams as $parameter){ + $param = isset($args[$parameter->getName()]) ? $args[$parameter->getName()] : ''; + + if(empty($param)) { + if(!$parameter->isOptional()){ + $this->error = 'Missing required parameter: '.$parameter->getName(); + return false; + } + $param = $parameter->getDefaultValue(); + } + + $orderedParams[$parameter->getPosition()] = $param; + } + + // instantiate KTAPI and invoke method + $ktapi = $this->get_ktapi($session_id); + $result = $reflectMethod->invokeArgs($ktapi, $orderedParams); + + return $result; + } + + /** + * Instantiate KTAPI and get the active session, if the session id is supplied + * + * @author KnowledgeTree Team + * @access protected + * @param string $session_id + * @return KTAPI + */ + protected function &get_ktapi($session_id = null) + { + if (!is_null($this->ktapi)) + { + return $this->ktapi; + } + + $kt = new KTAPI(); + + // if the session id has been passed through - get the active session. + if(!empty($session_id)){ + $session = $kt->get_active_session($session_id, null); + + if ( PEAR::isError($session)) + { + // return error / exception + return false; + } + } + $this->ktapi = $kt; + return $kt; + } + + /** + * Not sure what to do here yet + * @todo + * + * @author KnowledgeTree Team + * @access protected + * @param unknown_type $input + * @return unknown + */ + protected function flattenInput($input) + { + //echo gettype($input); + + if(is_array($input)){ + return $input; + } + + if(is_string($input)){ + return array($input); + } + + return $input; + } + + /** + * Implements the GET. Returns a result for the given method. + * + * @author KnowledgeTree Team + * @access protected + * @param array $args The request arguments + * @return bool FALSE if error + */ + protected function _get($args) + { + $result = $this->callMethod($args); + + // if an error occurred, initiate the error response + if($result === false){ + return false; + } + + $result = $this->flattenInput($result); + + $encoder = EncoderBase::getEncoder($this->responseType); + $this->output = $encoder->encode($result); + $this->headers = $encoder->getHeaders(); + } + + /** + * Implements the POST. Posts a set of parameters to a given method. + * + * @todo + * @author KnowledgeTree Team + * @access protected + * @param array $args + */ + protected function _post($args) + { + } + + /** + * Implements the PUT. Puts / creates a new resource. + * + * @todo + * @author KnowledgeTree Team + * @access protected + * @param array $args + */ + protected function _put($args) + { + } + + /** + * Implements the DELETE. Deletes a resource + * + * @todo + * @author KnowledgeTree Team + * @access protected + * @param array $args + */ + protected function _delete($args) + { + } + + /** + * Generates the response output / error output + * + * @author KnowledgeTree Team + * @access public + */ + public function output() + { + if(!empty($this->error)){ + $this->_respondError($this->error_code); + echo $this->error; + exit; + } + + $this->_respond($this->output, $this->headers); + } +} + +/** + * Webservice class - parses the request and initiates the response. + * + * @author KnowledgeTree Team + * @package KTWebService + * @version Version 0.9 + */ +class WebService +{ + /** + * Handles the output + * + * @access private + * @var Response object + */ + private $response; + + /** + * The requested method or function + * + * @access private + * @var string + */ + private $method; + + /** + * The request arguments and parameters + * + * @access private + * @var array + */ + private $arguments; + + /** + * Constructor for setting up the webservice + * + * @author KnowledgeTree Team + * @access public + */ + public function WebService() + { + // determine the method of request + $this->method = strtolower($_SERVER['REQUEST_METHOD']); + + // get the parameters / arguments based on the method + $this->getArguments(); + + // Check the response type - xml / json + $responseType = (isset($this->arguments['type'])) ? $this->arguments['type'] : 'xml'; + unset($this->arguments['type']); + + // Set the output handler + $this->response = new response($responseType); + } + + /** + * Gets the arguments / parameters from the request + * + * @author KnowledgeTree Team + * @access private + */ + private function getArguments() + { + $arguments = array(); + switch ($this->method){ + case 'put': + case 'delete': + parse_str(file_get_contents('php://input'), $arguments); + break; + case 'post': + $arguments = $_POST; + break; + case 'get': + default: + $arguments = $_GET; + } + $this->arguments = $arguments; + } + + /** + * Dispatches the request + * + * @author KnowledgeTree Team + * @access public + */ + public function handle() + { + $this->response->_dispatch($this->method, $this->arguments); + $this->response->output(); + } +} + +// Instantiate the webservice +$ws = new WebService(); +$ws->handle(); + +exit(); + +?> \ No newline at end of file