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