Commit 8168e5f6775dd901ac04e231ff85ad7f5d036463

Authored by Megan Watson
1 parent 4fd8c5b0

Added a new webservice layer that implements a REST framework.

Committed by: Megan Watson
Reviewed by: Donald Jackson



git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@9768 c91229c3-7414-0410-bfa2-8a42b809f60b
Showing 1 changed file with 622 additions and 0 deletions
ktwebservice/KTWebService.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Framework for a rest server
  4 + *
  5 + * KnowledgeTree Community Edition
  6 + * Document Management Made Simple
  7 + * Copyright (C) 2008, 2009 KnowledgeTree Inc.
  8 + * Portions copyright The Jam Warehouse Software (Pty) Limited
  9 + *
  10 + * This program is free software; you can redistribute it and/or modify it under
  11 + * the terms of the GNU General Public License version 3 as published by the
  12 + * Free Software Foundation.
  13 + *
  14 + * This program is distributed in the hope that it will be useful, but WITHOUT
  15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  16 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  17 + * details.
  18 + *
  19 + * You should have received a copy of the GNU General Public License
  20 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21 + *
  22 + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco,
  23 + * California 94120-7775, or email info@knowledgetree.com.
  24 + *
  25 + * The interactive user interfaces in modified source and object code versions
  26 + * of this program must display Appropriate Legal Notices, as required under
  27 + * Section 5 of the GNU General Public License version 3.
  28 + *
  29 + * In accordance with Section 7(b) of the GNU General Public License version 3,
  30 + * these Appropriate Legal Notices must retain the display of the "Powered by
  31 + * KnowledgeTree" logo and retain the original copyright notice. If the display of the
  32 + * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
  33 + * must display the words "Powered by KnowledgeTree" and retain the original
  34 + * copyright notice.
  35 + * Contributor( s): ______________________________________
  36 + *
  37 + */
  38 +
  39 +require_once('../config/dmsDefaults.php');
  40 +require_once('../ktapi/ktapi.inc.php');
  41 +
  42 +/**
  43 + * Base class for encoding the request response
  44 + *
  45 + * @author KnowledgeTree Team
  46 + * @package KTWebService
  47 + * @version Version 0.9
  48 + */
  49 +class EncoderBase
  50 +{
  51 + /**
  52 + * Constructor for the base encoder
  53 + *
  54 + * @author KnowledgeTree Team
  55 + * @access public
  56 + */
  57 + public function __construct()
  58 + {
  59 + }
  60 +
  61 + /**
  62 + * Gets the encoder based on type - XML or JSON
  63 + *
  64 + * @author KnowledgeTree Team
  65 + * @access public
  66 + * @static
  67 + * @param string $type The type of encoder - xml|json
  68 + * @return EncoderBase
  69 + */
  70 + public static function getEncoder($type)
  71 + {
  72 + $encoder = ucwords(strtolower($type)) . 'Encoder';
  73 + return new $encoder();
  74 + }
  75 +
  76 + /**
  77 + * Performs the encoding
  78 + *
  79 + * @author KnowledgeTree Team
  80 + * @access public
  81 + * @param mixed $input The response to be encoded
  82 + * @return string
  83 + */
  84 + public function encode($input)
  85 + {
  86 + return $input;
  87 + }
  88 +
  89 + /**
  90 + * Returns the headers associated with the encoding
  91 + *
  92 + * @author KnowledgeTree Team
  93 + * @access public
  94 + * @return array
  95 + */
  96 + public function getHeaders()
  97 + {
  98 + return array();
  99 + }
  100 +}
  101 +
  102 +/**
  103 + * Encodes the response output using json
  104 + *
  105 + * @author KnowledgeTree Team
  106 + * @package KTWebService
  107 + * @version Version 0.9
  108 + */
  109 +class JsonEncoder extends EncoderBase
  110 +{
  111 + /**
  112 + * Serialises the response using json
  113 + *
  114 + * @author KnowledgeTree Team
  115 + * @access public
  116 + * @param mixed $input The response to be serialised
  117 + * @return string The JSON string
  118 + */
  119 + public function encode($input)
  120 + {
  121 + return json_encode($input);
  122 + }
  123 +}
  124 +
  125 +/**
  126 + * Encodes the response output in XML
  127 + *
  128 + * @author KnowledgeTree Team
  129 + * @package KTWebService
  130 + * @version Version 0.9
  131 + */
  132 +class XmlEncoder extends EncoderBase
  133 +{
  134 + /**
  135 + * Formats the output as XML
  136 + *
  137 + * @author KnowledgeTree Team
  138 + * @access public
  139 + * @param array $input The response to be formatted
  140 + * @return string The XML
  141 + */
  142 + public function encode($input)
  143 + {
  144 + if(is_string($input)){
  145 + $input = array($input);
  146 + }
  147 +
  148 + if(!is_array($input)){
  149 + // throw exception
  150 + return false;
  151 + }
  152 +
  153 + $xml = '<?xml version="1.0">'."\n";
  154 + $xml .= '<response-status="ok">'."\n";
  155 +
  156 + $xml .= XmlEncoder::createXmlFromArray($input);
  157 +
  158 + return $xml;
  159 + }
  160 +
  161 + /**
  162 + * Creates an XML string from an array
  163 + *
  164 + * @author KnowledgeTree Team
  165 + * @access private
  166 + * @param array $input The array to be formatted
  167 + * @return string The XML
  168 + */
  169 + private static function createXmlFromArray($input)
  170 + {
  171 + $xml = '';
  172 + foreach ($input as $key => $value) {
  173 +
  174 + if(is_array($value)){
  175 + $value = XmlEncoder::createXmlFromArray($value);
  176 + }
  177 +
  178 + $xml .= "<$key>$value</$key>";
  179 + }
  180 + return $xml;
  181 + }
  182 +
  183 + /**
  184 + * Returns the xml headers
  185 + *
  186 + * @author KnowledgeTree Team
  187 + * @access public
  188 + * @return array The headers as an array
  189 + */
  190 + public function getHeaders()
  191 + {
  192 + $headers = array();
  193 + $headers['Content-type'] = 'text/xml';
  194 + return $headers;
  195 + }
  196 +}
  197 +
  198 +/**
  199 + * Base class for the request response
  200 + *
  201 + * @author KnowledgeTree Team
  202 + * @package KTWebService
  203 + * @version Version 0.9
  204 + */
  205 +class ResponseBase
  206 +{
  207 + /**
  208 + * The formatting to be used in the response
  209 + *
  210 + * @access protected
  211 + * @var string xml|json
  212 + */
  213 + protected $responseType;
  214 +
  215 + /**
  216 + * Create the response and set the type.
  217 + *
  218 + * @author KnowledgeTree Team
  219 + * @access public
  220 + * @param string $type xml|json
  221 + */
  222 + public function __construct($type = 'xml')
  223 + {
  224 + $this->responseType = $type;
  225 + }
  226 +
  227 + /**
  228 + * Dispatch function for calling the appropriate request method
  229 + *
  230 + * @author KnowledgeTree Team
  231 + * @access public
  232 + * @param string $method The request method - get|post|put|delete
  233 + * @param array $args The request arguments / parameters
  234 + * @return bool FALSE on fail
  235 + */
  236 + public function _dispatch($method, $args)
  237 + {
  238 + $dispatch_method = "_{$method}";
  239 + if(!is_callable(array($this, $dispatch_method))) {
  240 + $this->_respondError("501 Not Implemented");
  241 + return false;
  242 + }
  243 +
  244 + $this->{$dispatch_method}($args);
  245 + }
  246 +
  247 + /**
  248 + * returns the protocol used in the request
  249 + *
  250 + * @author KnowledgeTree Team
  251 + * @access protected
  252 + * @return string
  253 + */
  254 + protected function _getProtocol()
  255 + {
  256 + $protocol = "HTTP/1.1";
  257 + if(isset($_SERVER['SERVER_PROTOCOL'])) {
  258 + $protocol = $_SERVER['SERVER_PROTOCOL'];
  259 + }
  260 + return $protocol;
  261 + }
  262 +
  263 + /**
  264 + * Sets the error header
  265 + *
  266 + * @author KnowledgeTree Team
  267 + * @access protected
  268 + * @param string $error
  269 + */
  270 + protected function _respondError($error)
  271 + {
  272 + $protocol = $this->_getProtocol();
  273 + header("$protocol $error");
  274 + }
  275 +
  276 + /**
  277 + * Sets the response headers and body
  278 + *
  279 + * @author KnowledgeTree Team
  280 + * @access protected
  281 + * @param string $body The body of the response
  282 + * @param array $headers The headers for the response
  283 + */
  284 + protected function _respond($body, $headers = array())
  285 + {
  286 + foreach($headers AS $header => $value) {
  287 + header("{$header}: {$value}");
  288 + }
  289 + echo $body;
  290 + }
  291 +}
  292 +
  293 +/**
  294 + * Request response class - takes the input request and creates the response
  295 + *
  296 + * @author KnowledgeTree Team
  297 + * @package KTWebService
  298 + * @version Version 0.9
  299 + */
  300 +class Response extends ResponseBase
  301 +{
  302 + protected $output;
  303 + protected $headers;
  304 +
  305 + protected $error;
  306 + protected $error_code;
  307 +
  308 + protected $class;
  309 + protected $method;
  310 +
  311 + protected $ktapi;
  312 +
  313 + /**
  314 + * Creates the response with the given type
  315 + *
  316 + * @author KnowledgeTree Team
  317 + * @access public
  318 + * @param string $type xml|json
  319 + */
  320 + public function __construct($type)
  321 + {
  322 + parent::__construct($type);
  323 +
  324 + $this->class = 'KTAPI';
  325 + }
  326 +
  327 + /**
  328 + * Parses the arguments for the method and parameters.
  329 + * Uses reflection to determine the order of parameters and then runs the requested method.
  330 + *
  331 + * @author KnowledgeTree Team
  332 + * @access protected
  333 + * @param array $args The request arguments
  334 + * @return array The result of the method
  335 + */
  336 + protected function callMethod($args)
  337 + {
  338 + $method = (isset($args['method'])) ? $args['method'] : '';
  339 + $session_id = (isset($args['session_id'])) ? $args['session_id'] : NULL;
  340 + unset($args['method']);
  341 + unset($args['session_id']);
  342 +
  343 + // Get the available methods in KTAPI
  344 + $reflect = new ReflectionClass($this->class);
  345 + $methods = $reflect->getMethods();
  346 +
  347 + // Check that the method exists
  348 + $exists = false;
  349 + foreach ($methods as $var){
  350 + if($var->getName() == $method){
  351 + $exists = true;
  352 + break;
  353 + }
  354 + }
  355 +
  356 + if(!$exists){
  357 + $this->error = 'Method does not exist in the API: '.$method;
  358 + $this->error_code = 404;
  359 + return false;
  360 + }
  361 +
  362 + $this->method = $method;
  363 +
  364 + // Get method parameters
  365 + $reflectMethod = new ReflectionMethod($this->class, $method);
  366 + $methodParams = $reflectMethod->getParameters();
  367 +
  368 + $orderedParams = array();
  369 + // map parameters to supplied arguments and determine order
  370 + foreach ($methodParams as $parameter){
  371 + $param = isset($args[$parameter->getName()]) ? $args[$parameter->getName()] : '';
  372 +
  373 + if(empty($param)) {
  374 + if(!$parameter->isOptional()){
  375 + $this->error = 'Missing required parameter: '.$parameter->getName();
  376 + return false;
  377 + }
  378 + $param = $parameter->getDefaultValue();
  379 + }
  380 +
  381 + $orderedParams[$parameter->getPosition()] = $param;
  382 + }
  383 +
  384 + // instantiate KTAPI and invoke method
  385 + $ktapi = $this->get_ktapi($session_id);
  386 + $result = $reflectMethod->invokeArgs($ktapi, $orderedParams);
  387 +
  388 + return $result;
  389 + }
  390 +
  391 + /**
  392 + * Instantiate KTAPI and get the active session, if the session id is supplied
  393 + *
  394 + * @author KnowledgeTree Team
  395 + * @access protected
  396 + * @param string $session_id
  397 + * @return KTAPI
  398 + */
  399 + protected function &get_ktapi($session_id = null)
  400 + {
  401 + if (!is_null($this->ktapi))
  402 + {
  403 + return $this->ktapi;
  404 + }
  405 +
  406 + $kt = new KTAPI();
  407 +
  408 + // if the session id has been passed through - get the active session.
  409 + if(!empty($session_id)){
  410 + $session = $kt->get_active_session($session_id, null);
  411 +
  412 + if ( PEAR::isError($session))
  413 + {
  414 + // return error / exception
  415 + return false;
  416 + }
  417 + }
  418 + $this->ktapi = $kt;
  419 + return $kt;
  420 + }
  421 +
  422 + /**
  423 + * Not sure what to do here yet
  424 + * @todo
  425 + *
  426 + * @author KnowledgeTree Team
  427 + * @access protected
  428 + * @param unknown_type $input
  429 + * @return unknown
  430 + */
  431 + protected function flattenInput($input)
  432 + {
  433 + //echo gettype($input);
  434 +
  435 + if(is_array($input)){
  436 + return $input;
  437 + }
  438 +
  439 + if(is_string($input)){
  440 + return array($input);
  441 + }
  442 +
  443 + return $input;
  444 + }
  445 +
  446 + /**
  447 + * Implements the GET. Returns a result for the given method.
  448 + *
  449 + * @author KnowledgeTree Team
  450 + * @access protected
  451 + * @param array $args The request arguments
  452 + * @return bool FALSE if error
  453 + */
  454 + protected function _get($args)
  455 + {
  456 + $result = $this->callMethod($args);
  457 +
  458 + // if an error occurred, initiate the error response
  459 + if($result === false){
  460 + return false;
  461 + }
  462 +
  463 + $result = $this->flattenInput($result);
  464 +
  465 + $encoder = EncoderBase::getEncoder($this->responseType);
  466 + $this->output = $encoder->encode($result);
  467 + $this->headers = $encoder->getHeaders();
  468 + }
  469 +
  470 + /**
  471 + * Implements the POST. Posts a set of parameters to a given method.
  472 + *
  473 + * @todo
  474 + * @author KnowledgeTree Team
  475 + * @access protected
  476 + * @param array $args
  477 + */
  478 + protected function _post($args)
  479 + {
  480 + }
  481 +
  482 + /**
  483 + * Implements the PUT. Puts / creates a new resource.
  484 + *
  485 + * @todo
  486 + * @author KnowledgeTree Team
  487 + * @access protected
  488 + * @param array $args
  489 + */
  490 + protected function _put($args)
  491 + {
  492 + }
  493 +
  494 + /**
  495 + * Implements the DELETE. Deletes a resource
  496 + *
  497 + * @todo
  498 + * @author KnowledgeTree Team
  499 + * @access protected
  500 + * @param array $args
  501 + */
  502 + protected function _delete($args)
  503 + {
  504 + }
  505 +
  506 + /**
  507 + * Generates the response output / error output
  508 + *
  509 + * @author KnowledgeTree Team
  510 + * @access public
  511 + */
  512 + public function output()
  513 + {
  514 + if(!empty($this->error)){
  515 + $this->_respondError($this->error_code);
  516 + echo $this->error;
  517 + exit;
  518 + }
  519 +
  520 + $this->_respond($this->output, $this->headers);
  521 + }
  522 +}
  523 +
  524 +/**
  525 + * Webservice class - parses the request and initiates the response.
  526 + *
  527 + * @author KnowledgeTree Team
  528 + * @package KTWebService
  529 + * @version Version 0.9
  530 + */
  531 +class WebService
  532 +{
  533 + /**
  534 + * Handles the output
  535 + *
  536 + * @access private
  537 + * @var Response object
  538 + */
  539 + private $response;
  540 +
  541 + /**
  542 + * The requested method or function
  543 + *
  544 + * @access private
  545 + * @var string
  546 + */
  547 + private $method;
  548 +
  549 + /**
  550 + * The request arguments and parameters
  551 + *
  552 + * @access private
  553 + * @var array
  554 + */
  555 + private $arguments;
  556 +
  557 + /**
  558 + * Constructor for setting up the webservice
  559 + *
  560 + * @author KnowledgeTree Team
  561 + * @access public
  562 + */
  563 + public function WebService()
  564 + {
  565 + // determine the method of request
  566 + $this->method = strtolower($_SERVER['REQUEST_METHOD']);
  567 +
  568 + // get the parameters / arguments based on the method
  569 + $this->getArguments();
  570 +
  571 + // Check the response type - xml / json
  572 + $responseType = (isset($this->arguments['type'])) ? $this->arguments['type'] : 'xml';
  573 + unset($this->arguments['type']);
  574 +
  575 + // Set the output handler
  576 + $this->response = new response($responseType);
  577 + }
  578 +
  579 + /**
  580 + * Gets the arguments / parameters from the request
  581 + *
  582 + * @author KnowledgeTree Team
  583 + * @access private
  584 + */
  585 + private function getArguments()
  586 + {
  587 + $arguments = array();
  588 + switch ($this->method){
  589 + case 'put':
  590 + case 'delete':
  591 + parse_str(file_get_contents('php://input'), $arguments);
  592 + break;
  593 + case 'post':
  594 + $arguments = $_POST;
  595 + break;
  596 + case 'get':
  597 + default:
  598 + $arguments = $_GET;
  599 + }
  600 + $this->arguments = $arguments;
  601 + }
  602 +
  603 + /**
  604 + * Dispatches the request
  605 + *
  606 + * @author KnowledgeTree Team
  607 + * @access public
  608 + */
  609 + public function handle()
  610 + {
  611 + $this->response->_dispatch($this->method, $this->arguments);
  612 + $this->response->output();
  613 + }
  614 +}
  615 +
  616 +// Instantiate the webservice
  617 +$ws = new WebService();
  618 +$ws->handle();
  619 +
  620 +exit();
  621 +
  622 +?>
0 623 \ No newline at end of file
... ...