. * * @author TechFuze * @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net) * @copyright Copyright (c) 1996 - 2015, Free Software Foundation, Inc. (http://www.fsf.org/) * @license http://opensource.org/licenses/GPL-3.0 GPLv3 License * * @link http://fuzeworks.techfuze.net * @since Version 0.0.1 * * @version Version 0.0.1 */ namespace Module\Api; use FuzeWorks\Layout; /** * RestAPI class for creating API's out of modules or contrllers. * * Extend a Controller with this class, and be sure to return the data from methods of your controller. * This data will be parsed by this class and returned as valid JSON data. * The necessity of API keys can be configured in the controller by settings $this->requireApiKey = false; * * @author Abel Hoogeveen * @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net) */ abstract class RestAPI { /** * Property: method * The HTTP method this request was made in, either GET, POST, PUT or DELETE. */ protected $method = ''; /** * Property: endpoint * The Model requested in the URI. eg: /files. */ protected $endpoint = ''; /** * Property: verb * An optional additional descriptor about the endpoint, used for things that can * not be handled by the basic methods. eg: /files/process. */ protected $verb = ''; /** * Property: args * Any additional URI components after the endpoint and verb have been removed, in our * case, an integer ID for the resource. eg: //// * or //. */ protected $args = array(); /** * Property: file * Stores the input of the PUT request. */ protected $file = null; /** * Whether API authentication is needed before interacting with the API. */ protected $requireApiKey = true; /** * Constructor: __construct * Allow for CORS, assemble and pre-process the data. */ public function __construct($request) { header('Access-Control-Allow-Orgin: *'); header('Access-Control-Allow-Methods: *'); header('Content-Type: application/json'); // Return layout data as string Layout::setEngine('JSON'); Layout::returnAsString(false); $this->args = explode('/', rtrim($request, '/')); $this->endpoint = array_shift($this->args); if (array_key_exists(0, $this->args) && !is_numeric($this->args[0])) { $this->verb = array_shift($this->args); } $this->method = $_SERVER['REQUEST_METHOD']; if ($this->method == 'POST' && array_key_exists('HTTP_X_HTTP_METHOD', $_SERVER)) { if ($_SERVER['HTTP_X_HTTP_METHOD'] == 'DELETE') { $this->method = 'DELETE'; } elseif ($_SERVER['HTTP_X_HTTP_METHOD'] == 'PUT') { $this->method = 'PUT'; } else { throw new Exception('Unexpected Header'); } } switch ($this->method) { case 'DELETE': case 'POST': $this->request = $this->_cleanInputs($_POST); break; case 'GET': $this->request = $this->_cleanInputs($_GET); break; case 'PUT': $this->request = $this->_cleanInputs($_GET); $this->file = file_get_contents('php://input'); break; default: $this->_response('Invalid Method', 405); break; } // And afterwards process the data echo $this->processAPI(); // Halter for when the RestApi is used by a controller $this->halt = true; Layout::reset(); } /** * Process an API request when retrieving. * * @return string JSON encoded response */ public function processAPI() { if (method_exists($this, $this->endpoint)) { return $this->_response($this->{$this->endpoint}($this->args)); } return $this->_response("No Endpoint: $this->endpoint", 404); } private function _response($data, $status = 200) { header('HTTP/1.1 '.$status.' '.$this->_requestStatus($status)); return json_encode($data); } private function _cleanInputs($data) { $clean_input = array(); if (is_array($data)) { foreach ($data as $k => $v) { $clean_input[$k] = $this->_cleanInputs($v); } } else { $clean_input = trim(strip_tags($data)); } return $clean_input; } private function _requestStatus($code) { $status = array( 200 => 'OK', 404 => 'Not Found', 405 => 'Method Not Allowed', 500 => 'Internal Server Error', ); return ($status[$code]) ? $status[$code] : $status[500]; } }