PageRenderTime 40ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/4.9/components/com_rest/rest.class.php

http://miacms.googlecode.com/
PHP | 336 lines | 188 code | 33 blank | 115 comment | 35 complexity | 6b2e3f2099d1182354de12d880107b24 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0, LGPL-2.0
  1. <?php
  2. /**
  3. * @package mosRest
  4. * @author Chad Auld and Ozgur Cem Sen (code@brilaps.com)
  5. * @copyright Brilaps, LLC (http://brilaps.com)
  6. * @link http://brilaps.com || http://wiki.brilaps.com
  7. * @license http://www.opensource.org/licenses/gpl-license.php GNU/GPL v.2.
  8. */
  9. class mosRest extends mosDBTable {
  10. /** @public int Primary key */
  11. public $id=null;
  12. /** @public string */
  13. public $developer_name=null;
  14. /** @public string */
  15. public $product_name=null;
  16. /** @public string */
  17. public $web_app_url=null;
  18. /** @public string */
  19. public $contact_email=null;
  20. /** @public string */
  21. public $phone_number=null;
  22. /** @public string */
  23. public $description=null;
  24. /** @public string */
  25. public $activation=null;
  26. /** @public string */
  27. public $block=null;
  28. /** @public string */
  29. public $registration_date=null;
  30. /** @public string */
  31. public $approval_date=null;
  32. /**
  33. * @param database A database connector object
  34. */
  35. function mosRest() {
  36. global $database;
  37. $this->mosDBTable( '#__rest', 'id', $database );
  38. }
  39. }
  40. class mosRestLog extends mosDBTable {
  41. /** @public int Primary key */
  42. public $id=null;
  43. /** @public string */
  44. public $request_key=null;
  45. /** @public string */
  46. public $request_type=null;
  47. /** @public string */
  48. public $request_uri=null;
  49. /** @public string */
  50. public $request_timestamp=null;
  51. /**
  52. * @param database A database connector object
  53. */
  54. function mosRestLog() {
  55. global $database;
  56. $this->mosDBTable( '#__rest_log', 'id', $database );
  57. }
  58. }
  59. /**
  60. * This class brings RESTful functionality to the MiaCMS. This is just a base class that handles many of the
  61. * key items needed to implement REST. It should be used as a building block to RESTify MiaCMS components. We
  62. * have also released a RESTified com_content (com_rest_content). You can use that as an example to extend additional
  63. * components. Each component will need to implement its own data retrival routines.
  64. */
  65. class mRest {
  66. static $apiVersion = '1.0';
  67. /**
  68. * Handles the actual REST response
  69. * @param htmlReturnStatusCode - html request status code (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)
  70. * @param returnFormat - response format requested be user (ie) JSON, XML, PHP
  71. * @param data - data to be sent back
  72. */
  73. function restResponse($htmlReturnStatusCode, $returnFormat, $data) {
  74. //Produce the final output
  75. header( $this->setHtmlStatusCode($htmlReturnStatusCode) );
  76. header( 'Content-Type: '.$this->setReturnContentType($returnFormat).'; charset=utf-8' );
  77. //Force the browser to use fresh data
  78. header( 'Expires: Mon, 26 Jul 1997 05:00:00 GMT' );
  79. header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
  80. header( 'Cache-Control: no-store, no-cache, must-revalidate' );
  81. header( 'Cache-Control: post-check=0, pre-check=0', false );
  82. header( 'Pragma: no-cache' );
  83. echo $data;
  84. exit;
  85. }
  86. /**
  87. * Handles encoding of the final REST response
  88. * @param data - data to be encoded
  89. * @param returnFormat - return format requested by the client (i.e.) json, xml, or php
  90. * @param callback - optional JavaScript callback function to wrap request with
  91. */
  92. function encodeRestResponse($data, $returnFormat, $callback='') {
  93. global $mosConfig_absolute_path;
  94. if ($this->validateReturnFormat($returnFormat)===false) {
  95. $returnFormat='json'; //force json
  96. }
  97. if ($returnFormat=='json') {
  98. /* Handles JSON encoding using the standard PEAR class (native in later version of PHP5, but we
  99. want to support all v5 installs) */
  100. if (file_exists($mosConfig_absolute_path.'/includes/PEAR/JSON/json.php')) {
  101. require($mosConfig_absolute_path.'/includes/PEAR/JSON/json.php');
  102. } else {
  103. require('json.php');
  104. }
  105. $json = new Services_JSON();
  106. $returnData = $json->encode($data);
  107. //Wrap in JS callback function if requested
  108. if (!empty($callback)) {
  109. $returnData = $callback.'( '.$returnData.' )';
  110. }
  111. } else if ($returnFormat=='php') {
  112. $returnData = serialize($data);
  113. } else if ($returnFormat=='xml') {
  114. //We expect a fully formed XML doc so nothing else is needed in theis case
  115. $returnData = $data;
  116. }
  117. return $returnData;
  118. }
  119. /**
  120. * Verifies that the appid passed with the request is valid for tracking purposes
  121. * @param appId - application id assigned to developer during registration
  122. * @param requestType - type of request being made (ex) content, search, rank, popular, etc
  123. * @param uri - the full url with query params to log with request (best obtained by using the full_url() function)
  124. */
  125. function verifyRestAPIKey($appId, $requestType, $uri) {
  126. global $database, $mosConfig_absolute_path;
  127. //Include REST settings
  128. $log_api_requests = '';
  129. require($mosConfig_absolute_path.'/administrator/components/com_rest/config.rest.php');
  130. //Log request for tracking purposes if enabled
  131. if ($log_api_requests=='1') {
  132. $this->logAPIRequest($appId, $requestType, $uri);
  133. }
  134. $appId = $database->getEscaped($appId);
  135. $database->setQuery("SELECT id FROM #__rest WHERE rest_key='{$appId}'"
  136. . "\n AND block=0");
  137. if (!$database->loadResult()) {
  138. return false;
  139. }
  140. }
  141. /**
  142. * Used to verify the requested JSON callback function name is valid
  143. *
  144. * The following characters are allowed: A-Z, a-z, 0-9, [], and _
  145. * Start with a simple ctype check since it's a lighter process and more common.
  146. * Fall back on character checking if the whole this isn't alphanumeric
  147. *
  148. * @param callback - the requested JSON callback function name
  149. */
  150. function verifyJSONCallbackName($callbackName) {
  151. if (ctype_alpha($callbackName)) {
  152. return true;
  153. } else {
  154. $chrCount=strlen($callbackName);
  155. $altChars=array('[',']','_');
  156. for ($i=0;$i<$chrCount;$i++) {
  157. if ((!ctype_alnum($callbackName[$i])) && (!in_array($callbackName[$i], $altChars))) {
  158. return false;
  159. }
  160. }
  161. }
  162. }
  163. /**
  164. * Log API request for REST usage tracking purposes (normally called on by verifyRestAPIKey)
  165. * @param appId - application id assigned to developer during registration
  166. * @param requestType - type of request being made (ex) content, search, rank, popular, etc
  167. * @param uri - the full url with query params to log with request (best obtained by using the full_url() function)
  168. */
  169. function logAPIRequest($appId, $requestType, $uri) {
  170. global $database;
  171. $row = new mosRestLog( $database );
  172. $row->id = 0;
  173. $row->rest_key = $database->getEscaped($appId);
  174. $row->request_type = $database->getEscaped($requestType);
  175. $row->request_uri = $database->getEscaped($uri);
  176. $row->request_timestamp = date("Y-m-d H:i:s");
  177. if (!$row->store()) {
  178. die('Unable to log request: '.$row->getError());
  179. }
  180. }
  181. /**
  182. * Builds the error document used in negative responses.
  183. * @param errorMessage - custom message to be wrapped in the error doc
  184. * @param returnFormat - error doc sent back in the user defined return format
  185. */
  186. function restError($errorMessage, $returnFormat='json') {
  187. if ($this->validateReturnFormat($returnFormat)===false) {
  188. $returnFormat='json'; //force json
  189. }
  190. if ($returnFormat=='json' || $returnFormat=='php') {
  191. $returnMessage = array('Error'=>array('Message'=>$errorMessage));
  192. } else if ($returnFormat=='xml') {
  193. //Send back XML
  194. $dom = new DOMDocument("1.0");
  195. //Create the root element
  196. $root = $dom->createElement("Error");
  197. $dom->appendChild($root);
  198. //Create child elements
  199. $message = $dom->createElement("Message");
  200. $root->appendChild($message);
  201. $message->appendChild($dom->createTextNode("$errorMessage"));
  202. //Save xml tree
  203. $returnMessage = $dom->saveXML();
  204. }
  205. $data = $this->encodeRestResponse($returnMessage, $returnFormat);
  206. return $data;
  207. }
  208. /**
  209. * Validate requested return format is one of the supported types
  210. * @param returnFormat - client requested output format
  211. */
  212. function validateReturnFormat($returnFormat) {
  213. $validReturnFormat = array('json', 'xml', 'php');
  214. if (!in_array($returnFormat, $validReturnFormat)) {
  215. return false;
  216. }
  217. }
  218. /**
  219. * Returns version number of the REST API. Needed so that developers can code to different versions of the API.
  220. * @param returnFormat - error doc sent back in the user defined return format
  221. */
  222. function getAPIVersion($returnFormat) {
  223. $version = self::$apiVersion;
  224. if ($this->validateReturnFormat($returnFormat)===false) {
  225. $returnFormat='json'; //force json
  226. }
  227. if ($returnFormat=='json' || $returnFormat=='php') {
  228. $returnMessage = array('API'=>array('Version'=>$version));
  229. } else if ($returnFormat=='xml') {
  230. //Send back XML
  231. $dom = new DOMDocument("1.0");
  232. //Create the root element
  233. $root = $dom->createElement("API");
  234. $dom->appendChild($root);
  235. //Create child elements
  236. $message = $dom->createElement("Version");
  237. $root->appendChild($message);
  238. $message->appendChild($dom->createTextNode("$version"));
  239. //Save xml tree
  240. $returnMessage = $dom->saveXML();
  241. }
  242. $data = $this->encodeRestResponse($returnMessage, $returnFormat);
  243. return $data;
  244. }
  245. /**
  246. * Used to aid in the building of the HTTP header to send back with the request response
  247. * @param htmlStatusCode - the code to sent back
  248. */
  249. function setHtmlStatusCode($htmlStatusCode) {
  250. $statusMessages = array(200=>'OK', 400=>'Bad Request', 401=>'Unauthorized');
  251. if (array_key_exists($htmlStatusCode, $statusMessages)) {
  252. $statusText = 'HTTP/1.1 '.$htmlStatusCode.' '.$statusMessages[$htmlStatusCode];
  253. return $statusText;
  254. } else {
  255. //Fix to allow compatibility with J!
  256. if (!function_exists( 'T_' )) { function T_($string) {return $string;} }
  257. die(T_('Invalid Html Response Code!'));
  258. }
  259. }
  260. /**
  261. * Set HTTP header content type to send back with the request response
  262. * @param returnFormat - user requested return format
  263. */
  264. function setReturnContentType($returnFormat) {
  265. //Determine return header content type
  266. switch ($returnFormat) {
  267. case 'json':
  268. case 'php':
  269. $ctype = 'text/html';
  270. break;
  271. case 'xml':
  272. $ctype = 'text/xml';
  273. break;
  274. default:
  275. $ctype = 'text/html';
  276. break;
  277. }
  278. return $ctype;
  279. }
  280. /**
  281. * Converts and object to an array
  282. * Code comes from PHP.net comments - http://us2.php.net/manual/en/function.get-object-vars.php#62470
  283. */
  284. function object_to_array($obj) {
  285. $_arr = is_object($obj) ? get_object_vars($obj) : $obj;
  286. foreach ($_arr as $key => $val) {
  287. $val = (is_array($val) || is_object($val)) ? $this->object_to_array($val) : $val;
  288. $arr[$key] = $val;
  289. }
  290. return $arr;
  291. }
  292. /**
  293. * Returns the full URL of the current page - including any query parameters.
  294. * Code comes from snipplr.com - http://snipplr.com/view/2734/get-full-url/
  295. */
  296. function full_url() {
  297. $s = empty($_SERVER["HTTPS"]) ? '' : ($_SERVER["HTTPS"] == "on") ? "s" : "";
  298. $protocol = substr(strtolower($_SERVER["SERVER_PROTOCOL"]), 0, strpos(strtolower($_SERVER["SERVER_PROTOCOL"]), "/")) . $s;
  299. $port = ($_SERVER["SERVER_PORT"] == "80") ? "" : (":".$_SERVER["SERVER_PORT"]);
  300. return $protocol . "://" . $_SERVER['SERVER_NAME'] . $port . $_SERVER['REQUEST_URI'];
  301. }
  302. }
  303. ?>