diff --git a/index.php b/index.php index 1b26c0a..b8e53e6 100644 --- a/index.php +++ b/index.php @@ -1,8 +1,11 @@ exec(); /* diff --git a/json.php b/json.php index 9c75c1b..30f62c2 100644 --- a/json.php +++ b/json.php @@ -3,21 +3,21 @@ /** * PHP REST SQL JSON renderer class * This class renders the REST response data as JSON. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * */ class PHPRestSQLRenderer { diff --git a/mssql.php b/mssql.php index 2c5b054..73ad1ae 100644 --- a/mssql.php +++ b/mssql.php @@ -105,7 +105,7 @@ function getRow($table, $where) { * @param str table * @return resource A resultset resource */ - function getTable($primary, $table) { + function getTable($primary, $table, $from = NULL, $to = NULL, $orderby = NULL, $filters = NULL) { $result = mssql_query(sprintf('SELECT %s FROM %s', $primary, $table)); if ($result) { $this->lastQueryResultResource = $result; diff --git a/mysql.php b/mysql.php index 498b8cb..0b75c3e 100644 --- a/mysql.php +++ b/mysql.php @@ -40,13 +40,13 @@ class mysql { */ function connect($config) { if ($this->db = @mysql_pconnect( - $config['server'], - $config['username'], - $config['password'] - )) { - if ($this->select_db($config['database'])) { - return TRUE; - } + $config['server'], + $config['username'], + $config['password'] + )) { + if ($this->select_db($config['database'])) { + return TRUE; + } } return FALSE; } @@ -81,20 +81,86 @@ function getColumns($table) { * Get a row from a table. * @param str table * @param str where + * @param str[] contains requested columns * @return resource A resultset resource */ - function getRow($table, $where) { - return mysql_query(sprintf('SELECT * FROM %s WHERE %s', $table, $where)); + function getRow($table, $where, $fields = NULL) { + $inject = ''; + if($fields == NULL) $inject = "*"; + else { + $fieldnames = explode(',', $fields); + foreach($fieldnames as $field) { + $inject .= $field . ','; + } + // remove redundant comma + $inject = substr($inject, 0, strlen($inject)-1); + } + return mysql_query(sprintf('SELECT %s FROM %s WHERE %s', $inject, $table, $where)); } /** * Get the rows in a table. * @param str primary The names of the primary columns to return * @param str table + * @param int from lowerbound for the LIMIT + * @param int to denoting the interval starting at lowerbound + * @param str[] sort contains columns for sorting purposes + * @param str[] filter contains search criteria for the rows * @return resource A resultset resource */ - function getTable($primary, $table) { - return mysql_query(sprintf('SELECT %s FROM %s', $primary, $table)); + function getTable($primary, $table, $from = NULL, $to = NULL, $orderby = NULL, $filters = NULL) { + + // prepare LIMIT clause + $limit_clause = ''; + if(($from !== NULL) && ($to !== NULL)) { + $limit_clause .= ' LIMIT ' . $from . ', ' . $to . ' '; + } + + // pepare ORDER BY clause + $orderbys = explode(',', $orderby); + if($orderby != NULL) { + $orderby_clause = 'ORDER BY '; + foreach($orderbys as $order) { + if($order[0] == '-') { + $order = ltrim($order, '-'); + $orderby_clause .= ' ' . $order . ' DESC,'; + } else { + $orderby_clause .= ' ' . $order . ' ASC,'; + } + } + $orderby_clause = rtrim($orderby_clause, ","); + } else { + $orderby_clause = ''; + } + + // prepare WHERE clause + $where_clause = ''; + if(count($filters) > 0) { + $where_clause = 'WHERE '; + foreach($filters as $key => $value) { + $operator = $value[0]; + $value = mysql_real_escape_string(substr($value, 1)); + $key = mysql_real_escape_string($key); + switch($operator) { + case '~': + $where_clause .= ' ' . '`' . $key . '`' . ' LIKE ' . '\'%' . $value . '%\'' . ' AND'; + break; + case '=': + $where_clause .= ' ' . '`' . $key . '`' . '=' . $value . ' AND'; + break; + case '<': + $where_clause .= ' ' . '`' . $key . '`' . '<' . $value . 'AND'; + break; + case '>': + $where_clause .= ' ' . '`' . $key . '`' . '>' . $value . ' AND'; + break; + } + } + $where_clause = rtrim($where_clause, "AND"); + } + + $query = sprintf('SELECT %s FROM %s %s %s %s', $primary, $table, $where_clause, $orderby_clause, $limit_clause); + return mysql_query($query); } /** diff --git a/phprestsql.ini b/phprestsql.ini index 397c16f..e512562 100644 --- a/phprestsql.ini +++ b/phprestsql.ini @@ -1,5 +1,6 @@ [settings] baseURL = "/phprestsql" +paging = 20 [database] type = "mysql" diff --git a/phprestsql.php b/phprestsql.php index 382ba49..8728a94 100644 --- a/phprestsql.php +++ b/phprestsql.php @@ -1,4 +1,5 @@ @@ -41,19 +42,13 @@ class PHPRestSQL { * @var str */ var $method = 'GET'; - + /** * The HTTP request data sent (if any). * @var str */ var $requestData = NULL; - - /** - * The URL extension stripped off of the request URL - * @var str - */ - var $extension = NULL; - + /** * The database table to query. * @var str @@ -74,9 +69,52 @@ class PHPRestSQL { /** * Type of display, database, table or row. + * @var str */ var $display = NULL; + /** + * Holds the page type (tables|table|row). + * @var str + */ + var $type = NULL; + + /** + * Stores filter criteria for presenting the table. + * @var str[] + */ + var $filters = array(); + + /** + * Stores sort parameter criteria for tables. + * @var str[] + */ + var $orderby = NULL; + + /** + * Stores a subset of columns to be presented on item view. + * @var str[] + */ + var $fields = NULL; + + /** + * Holds the current page number. + * @var int + */ + var $page = NULL; + + /** + * Defines how many resources a page contains. + * @var int + */ + var $per_page = NULL; + + /** + * Denotes the format of the output. + * @var str + */ + var $format = NULL; + /** * Constructor. Parses the configuration file "phprestsql.ini", grabs any request data sent, records the HTTP * request method used and parses the request URL to find out the requested table name and primary key values. @@ -85,6 +123,11 @@ class PHPRestSQL { function PHPRestSQL($iniFile = 'phprestsql.ini') { $this->config = parse_ini_file($iniFile, TRUE); + // Setting default values if parameter is undefined. + $this->per_page = $this->config['settings']['paging']; + $this->page = 0; + $this->format = 'html'; + if (isset($_SERVER['REQUEST_URI']) && isset($_SERVER['REQUEST_METHOD'])) { if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH'] > 0) { @@ -96,32 +139,52 @@ function PHPRestSQL($iniFile = 'phprestsql.ini') { fclose($httpContent); } - $urlString = substr($_SERVER['REQUEST_URI'], strlen($this->config['settings']['baseURL'])); - $urlParts = explode('/', $urlString); - - $lastPart = array_pop($urlParts); - $dotPosition = strpos($lastPart, '.'); - if ($dotPosition !== FALSE) { - $this->extension = substr($lastPart, $dotPosition + 1); - $lastPart = substr($lastPart, 0, $dotPosition); - } - array_push($urlParts, $lastPart); - - if (isset($urlParts[0]) && $urlParts[0] == '') { - array_shift($urlParts); - } - - if (isset($urlParts[0])) $this->table = $urlParts[0]; - if (count($urlParts) > 1 && $urlParts[1] != '') { - array_shift($urlParts); - foreach ($urlParts as $uid) { - if ($uid != '') { - $this->uid[] = $uid; + $this->method = $_SERVER['REQUEST_METHOD']; + + // Parsing URL to load all parameters and directories. + $parser = new RequestParser($_SERVER['REQUEST_URI'], $this->config['settings']['baseURL']); + $params = $parser->getParameters(); + + // Evaluate the params detected by urlparser. + if(is_array($params)) { + foreach($params as $key => $value) { + switch($key) { + case "format": + $this->format = $value; + break; + case "orderby": + $this->orderby = $value; + break; + case "page": + $this->page = $value; + break; + case "per_page": + $this->per_page = $value; + break; + case "fields": + $this->fields = $value; + break; + default: + $this->filters[$key] = $value; } } } - - $this->method = $_SERVER['REQUEST_METHOD']; + + $dirs = $parser->getDirs(); + if(count($dirs) == 0) { + $this->type = 'tables'; + } + else if(count($dirs) == 1) { + $this->table = $dirs[0]; + $this->type = 'table'; + } else if(count($dirs) == 2) { + $this->type = 'row'; + $uids = explode(',', $dirs[1]); + foreach($uids as $uid) { + if($uid !== '') $this->uid[] = $uid; + } + $this->table = $dirs[0]; + } } } @@ -138,8 +201,8 @@ function connect() { trigger_error('Could not connect to server', E_USER_ERROR); } } elseif (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) { - $this->config['database']['username'] = $_SERVER['PHP_AUTH_USER']; - $this->config['database']['password'] = $_SERVER['PHP_AUTH_PW']; + $this->config['database']['username'] = $_SERVER['PHP_AUTH_USER']; + $this->config['database']['password'] = $_SERVER['PHP_AUTH_PW']; if (!$this->db->connect($this->config['database'])) { $this->unauthorized(); exit; @@ -148,13 +211,13 @@ function connect() { $this->unauthorized(); exit; } + } /** * Execute the request. */ function exec() { - $this->connect(); switch ($this->method) { @@ -181,18 +244,7 @@ function exec() { * @return str[] The primary key field names */ function getPrimaryKeys() { - return $this->db->getPrimaryKeys($this->table); - - #$resource = $this->db->getColumns($this->table); - #$primary = NULL; - #if ($resource) { - # while ($row = $this->db->row($resource)) { - # if ($row['Key'] == 'PRI') { - # $primary[] = $row['Field']; - # } - # } - #} - #return $primary; + return $this->db->getPrimaryKeys($this->table); } /** @@ -201,79 +253,116 @@ function getPrimaryKeys() { * database contents. */ function get() { - if ($this->table) { - $primary = $this->getPrimaryKeys(); - if ($primary) { - if ($this->uid && count($primary) == count($this->uid)) { // get a row - $this->display = 'row'; - $where = ''; - foreach($primary as $key => $pri) { - $where .= $pri.' = \''.$this->uid[$key].'\' AND '; - } - $where = substr($where, 0, -5); - $resource = $this->db->getRow($this->table, $where); - if ($resource) { - if ($this->db->numRows($resource) > 0) { - while ($row = $this->db->row($resource)) { - $values = array(); - foreach ($row as $column => $data) { - $field = array( - 'field' => $column, - 'value' => $data - ); - if (substr($column, -strlen($this->config['database']['foreignKeyPostfix'])) == $this->config['database']['foreignKeyPostfix']) { - $field['xlink'] = $this->config['settings']['baseURL'].'/'.substr($column, 0, -strlen($this->config['database']['foreignKeyPostfix'])).'/'.$data; - } - $values[] = $field; - } - $this->output['row'] = $values; - } - $this->generateResponseData(); - } else { - $this->notFound(); - } - } else { - $this->unauthorized(); - } - } else { // get table - $this->display = 'table'; - $resource = $this->db->getTable(join(', ', $primary), $this->table); - if ($resource) { - if ($this->db->numRows($resource) > 0) { - while ($row = $this->db->row($resource)) { - $this->output['table'][] = array( - 'xlink' => $this->config['settings']['baseURL'].'/'.$this->table.'/'.join('/', $row), - 'value' => join(' ', $row) - ); - } + $primary = $this->getPrimaryKeys(); + + switch($this->type) { + case "tables": + $this->get_tables(); + break; + case "table": + $this->get_table($primary); + break; + case "row": + $this->get_row($primary); + break; + } + } + + /** + * Is responsible for fetching the requested content of a row. + * - fields can be used to only present a subset of all columns. + * - format parameter can be used to choose representation + */ + private function get_row($primary) { + $this->display = 'row'; + $where = ''; + foreach($primary as $key => $pri) { + $where .= $pri.' = \''.$this->uid[$key].'\' AND '; + } + $where = substr($where, 0, -5); + $resource = $this->db->getRow($this->table, $where, $this->fields); + + if ($resource) { + if ($this->db->numRows($resource) > 0) { + while ($row = $this->db->row($resource)) { + $values = array(); + foreach ($row as $column => $data) { + $field = array( + 'field' => $column, + 'value' => $data + ); + if (substr($column, -strlen($this->config['database']['foreignKeyPostfix'])) == $this->config['database']['foreignKeyPostfix']) { + $field['xlink'] = $this->config['settings']['baseURL'].'/'.substr($column, 0, -strlen($this->config['database']['foreignKeyPostfix'])).'/'.$data; } - $this->generateResponseData(); - } else { - $this->unauthorized(); + $values[] = $field; } + $this->output['row'] = $values; } + $this->generateResponseData(); + } else { + $this->notFound(); } - } else { // get database - $this->display = 'database'; - $resource = $this->db->getDatabase(); - if ($resource) { - if ($this->db->numRows($resource) > 0) { - while ($row = $this->db->row($resource)) { - $this->output['database'][] = array( - 'xlink' => $this->config['settings']['baseURL'].'/'.reset($row), - 'value' => reset($row) - ); - } - $this->generateResponseData(); - } else { - $this->notFound(); + } else { + $this->unauthorized(); + } + } + + /** + * Is responsibel for loading the requested table content. + * Based on the following GET parameters: page, per_page, sort, col1=filter, format parameter. + */ + private function get_table($primary) { + $this->display = 'table'; + + /* TODO: Read GET parameters to find out about the where clause */ + $from = $this->page * $this->per_page; + $resource = $this->db->getTable(join(', ', $primary), + $this->table, + $from, + $this->per_page, + $this->orderby, + $this->filters); + + if ($resource) { + if ($this->db->numRows($resource) > 0) { + while ($row = $this->db->row($resource)) { + $this->output['table'][] = array( + 'xlink' => $this->config['settings']['baseURL'].'/'.$this->table.'/'.join('/', $row), + 'value' => join(' ', $row) + ); + } + } + $this->generateResponseData(); + } else { + $this->unauthorized(); + } + } + + /** + * Loads all tables of a given database. + */ + private function get_tables() { + $this->display = 'database'; + + $resource = $this->db->getDatabase(); + if ($resource) { + if ($this->db->numRows($resource) > 0) { + while ($row = $this->db->row($resource)) { + $this->output['database'][] = array( + 'xlink' => $this->config['settings']['baseURL'].'/'.reset($row), + 'value' => reset($row) + ); } + $this->generateResponseData(); } else { - $this->unauthorized(); + $this->notFound(); + } + } else { + $this->unauthorized(); } } - + /** * Execute a POST request. */ @@ -329,7 +418,7 @@ function post() { $resource = $this->db->insertRow($this->table, $names, $values); if ($resource) { if ($this->db->numAffected() > 0) { - $this->created($this->config['settings']['baseURL'].'/'.$this->table.'/'.$this->db->lastInsertId().'/'); + $this->created($this->config['settings']['baseURL'].'/'.$this->table.'/'.$this->db->lastInsertId().'/'); } else { $this->badRequest(); } @@ -419,7 +508,7 @@ function put() { $this->methodNotAllowed('GET, HEAD'); } } - + /** * Execute a DELETE request. A DELETE request removes a row from the database given a table and primary key(s). */ @@ -470,14 +559,14 @@ function parseRequestData() { * Generate the HTTP response data. */ function generateResponseData() { - if ($this->extension) { - if (isset($this->config['mimetypes'][$this->extension])) { - $mimetype = $this->config['mimetypes'][$this->extension]; - if (isset($this->config['renderers'][$mimetype])) { - $renderClass = $this->config['renderers'][$mimetype]; - } - } - } elseif (isset($_SERVER['HTTP_ACCEPT'])) { + if ($this->format) { + if (isset($this->config['mimetypes'][$this->format])) { + $mimetype = $this->config['mimetypes'][$this->format]; + if (isset($this->config['renderers'][$mimetype])) { + $renderClass = $this->config['renderers'][$mimetype]; + } + } + } elseif (isset($_SERVER['HTTP_ACCEPT'])) { $accepts = explode(',', $_SERVER['HTTP_ACCEPT']); $orderedAccepts = array(); foreach ($accepts as $key => $accept) { @@ -506,14 +595,14 @@ function generateResponseData() { } else { $renderClass = array_shift($this->config['renderers']); } - if (isset($renderClass)) { - require_once($renderClass); - $renderer = new PHPRestSQLRenderer(); - $renderer->render($this); - } else { - $this->notAcceptable(); - exit; - } + if (isset($renderClass)) { + require_once($renderClass); + $renderer = new PHPRestSQLRenderer(); + $renderer->render($this); + } else { + $this->notAcceptable(); + exit; + } } /** @@ -587,4 +676,4 @@ function internalServerError() { } -?> +?> \ No newline at end of file diff --git a/postgresql.php b/postgresql.php index 0f4574d..c08fcf4 100644 --- a/postgresql.php +++ b/postgresql.php @@ -104,7 +104,7 @@ function getRow($table, $where) { * @param str table * @return resource A resultset resource */ - function getTable($primary, $table) { + function getTable($primary, $table, $from = NULL, $to = NULL, $orderby = NULL, $filters = NULL) { $result = pg_query(sprintf('SELECT %s FROM %s', $primary, $table)); if ($result) { $this->lastQueryResultResource = $result; diff --git a/urlparser.php b/urlparser.php new file mode 100644 index 0000000..47473f2 --- /dev/null +++ b/urlparser.php @@ -0,0 +1,58 @@ +URL = $URL; + $this->BASEURL = $baseUrl; + $this->parse(); + } + + function parse() { + $pos = strpos($this->URL, $this->BASEURL); + if($pos == 0) { + $substr = substr($this->URL, strlen($this->BASEURL)); + $this->URL = $substr; + } + + // load all get parameters in arrays + foreach ($_GET as $key => $value) { + //TODO: hash map: check if it's e predefined parameter... if no, add to filter bundle + $this->PARAMS[$key] = $value; + } + + if($this->URL == '/') { + return; + } else { + $urlParts = explode('/', $this->URL); + $i = 0; + foreach($urlParts as $val) { + // making sure /?param=value is not a added as direction + if($val !== '' && isset($val) && ($val[0] !== '?' && $val[0] !== '&')) { + // making sure we cut off the params + $pos = strpos($val, "?"); + if($pos == false) $this->DIRS[$i++] = $val; + else $this->DIRS[$i++] = substr($val, 0, $pos); + } + } + } + + } + + function getDirs() { + return $this->DIRS; + } + + function getParameters() { + return $this->PARAMS; + } + +} +?> \ No newline at end of file