From 3d872d4b751c1a8254894891647e5ad24643ea6e Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Mon, 20 May 2013 18:14:34 -0400 Subject: [PATCH 01/43] initial commit of ArrestDB --- LICENSE | 21 ++ README.md | 32 +-- config.php | 11 - index.php | 492 ++++++++++++++++++++++++++++++++++++++++--- lib/arrest-mysql.php | 399 ----------------------------------- lib/db.php | 330 ----------------------------- 6 files changed, 506 insertions(+), 779 deletions(-) create mode 100644 LICENSE delete mode 100644 config.php delete mode 100644 lib/arrest-mysql.php delete mode 100644 lib/db.php diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..1538308f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2013 Alix Axel + +Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 09ad167c..30010935 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -#Arrest MySQL +#ArrestDB -Arrest MySQL is a "plug-n-play" RESTful API for your MySQL database. Arrest MySQL provides a REST API that maps directly to your database stucture with no configuation. +ArrestDB is a "plug-n-play" RESTful API for your MySQL database. Arrest MySQL provides a REST API that maps directly to your database stucture with no configuation. For example lets suppose you have set up Arrest MySQL at http://api.example.com and your database has a table in it called "customers". To get a list of customers you would simply need to do: @@ -14,10 +14,12 @@ Where "123" here is the ID of the customer. For more information on using Arrest ##Requirements -1. Apache Server with PHP 5+ -2. MySQL 5+ +- PHP 5.3+ +- MySQL 5+ / SQLite 3+ -##30 Second Installation +##Installation + +Place `index.php` in a publicly accessible directory (feel free to change the filename to whatever you want). Simply put these files into a folder somewhere on your server. Then edit config.php and fill in your database details and you are good to go. @@ -33,21 +35,21 @@ require('lib/arrest-mysql.php'); try { /** - * Note: You will need to provide a base_uri as the second param if this file + * Note: You will need to provide a base_uri as the second param if this file * resides in a subfolder e.g. if the URL to this file is http://example.com/some/sub/folder/index.php * then the base_uri should be "some/sub/folder" */ $arrest = new ArrestMySQL($db_config); - + /** * By default it is assumed that the primary key of a table is "id". If this * is not the case then you can set a custom index by using the * set_table_index($table, $field) method */ //$arrest->set_table_index('my_table', 'some_index'); - + $arrest->rest(); - + } catch (Exception $e) { echo $e; } @@ -150,14 +152,14 @@ Please contribute by [reporting bugs](Arrest-MySQL/issues) and submitting [pull Copyright © 2013 Dev7studios -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation -files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, -modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/config.php b/config.php deleted file mode 100644 index e0812d9c..00000000 --- a/config.php +++ /dev/null @@ -1,11 +0,0 @@ - 'localhost', // Usually localhost - 'database' => '', // The database name - 'username' => '', // The database username - 'password' => '', // The database password - 'verbose' => false // If true errors will be shown -); - -?> \ No newline at end of file diff --git a/index.php b/index.php index c00d6d77..1aa81bc0 100644 --- a/index.php +++ b/index.php @@ -1,28 +1,472 @@ set_table_index('my_table', 'some_index'); - - $arrest->rest(); - -} catch (Exception $e) { - echo $e; +$dsn = ''; +$clients = array +( +); + +/** +* The MIT License +* http://creativecommons.org/licenses/MIT/ +* +* ArrestDB 1.0.0 (github.com/alixaxel/ArrestDB/) +* Copyright (c) 2013 Alix Axel +**/ + +if (strcmp('cli', PHP_SAPI) === 0) +{ + exit('Arrest-DB should not be ran from CLI.'); +} + +header('Content-Type: application/json'); + +if ((empty($clients) !== true) && (in_array($_SERVER['REMOTE_ADDR'], (array) $clients) !== true)) +{ + $result = array + ( + 'error' => array + ( + 'code' => 403, + 'status' => 'Forbidden', + ), + ); + + exit(json_encode($result, 480)); } -?> \ No newline at end of file +else if (ArrestDB::Query($dsn) === false) +{ + $result = array + ( + 'error' => array + ( + 'code' => 503, + 'status' => 'Service Unavailable', + ), + ); + + exit(json_encode($result, 480)); +} + +ArrestDB::Serve('GET', '/(#any)/(#num)?', function ($table, $id = null) +{ + $query = array + ( + sprintf('SELECT * FROM `%s`', $table), + ); + + if (isset($id) === true) + { + $query[] = sprintf('WHERE `%s` = ? LIMIT 1', 'id'); + } + + else + { + if (isset($_GET['by']) === true) + { + if (isset($_GET['order']) !== true) + { + $_GET['order'] = 'ASC'; + } + + $query[] = sprintf('ORDER BY `%s` %s', $_GET['by'], $_GET['order']); + } + + if (isset($_GET['limit']) === true) + { + $query[] = sprintf('LIMIT %u', $_GET['limit']); + + if (isset($_GET['offset']) === true) + { + $query[] = sprintf('OFFSET %u', $_GET['offset']); + } + } + } + + $query = sprintf('%s;', implode(' ', $query)); + $result = (isset($id) === true) ? ArrestDB::Query($query, $id) : ArrestDB::Query($query); + + if ($result === false) + { + $result = array + ( + 'error' => array + ( + 'code' => 404, + 'status' => 'Not Found', + ), + ); + } + + else if (empty($result) === true) + { + $result = array + ( + 'error' => array + ( + 'code' => 204, + 'status' => 'No Content', + ), + ); + } + + else if (isset($id) === true) + { + $result = array_shift($result); + } + + return json_encode($result, 480); +}); + +ArrestDB::Serve('POST', '/(#any)', function ($table) +{ + if (empty($_POST) === true) + { + $result = array + ( + 'error' => array + ( + 'code' => 204, + 'status' => 'No Content', + ), + ); + } + + else + { + $data = array(); + + foreach ($_POST as $key => $value) + { + $data[sprintf('`%s`', $key)] = '?'; + } + + $query = array + ( + sprintf('INSERT INTO `%s` (%s) VALUES (%s)', $table, implode(', ', array_keys($data)), implode(', ', $data)), + ); + + $query = sprintf('%s;', implode(' ', $query)); + $result = ArrestDB::Query($query, $_POST); + + if ($result === false) + { + $result = array + ( + 'error' => array + ( + 'code' => 404, + 'status' => 'Not Found', + ), + ); + } + + else + { + $result = array + ( + 'success' => array + ( + 'code' => 200, + 'status' => 'OK', + ), + ); + } + } + + return json_encode($result, 480); +}); + +ArrestDB::Serve('PUT', '/(#any)/(#num)', function ($table, $id) +{ + parse_str(file_get_contents('php://input'), $_PUT); + + if (empty($_PUT) === true) + { + $result = array + ( + 'error' => array + ( + 'code' => 204, + 'status' => 'No Content', + ), + ); + } + + else + { + $data = array(); + + foreach ($_PUT as $key => $value) + { + $data[$key] = sprintf('`%s` = ?', $key); + } + + $query = array + ( + sprintf('UPDATE `%s` SET %s WHERE `%s` = ?', $table, implode(', ', $data), 'id'), + ); + + $query = sprintf('%s;', implode(' ', $query)); + $result = ArrestDB::Query($query, $_PUT); + + if ($result === false) + { + $result = array + ( + 'error' => array + ( + 'code' => 404, + 'status' => 'Not Found', + ), + ); + } + + else + { + $result = array + ( + 'success' => array + ( + 'code' => 200, + 'status' => 'OK', + ), + ); + } + } + + return json_encode($result, 480); +}); + +ArrestDB::Serve('DELETE', '/(#any)/(#num)', function ($table, $id) +{ + $query = array + ( + sprintf('DELETE FROM `%s` WHERE `%s` = ?', $table, 'id'), + ); + + $query = sprintf('%s;', implode(' ', $query)); + $result = ArrestDB::Query($query, $id); + + if ($result === false) + { + $result = array + ( + 'error' => array + ( + 'code' => 404, + 'status' => 'Not Found', + ), + ); + } + + else if (empty($result) === true) + { + $result = array + ( + 'error' => array + ( + 'code' => 204, + 'status' => 'No Content', + ), + ); + } + + else + { + $result = array + ( + 'success' => array + ( + 'code' => 200, + 'status' => 'OK', + ), + ); + } + + return json_encode($result, 480); +}); + +$result = array +( + 'error' => array + ( + 'code' => 400, + 'status' => 'Bad Request', + ), +); + +exit(json_encode($result, 480)); + +class ArrestDB +{ + public static function Query($query = null) + { + static $db = null; + static $result = array(); + + try + { + if (isset($db, $query) === true) + { + if (empty($result[$hash = crc32($query)]) === true) + { + $result[$hash] = $db->prepare($query); + } + + $data = array_slice(func_get_args(), 1); + + if (count($data, COUNT_RECURSIVE) > count($data)) + { + $data = iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveArrayIterator($data)), false); + } + + if ($result[$hash]->execute($data) === true) + { + switch (strstr($query, ' ', true)) + { + case 'INSERT': + case 'REPLACE': + return $db->lastInsertId(); + + case 'UPDATE': + case 'DELETE': + return $result[$hash]->rowCount(); + + case 'SELECT': + case 'EXPLAIN': + case 'PRAGMA': + case 'SHOW': + return $result[$hash]->fetchAll(); + } + + return true; + } + + return false; + } + + else if (isset($query) === true) + { + $options = array + ( + \PDO::ATTR_CASE => \PDO::CASE_NATURAL, + \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, + \PDO::ATTR_EMULATE_PREPARES => false, + \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, + \PDO::ATTR_ORACLE_NULLS => \PDO::NULL_NATURAL, + \PDO::ATTR_STRINGIFY_FETCHES => false, + ); + + if (preg_match('~^sqlite://([[:print:]]++)$~i', $query, $dsn) > 0) + { + $options += array + ( + \PDO::ATTR_TIMEOUT => 0, + ); + + $db = new \PDO(sprintf('sqlite:%s', $dsn[1]), null, null, $options); + $pragmas = array + ( + 'busy_timeout' => '0', + 'cache_size' => '8192', + 'encoding' => '"UTF-8"', + 'foreign_keys' => 'ON', + 'journal_mode' => 'WAL', + 'journal_size_limit' => '67110000', + 'legacy_file_format' => 'OFF', + 'page_size' => '4096', + 'recursive_triggers' => 'ON', + 'secure_delete' => 'ON', + 'synchronous' => 'NORMAL', + 'temp_store' => 'MEMORY', + 'wal_autocheckpoint' => '4096', + ); + + if (strncasecmp('WIN', PHP_OS, 3) !== 0) + { + if (($page = intval(shell_exec('getconf PAGESIZE'))) > 0) + { + $pragmas['page_size'] = $page; + } + + if ((is_file('/proc/meminfo') === true) && (is_readable('/proc/meminfo') === true)) + { + $memory = 131072; + + if (is_resource($handle = fopen('/proc/meminfo', 'rb')) === true) + { + while (($line = fgets($handle, 1024)) !== false) + { + if (sscanf($line, 'MemTotal: %d kB', $memory) == 1) + { + $memory = round($memory / 131072) * 131072; break; + } + } + + fclose($handle); + } + + $pragmas['cache_size'] = intval($memory * 0.25 / ($pragmas['page_size'] / 1024)); + $pragmas['wal_autocheckpoint'] = $pragmas['cache_size'] / 2; + } + } + + foreach ($pragmas as $key => $value) + { + $db->exec(sprintf('PRAGMA %s=%s;', $key, $value)); + } + } + + else if (preg_match('~^mysql://(?:(.+?)(?::(.+?))?@)?([^/:@]++)(?::(\d++))?/(\w++)/?$~i', $query, $dsn) > 0) + { + $options += array + ( + \PDO::ATTR_AUTOCOMMIT => true, + \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES "utf8" COLLATE "utf8_general_ci", time_zone = "+00:00";', + \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, + ); + + $db = new \PDO(sprintf('mysql:host=%s;port=%s;dbname=%s', $dsn[3], $dsn[4], $dsn[5]), $dsn[1], $dsn[2], $options); + } + } + } + + catch (\PDOException $e) + { + return false; + } + + catch (\Exception $e) + { + return false; + } + + return (isset($db) === true) ? $db : false; + } + + public static function Serve($on = null, $route = null, $callback = null) + { + static $root = null; + + if (isset($_SERVER['REQUEST_METHOD']) !== true) + { + $_SERVER['REQUEST_METHOD'] = 'CLI'; + } + + if ((empty($on) === true) || (strcasecmp($on, $_SERVER['REQUEST_METHOD']) === 0)) + { + if (is_null($root) === true) + { + $root = preg_replace('~/++~', '/', substr($_SERVER['PHP_SELF'], strlen($_SERVER['SCRIPT_NAME'])) . '/'); + } + + if (preg_match('~^' . str_replace(array('#any', '#num'), array('[^/]++', '[0-9]++'), $route) . '~i', $root, $parts) > 0) + { + return (empty($callback) === true) ? true : exit(call_user_func_array($callback, array_slice($parts, 1))); + } + } + + return false; + } +} diff --git a/lib/arrest-mysql.php b/lib/arrest-mysql.php deleted file mode 100644 index 2b870938..00000000 --- a/lib/arrest-mysql.php +++ /dev/null @@ -1,399 +0,0 @@ - - * $arrest = new ArrestMySQL($db_config); - * $arrest->rest(); - * - * - * Author: Gilbert Pellegrom - * Website: http://dev7studios.com - * Date: Jan 2013 - * Version 1.0 - */ -require('lib/db.php'); - -class ArrestMySQL { - - /** - * The instance of Database - * - * @var Database - */ - private $db; - /** - * The structure of the database - * - * @var array - */ - private $db_structure; - /** - * The URI segments - * - * @var array - */ - private $segments; - /** - * Array of custom table indexes - * - * @var array - */ - private $table_index; - - /** - * Create an instance, optionally setting a base URI - * - * @param array $db_config An array of database config options. Format: - * - * $db_config = array( - * 'server' => 'localhost', - * 'database' => '', - * 'username' => '', - * 'password' => '', - * 'verbose' => false - * ); - * - * @param string $base_uri Optional base URI if not in root folder - * @access public - */ - public function __construct($db_config, $base_uri = '') - { - $this->db = new Database($db_config); - if(!$this->db->init()) throw new Exception($this->db->get_error()); - - $this->db_structure = $this->map_db($db_config['database']); - $this->segments = $this->get_uri_segments($base_uri); - $this->table_index = array(); - } - - /** - * Handle the REST calls and map them to corresponding CRUD - * - * @access public - */ - public function rest() - { - header('Content-type: application/json'); - /* - create > POST /table - read > GET /table[/id] - update > PUT /table/id - delete > DELETE /table/id - */ - switch ($_SERVER['REQUEST_METHOD']) { - case 'POST': - $this->create(); - break; - case 'GET': - $this->read(); - break; - case 'PUT': - $this->update(); - break; - case 'DELETE': - $this->delete(); - break; - } - } - - /** - * Add a custom index (usually primary key) for a table - * - * @param string $table Name of the table - * @param string $field Name of the index field - * @access public - */ - public function set_table_index($table, $field) - { - $this->table_index[$table] = $field; - } - - /** - * Map the stucture of the MySQL db to an array - * - * @param string $database Name of the database - * @return array Returns array of db structure - * @access private - */ - private function map_db($database) - { - // Map db structure to array - $tables_arr = array(); - $this->db->query('SHOW TABLES FROM '. $database); - while($table = $this->db->fetch_array()){ - if(isset($table['Tables_in_'. $database])){ - $table_name = $table['Tables_in_'. $database]; - $tables_arr[$table_name] = array(); - } - } - foreach($tables_arr as $table_name=>$val){ - $this->db->query('SHOW COLUMNS FROM '. $table_name); - $fields = $this->db->fetch_all(); - $tables_arr[$table_name] = $fields; - } - return $tables_arr; - } - - /** - * Get the URI segments from the URL - * - * @param string $base_uri Optional base URI if not in root folder - * @return array Returns array of URI segments - * @access private - */ - private function get_uri_segments($base_uri) - { - // Fix REQUEST_URI if required - if(!isset($_SERVER['REQUEST_URI'])){ - $_SERVER['REQUEST_URI'] = substr($_SERVER['PHP_SELF'], 1); - if(isset($_SERVER['QUERY_STRING'])) $_SERVER['REQUEST_URI'] .= '?'. $_SERVER['QUERY_STRING']; - } - - $url = ''; - $request_url = $_SERVER['REQUEST_URI']; - $script_url = $_SERVER['PHP_SELF']; - $request_url = str_replace($base_uri, '', $request_url); - if($request_url != $script_url) $url = trim(preg_replace('/'. str_replace('/', '\/', str_replace('index.php', '', $script_url)) .'/', '', $request_url, 1), '/'); - $url = rtrim(preg_replace('/\?.*/', '', $url), '/'); - - return explode('/', $url); - } - - /** - * Get a URI segment - * - * @param int $index Index of the URI segment - * @return mixed Returns URI segment or false if none exists - * @access private - */ - private function segment($index) - { - if(isset($this->segments[$index])) return $this->segments[$index]; - return false; - } - - /** - * Handles a POST and inserts into the database - * - * @access private - */ - private function create() - { - $table = $this->segment(0); - - if(!$table || !isset($this->db_structure[$table])){ - $error = array('error' => array( - 'message' => 'Not Found', - 'code' => 404 - )); - die(json_encode($error)); - } - - if($data = $this->_post()){ - $this->db->insert($table, $data) - ->query(); - $success = array('success' => array( - 'message' => 'Success', - 'code' => 200 - )); - die(json_encode($success)); - } else { - $error = array('error' => array( - 'message' => 'No Content', - 'code' => 204 - )); - die(json_encode($error)); - } - } - - /** - * Handles a GET and reads from the database - * - * @access private - */ - private function read() - { - $table = $this->segment(0); - $id = intval($this->segment(1)); - - if(!$table || !isset($this->db_structure[$table])){ - $error = array('error' => array( - 'message' => 'Not Found', - 'code' => 404 - )); - die(json_encode($error)); - } - - if($id && is_int($id)) { - $index = 'id'; - if(isset($this->table_index[$table])) $index = $this->table_index[$table]; - $this->db->select('*') - ->from($table) - ->where($index, $id) - ->query(); - if($result = $this->db->fetch_array()){ - die(json_encode($result)); - } else { - $error = array('error' => array( - 'message' => 'No Content', - 'code' => 204 - )); - die(json_encode($error)); - } - } else { - $this->db->select('*') - ->from($table) - ->order_by($this->_get('order_by'), $this->_get('order')) - ->limit(intval($this->_get('limit')), intval($this->_get('offset'))) - ->query(); - if($result = $this->db->fetch_all()){ - die(json_encode($result)); - } else { - $error = array('error' => array( - 'message' => 'No Content', - 'code' => 204 - )); - die(json_encode($error)); - } - } - } - - /** - * Handles a PUT and updates the database - * - * @access private - */ - private function update() - { - $table = $this->segment(0); - $id = intval($this->segment(1)); - - if(!$table || !isset($this->db_structure[$table]) || !$id){ - $error = array('error' => array( - 'message' => 'Not Found', - 'code' => 404 - )); - die(json_encode($error)); - } - - $index = 'id'; - if(isset($this->table_index[$table])) $index = $this->table_index[$table]; - $this->db->select('*') - ->from($table) - ->where($index, $id) - ->query(); - if($result = $this->db->fetch_array()){ - $this->db->update($table) - ->set($this->_put()) - ->where($index, $id) - ->query(); - $success = array('success' => array( - 'message' => 'Success', - 'code' => 200 - )); - die(json_encode($success)); - } else { - $error = array('error' => array( - 'message' => 'No Content', - 'code' => 204 - )); - die(json_encode($error)); - } - } - - /** - * Handles a DELETE and deletes from the database - * - * @access private - */ - private function delete() - { - $table = $this->segment(0); - $id = intval($this->segment(1)); - - if(!$table || !isset($this->db_structure[$table]) || !$id){ - $error = array('error' => array( - 'message' => 'Not Found', - 'code' => 404 - )); - die(json_encode($error)); - } - - $index = 'id'; - if(isset($this->table_index[$table])) $index = $this->table_index[$table]; - $this->db->select('*') - ->from($table) - ->where($index, $id) - ->query(); - if($result = $this->db->fetch_array()){ - $this->db->delete($table) - ->where($index, $id) - ->query(); - $success = array('success' => array( - 'message' => 'Success', - 'code' => 200 - )); - die(json_encode($success)); - } else { - $error = array('error' => array( - 'message' => 'No Content', - 'code' => 204 - )); - die(json_encode($error)); - } - } - - /** - * Helper function to retrieve $_GET variables - * - * @param string $index Optional $_GET index - * @return mixed Returns the $_GET var at the specified index, - * the whole $_GET array or false - * @access private - */ - private function _get($index = '') - { - if($index){ - if(isset($_GET[$index]) && $_GET[$index]) return strip_tags($_GET[$index]); - } else { - if(isset($_GET) && !empty($_GET)) return $_GET; - } - return false; - } - - /** - * Helper function to retrieve $_POST variables - * - * @param string $index Optional $_POST index - * @return mixed Returns the $_POST var at the specified index, - * the whole $_POST array or false - * @access private - */ - private function _post($index = '') - { - if($index){ - if(isset($_POST[$index]) && $_POST[$index]) return $_POST[$index]; - } else { - if(isset($_POST) && !empty($_POST)) return $_POST; - } - return false; - } - - /** - * Helper function to retrieve PUT variables - * - * @return mixed Returns the contents of PUT as an array - * @access private - */ - private function _put() - { - $output = array(); - parse_str(file_get_contents('php://input'), $output); - return $output; - } - -} - -?> \ No newline at end of file diff --git a/lib/db.php b/lib/db.php deleted file mode 100644 index 34c5389e..00000000 --- a/lib/db.php +++ /dev/null @@ -1,330 +0,0 @@ -_config = $config; - } - - /* - * Initializes the database. Checks the configuration, connects, selects database. - */ - public function init() { - if(!$this->__check_config()) { - return false; - } - - if(!$this->__connect()) { - return false; - } - - if(!$this->__select_db()) { - return false; - } - - return true; - } - - /* - * Checks the configuration for blanks. - */ - private function __check_config() { - $config = $this->_config; - - if(empty($config["server"]) || empty($config["username"]) || empty($config["database"])) { - $this->_error = "Configuration details were blank."; - return false; - } - - $this->_verbose = ($config["verbose"]) ? true : false; - - return true; - } - - /* - * Connects to the database. - */ - private function __connect() { - $connection = @mysql_connect($this->_config["server"], $this->_config["username"], $this->_config["password"]); - - if(!$connection) { - $this->_error = ($this->_verbose) ? mysql_error() : "Could not connect to database."; - return false; - } - - return true; - } - - /* - * Selects the database to be working with. - */ - private function __select_db() { - $database = @mysql_select_db($this->_config["database"]); - - if(!$database) { - $this->_error = ($this->_verbose) ? mysql_error() : "Could not select database."; - return false; - } - - return true; - } - - /* - * SELECT starter. $fields can be either a string or an array of strings to select. - */ - public function select($fields) { - $query = "SELECT"; - - if(!empty($fields) && !is_array($fields)) { - $query .= " {$fields}"; - } else if(is_array($fields)) { - $query .= " `"; - $query .= implode("`,`", $fields); - $query .= "`"; - } else { - $query .= " *"; - } - - $this->_buildQuery = $query; - return $this; - } - - /* - * Adds where the SELECT is going to be coming from (table wise). - * select("*") - * select("username") - * select(array("username", "password")) - */ - public function from($table) { - $this->_buildQuery .= " FROM `{$table}`"; - return $this; - } - - /* - * UPDATE starter. - * update("users") - */ - public function update($table) { - $this->_buildQuery = "UPDATE `{$table}`"; - return $this; - } - - /* - * DELETE starter. - * delete("users") - */ - public function delete($table) { - $this->_buildQuery = "DELETE FROM `{$table}`"; - return $this; - } - - /* - * INSERT starter. $data is an array matched columns to values: - * $data = array("username" => "Caleb", "email" => "caleb@mingle-graphics.com"); - * insert("users", array("username" => "Caleb", "password" => "hash")) - */ - public function insert($table, $data) { - $query = "INSERT INTO `{$table}` ("; - $keys = array_keys($data); - $values = array_values($data); - - $query .= implode(", ", $keys); - $query .= ") VALUES ("; - - $array = array(); - - foreach($values as $value) { - $array[] = "'{$value}'"; - } - - $query .= implode(", ", $array) . ")"; - - $this->_buildQuery = $query; - return $this; - } - - /* - * SET. $data is an array matched key => value. - * set(array("username" => "Caleb")) - */ - public function set($data) { - if(!is_array($data)) return $this; - - $query = "SET "; - $array = array(); - - foreach($data as $key => $value) { - $array[] = "`{$key}`='{$value}'"; - } - - $query .= implode(", ", $array); - - $this->_buildQuery .= " " . $query; - return $this; - } - - /* - * WHERE. $fields and $values can either be strings or arrays based on how many you need. - * $operators can be an array to add in <, >, etc. Must match the index for $fields and $values. - * where("username", "Caleb") - * where(array("username", "password"), array("Caleb", "testing")) - * where(array("username", "level"), array("Caleb", "10"), array("=", "<")) - */ - public function where($fields, $values, $operators = '') { - if(!is_array($fields) && !is_array($values)) { - $operator = (empty($operators)) ? '=' : $operators[0]; - $query = " WHERE `{$fields}` {$operator} '{$values}'"; - } else { - $array = array_combine($fields, $values); - $query = " WHERE "; - - $data = array(); - $counter = 0; - - foreach($array as $key => $value) { - - $operator = (!empty($operators) && !empty($operators[$counter])) ? $operators[$counter] : '='; - - $data[] = "`{$key}` {$operator} '{$value}'"; - - $counter++; - } - - $query .= implode(" AND ", $data); - } - - $this->_buildQuery .= $query; - return $this; - } - - /* - * Order By: - * order_by("username", "asc") - */ - public function order_by($field, $direction = 'asc') { - if($field) $this->_buildQuery .= " ORDER BY `{$field}` " . strtoupper($direction); - return $this; - } - - /* - * Limit: - * limit(1) - * limit(1, 0) - */ - public function limit($max, $min = '0') { - if($max) $this->_buildQuery .= " LIMIT {$min},{$max}"; - return $this; - } - - /* - * Will return the object of data from the query. - */ - public function fetch_object() { - $object = @mysql_fetch_object($this->_query); - - if(!$object && $this->_verbose) { - $this->_error = mysql_error(); - } - - return $object; - } - - /* - * Will return the array of data from the query. - */ - public function fetch_array() { - $array = @mysql_fetch_array($this->_query); - if($array){ - foreach($array as $key=>$val){ - if(is_numeric($key)){ - unset($array[$key]); - } - } - } - - if(!$array && $this->_verbose) { - $this->_error = mysql_error(); - } - - return $array; - } - - public function fetch_all() { - $results = array(); - while($array = @mysql_fetch_array($this->_query)){ - foreach($array as $key=>$val){ - if(is_numeric($key)){ - unset($array[$key]); - } - } - $results[] = $array; - } - - - - if(!$array && $this->_verbose) { - $this->_error = mysql_error(); - } - - return $results; - } - - /* - * Will return the number or rows affected from the query. - */ - public function num_rows() { - $num = @mysql_num_rows($this->_query); - - if(!$num && $this->_verbose) { - $this->_error = mysql_error(); - } - - return $num; - } - - /* - * If $query_text is blank, query will be performed on the built query stored. - */ - public function query($query_text = '') { - $query_text = ($query_text == '') ? $this->_buildQuery : $query_text; - - $query = @mysql_query($query_text); - - if(!$query && $this->_verbose) { - echo "

MySQL Error:

"; - echo "

" . mysql_error() . "

"; - } - - $this->_query = $query; - - return $this; - } - - /* - * Will return the current built query story in $this->_buildQuery; - */ - public function get_query() { - return $this->_buildQuery; - } - - /* - * Will return the current stored error. - */ - public function get_error() { - return $this->_error; - } -} - -?> \ No newline at end of file From 22788facbc6fd713ef11a33de073005d93b25e00 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Mon, 20 May 2013 18:16:08 -0400 Subject: [PATCH 02/43] initial commit of ArrestDB --- .htaccess | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .htaccess diff --git a/.htaccess b/.htaccess deleted file mode 100644 index 2ac8058d..00000000 --- a/.htaccess +++ /dev/null @@ -1,6 +0,0 @@ - -RewriteEngine on -RewriteCond %{REQUEST_FILENAME} !-f -RewriteCond %{REQUEST_FILENAME} !-d -RewriteRule ^(.*)$ index.php/$1 [L] - \ No newline at end of file From 14971f679655006837ca265a020081fb39bb5f65 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Tue, 21 May 2013 01:04:34 +0200 Subject: [PATCH 03/43] updated README --- README.md | 217 ++++++++++++++++++++++++------------------------------ 1 file changed, 97 insertions(+), 120 deletions(-) diff --git a/README.md b/README.md index 30010935..138d69a1 100644 --- a/README.md +++ b/README.md @@ -1,165 +1,142 @@ #ArrestDB -ArrestDB is a "plug-n-play" RESTful API for your MySQL database. Arrest MySQL provides a REST API that maps directly to your database stucture with no configuation. +ArrestDB is a "plug-n-play" RESTful API for SQLite and MySQL databases. -For example lets suppose you have set up Arrest MySQL at http://api.example.com and your database has a table in it called "customers". To get a list of customers you would simply need to do: +ArrestDB provides a REST API that maps directly to your database stucture with no configuation. -```GET http://api.example.com/customers``` +Lets suppose you have set up ArrestDB at `http://api.example.com/` and your database has a table in it called `customers`. -Where "customers" is the table name. As a response you would get a JSON formatted list of customers. Or say you only want to get one customer, then you would do this: +To get a list of customers you would simply need to do: -```GET http://api.example.com/customers/123``` + GET http://api.example.com/customers/ -Where "123" here is the ID of the customer. For more information on using Arrest MySQL see the Usage section below. +Where `customers` is the table name. As a response you would get a JSON formatted list of customers. + +Or, if you only want to get one customer, then you would append the customer `id` to the URL: + + GET http://api.example.com/customers/123/ ##Requirements - PHP 5.3+ -- MySQL 5+ / SQLite 3+ +- MySQL 5.1+ / SQLite 3.0+ ##Installation -Place `index.php` in a publicly accessible directory (feel free to change the filename to whatever you want). +Edit `index.php` and change the `$dsn` variable located at the top, here are some examples: + +- SQLite: `$dsn = 'sqlite://./path/to/database.sqlite';` +- MySQL: `$dsn = 'mysql://[user[:pass]@]host[:port]/db/;` -Simply put these files into a folder somewhere on your server. Then edit config.php and fill in your database details and you are good to go. +Additionally, you may wish to restrict access to specific IP addresses. If so, add them in the `$clients` array: -##Usage + $clients = array + ( + '127.0.0.1', + '127.0.0.2', + '127.0.0.3', + ); -If you edit index.php you will see how incredibly simple it is to set up Arrest MySQL. Note that you are left to **provide your own authentication** for your API when using Arrest MySQL. +After you're done editing the file, place it in a publicly accessible directory (feel free to change the filename to whatever you want). -```php - POST /table + (R)ead > GET /table[/id] + (U)pdate > PUT /table/id + (D)elete > DELETE /table/id - /** - * By default it is assumed that the primary key of a table is "id". If this - * is not the case then you can set a custom index by using the - * set_table_index($table, $field) method - */ - //$arrest->set_table_index('my_table', 'some_index'); +To put this into practice below are some example of how you would use the ArrestDB API: - $arrest->rest(); + # Get all rows from the "customers" table + GET http://api.example.com/customers/ -} catch (Exception $e) { - echo $e; -} -?> -``` + # Get a single row from the "customers" table (where "123" is the ID) + GET http://api.example.com/customers/123/ -###API Design + # Get 50 rows from the "customers" table + GET http://api.example.com/customers/?limit=50 -The actual API design is very straight forward and follows the design patterns of most other API's. + # Get 50 rows from the "customers" table ordered by the "date" field + GET http://api.example.com/customers/?limit=50&by=date&order=desc -``` -create > POST /table -read > GET /table[/id] -update > PUT /table/id -delete > DELETE /table/id -``` + # Create a new row in the "customers" table where the POST data corresponds to the database fields + POST http://api.example.com/customers/ -To put this into practice below are some example of how you would use an Arrest MySQL API: + # Update customer "123" in the "customers" table where the PUT data corresponds to the database fields + PUT http://api.example.com/customers/123/ -``` -// Get all rows from the "customers" table -GET http://api.example.com/customers -// Get a single row from the "customers" table (where "123" is the ID) -GET http://api.example.com/customers/123 -// Get 50 rows from the "customers" table -GET http://api.example.com/customers?limit=50 -// Get 50 rows from the "customers" table ordered by the "date" field -GET http://api.example.com/customers?limit=50&order_by=date&order=desc + # Delete customer "123" from the "customers" table + DELETE http://api.example.com/customers/123/ + +Please note that `GET` calls accept the following query string variables: -// Create a new row in the "customers" table where the POST data corresponds to the database fields -POST http://api.example.com/customers +- `by` (column to order by) + - `order` (order direction: `ASC` or `DESC`) +- `limit` (`LIMIT x` SQL clause) + - `offset` (`OFFSET x` SQL clause) -// Update customer "123" in the "customers" table where the PUT data corresponds to the database fields -PUT http://api.example.com/customers/123 +##Responses -// Delete customer "123" from the "customers" table -DELETE http://api.example.com/customers/123 -``` +All responses are in the JSON format. For example a `GET` response from the `customers` table might look like: -###Responses + [ + { + "id": "114", + "customerName": "Australian Collectors, Co.", + "contactLastName": "Ferguson", + "contactFirstName": "Peter", + "phone": "123456", + "addressLine1": "636 St Kilda Road", + "addressLine2": "Level 3", + "city": "Melbourne", + "state": "Victoria", + "postalCode": "3004", + "country": "Australia", + "salesRepEmployeeNumber": "1611", + "creditLimit": "117300" + }, + ... + ] -All responses are in the JSON format. For example a GET response from the "customers" table might look like: -```json -[ +Successful `POST`, `PUT`, and `DELETE` responses will look like: + { - "id": "114", - "customerName": "Australian Collectors, Co.", - "contactLastName": "Ferguson", - "contactFirstName": "Peter", - "phone": "123456", - "addressLine1": "636 St Kilda Road", - "addressLine2": "Level 3", - "city": "Melbourne", - "state": "Victoria", - "postalCode": "3004", - "country": "Australia", - "salesRepEmployeeNumber": "1611", - "creditLimit": "117300" - }, - ... -] -``` - -Successful POST, PUT, and DELETE responses will look like - -```json -{ - "success": { - "message": "Success", - "code": 200 - } -} -``` - -Errors are in the format: - -```json -{ - "error": { - "message": "No Content", - "code": 204 - } -} -``` + "success": { + "code": 200, + "status": "OK" + } + } -The following codes and message are avaiable: +Errors are expressed in the format: -* 200 Success -* 204 No Content -* 404 Not Found + { + "error": { + "code": 204, + "status": "No Content" + } + } -##Credits +The following codes and message are avaiable: -Arrest MySQL was created by [Gilbert Pellegrom](http://gilbert.pellegrom.me) from [Dev7studios](http://dev7studios.com). +* `200` OK +* `204` No Content +* `400` Bad Request +* `403` Forbidden +* `404` Not Found +* `503` Service Unavailable -Please contribute by [reporting bugs](Arrest-MySQL/issues) and submitting [pull requests](Arrest-MySQL/pulls). +##Todo -##License (MIT) +- Implement bulk insert (`POST`) support. -Copyright © 2013 Dev7studios +##Credits -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation -files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, -modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following conditions: +ArrestDB is a complete rewrite of [Arrest-MySQL](https://github.com/gilbitron/Arrest-MySQL). -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +##License (MIT) -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Copyright (c) 2013 Alix Axel . From da47c277a638abc8e7991bdad29648ca11622600 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Tue, 21 May 2013 01:20:09 +0200 Subject: [PATCH 04/43] updated README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 138d69a1..90dc2fbc 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Or, if you only want to get one customer, then you would append the customer `id ##Requirements -- PHP 5.3+ +- PHP 5.3+ & PDO - MySQL 5.1+ / SQLite 3.0+ ##Installation From 2ab534454add3daa4f5e759d5e53d96402fbf2f4 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Tue, 21 May 2013 01:21:24 +0200 Subject: [PATCH 05/43] updated README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 90dc2fbc..4de45cc4 100644 --- a/README.md +++ b/README.md @@ -139,4 +139,4 @@ ArrestDB is a complete rewrite of [Arrest-MySQL](https://github.com/gilbitron/Ar ##License (MIT) -Copyright (c) 2013 Alix Axel . +Copyright (c) 2013 Alix Axel (alix.axel@gmail.com). From 456fc432fc060229c5d9b0ef15c70199ce809f81 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Tue, 21 May 2013 01:46:29 +0200 Subject: [PATCH 06/43] added some todos --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4de45cc4..45f7e5e0 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,10 @@ The following codes and message are avaiable: ##Todo -- Implement bulk insert (`POST`) support. +- support for JSON payloads in `POST` and `PUT` (optionally gzipped) +- support for bulk inserts in `POST` +- support for HTTP method overrides +- support for JSON-P responses ##Credits From 813cbd3aa7909eff073c1c45afa5e7d7f74ac15b Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Tue, 28 May 2013 19:53:16 +0200 Subject: [PATCH 07/43] added a note about including the file --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 45f7e5e0..0062ab98 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,8 @@ Additionally, you may wish to restrict access to specific IP addresses. If so, a After you're done editing the file, place it in a publicly accessible directory (feel free to change the filename to whatever you want). +***Nota bene:*** You must access the file directly, including it from another file won't work. + ##API Design The actual API design is very straightforward and follows the design patterns of the majority of APIs. From ede090a875c9161a803b1ebce6e019777646462f Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Sun, 2 Jun 2013 06:43:51 +0200 Subject: [PATCH 08/43] added support for (zlib compressed and/or JSON encoded) POST and PUT payloads --- index.php | 112 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 98 insertions(+), 14 deletions(-) diff --git a/index.php b/index.php index 1aa81bc0..d497f22c 100644 --- a/index.php +++ b/index.php @@ -9,7 +9,7 @@ * The MIT License * http://creativecommons.org/licenses/MIT/ * -* ArrestDB 1.0.0 (github.com/alixaxel/ArrestDB/) +* ArrestDB 1.2.0 (github.com/alixaxel/ArrestDB/) * Copyright (c) 2013 Alix Axel **/ @@ -18,7 +18,7 @@ exit('Arrest-DB should not be ran from CLI.'); } -header('Content-Type: application/json'); +header('Content-Type: application/json; charset=utf-8'); if ((empty($clients) !== true) && (in_array($_SERVER['REMOTE_ADDR'], (array) $clients) !== true)) { @@ -31,7 +31,7 @@ ), ); - exit(json_encode($result, 480)); + exit(json_encode($result, 448)); } else if (ArrestDB::Query($dsn) === false) @@ -45,9 +45,67 @@ ), ); - exit(json_encode($result, 480)); + exit(json_encode($result, 448)); } +ArrestDB::Serve('GET', '/(#any)/(#any)/(#any)', function ($table, $key, $value) +{ + $query = array + ( + sprintf('SELECT * FROM `%s`', $table), + sprintf('WHERE `%s` LIKE ?', $key), + ); + + if (isset($_GET['by']) === true) + { + if (isset($_GET['order']) !== true) + { + $_GET['order'] = 'ASC'; + } + + $query[] = sprintf('ORDER BY `%s` %s', $_GET['by'], $_GET['order']); + } + + if (isset($_GET['limit']) === true) + { + $query[] = sprintf('LIMIT %u', $_GET['limit']); + + if (isset($_GET['offset']) === true) + { + $query[] = sprintf('OFFSET %u', $_GET['offset']); + } + } + + $query = sprintf('%s;', implode(' ', $query)); + $result = ArrestDB::Query($query, $value); + + if ($result === false) + { + $result = array + ( + 'error' => array + ( + 'code' => 404, + 'status' => 'Not Found', + ), + ); + } + + else if (empty($result) === true) + { + $result = array + ( + 'error' => array + ( + 'code' => 204, + 'status' => 'No Content', + ), + ); + } + + return json_encode($result, 448); +}); + ArrestDB::Serve('GET', '/(#any)/(#num)?', function ($table, $id = null) { $query = array @@ -115,9 +173,37 @@ $result = array_shift($result); } - return json_encode($result, 480); + return json_encode($result, 448); }); +if (in_array($http = strtoupper($_SERVER['REQUEST_METHOD']), array('POST', 'PUT')) === true) +{ + if (preg_match('~^\x78[\x01\x5E\x9C\xDA]~', $data = file_get_contents('php://input')) > 0) + { + $data = gzuncompress($data); + } + + if ((array_key_exists('CONTENT_TYPE', $_SERVER) === true) && (empty($data) !== true)) + { + if (strcasecmp($_SERVER['CONTENT_TYPE'], 'application/json') === 0) + { + $GLOBALS['_' . $http] = json_decode($data, true); + } + + else if ((strcasecmp($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded') === 0) && (strcasecmp($_SERVER['REQUEST_METHOD'], 'PUT') === 0)) + { + parse_str($data, $GLOBALS['_' . $http]); + } + } + + if ((isset($GLOBALS['_' . $http]) !== true) || (is_array($GLOBALS['_' . $http]) !== true)) + { + $GLOBALS['_' . $http] = array(); + } + + unset($data); +} + ArrestDB::Serve('POST', '/(#any)', function ($table) { if (empty($_POST) === true) @@ -174,14 +260,12 @@ } } - return json_encode($result, 480); + return json_encode($result, 448); }); ArrestDB::Serve('PUT', '/(#any)/(#num)', function ($table, $id) { - parse_str(file_get_contents('php://input'), $_PUT); - - if (empty($_PUT) === true) + if (empty($GLOBALS['_PUT']) === true) { $result = array ( @@ -197,7 +281,7 @@ { $data = array(); - foreach ($_PUT as $key => $value) + foreach ($GLOBALS['_PUT'] as $key => $value) { $data[$key] = sprintf('`%s` = ?', $key); } @@ -208,7 +292,7 @@ ); $query = sprintf('%s;', implode(' ', $query)); - $result = ArrestDB::Query($query, $_PUT); + $result = ArrestDB::Query($query, $GLOBALS['_PUT']); if ($result === false) { @@ -235,7 +319,7 @@ } } - return json_encode($result, 480); + return json_encode($result, 448); }); ArrestDB::Serve('DELETE', '/(#any)/(#num)', function ($table, $id) @@ -284,7 +368,7 @@ ); } - return json_encode($result, 480); + return json_encode($result, 448); }); $result = array @@ -296,7 +380,7 @@ ), ); -exit(json_encode($result, 480)); +exit(json_encode($result, 448)); class ArrestDB { From cf55b5fb537df3a199b4c7a5cda9255476323ca4 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Sun, 2 Jun 2013 06:53:20 +0200 Subject: [PATCH 09/43] updated readme --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0062ab98..0dbc9593 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ The actual API design is very straightforward and follows the design patterns of (C)reate > POST /table (R)ead > GET /table[/id] + (R)ead > GET /table[/column/content] (U)pdate > PUT /table/id (D)elete > DELETE /table/id @@ -58,6 +59,9 @@ To put this into practice below are some example of how you would use the Arrest # Get a single row from the "customers" table (where "123" is the ID) GET http://api.example.com/customers/123/ + # Get all rows from the "customers" table where the "country" field matches "Australia" (`LIKE`) + GET http://api.example.com/customers/country/Australia/ + # Get 50 rows from the "customers" table GET http://api.example.com/customers/?limit=50 @@ -72,7 +76,7 @@ To put this into practice below are some example of how you would use the Arrest # Delete customer "123" from the "customers" table DELETE http://api.example.com/customers/123/ - + Please note that `GET` calls accept the following query string variables: - `by` (column to order by) @@ -80,6 +84,8 @@ Please note that `GET` calls accept the following query string variables: - `limit` (`LIMIT x` SQL clause) - `offset` (`OFFSET x` SQL clause) +Additionally, `POST` and `PUT` requests accept JSON-encoded and/or zlib-compressed payloads. + ##Responses All responses are in the JSON format. For example a `GET` response from the `customers` table might look like: @@ -133,14 +139,14 @@ The following codes and message are avaiable: ##Todo -- support for JSON payloads in `POST` and `PUT` (optionally gzipped) +- ~~support for JSON payloads in `POST` and `PUT` (optionally gzipped)~~ - support for bulk inserts in `POST` - support for HTTP method overrides - support for JSON-P responses ##Credits -ArrestDB is a complete rewrite of [Arrest-MySQL](https://github.com/gilbitron/Arrest-MySQL). +ArrestDB is a complete rewrite of [Arrest-MySQL](https://github.com/gilbitron/Arrest-MySQL) with several additional features and optimizations. ##License (MIT) From 9ad635e0b0454335a105abb7dc3b41349e9ca96f Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Fri, 7 Jun 2013 11:57:00 +0200 Subject: [PATCH 10/43] implemented support for JSON-P callbacks --- index.php | 136 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 76 insertions(+), 60 deletions(-) diff --git a/index.php b/index.php index d497f22c..ddb12aa3 100644 --- a/index.php +++ b/index.php @@ -9,17 +9,15 @@ * The MIT License * http://creativecommons.org/licenses/MIT/ * -* ArrestDB 1.2.0 (github.com/alixaxel/ArrestDB/) +* ArrestDB 1.3.0 (github.com/alixaxel/ArrestDB/) * Copyright (c) 2013 Alix Axel **/ if (strcmp('cli', PHP_SAPI) === 0) { - exit('Arrest-DB should not be ran from CLI.'); + exit('Arrest-DB should not be run from CLI.'); } -header('Content-Type: application/json; charset=utf-8'); - if ((empty($clients) !== true) && (in_array($_SERVER['REMOTE_ADDR'], (array) $clients) !== true)) { $result = array @@ -31,7 +29,7 @@ ), ); - exit(json_encode($result, 448)); + exit(ArrestDB::Reply($result)); } else if (ArrestDB::Query($dsn) === false) @@ -45,7 +43,7 @@ ), ); - exit(json_encode($result, 448)); + exit(ArrestDB::Reply($result)); } ArrestDB::Serve('GET', '/(#any)/(#any)/(#any)', function ($table, $key, $value) @@ -103,7 +101,7 @@ ); } - return json_encode($result, 448); + return ArrestDB::Reply($result); }); ArrestDB::Serve('GET', '/(#any)/(#num)?', function ($table, $id = null) @@ -173,7 +171,56 @@ $result = array_shift($result); } - return json_encode($result, 448); + return ArrestDB::Reply($result); +}); + +ArrestDB::Serve('DELETE', '/(#any)/(#num)', function ($table, $id) +{ + $query = array + ( + sprintf('DELETE FROM `%s` WHERE `%s` = ?', $table, 'id'), + ); + + $query = sprintf('%s;', implode(' ', $query)); + $result = ArrestDB::Query($query, $id); + + if ($result === false) + { + $result = array + ( + 'error' => array + ( + 'code' => 404, + 'status' => 'Not Found', + ), + ); + } + + else if (empty($result) === true) + { + $result = array + ( + 'error' => array + ( + 'code' => 204, + 'status' => 'No Content', + ), + ); + } + + else + { + $result = array + ( + 'success' => array + ( + 'code' => 200, + 'status' => 'OK', + ), + ); + } + + return ArrestDB::Reply($result); }); if (in_array($http = strtoupper($_SERVER['REQUEST_METHOD']), array('POST', 'PUT')) === true) @@ -260,7 +307,7 @@ } } - return json_encode($result, 448); + return ArrestDB::Reply($result); }); ArrestDB::Serve('PUT', '/(#any)/(#num)', function ($table, $id) @@ -319,56 +366,7 @@ } } - return json_encode($result, 448); -}); - -ArrestDB::Serve('DELETE', '/(#any)/(#num)', function ($table, $id) -{ - $query = array - ( - sprintf('DELETE FROM `%s` WHERE `%s` = ?', $table, 'id'), - ); - - $query = sprintf('%s;', implode(' ', $query)); - $result = ArrestDB::Query($query, $id); - - if ($result === false) - { - $result = array - ( - 'error' => array - ( - 'code' => 404, - 'status' => 'Not Found', - ), - ); - } - - else if (empty($result) === true) - { - $result = array - ( - 'error' => array - ( - 'code' => 204, - 'status' => 'No Content', - ), - ); - } - - else - { - $result = array - ( - 'success' => array - ( - 'code' => 200, - 'status' => 'OK', - ), - ); - } - - return json_encode($result, 448); + return ArrestDB::Reply($result); }); $result = array @@ -380,7 +378,7 @@ ), ); -exit(json_encode($result, 448)); +exit(ArrestDB::Reply($result)); class ArrestDB { @@ -529,6 +527,24 @@ public static function Query($query = null) return (isset($db) === true) ? $db : false; } + public static function Reply($data) + { + $callback = null; + $response = json_encode($data, 448); + + if (array_key_exists('callback', $_GET) === true) + { + $callback = trim(preg_replace('~[^[:alnum:]\[\]_.]~', '', $_GET['callback'])); + } + + if (headers_sent() !== true) + { + header(sprintf('Content-Type: application/%s; charset=utf-8', (empty($callback) === true) ? 'json' : 'javascript')); + } + + return preg_replace('~^[(](.+)[)];$~s', '$1', sprintf('%s(%s);', $callback, $response)); + } + public static function Serve($on = null, $route = null, $callback = null) { static $root = null; From 870ba5d694788e7ebe7987bb3e3ef566a271528c Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Fri, 7 Jun 2013 12:06:25 +0200 Subject: [PATCH 11/43] updated readme --- README.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0dbc9593..3b1c1271 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,13 @@ Lets suppose you have set up ArrestDB at `http://api.example.com/` and your data To get a list of customers you would simply need to do: - GET http://api.example.com/customers/ + GET http://api.example.com/customers/ Where `customers` is the table name. As a response you would get a JSON formatted list of customers. Or, if you only want to get one customer, then you would append the customer `id` to the URL: - GET http://api.example.com/customers/123/ + GET http://api.example.com/customers/123/ ##Requirements @@ -30,7 +30,7 @@ Edit `index.php` and change the `$dsn` variable located at the top, here are som Additionally, you may wish to restrict access to specific IP addresses. If so, add them in the `$clients` array: - $clients = array + $clients = array ( '127.0.0.1', '127.0.0.2', @@ -45,7 +45,7 @@ After you're done editing the file, place it in a publicly accessible directory The actual API design is very straightforward and follows the design patterns of the majority of APIs. - (C)reate > POST /table + (C)reate > POST /table (R)ead > GET /table[/id] (R)ead > GET /table[/column/content] (U)pdate > PUT /table/id @@ -53,7 +53,7 @@ The actual API design is very straightforward and follows the design patterns of To put this into practice below are some example of how you would use the ArrestDB API: - # Get all rows from the "customers" table + # Get all rows from the "customers" table GET http://api.example.com/customers/ # Get a single row from the "customers" table (where "123" is the ID) @@ -90,7 +90,7 @@ Additionally, `POST` and `PUT` requests accept JSON-encoded and/or zlib-compress All responses are in the JSON format. For example a `GET` response from the `customers` table might look like: - [ + [ { "id": "114", "customerName": "Australian Collectors, Co.", @@ -112,7 +112,7 @@ All responses are in the JSON format. For example a `GET` response from the `cus Successful `POST`, `PUT`, and `DELETE` responses will look like: - { + { "success": { "code": 200, "status": "OK" @@ -121,7 +121,7 @@ Successful `POST`, `PUT`, and `DELETE` responses will look like: Errors are expressed in the format: - { + { "error": { "code": 204, "status": "No Content" @@ -137,16 +137,20 @@ The following codes and message are avaiable: * `404` Not Found * `503` Service Unavailable +Also, if the `callback` query string is set *and* is a valid Javascript callback, the returned result will be a [JSON-P response](http://en.wikipedia.org/wiki/JSONP): + + callback(JSON); + ##Todo - ~~support for JSON payloads in `POST` and `PUT` (optionally gzipped)~~ - support for bulk inserts in `POST` - support for HTTP method overrides -- support for JSON-P responses +- ~~support for JSON-P responses~~ ##Credits -ArrestDB is a complete rewrite of [Arrest-MySQL](https://github.com/gilbitron/Arrest-MySQL) with several additional features and optimizations. +ArrestDB is a complete rewrite of [Arrest-MySQL](https://github.com/gilbitron/Arrest-MySQL) with several optimizations and additional features. ##License (MIT) From 6e3b06171f00702716d336b0d227d0defa55ab61 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Thu, 13 Jun 2013 04:40:31 +0200 Subject: [PATCH 12/43] added 201 and 409 HTTP status codes --- index.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/index.php b/index.php index ddb12aa3..b8d48dca 100644 --- a/index.php +++ b/index.php @@ -9,7 +9,7 @@ * The MIT License * http://creativecommons.org/licenses/MIT/ * -* ArrestDB 1.3.0 (github.com/alixaxel/ArrestDB/) +* ArrestDB 1.3.1 (github.com/alixaxel/ArrestDB/) * Copyright (c) 2013 Alix Axel **/ @@ -288,8 +288,8 @@ ( 'error' => array ( - 'code' => 404, - 'status' => 'Not Found', + 'code' => 409, + 'status' => 'Conflict', ), ); } @@ -300,8 +300,8 @@ ( 'success' => array ( - 'code' => 200, - 'status' => 'OK', + 'code' => 201, + 'status' => 'Created', ), ); } @@ -347,8 +347,8 @@ ( 'error' => array ( - 'code' => 404, - 'status' => 'Not Found', + 'code' => 409, + 'status' => 'Conflict', ), ); } From 8d7d36da3118cdd70bfde692574041784e289546 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Thu, 13 Jun 2013 04:44:37 +0200 Subject: [PATCH 13/43] updated descriptions of HTTP status codes --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b1c1271..58a8dca5 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,16 @@ All responses are in the JSON format. For example a `GET` response from the `cus ] -Successful `POST`, `PUT`, and `DELETE` responses will look like: +Successful `POST` responses will look like: + + { + "success": { + "code": 201, + "status": "Created" + } + } + +Successful `PUT` and `DELETE` responses will look like: { "success": { @@ -131,10 +140,12 @@ Errors are expressed in the format: The following codes and message are avaiable: * `200` OK +* `201` Created * `204` No Content * `400` Bad Request * `403` Forbidden * `404` Not Found +* `409` Conflict * `503` Service Unavailable Also, if the `callback` query string is set *and* is a valid Javascript callback, the returned result will be a [JSON-P response](http://en.wikipedia.org/wiki/JSONP): From ced5de205cd188019cfae1da332855d900390fd5 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Fri, 21 Jun 2013 10:58:54 -0400 Subject: [PATCH 14/43] support for HTTP method overriding, minor changes --- README.md | 18 +++++++++++------- index.php | 50 +++++++++++++++++++++++++++++++------------------- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 58a8dca5..dd2efa26 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ArrestDB is a "plug-n-play" RESTful API for SQLite and MySQL databases. ArrestDB provides a REST API that maps directly to your database stucture with no configuation. -Lets suppose you have set up ArrestDB at `http://api.example.com/` and your database has a table in it called `customers`. +Lets suppose you have set up ArrestDB at `http://api.example.com/` and a table named `customers`. To get a list of customers you would simply need to do: @@ -37,7 +37,7 @@ Additionally, you may wish to restrict access to specific IP addresses. If so, a '127.0.0.3', ); -After you're done editing the file, place it in a publicly accessible directory (feel free to change the filename to whatever you want). +After you're done editing the file, place it in a public directory (feel free to change the filename). ***Nota bene:*** You must access the file directly, including it from another file won't work. @@ -86,6 +86,11 @@ Please note that `GET` calls accept the following query string variables: Additionally, `POST` and `PUT` requests accept JSON-encoded and/or zlib-compressed payloads. +If your REST client does not support certain requests, you can use the `X-HTTP-Method-Override` header: + +- `PUT` = `POST` + `X-HTTP-Method-Override: PUT` +- `DELETE` = `GET` + `X-HTTP-Method-Override: DELETE` + ##Responses All responses are in the JSON format. For example a `GET` response from the `customers` table might look like: @@ -109,7 +114,6 @@ All responses are in the JSON format. For example a `GET` response from the `cus ... ] - Successful `POST` responses will look like: { @@ -132,8 +136,8 @@ Errors are expressed in the format: { "error": { - "code": 204, - "status": "No Content" + "code": 400, + "status": "Bad Request" } } @@ -148,7 +152,7 @@ The following codes and message are avaiable: * `409` Conflict * `503` Service Unavailable -Also, if the `callback` query string is set *and* is a valid Javascript callback, the returned result will be a [JSON-P response](http://en.wikipedia.org/wiki/JSONP): +Also, if the `callback` query string is set *and* is valid, the returned result will be a [JSON-P response](http://en.wikipedia.org/wiki/JSONP): callback(JSON); @@ -156,7 +160,7 @@ Also, if the `callback` query string is set *and* is a valid Javascript callback - ~~support for JSON payloads in `POST` and `PUT` (optionally gzipped)~~ - support for bulk inserts in `POST` -- support for HTTP method overrides +- ~~support for HTTP method overrides using the `X-HTTP-Method-Override` header~~ - ~~support for JSON-P responses~~ ##Credits diff --git a/index.php b/index.php index b8d48dca..fcf6a0e1 100644 --- a/index.php +++ b/index.php @@ -9,7 +9,7 @@ * The MIT License * http://creativecommons.org/licenses/MIT/ * -* ArrestDB 1.3.1 (github.com/alixaxel/ArrestDB/) +* ArrestDB 1.4.0 (github.com/alixaxel/ArrestDB/) * Copyright (c) 2013 Alix Axel **/ @@ -46,12 +46,22 @@ exit(ArrestDB::Reply($result)); } -ArrestDB::Serve('GET', '/(#any)/(#any)/(#any)', function ($table, $key, $value) +if (array_key_exists('_method', $_GET) === true) +{ + $_SERVER['REQUEST_METHOD'] = strtoupper(trim($_GET['_method'])); +} + +else if (array_key_exists('HTTP_X_HTTP_METHOD_OVERRIDE', $_SERVER) === true) +{ + $_SERVER['REQUEST_METHOD'] = strtoupper(trim($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])); +} + +ArrestDB::Serve('GET', '/(#any)/(#any)/(#any)', function ($table, $id, $data) { $query = array ( sprintf('SELECT * FROM `%s`', $table), - sprintf('WHERE `%s` LIKE ?', $key), + sprintf('WHERE `%s` LIKE ?', $id), ); if (isset($_GET['by']) === true) @@ -75,7 +85,7 @@ } $query = sprintf('%s;', implode(' ', $query)); - $result = ArrestDB::Query($query, $value); + $result = ArrestDB::Query($query, $data); if ($result === false) { @@ -514,11 +524,6 @@ public static function Query($query = null) } } - catch (\PDOException $e) - { - return false; - } - catch (\Exception $e) { return false; @@ -529,20 +534,27 @@ public static function Query($query = null) public static function Reply($data) { - $callback = null; - $response = json_encode($data, 448); - - if (array_key_exists('callback', $_GET) === true) + if (($result = json_encode($data, 448)) !== false) { - $callback = trim(preg_replace('~[^[:alnum:]\[\]_.]~', '', $_GET['callback'])); - } + $callback = null; - if (headers_sent() !== true) - { - header(sprintf('Content-Type: application/%s; charset=utf-8', (empty($callback) === true) ? 'json' : 'javascript')); + if (array_key_exists('callback', $_GET) === true) + { + $callback = trim(preg_replace('~[^[:alnum:]\[\]_.]~', '', $_GET['callback'])); + + if (empty($callback) !== true) + { + $result = sprintf('%s(%s);', $callback, $result); + } + } + + if (headers_sent() !== true) + { + header(sprintf('Content-Type: application/%s; charset=utf-8', (empty($callback) === true) ? 'json' : 'javascript')); + } } - return preg_replace('~^[(](.+)[)];$~s', '$1', sprintf('%s(%s);', $callback, $response)); + return $result; } public static function Serve($on = null, $route = null, $callback = null) From 0000bfaa1c519f3181d79fc585f214714ab5efbc Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Fri, 21 Jun 2013 11:04:26 -0400 Subject: [PATCH 15/43] updated readme --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dd2efa26..dded8d40 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Edit `index.php` and change the `$dsn` variable located at the top, here are som - SQLite: `$dsn = 'sqlite://./path/to/database.sqlite';` - MySQL: `$dsn = 'mysql://[user[:pass]@]host[:port]/db/;` -Additionally, you may wish to restrict access to specific IP addresses. If so, add them in the `$clients` array: +If you want to restrict access to specific IP addresses, add them to the `$clients` array: $clients = array ( @@ -91,9 +91,11 @@ If your REST client does not support certain requests, you can use the `X-HTTP-M - `PUT` = `POST` + `X-HTTP-Method-Override: PUT` - `DELETE` = `GET` + `X-HTTP-Method-Override: DELETE` +Alternatively, you can also override the HTTP method by using the `_method` query string parameter. + ##Responses -All responses are in the JSON format. For example a `GET` response from the `customers` table might look like: +All responses are in the JSON format. A `GET` response from the `customers` table might look like this: [ { From bc9df54ab1138ad2e4f7b6a0ddf6581ab2fa8b34 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Thu, 3 Oct 2013 03:28:52 +0100 Subject: [PATCH 16/43] support for batch INSERTs --- index.php | 62 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/index.php b/index.php index fcf6a0e1..9a123498 100644 --- a/index.php +++ b/index.php @@ -9,7 +9,7 @@ * The MIT License * http://creativecommons.org/licenses/MIT/ * -* ArrestDB 1.4.0 (github.com/alixaxel/ArrestDB/) +* ArrestDB 1.5.0 (github.com/alixaxel/ArrestDB/) * Copyright (c) 2013 Alix Axel **/ @@ -275,22 +275,58 @@ ); } - else + else if (is_array($_POST) === true) { - $data = array(); + $queries = array(); - foreach ($_POST as $key => $value) + if (count($_POST) == count($_POST, COUNT_RECURSIVE)) { - $data[sprintf('`%s`', $key)] = '?'; + $_POST = array($_POST); } - $query = array - ( - sprintf('INSERT INTO `%s` (%s) VALUES (%s)', $table, implode(', ', array_keys($data)), implode(', ', $data)), - ); + foreach ($_POST as $row) + { + $data = array(); - $query = sprintf('%s;', implode(' ', $query)); - $result = ArrestDB::Query($query, $_POST); + foreach ($row as $key => $value) + { + $data[sprintf('`%s`', $key)] = '?'; + } + + $query = array + ( + sprintf('INSERT INTO `%s` (%s) VALUES (%s)', $table, implode(', ', array_keys($data)), implode(', ', $data)), + ); + + $queries[] = array + ( + sprintf('%s;', implode(' ', $query)), + $data, + ); + } + + if (count($queries) > 1) + { + ArrestDB::Query()->beginTransaction(); + + while (is_null($query = array_shift($queries)) !== true) + { + if (($result = ArrestDB::Query($query[0], $query[1])) === false) + { + ArrestDB::Query()->rollBack(); break; + } + } + + if (($result !== false) && (ArrestDB::Query()->inTransaction() === true)) + { + $result = ArrestDB::Query()->commit(); + } + } + + else if (is_null($query = array_shift($queries)) !== true) + { + $result = ArrestDB::Query($query[0], $query[1]); + } if ($result === false) { @@ -334,7 +370,7 @@ ); } - else + else if (is_array($GLOBALS['_PUT']) === true) { $data = array(); @@ -464,7 +500,6 @@ public static function Query($query = null) 'cache_size' => '8192', 'encoding' => '"UTF-8"', 'foreign_keys' => 'ON', - 'journal_mode' => 'WAL', 'journal_size_limit' => '67110000', 'legacy_file_format' => 'OFF', 'page_size' => '4096', @@ -472,6 +507,7 @@ public static function Query($query = null) 'secure_delete' => 'ON', 'synchronous' => 'NORMAL', 'temp_store' => 'MEMORY', + 'journal_mode' => 'WAL', 'wal_autocheckpoint' => '4096', ); From c0447f8424f721a96185c48c231ff90afaa4f6e6 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Thu, 3 Oct 2013 03:34:13 +0100 Subject: [PATCH 17/43] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dded8d40..3bc5b1e5 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,8 @@ If your REST client does not support certain requests, you can use the `X-HTTP-M Alternatively, you can also override the HTTP method by using the `_method` query string parameter. +As of version 1.5.0, it's also possible to atomically `INSERT` a batch of records by POSTing an array of arrays. + ##Responses All responses are in the JSON format. A `GET` response from the `customers` table might look like this: @@ -161,7 +163,7 @@ Also, if the `callback` query string is set *and* is valid, the returned result ##Todo - ~~support for JSON payloads in `POST` and `PUT` (optionally gzipped)~~ -- support for bulk inserts in `POST` +- ~~support for bulk inserts in `POST`~~ - ~~support for HTTP method overrides using the `X-HTTP-Method-Override` header~~ - ~~support for JSON-P responses~~ From 4df2cf47f2db2e80d5d81c51792e615ce8678c26 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Thu, 3 Oct 2013 03:40:22 +0100 Subject: [PATCH 18/43] updated changelog --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3bc5b1e5..a8105705 100644 --- a/README.md +++ b/README.md @@ -160,12 +160,12 @@ Also, if the `callback` query string is set *and* is valid, the returned result callback(JSON); -##Todo +##Changelog -- ~~support for JSON payloads in `POST` and `PUT` (optionally gzipped)~~ -- ~~support for bulk inserts in `POST`~~ -- ~~support for HTTP method overrides using the `X-HTTP-Method-Override` header~~ -- ~~support for JSON-P responses~~ +- **1.2.0** ~~support for JSON payloads in `POST` and `PUT` (optionally gzipped)~~ +- **1.3.0** ~~support for JSON-P responses~~ +- **1.4.0** ~~support for HTTP method overrides using the `X-HTTP-Method-Override` header~~ +- **1.5.0** ~~support for bulk inserts in `POST`~~ ##Credits From f68b5dc8592f29e9cdea77b6a0340845121e74ba Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Mon, 16 Dec 2013 15:18:21 +0000 Subject: [PATCH 19/43] added support for PostgreSQL --- index.php | 52 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/index.php b/index.php index 9a123498..6707f5fb 100644 --- a/index.php +++ b/index.php @@ -9,7 +9,7 @@ * The MIT License * http://creativecommons.org/licenses/MIT/ * -* ArrestDB 1.5.0 (github.com/alixaxel/ArrestDB/) +* ArrestDB 1.6.0 (github.com/alixaxel/ArrestDB/) * Copyright (c) 2013 Alix Axel **/ @@ -451,11 +451,21 @@ public static function Query($query = null) if ($result[$hash]->execute($data) === true) { + $sequence = null; + + if (strncmp('pgsql', $db->getAttribute(\PDO::ATTR_DRIVER_NAME), 5) === 0) + { + if (sscanf($query, 'INSERT INTO %s', $sequence) > 0) + { + $sequence = sprintf('%s_id_seq', trim($sequence, '`')); + } + } + switch (strstr($query, ' ', true)) { case 'INSERT': case 'REPLACE': - return $db->lastInsertId(); + return $db->lastInsertId($sequence); case 'UPDATE': case 'DELETE': @@ -490,18 +500,17 @@ public static function Query($query = null) { $options += array ( - \PDO::ATTR_TIMEOUT => 0, + \PDO::ATTR_TIMEOUT => 3, ); $db = new \PDO(sprintf('sqlite:%s', $dsn[1]), null, null, $options); $pragmas = array ( - 'busy_timeout' => '0', + 'automatic_index' => 'ON', 'cache_size' => '8192', - 'encoding' => '"UTF-8"', 'foreign_keys' => 'ON', 'journal_size_limit' => '67110000', - 'legacy_file_format' => 'OFF', + 'locking_mode' => 'NORMAL', 'page_size' => '4096', 'recursive_triggers' => 'ON', 'secure_delete' => 'ON', @@ -513,15 +522,15 @@ public static function Query($query = null) if (strncasecmp('WIN', PHP_OS, 3) !== 0) { + $memory = 131072; + if (($page = intval(shell_exec('getconf PAGESIZE'))) > 0) { $pragmas['page_size'] = $page; } - - if ((is_file('/proc/meminfo') === true) && (is_readable('/proc/meminfo') === true)) + + if (is_readable('/proc/meminfo') === true) { - $memory = 131072; - if (is_resource($handle = fopen('/proc/meminfo', 'rb')) === true) { while (($line = fgets($handle, 1024)) !== false) @@ -531,13 +540,13 @@ public static function Query($query = null) $memory = round($memory / 131072) * 131072; break; } } - + fclose($handle); } - - $pragmas['cache_size'] = intval($memory * 0.25 / ($pragmas['page_size'] / 1024)); - $pragmas['wal_autocheckpoint'] = $pragmas['cache_size'] / 2; } + + $pragmas['cache_size'] = intval($memory * 0.25 / ($pragmas['page_size'] / 1024)); + $pragmas['wal_autocheckpoint'] = $pragmas['cache_size'] / 2; } foreach ($pragmas as $key => $value) @@ -546,16 +555,23 @@ public static function Query($query = null) } } - else if (preg_match('~^mysql://(?:(.+?)(?::(.+?))?@)?([^/:@]++)(?::(\d++))?/(\w++)/?$~i', $query, $dsn) > 0) + else if (preg_match('~^(mysql|pgsql)://(?:(.+?)(?::(.+?))?@)?([^/:@]++)(?::(\d++))?/(\w++)/?$~i', $query, $dsn) > 0) { $options += array ( \PDO::ATTR_AUTOCOMMIT => true, - \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES "utf8" COLLATE "utf8_general_ci", time_zone = "+00:00";', - \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, ); - $db = new \PDO(sprintf('mysql:host=%s;port=%s;dbname=%s', $dsn[3], $dsn[4], $dsn[5]), $dsn[1], $dsn[2], $options); + if (strncasecmp('mysql', $query, 5) === 0) + { + $options += array + ( + \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES "utf8" COLLATE "utf8_general_ci", time_zone = "+00:00";', + \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, + ); + } + + $db = new \PDO(sprintf('%s:host=%s;port=%s;dbname=%s', $dsn[1], $dsn[4], $dsn[5], $dsn[6]), $dsn[2], $dsn[3], $options); } } } From 553e56aa72419bc9d0fe7f9ec7540b5dea1ceda2 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Mon, 16 Dec 2013 15:21:08 +0000 Subject: [PATCH 20/43] minor code cleanup --- index.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/index.php b/index.php index 6707f5fb..9c335ac7 100644 --- a/index.php +++ b/index.php @@ -9,7 +9,7 @@ * The MIT License * http://creativecommons.org/licenses/MIT/ * -* ArrestDB 1.6.0 (github.com/alixaxel/ArrestDB/) +* ArrestDB 1.6.1 (github.com/alixaxel/ArrestDB/) * Copyright (c) 2013 Alix Axel **/ @@ -453,12 +453,9 @@ public static function Query($query = null) { $sequence = null; - if (strncmp('pgsql', $db->getAttribute(\PDO::ATTR_DRIVER_NAME), 5) === 0) + if ((strncmp('pgsql', $db->getAttribute(\PDO::ATTR_DRIVER_NAME), 5) === 0) && (sscanf($query, 'INSERT INTO %s', $sequence) > 0)) { - if (sscanf($query, 'INSERT INTO %s', $sequence) > 0) - { - $sequence = sprintf('%s_id_seq', trim($sequence, '`')); - } + $sequence = sprintf('%s_id_seq', trim($sequence, '`')); } switch (strstr($query, ' ', true)) From c1d9561f4ce2073a04b056d65707aba4ce5aadc2 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Mon, 16 Dec 2013 15:23:24 +0000 Subject: [PATCH 21/43] updated README --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a8105705..41626c68 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ #ArrestDB -ArrestDB is a "plug-n-play" RESTful API for SQLite and MySQL databases. +ArrestDB is a "plug-n-play" RESTful API for SQLite, MySQL and PostgreSQL databases. ArrestDB provides a REST API that maps directly to your database stucture with no configuation. @@ -19,7 +19,7 @@ Or, if you only want to get one customer, then you would append the customer `id ##Requirements - PHP 5.3+ & PDO -- MySQL 5.1+ / SQLite 3.0+ +- SQLite / MySQL / PostgreSQL ##Installation @@ -27,8 +27,9 @@ Edit `index.php` and change the `$dsn` variable located at the top, here are som - SQLite: `$dsn = 'sqlite://./path/to/database.sqlite';` - MySQL: `$dsn = 'mysql://[user[:pass]@]host[:port]/db/;` +- PostgreSQL: `$dsn = 'pgsql://[user[:pass]@]host[:port]/db/;` -If you want to restrict access to specific IP addresses, add them to the `$clients` array: +If you want to restrict access to allow only specific IP addresses, add them to the `$clients` array: $clients = array ( @@ -166,6 +167,7 @@ Also, if the `callback` query string is set *and* is valid, the returned result - **1.3.0** ~~support for JSON-P responses~~ - **1.4.0** ~~support for HTTP method overrides using the `X-HTTP-Method-Override` header~~ - **1.5.0** ~~support for bulk inserts in `POST`~~ +- **1.6.0** ~~added support for PostgreSQL~~ ##Credits From 5496de460b330aebbc7dabe8f840565f83ab2af0 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Sun, 29 Dec 2013 00:49:11 +0000 Subject: [PATCH 22/43] dynamic json_encode() bitmask --- index.php | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/index.php b/index.php index 9c335ac7..22f1902a 100644 --- a/index.php +++ b/index.php @@ -9,7 +9,7 @@ * The MIT License * http://creativecommons.org/licenses/MIT/ * -* ArrestDB 1.6.1 (github.com/alixaxel/ArrestDB/) +* ArrestDB 1.6.2 (github.com/alixaxel/ArrestDB/) * Copyright (c) 2013 Alix Axel **/ @@ -583,7 +583,23 @@ public static function Query($query = null) public static function Reply($data) { - if (($result = json_encode($data, 448)) !== false) + $bitmask = 0; + $options = array('UNESCAPED_SLASHES', 'UNESCAPED_UNICODE'); + + if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) === true) + { + $options[] = 'PRETTY_PRINT'; + } + + foreach ($options as $option) + { + if (defined('JSON_' . $option) === true) + { + $bitmask |= constant('JSON_' . $option); + } + } + + if (($result = json_encode($data, $bitmask)) !== false) { $callback = null; From 246d892c4c77f247d8cb81c0af749e54717f4f64 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Sun, 29 Dec 2013 00:51:31 +0000 Subject: [PATCH 23/43] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 41626c68..f27c26e3 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,8 @@ Also, if the `callback` query string is set *and* is valid, the returned result callback(JSON); +Ajax-like requests will be minified, whereas normal browser requests will be human-readable. + ##Changelog - **1.2.0** ~~support for JSON payloads in `POST` and `PUT` (optionally gzipped)~~ From db9d69f9bf76317ee09a9880c971edfb202bee55 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Sun, 29 Dec 2013 00:55:58 +0000 Subject: [PATCH 24/43] minor refactor --- index.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/index.php b/index.php index 22f1902a..c16fbc7c 100644 --- a/index.php +++ b/index.php @@ -593,10 +593,7 @@ public static function Reply($data) foreach ($options as $option) { - if (defined('JSON_' . $option) === true) - { - $bitmask |= constant('JSON_' . $option); - } + $bitmask |= (defined('JSON_' . $option) === true) ? constant('JSON_' . $option) : 0; } if (($result = json_encode($data, $bitmask)) !== false) From e1bc1826c23ff11acf864403c1d7c87e41b4f1e5 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Sun, 29 Dec 2013 01:09:05 +0000 Subject: [PATCH 25/43] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f27c26e3..6da744a3 100644 --- a/README.md +++ b/README.md @@ -175,6 +175,8 @@ Ajax-like requests will be minified, whereas normal browser requests will be hum ArrestDB is a complete rewrite of [Arrest-MySQL](https://github.com/gilbitron/Arrest-MySQL) with several optimizations and additional features. +[![Donate Bitcoins](https://coinbase.com/assets/buttons/donation_small.png)](https://coinbase.com/checkouts/89e8aa2876ba534f9db3fafa8be4e5fa) + ##License (MIT) Copyright (c) 2013 Alix Axel (alix.axel@gmail.com). From 5aacb9f091be6180c2976ed37745c156f5a50a42 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Sun, 29 Dec 2013 01:09:26 +0000 Subject: [PATCH 26/43] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6da744a3..37f1e819 100644 --- a/README.md +++ b/README.md @@ -175,8 +175,8 @@ Ajax-like requests will be minified, whereas normal browser requests will be hum ArrestDB is a complete rewrite of [Arrest-MySQL](https://github.com/gilbitron/Arrest-MySQL) with several optimizations and additional features. -[![Donate Bitcoins](https://coinbase.com/assets/buttons/donation_small.png)](https://coinbase.com/checkouts/89e8aa2876ba534f9db3fafa8be4e5fa) - ##License (MIT) Copyright (c) 2013 Alix Axel (alix.axel@gmail.com). + +[![Donate Bitcoins](https://coinbase.com/assets/buttons/donation_small.png)](https://coinbase.com/checkouts/89e8aa2876ba534f9db3fafa8be4e5fa) From 10c1829e6ab19d983ef97a4e81c2003a1d0aec47 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Thu, 16 Jan 2014 09:06:48 -0500 Subject: [PATCH 27/43] fixed pgsql connection bug, some other improvements --- LICENSE | 2 +- README.md | 3 ++- index.php | 59 ++++++++++++++++++++++++++++--------------------------- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/LICENSE b/LICENSE index 1538308f..1b0003fb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2013 Alix Axel +Copyright (c) 2014 Alix Axel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to diff --git a/README.md b/README.md index 37f1e819..947509bb 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,7 @@ Ajax-like requests will be minified, whereas normal browser requests will be hum - **1.4.0** ~~support for HTTP method overrides using the `X-HTTP-Method-Override` header~~ - **1.5.0** ~~support for bulk inserts in `POST`~~ - **1.6.0** ~~added support for PostgreSQL~~ +- **1.7.0** ~~fixed PostgreSQL connection bug, other minor improvements~~ ##Credits @@ -177,6 +178,6 @@ ArrestDB is a complete rewrite of [Arrest-MySQL](https://github.com/gilbitron/Ar ##License (MIT) -Copyright (c) 2013 Alix Axel (alix.axel@gmail.com). +Copyright (c) 2014 Alix Axel (alix.axel@gmail.com). [![Donate Bitcoins](https://coinbase.com/assets/buttons/donation_small.png)](https://coinbase.com/checkouts/89e8aa2876ba534f9db3fafa8be4e5fa) diff --git a/index.php b/index.php index c16fbc7c..bb2e81ae 100644 --- a/index.php +++ b/index.php @@ -9,13 +9,13 @@ * The MIT License * http://creativecommons.org/licenses/MIT/ * -* ArrestDB 1.6.2 (github.com/alixaxel/ArrestDB/) -* Copyright (c) 2013 Alix Axel +* ArrestDB 1.7.0 (github.com/alixaxel/ArrestDB/) +* Copyright (c) 2014 Alix Axel **/ -if (strcmp('cli', PHP_SAPI) === 0) +if (strcmp(PHP_SAPI, 'cli') === 0) { - exit('Arrest-DB should not be run from CLI.'); + exit('ArrestDB should not be run from CLI.' . PHP_EOL); } if ((empty($clients) !== true) && (in_array($_SERVER['REMOTE_ADDR'], (array) $clients) !== true)) @@ -60,8 +60,8 @@ { $query = array ( - sprintf('SELECT * FROM `%s`', $table), - sprintf('WHERE `%s` LIKE ?', $id), + sprintf('SELECT * FROM "%s"', $table), + sprintf('WHERE "%s" %s ?', $id, (ctype_digit($data) === true) ? '=' : 'LIKE'), ); if (isset($_GET['by']) === true) @@ -71,7 +71,7 @@ $_GET['order'] = 'ASC'; } - $query[] = sprintf('ORDER BY `%s` %s', $_GET['by'], $_GET['order']); + $query[] = sprintf('ORDER BY "%s" %s', $_GET['by'], $_GET['order']); } if (isset($_GET['limit']) === true) @@ -118,12 +118,12 @@ { $query = array ( - sprintf('SELECT * FROM `%s`', $table), + sprintf('SELECT * FROM "%s"', $table), ); if (isset($id) === true) { - $query[] = sprintf('WHERE `%s` = ? LIMIT 1', 'id'); + $query[] = sprintf('WHERE "%s" = ? LIMIT 1', 'id'); } else @@ -135,7 +135,7 @@ $_GET['order'] = 'ASC'; } - $query[] = sprintf('ORDER BY `%s` %s', $_GET['by'], $_GET['order']); + $query[] = sprintf('ORDER BY "%s" %s', $_GET['by'], $_GET['order']); } if (isset($_GET['limit']) === true) @@ -188,7 +188,7 @@ { $query = array ( - sprintf('DELETE FROM `%s` WHERE `%s` = ?', $table, 'id'), + sprintf('DELETE FROM "%s" WHERE "%s" = ?', $table, 'id'), ); $query = sprintf('%s;', implode(' ', $query)); @@ -290,12 +290,12 @@ foreach ($row as $key => $value) { - $data[sprintf('`%s`', $key)] = '?'; + $data[sprintf('"%s"', $key)] = '?'; } $query = array ( - sprintf('INSERT INTO `%s` (%s) VALUES (%s)', $table, implode(', ', array_keys($data)), implode(', ', $data)), + sprintf('INSERT INTO "%s" (%s) VALUES (%s)', $table, implode(', ', array_keys($data)), implode(', ', $data)), ); $queries[] = array @@ -376,12 +376,12 @@ foreach ($GLOBALS['_PUT'] as $key => $value) { - $data[$key] = sprintf('`%s` = ?', $key); + $data[$key] = sprintf('"%s" = ?', $key); } $query = array ( - sprintf('UPDATE `%s` SET %s WHERE `%s` = ?', $table, implode(', ', $data), 'id'), + sprintf('UPDATE "%s" SET %s WHERE "%s" = ?', $table, implode(', ', $data), 'id'), ); $query = sprintf('%s;', implode(' ', $query)); @@ -437,6 +437,11 @@ public static function Query($query = null) { if (isset($db, $query) === true) { + if (strncasecmp($db->getAttribute(\PDO::ATTR_DRIVER_NAME), 'mysql', 5) === 0) + { + $query = str_replace('"', '`', $query); + } + if (empty($result[$hash = crc32($query)]) === true) { $result[$hash] = $db->prepare($query); @@ -453,9 +458,9 @@ public static function Query($query = null) { $sequence = null; - if ((strncmp('pgsql', $db->getAttribute(\PDO::ATTR_DRIVER_NAME), 5) === 0) && (sscanf($query, 'INSERT INTO %s', $sequence) > 0)) + if ((strncmp($db->getAttribute(\PDO::ATTR_DRIVER_NAME), 'pgsql', 5) === 0) && (sscanf($query, 'INSERT INTO %s', $sequence) > 0)) { - $sequence = sprintf('%s_id_seq', trim($sequence, '`')); + $sequence = sprintf('%s_id_seq', trim($sequence, '"')); } switch (strstr($query, ' ', true)) @@ -517,15 +522,15 @@ public static function Query($query = null) 'wal_autocheckpoint' => '4096', ); - if (strncasecmp('WIN', PHP_OS, 3) !== 0) + if (strncasecmp(PHP_OS, 'WIN', 3) !== 0) { $memory = 131072; - + if (($page = intval(shell_exec('getconf PAGESIZE'))) > 0) { $pragmas['page_size'] = $page; } - + if (is_readable('/proc/meminfo') === true) { if (is_resource($handle = fopen('/proc/meminfo', 'rb')) === true) @@ -537,11 +542,11 @@ public static function Query($query = null) $memory = round($memory / 131072) * 131072; break; } } - + fclose($handle); } } - + $pragmas['cache_size'] = intval($memory * 0.25 / ($pragmas['page_size'] / 1024)); $pragmas['wal_autocheckpoint'] = $pragmas['cache_size'] / 2; } @@ -554,15 +559,11 @@ public static function Query($query = null) else if (preg_match('~^(mysql|pgsql)://(?:(.+?)(?::(.+?))?@)?([^/:@]++)(?::(\d++))?/(\w++)/?$~i', $query, $dsn) > 0) { - $options += array - ( - \PDO::ATTR_AUTOCOMMIT => true, - ); - - if (strncasecmp('mysql', $query, 5) === 0) + if (strncasecmp($query, 'mysql', 5) === 0) { $options += array ( + \PDO::ATTR_AUTOCOMMIT => true, \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES "utf8" COLLATE "utf8_general_ci", time_zone = "+00:00";', \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, ); @@ -628,7 +629,7 @@ public static function Serve($on = null, $route = null, $callback = null) $_SERVER['REQUEST_METHOD'] = 'CLI'; } - if ((empty($on) === true) || (strcasecmp($on, $_SERVER['REQUEST_METHOD']) === 0)) + if ((empty($on) === true) || (strcasecmp($_SERVER['REQUEST_METHOD'], $on) === 0)) { if (is_null($root) === true) { From d934a61b8b20e89feb7fc7d5beb0492341751841 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Thu, 16 Jan 2014 09:18:30 -0500 Subject: [PATCH 28/43] no need for str_replace, strtr does the job just fine --- index.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.php b/index.php index bb2e81ae..d4375e85 100644 --- a/index.php +++ b/index.php @@ -9,7 +9,7 @@ * The MIT License * http://creativecommons.org/licenses/MIT/ * -* ArrestDB 1.7.0 (github.com/alixaxel/ArrestDB/) +* ArrestDB 1.7.1 (github.com/alixaxel/ArrestDB/) * Copyright (c) 2014 Alix Axel **/ @@ -439,7 +439,7 @@ public static function Query($query = null) { if (strncasecmp($db->getAttribute(\PDO::ATTR_DRIVER_NAME), 'mysql', 5) === 0) { - $query = str_replace('"', '`', $query); + $query = strtr($query, '"', '`'); } if (empty($result[$hash = crc32($query)]) === true) From ffecb4d4ea298a6ca03270adf90e6768cc17c6b7 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Fri, 17 Jan 2014 09:22:50 +0000 Subject: [PATCH 29/43] updated README --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 947509bb..2788130d 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,17 @@ If you want to restrict access to allow only specific IP addresses, add them to After you're done editing the file, place it in a public directory (feel free to change the filename). +If you're using Apache, you can use the following `mod_rewrite` rules in a `.htaccess` file: + +```apache + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ index.php/$1 [L,QSA] + +``` + ***Nota bene:*** You must access the file directly, including it from another file won't work. ##API Design From 06bd866b29809c89882f5a51bc0883eab8652054 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Fri, 17 Jan 2014 09:30:12 +0000 Subject: [PATCH 30/43] updated README --- README.md | 98 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 2788130d..8fc50bbc 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,14 @@ Edit `index.php` and change the `$dsn` variable located at the top, here are som If you want to restrict access to allow only specific IP addresses, add them to the `$clients` array: - $clients = array - ( - '127.0.0.1', - '127.0.0.2', - '127.0.0.3', - ); +```php +$clients = array +( + '127.0.0.1', + '127.0.0.2', + '127.0.0.3', +); +``` After you're done editing the file, place it in a public directory (feel free to change the filename). @@ -111,51 +113,59 @@ As of version 1.5.0, it's also possible to atomically `INSERT` a batch of record All responses are in the JSON format. A `GET` response from the `customers` table might look like this: - [ - { - "id": "114", - "customerName": "Australian Collectors, Co.", - "contactLastName": "Ferguson", - "contactFirstName": "Peter", - "phone": "123456", - "addressLine1": "636 St Kilda Road", - "addressLine2": "Level 3", - "city": "Melbourne", - "state": "Victoria", - "postalCode": "3004", - "country": "Australia", - "salesRepEmployeeNumber": "1611", - "creditLimit": "117300" - }, - ... - ] +```json +[ + { + "id": "114", + "customerName": "Australian Collectors, Co.", + "contactLastName": "Ferguson", + "contactFirstName": "Peter", + "phone": "123456", + "addressLine1": "636 St Kilda Road", + "addressLine2": "Level 3", + "city": "Melbourne", + "state": "Victoria", + "postalCode": "3004", + "country": "Australia", + "salesRepEmployeeNumber": "1611", + "creditLimit": "117300" + }, + ... +] +``` Successful `POST` responses will look like: - { - "success": { - "code": 201, - "status": "Created" - } - } +```json +{ + "success": { + "code": 201, + "status": "Created" + } +} +``` Successful `PUT` and `DELETE` responses will look like: - { - "success": { - "code": 200, - "status": "OK" - } - } +```json +{ + "success": { + "code": 200, + "status": "OK" + } +} +``` Errors are expressed in the format: - { - "error": { - "code": 400, - "status": "Bad Request" - } - } +```json +{ + "error": { + "code": 400, + "status": "Bad Request" + } +} +``` The following codes and message are avaiable: @@ -170,7 +180,9 @@ The following codes and message are avaiable: Also, if the `callback` query string is set *and* is valid, the returned result will be a [JSON-P response](http://en.wikipedia.org/wiki/JSONP): - callback(JSON); +```javascript +callback(JSON); +``` Ajax-like requests will be minified, whereas normal browser requests will be human-readable. From 1bf6ee2d6c20537ddc1d9eba36cec949faf72631 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Thu, 30 Jan 2014 19:35:31 +0000 Subject: [PATCH 31/43] fixed bug in POST, PUT --- index.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/index.php b/index.php index d4375e85..ed31d942 100644 --- a/index.php +++ b/index.php @@ -9,7 +9,7 @@ * The MIT License * http://creativecommons.org/licenses/MIT/ * -* ArrestDB 1.7.1 (github.com/alixaxel/ArrestDB/) +* ArrestDB 1.8.0 (github.com/alixaxel/ArrestDB/) * Copyright (c) 2014 Alix Axel **/ @@ -290,12 +290,12 @@ foreach ($row as $key => $value) { - $data[sprintf('"%s"', $key)] = '?'; + $data[sprintf('"%s"', $key)] = $value; } $query = array ( - sprintf('INSERT INTO "%s" (%s) VALUES (%s)', $table, implode(', ', array_keys($data)), implode(', ', $data)), + sprintf('INSERT INTO "%s" (%s) VALUES (%s)', $table, implode(', ', array_keys($data)), implode(', ', array_fill(0, count($data), '?'))), ); $queries[] = array @@ -385,7 +385,7 @@ ); $query = sprintf('%s;', implode(' ', $query)); - $result = ArrestDB::Query($query, $GLOBALS['_PUT']); + $result = ArrestDB::Query($query, $GLOBALS['_PUT'], $id); if ($result === false) { @@ -574,7 +574,7 @@ public static function Query($query = null) } } - catch (\Exception $e) + catch (\Exception $exception) { return false; } From 15052f32378b00d6939b5830a8a17d5a9cdb30c4 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Thu, 30 Jan 2014 19:37:12 +0000 Subject: [PATCH 32/43] updated README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8fc50bbc..b2a1ce53 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,7 @@ Ajax-like requests will be minified, whereas normal browser requests will be hum - **1.5.0** ~~support for bulk inserts in `POST`~~ - **1.6.0** ~~added support for PostgreSQL~~ - **1.7.0** ~~fixed PostgreSQL connection bug, other minor improvements~~ +- **1.8.0** ~~fixed POST / PUT bug introduced in 1.5.0~~ ##Credits From ac399ae13c2b6eb33cb1e9d92c882dc5455d6174 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Thu, 30 Jan 2014 21:42:41 +0000 Subject: [PATCH 33/43] updated README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b2a1ce53..abe36b15 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,10 @@ Please note that `GET` calls accept the following query string variables: Additionally, `POST` and `PUT` requests accept JSON-encoded and/or zlib-compressed payloads. +> `POST` and `PUT` requests are only able to parse data encoded in `application/x-www-form-urlencoded`. + +> Support for `multipart/form-data` payloads will be added in the future. + If your REST client does not support certain requests, you can use the `X-HTTP-Method-Override` header: - `PUT` = `POST` + `X-HTTP-Method-Override: PUT` From 76884adefab530ea3ac5cdec81a27104f36a5ce0 Mon Sep 17 00:00:00 2001 From: Lieven Janssen Date: Thu, 8 May 2014 15:01:52 +0200 Subject: [PATCH 34/43] content-type header can contain encoding, e.g. application/x-www-form-urlencoded; charset=utf-8 Did a string comparison on the string before the first ;. --- index.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/index.php b/index.php index ed31d942..34389703 100644 --- a/index.php +++ b/index.php @@ -242,12 +242,13 @@ if ((array_key_exists('CONTENT_TYPE', $_SERVER) === true) && (empty($data) !== true)) { - if (strcasecmp($_SERVER['CONTENT_TYPE'], 'application/json') === 0) + $contenttype = explode(";",$_SERVER['CONTENT_TYPE'])[0]; + if (strcasecmp($contenttype, 'application/json') === 0) { $GLOBALS['_' . $http] = json_decode($data, true); } - else if ((strcasecmp($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded') === 0) && (strcasecmp($_SERVER['REQUEST_METHOD'], 'PUT') === 0)) + else if ((strcasecmp($contenttype, 'application/x-www-form-urlencoded') === 0) && (strcasecmp($_SERVER['REQUEST_METHOD'], 'PUT') === 0)) { parse_str($data, $GLOBALS['_' . $http]); } From af308ee23ac0dfc32ac8855151bf870051433d43 Mon Sep 17 00:00:00 2001 From: Lieven Janssen Date: Fri, 9 May 2014 17:13:36 +0200 Subject: [PATCH 35/43] content-type fix with strncasecmp --- index.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/index.php b/index.php index 34389703..8c8df4e4 100644 --- a/index.php +++ b/index.php @@ -242,13 +242,12 @@ if ((array_key_exists('CONTENT_TYPE', $_SERVER) === true) && (empty($data) !== true)) { - $contenttype = explode(";",$_SERVER['CONTENT_TYPE'])[0]; - if (strcasecmp($contenttype, 'application/json') === 0) + if (strncasecmp($_SERVER['CONTENT_TYPE'], 'application/json', 16) === 0) { $GLOBALS['_' . $http] = json_decode($data, true); } - else if ((strcasecmp($contenttype, 'application/x-www-form-urlencoded') === 0) && (strcasecmp($_SERVER['REQUEST_METHOD'], 'PUT') === 0)) + else if ((strncasecmp($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded', 33) === 0) && (strcasecmp($_SERVER['REQUEST_METHOD'], 'PUT') === 0)) { parse_str($data, $GLOBALS['_' . $http]); } From 236e764b12615c45cdb397958faf9295379c70e4 Mon Sep 17 00:00:00 2001 From: bign8 Date: Tue, 10 Jun 2014 10:25:42 -0600 Subject: [PATCH 36/43] Moving all responses to a single location Allows for global modification of a particular type of response --- index.php | 196 +++++++++++++++++++----------------------------------- 1 file changed, 68 insertions(+), 128 deletions(-) diff --git a/index.php b/index.php index 8c8df4e4..ad2845e7 100644 --- a/index.php +++ b/index.php @@ -20,28 +20,14 @@ if ((empty($clients) !== true) && (in_array($_SERVER['REMOTE_ADDR'], (array) $clients) !== true)) { - $result = array - ( - 'error' => array - ( - 'code' => 403, - 'status' => 'Forbidden', - ), - ); + $result = ArrestDB::$FORBIDDEN; exit(ArrestDB::Reply($result)); } else if (ArrestDB::Query($dsn) === false) { - $result = array - ( - 'error' => array - ( - 'code' => 503, - 'status' => 'Service Unavailable', - ), - ); + $result = ArrestDB::$SERVICE_UNAVAILABLE; exit(ArrestDB::Reply($result)); } @@ -89,26 +75,12 @@ if ($result === false) { - $result = array - ( - 'error' => array - ( - 'code' => 404, - 'status' => 'Not Found', - ), - ); + $result = ArrestDB::$NOT_FOUND; } else if (empty($result) === true) { - $result = array - ( - 'error' => array - ( - 'code' => 204, - 'status' => 'No Content', - ), - ); + $result = ArrestDB::$NO_CONTENT; } return ArrestDB::Reply($result); @@ -154,26 +126,12 @@ if ($result === false) { - $result = array - ( - 'error' => array - ( - 'code' => 404, - 'status' => 'Not Found', - ), - ); + $result = ArrestDB::$NOT_FOUND; } else if (empty($result) === true) { - $result = array - ( - 'error' => array - ( - 'code' => 204, - 'status' => 'No Content', - ), - ); + $result = ArrestDB::$NO_CONTENT; } else if (isset($id) === true) @@ -196,38 +154,17 @@ if ($result === false) { - $result = array - ( - 'error' => array - ( - 'code' => 404, - 'status' => 'Not Found', - ), - ); + $result = ArrestDB::$NOT_FOUND; } else if (empty($result) === true) { - $result = array - ( - 'error' => array - ( - 'code' => 204, - 'status' => 'No Content', - ), - ); + $result = ArrestDB::$NO_CONTENT; } else { - $result = array - ( - 'success' => array - ( - 'code' => 200, - 'status' => 'OK', - ), - ); + $result = ArrestDB::$OK; } return ArrestDB::Reply($result); @@ -265,14 +202,7 @@ { if (empty($_POST) === true) { - $result = array - ( - 'error' => array - ( - 'code' => 204, - 'status' => 'No Content', - ), - ); + $result = ArrestDB::$NO_CONTENT; } else if (is_array($_POST) === true) @@ -330,26 +260,12 @@ if ($result === false) { - $result = array - ( - 'error' => array - ( - 'code' => 409, - 'status' => 'Conflict', - ), - ); + $result = ArrestDB::$CONFLICT; } else { - $result = array - ( - 'success' => array - ( - 'code' => 201, - 'status' => 'Created', - ), - ); + $result = ArrestDB::$CREATED; } } @@ -360,14 +276,7 @@ { if (empty($GLOBALS['_PUT']) === true) { - $result = array - ( - 'error' => array - ( - 'code' => 204, - 'status' => 'No Content', - ), - ); + $result = ArrestDB::$NO_CONTENT; } else if (is_array($GLOBALS['_PUT']) === true) @@ -389,45 +298,76 @@ if ($result === false) { - $result = array - ( - 'error' => array - ( - 'code' => 409, - 'status' => 'Conflict', - ), - ); + $result = ArrestDB::$CONFLICT; } else { - $result = array - ( - 'success' => array - ( - 'code' => 200, - 'status' => 'OK', - ), - ); + $result = ArrestDB::$OK; } } return ArrestDB::Reply($result); }); -$result = array -( - 'error' => array - ( - 'code' => 400, - 'status' => 'Bad Request', - ), -); +$result = ArrestDB::$BAD_REQUEST; exit(ArrestDB::Reply($result)); class ArrestDB { + + // Result Messages + public static $OK = array( + 'success' => array( + 'code' => 200, + 'status' => 'OK', + ), + ); + public static $CREATED = array( + 'success' => array( + 'code' => 201, + 'status' => 'Created', + ), + ); + public static $NO_CONTENT = array( + 'error' => array( + 'code' => 204, + 'status' => 'No Content', + ), + ); + public static $BAD_REQUEST = array( + 'error' => array( + 'code' => 400, + 'status' => 'Bad Request', + ), + ); + public static $FORBIDDEN = array( + 'error' => array( + 'code' => 403, + 'status' => 'Forbidden' + ) + ); + public static $NOT_FOUND = array( + 'error' => array( + 'code' => 404, + 'status' => 'Not Found', + ), + ); + public static $CONFLICT = array( + 'error' => array( + 'code' => 409, + 'status' => 'Conflict', + ), + ); + public static $SERVICE_UNAVAILABLE = array( + 'error' => array( + 'code' => 503, + 'status' => 'Service Unavailable', + ), + ); + + // Operating Functions public static function Query($query = null) { static $db = null; From 9161519568af01347214bd64744993f448582da4 Mon Sep 17 00:00:00 2001 From: bign8 Date: Tue, 10 Jun 2014 12:56:16 -0600 Subject: [PATCH 37/43] Switching Indent Style: Allman to BSD-KNF http://en.wikipedia.org/wiki/Indent_style --- index.php | 336 ++++++++++++++++-------------------------------------- 1 file changed, 96 insertions(+), 240 deletions(-) diff --git a/index.php b/index.php index ad2845e7..42bead82 100644 --- a/index.php +++ b/index.php @@ -1,8 +1,7 @@ **/ -if (strcmp(PHP_SAPI, 'cli') === 0) -{ - exit('ArrestDB should not be run from CLI.' . PHP_EOL); -} +if (strcmp(PHP_SAPI, 'cli') === 0) exit('ArrestDB should not be run from CLI.' . PHP_EOL); -if ((empty($clients) !== true) && (in_array($_SERVER['REMOTE_ADDR'], (array) $clients) !== true)) -{ +if ((empty($clients) !== true) && (in_array($_SERVER['REMOTE_ADDR'], (array) $clients) !== true)) { $result = ArrestDB::$FORBIDDEN; - exit(ArrestDB::Reply($result)); -} - -else if (ArrestDB::Query($dsn) === false) -{ +} else if (ArrestDB::Query($dsn) === false) { $result = ArrestDB::$SERVICE_UNAVAILABLE; - exit(ArrestDB::Reply($result)); } -if (array_key_exists('_method', $_GET) === true) -{ +if (array_key_exists('_method', $_GET) === true) { $_SERVER['REQUEST_METHOD'] = strtoupper(trim($_GET['_method'])); -} - -else if (array_key_exists('HTTP_X_HTTP_METHOD_OVERRIDE', $_SERVER) === true) -{ +} else if (array_key_exists('HTTP_X_HTTP_METHOD_OVERRIDE', $_SERVER) === true) { $_SERVER['REQUEST_METHOD'] = strtoupper(trim($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])); } -ArrestDB::Serve('GET', '/(#any)/(#any)/(#any)', function ($table, $id, $data) -{ - $query = array - ( +ArrestDB::Serve('GET', '/(#any)/(#any)/(#any)', function ($table, $id, $data) { + $query = array( sprintf('SELECT * FROM "%s"', $table), sprintf('WHERE "%s" %s ?', $id, (ctype_digit($data) === true) ? '=' : 'LIKE'), ); - if (isset($_GET['by']) === true) - { - if (isset($_GET['order']) !== true) - { - $_GET['order'] = 'ASC'; - } + if (isset($_GET['by']) === true) { + if (isset($_GET['order']) !== true) $_GET['order'] = 'ASC'; $query[] = sprintf('ORDER BY "%s" %s', $_GET['by'], $_GET['order']); } - if (isset($_GET['limit']) === true) - { + if (isset($_GET['limit']) === true) { $query[] = sprintf('LIMIT %u', $_GET['limit']); - if (isset($_GET['offset']) === true) - { + if (isset($_GET['offset']) === true) { $query[] = sprintf('OFFSET %u', $_GET['offset']); } } @@ -73,49 +51,33 @@ $query = sprintf('%s;', implode(' ', $query)); $result = ArrestDB::Query($query, $data); - if ($result === false) - { + if ($result === false) { $result = ArrestDB::$NOT_FOUND; - } - - else if (empty($result) === true) - { + } else if (empty($result) === true) { $result = ArrestDB::$NO_CONTENT; } return ArrestDB::Reply($result); }); -ArrestDB::Serve('GET', '/(#any)/(#num)?', function ($table, $id = null) -{ - $query = array - ( +ArrestDB::Serve('GET', '/(#any)/(#num)?', function ($table, $id = null) { + $query = array( sprintf('SELECT * FROM "%s"', $table), ); - if (isset($id) === true) - { + if (isset($id) === true) { $query[] = sprintf('WHERE "%s" = ? LIMIT 1', 'id'); - } - - else - { - if (isset($_GET['by']) === true) - { - if (isset($_GET['order']) !== true) - { - $_GET['order'] = 'ASC'; - } + } else { + if (isset($_GET['by']) === true) { + if (isset($_GET['order']) !== true) $_GET['order'] = 'ASC'; $query[] = sprintf('ORDER BY "%s" %s', $_GET['by'], $_GET['order']); } - if (isset($_GET['limit']) === true) - { + if (isset($_GET['limit']) === true) { $query[] = sprintf('LIMIT %u', $_GET['limit']); - if (isset($_GET['offset']) === true) - { + if (isset($_GET['offset']) === true) { $query[] = sprintf('OFFSET %u', $_GET['offset']); } } @@ -124,147 +86,96 @@ $query = sprintf('%s;', implode(' ', $query)); $result = (isset($id) === true) ? ArrestDB::Query($query, $id) : ArrestDB::Query($query); - if ($result === false) - { + if ($result === false) { $result = ArrestDB::$NOT_FOUND; - } - - else if (empty($result) === true) - { + } else if (empty($result) === true) { $result = ArrestDB::$NO_CONTENT; - } - - else if (isset($id) === true) - { + } else if (isset($id) === true) { $result = array_shift($result); } return ArrestDB::Reply($result); }); -ArrestDB::Serve('DELETE', '/(#any)/(#num)', function ($table, $id) -{ - $query = array - ( +ArrestDB::Serve('DELETE', '/(#any)/(#num)', function ($table, $id) { + $query = array( sprintf('DELETE FROM "%s" WHERE "%s" = ?', $table, 'id'), ); $query = sprintf('%s;', implode(' ', $query)); $result = ArrestDB::Query($query, $id); - if ($result === false) - { + if ($result === false) { $result = ArrestDB::$NOT_FOUND; - } - - else if (empty($result) === true) - { + } else if (empty($result) === true) { $result = ArrestDB::$NO_CONTENT; - } - - else - { + } else { $result = ArrestDB::$OK; } return ArrestDB::Reply($result); }); -if (in_array($http = strtoupper($_SERVER['REQUEST_METHOD']), array('POST', 'PUT')) === true) -{ - if (preg_match('~^\x78[\x01\x5E\x9C\xDA]~', $data = file_get_contents('php://input')) > 0) - { - $data = gzuncompress($data); - } +if (in_array($http = strtoupper($_SERVER['REQUEST_METHOD']), array('POST', 'PUT')) === true) { + if (preg_match('~^\x78[\x01\x5E\x9C\xDA]~', $data = file_get_contents('php://input')) > 0) $data = gzuncompress($data); - if ((array_key_exists('CONTENT_TYPE', $_SERVER) === true) && (empty($data) !== true)) - { - if (strncasecmp($_SERVER['CONTENT_TYPE'], 'application/json', 16) === 0) - { + if ((array_key_exists('CONTENT_TYPE', $_SERVER) === true) && (empty($data) !== true)) { + if (strncasecmp($_SERVER['CONTENT_TYPE'], 'application/json', 16) === 0) { $GLOBALS['_' . $http] = json_decode($data, true); - } - - else if ((strncasecmp($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded', 33) === 0) && (strcasecmp($_SERVER['REQUEST_METHOD'], 'PUT') === 0)) - { + } else if ((strncasecmp($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded', 33) === 0) && (strcasecmp($_SERVER['REQUEST_METHOD'], 'PUT') === 0)) { parse_str($data, $GLOBALS['_' . $http]); } } - if ((isset($GLOBALS['_' . $http]) !== true) || (is_array($GLOBALS['_' . $http]) !== true)) - { - $GLOBALS['_' . $http] = array(); - } + if ((isset($GLOBALS['_' . $http]) !== true) || (is_array($GLOBALS['_' . $http]) !== true)) $GLOBALS['_' . $http] = array(); unset($data); } -ArrestDB::Serve('POST', '/(#any)', function ($table) -{ - if (empty($_POST) === true) - { +ArrestDB::Serve('POST', '/(#any)', function ($table) { + if (empty($_POST) === true) { $result = ArrestDB::$NO_CONTENT; - } - - else if (is_array($_POST) === true) - { + } else if (is_array($_POST) === true) { $queries = array(); - if (count($_POST) == count($_POST, COUNT_RECURSIVE)) - { - $_POST = array($_POST); - } + if (count($_POST) == count($_POST, COUNT_RECURSIVE)) $_POST = array($_POST); - foreach ($_POST as $row) - { + foreach ($_POST as $row) { $data = array(); - foreach ($row as $key => $value) - { + foreach ($row as $key => $value) { $data[sprintf('"%s"', $key)] = $value; } - $query = array - ( + $query = array( sprintf('INSERT INTO "%s" (%s) VALUES (%s)', $table, implode(', ', array_keys($data)), implode(', ', array_fill(0, count($data), '?'))), ); - $queries[] = array - ( + $queries[] = array( sprintf('%s;', implode(' ', $query)), $data, ); } - if (count($queries) > 1) - { + if (count($queries) > 1) { ArrestDB::Query()->beginTransaction(); - while (is_null($query = array_shift($queries)) !== true) - { - if (($result = ArrestDB::Query($query[0], $query[1])) === false) - { + while (is_null($query = array_shift($queries)) !== true) { + if (($result = ArrestDB::Query($query[0], $query[1])) === false) { ArrestDB::Query()->rollBack(); break; } } - if (($result !== false) && (ArrestDB::Query()->inTransaction() === true)) - { + if (($result !== false) && (ArrestDB::Query()->inTransaction() === true)) { $result = ArrestDB::Query()->commit(); } - } - - else if (is_null($query = array_shift($queries)) !== true) - { + } else if (is_null($query = array_shift($queries)) !== true) { $result = ArrestDB::Query($query[0], $query[1]); } - if ($result === false) - { + if ($result === false) { $result = ArrestDB::$CONFLICT; - } - - else - { + } else { $result = ArrestDB::$CREATED; } } @@ -272,37 +183,26 @@ return ArrestDB::Reply($result); }); -ArrestDB::Serve('PUT', '/(#any)/(#num)', function ($table, $id) -{ - if (empty($GLOBALS['_PUT']) === true) - { +ArrestDB::Serve('PUT', '/(#any)/(#num)', function ($table, $id) { + if (empty($GLOBALS['_PUT']) === true) { $result = ArrestDB::$NO_CONTENT; - } - - else if (is_array($GLOBALS['_PUT']) === true) - { + } else if (is_array($GLOBALS['_PUT']) === true) { $data = array(); - foreach ($GLOBALS['_PUT'] as $key => $value) - { + foreach ($GLOBALS['_PUT'] as $key => $value) { $data[$key] = sprintf('"%s" = ?', $key); } - $query = array - ( + $query = array( sprintf('UPDATE "%s" SET %s WHERE "%s" = ?', $table, implode(', ', $data), 'id'), ); $query = sprintf('%s;', implode(' ', $query)); $result = ArrestDB::Query($query, $GLOBALS['_PUT'], $id); - if ($result === false) - { + if ($result === false) { $result = ArrestDB::$CONFLICT; - } - - else - { + } else { $result = ArrestDB::$OK; } } @@ -314,8 +214,7 @@ exit(ArrestDB::Reply($result)); -class ArrestDB -{ +class ArrestDB { // Result Messages public static $OK = array( @@ -368,43 +267,34 @@ class ArrestDB ); // Operating Functions - public static function Query($query = null) - { + public static function Query($query = null) { static $db = null; static $result = array(); - try - { - if (isset($db, $query) === true) - { - if (strncasecmp($db->getAttribute(\PDO::ATTR_DRIVER_NAME), 'mysql', 5) === 0) - { + try { + if (isset($db, $query) === true) { + if (strncasecmp($db->getAttribute(\PDO::ATTR_DRIVER_NAME), 'mysql', 5) === 0) { $query = strtr($query, '"', '`'); } - if (empty($result[$hash = crc32($query)]) === true) - { + if (empty($result[$hash = crc32($query)]) === true) { $result[$hash] = $db->prepare($query); } $data = array_slice(func_get_args(), 1); - if (count($data, COUNT_RECURSIVE) > count($data)) - { + if (count($data, COUNT_RECURSIVE) > count($data)) { $data = iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveArrayIterator($data)), false); } - if ($result[$hash]->execute($data) === true) - { + if ($result[$hash]->execute($data) === true) { $sequence = null; - if ((strncmp($db->getAttribute(\PDO::ATTR_DRIVER_NAME), 'pgsql', 5) === 0) && (sscanf($query, 'INSERT INTO %s', $sequence) > 0)) - { + if ((strncmp($db->getAttribute(\PDO::ATTR_DRIVER_NAME), 'pgsql', 5) === 0) && (sscanf($query, 'INSERT INTO %s', $sequence) > 0)) { $sequence = sprintf('%s_id_seq', trim($sequence, '"')); } - switch (strstr($query, ' ', true)) - { + switch (strstr($query, ' ', true)) { case 'INSERT': case 'REPLACE': return $db->lastInsertId($sequence); @@ -424,12 +314,8 @@ public static function Query($query = null) } return false; - } - - else if (isset($query) === true) - { - $options = array - ( + } else if (isset($query) === true) { + $options = array( \PDO::ATTR_CASE => \PDO::CASE_NATURAL, \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, \PDO::ATTR_EMULATE_PREPARES => false, @@ -438,16 +324,13 @@ public static function Query($query = null) \PDO::ATTR_STRINGIFY_FETCHES => false, ); - if (preg_match('~^sqlite://([[:print:]]++)$~i', $query, $dsn) > 0) - { - $options += array - ( + if (preg_match('~^sqlite://([[:print:]]++)$~i', $query, $dsn) > 0) { + $options += array( \PDO::ATTR_TIMEOUT => 3, ); $db = new \PDO(sprintf('sqlite:%s', $dsn[1]), null, null, $options); - $pragmas = array - ( + $pragmas = array( 'automatic_index' => 'ON', 'cache_size' => '8192', 'foreign_keys' => 'ON', @@ -462,23 +345,17 @@ public static function Query($query = null) 'wal_autocheckpoint' => '4096', ); - if (strncasecmp(PHP_OS, 'WIN', 3) !== 0) - { + if (strncasecmp(PHP_OS, 'WIN', 3) !== 0) { $memory = 131072; - if (($page = intval(shell_exec('getconf PAGESIZE'))) > 0) - { + if (($page = intval(shell_exec('getconf PAGESIZE'))) > 0) { $pragmas['page_size'] = $page; } - if (is_readable('/proc/meminfo') === true) - { - if (is_resource($handle = fopen('/proc/meminfo', 'rb')) === true) - { - while (($line = fgets($handle, 1024)) !== false) - { - if (sscanf($line, 'MemTotal: %d kB', $memory) == 1) - { + if (is_readable('/proc/meminfo') === true) { + if (is_resource($handle = fopen('/proc/meminfo', 'rb')) === true) { + while (($line = fgets($handle, 1024)) !== false) { + if (sscanf($line, 'MemTotal: %d kB', $memory) == 1) { $memory = round($memory / 131072) * 131072; break; } } @@ -491,18 +368,12 @@ public static function Query($query = null) $pragmas['wal_autocheckpoint'] = $pragmas['cache_size'] / 2; } - foreach ($pragmas as $key => $value) - { + foreach ($pragmas as $key => $value) { $db->exec(sprintf('PRAGMA %s=%s;', $key, $value)); } - } - - else if (preg_match('~^(mysql|pgsql)://(?:(.+?)(?::(.+?))?@)?([^/:@]++)(?::(\d++))?/(\w++)/?$~i', $query, $dsn) > 0) - { - if (strncasecmp($query, 'mysql', 5) === 0) - { - $options += array - ( + } else if (preg_match('~^(mysql|pgsql)://(?:(.+?)(?::(.+?))?@)?([^/:@]++)(?::(\d++))?/(\w++)/?$~i', $query, $dsn) > 0) { + if (strncasecmp($query, 'mysql', 5) === 0) { + $options += array( \PDO::ATTR_AUTOCOMMIT => true, \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES "utf8" COLLATE "utf8_general_ci", time_zone = "+00:00";', \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, @@ -512,47 +383,37 @@ public static function Query($query = null) $db = new \PDO(sprintf('%s:host=%s;port=%s;dbname=%s', $dsn[1], $dsn[4], $dsn[5], $dsn[6]), $dsn[2], $dsn[3], $options); } } - } - - catch (\Exception $exception) - { + } catch (\Exception $exception) { return false; } return (isset($db) === true) ? $db : false; } - public static function Reply($data) - { + public static function Reply($data) { $bitmask = 0; $options = array('UNESCAPED_SLASHES', 'UNESCAPED_UNICODE'); - if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) === true) - { + if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) === true) { $options[] = 'PRETTY_PRINT'; } - foreach ($options as $option) - { + foreach ($options as $option) { $bitmask |= (defined('JSON_' . $option) === true) ? constant('JSON_' . $option) : 0; } - if (($result = json_encode($data, $bitmask)) !== false) - { + if (($result = json_encode($data, $bitmask)) !== false) { $callback = null; - if (array_key_exists('callback', $_GET) === true) - { + if (array_key_exists('callback', $_GET) === true) { $callback = trim(preg_replace('~[^[:alnum:]\[\]_.]~', '', $_GET['callback'])); - if (empty($callback) !== true) - { + if (empty($callback) !== true) { $result = sprintf('%s(%s);', $callback, $result); } } - if (headers_sent() !== true) - { + if (headers_sent() !== true) { header(sprintf('Content-Type: application/%s; charset=utf-8', (empty($callback) === true) ? 'json' : 'javascript')); } } @@ -560,24 +421,19 @@ public static function Reply($data) return $result; } - public static function Serve($on = null, $route = null, $callback = null) - { + public static function Serve($on = null, $route = null, $callback = null) { static $root = null; - if (isset($_SERVER['REQUEST_METHOD']) !== true) - { + if (isset($_SERVER['REQUEST_METHOD']) !== true) { $_SERVER['REQUEST_METHOD'] = 'CLI'; } - if ((empty($on) === true) || (strcasecmp($_SERVER['REQUEST_METHOD'], $on) === 0)) - { - if (is_null($root) === true) - { + if ((empty($on) === true) || (strcasecmp($_SERVER['REQUEST_METHOD'], $on) === 0)) { + if (is_null($root) === true) { $root = preg_replace('~/++~', '/', substr($_SERVER['PHP_SELF'], strlen($_SERVER['SCRIPT_NAME'])) . '/'); } - if (preg_match('~^' . str_replace(array('#any', '#num'), array('[^/]++', '[0-9]++'), $route) . '~i', $root, $parts) > 0) - { + if (preg_match('~^' . str_replace(array('#any', '#num'), array('[^/]++', '[0-9]++'), $route) . '~i', $root, $parts) > 0) { return (empty($callback) === true) ? true : exit(call_user_func_array($callback, array_slice($parts, 1))); } } From 63d588c07456f8d56e7e8c4ae588846a8fb26e94 Mon Sep 17 00:00:00 2001 From: bign8 Date: Tue, 10 Jun 2014 16:23:33 -0600 Subject: [PATCH 38/43] Revert "Switching Indent Style: Allman to BSD-KNF" This reverts commit 9161519568af01347214bd64744993f448582da4. --- index.php | 336 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 240 insertions(+), 96 deletions(-) diff --git a/index.php b/index.php index 42bead82..ad2845e7 100644 --- a/index.php +++ b/index.php @@ -1,7 +1,8 @@ **/ -if (strcmp(PHP_SAPI, 'cli') === 0) exit('ArrestDB should not be run from CLI.' . PHP_EOL); +if (strcmp(PHP_SAPI, 'cli') === 0) +{ + exit('ArrestDB should not be run from CLI.' . PHP_EOL); +} -if ((empty($clients) !== true) && (in_array($_SERVER['REMOTE_ADDR'], (array) $clients) !== true)) { +if ((empty($clients) !== true) && (in_array($_SERVER['REMOTE_ADDR'], (array) $clients) !== true)) +{ $result = ArrestDB::$FORBIDDEN; + exit(ArrestDB::Reply($result)); -} else if (ArrestDB::Query($dsn) === false) { +} + +else if (ArrestDB::Query($dsn) === false) +{ $result = ArrestDB::$SERVICE_UNAVAILABLE; + exit(ArrestDB::Reply($result)); } -if (array_key_exists('_method', $_GET) === true) { +if (array_key_exists('_method', $_GET) === true) +{ $_SERVER['REQUEST_METHOD'] = strtoupper(trim($_GET['_method'])); -} else if (array_key_exists('HTTP_X_HTTP_METHOD_OVERRIDE', $_SERVER) === true) { +} + +else if (array_key_exists('HTTP_X_HTTP_METHOD_OVERRIDE', $_SERVER) === true) +{ $_SERVER['REQUEST_METHOD'] = strtoupper(trim($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])); } -ArrestDB::Serve('GET', '/(#any)/(#any)/(#any)', function ($table, $id, $data) { - $query = array( +ArrestDB::Serve('GET', '/(#any)/(#any)/(#any)', function ($table, $id, $data) +{ + $query = array + ( sprintf('SELECT * FROM "%s"', $table), sprintf('WHERE "%s" %s ?', $id, (ctype_digit($data) === true) ? '=' : 'LIKE'), ); - if (isset($_GET['by']) === true) { - if (isset($_GET['order']) !== true) $_GET['order'] = 'ASC'; + if (isset($_GET['by']) === true) + { + if (isset($_GET['order']) !== true) + { + $_GET['order'] = 'ASC'; + } $query[] = sprintf('ORDER BY "%s" %s', $_GET['by'], $_GET['order']); } - if (isset($_GET['limit']) === true) { + if (isset($_GET['limit']) === true) + { $query[] = sprintf('LIMIT %u', $_GET['limit']); - if (isset($_GET['offset']) === true) { + if (isset($_GET['offset']) === true) + { $query[] = sprintf('OFFSET %u', $_GET['offset']); } } @@ -51,33 +73,49 @@ $query = sprintf('%s;', implode(' ', $query)); $result = ArrestDB::Query($query, $data); - if ($result === false) { + if ($result === false) + { $result = ArrestDB::$NOT_FOUND; - } else if (empty($result) === true) { + } + + else if (empty($result) === true) + { $result = ArrestDB::$NO_CONTENT; } return ArrestDB::Reply($result); }); -ArrestDB::Serve('GET', '/(#any)/(#num)?', function ($table, $id = null) { - $query = array( +ArrestDB::Serve('GET', '/(#any)/(#num)?', function ($table, $id = null) +{ + $query = array + ( sprintf('SELECT * FROM "%s"', $table), ); - if (isset($id) === true) { + if (isset($id) === true) + { $query[] = sprintf('WHERE "%s" = ? LIMIT 1', 'id'); - } else { - if (isset($_GET['by']) === true) { - if (isset($_GET['order']) !== true) $_GET['order'] = 'ASC'; + } + + else + { + if (isset($_GET['by']) === true) + { + if (isset($_GET['order']) !== true) + { + $_GET['order'] = 'ASC'; + } $query[] = sprintf('ORDER BY "%s" %s', $_GET['by'], $_GET['order']); } - if (isset($_GET['limit']) === true) { + if (isset($_GET['limit']) === true) + { $query[] = sprintf('LIMIT %u', $_GET['limit']); - if (isset($_GET['offset']) === true) { + if (isset($_GET['offset']) === true) + { $query[] = sprintf('OFFSET %u', $_GET['offset']); } } @@ -86,96 +124,147 @@ $query = sprintf('%s;', implode(' ', $query)); $result = (isset($id) === true) ? ArrestDB::Query($query, $id) : ArrestDB::Query($query); - if ($result === false) { + if ($result === false) + { $result = ArrestDB::$NOT_FOUND; - } else if (empty($result) === true) { + } + + else if (empty($result) === true) + { $result = ArrestDB::$NO_CONTENT; - } else if (isset($id) === true) { + } + + else if (isset($id) === true) + { $result = array_shift($result); } return ArrestDB::Reply($result); }); -ArrestDB::Serve('DELETE', '/(#any)/(#num)', function ($table, $id) { - $query = array( +ArrestDB::Serve('DELETE', '/(#any)/(#num)', function ($table, $id) +{ + $query = array + ( sprintf('DELETE FROM "%s" WHERE "%s" = ?', $table, 'id'), ); $query = sprintf('%s;', implode(' ', $query)); $result = ArrestDB::Query($query, $id); - if ($result === false) { + if ($result === false) + { $result = ArrestDB::$NOT_FOUND; - } else if (empty($result) === true) { + } + + else if (empty($result) === true) + { $result = ArrestDB::$NO_CONTENT; - } else { + } + + else + { $result = ArrestDB::$OK; } return ArrestDB::Reply($result); }); -if (in_array($http = strtoupper($_SERVER['REQUEST_METHOD']), array('POST', 'PUT')) === true) { - if (preg_match('~^\x78[\x01\x5E\x9C\xDA]~', $data = file_get_contents('php://input')) > 0) $data = gzuncompress($data); +if (in_array($http = strtoupper($_SERVER['REQUEST_METHOD']), array('POST', 'PUT')) === true) +{ + if (preg_match('~^\x78[\x01\x5E\x9C\xDA]~', $data = file_get_contents('php://input')) > 0) + { + $data = gzuncompress($data); + } - if ((array_key_exists('CONTENT_TYPE', $_SERVER) === true) && (empty($data) !== true)) { - if (strncasecmp($_SERVER['CONTENT_TYPE'], 'application/json', 16) === 0) { + if ((array_key_exists('CONTENT_TYPE', $_SERVER) === true) && (empty($data) !== true)) + { + if (strncasecmp($_SERVER['CONTENT_TYPE'], 'application/json', 16) === 0) + { $GLOBALS['_' . $http] = json_decode($data, true); - } else if ((strncasecmp($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded', 33) === 0) && (strcasecmp($_SERVER['REQUEST_METHOD'], 'PUT') === 0)) { + } + + else if ((strncasecmp($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded', 33) === 0) && (strcasecmp($_SERVER['REQUEST_METHOD'], 'PUT') === 0)) + { parse_str($data, $GLOBALS['_' . $http]); } } - if ((isset($GLOBALS['_' . $http]) !== true) || (is_array($GLOBALS['_' . $http]) !== true)) $GLOBALS['_' . $http] = array(); + if ((isset($GLOBALS['_' . $http]) !== true) || (is_array($GLOBALS['_' . $http]) !== true)) + { + $GLOBALS['_' . $http] = array(); + } unset($data); } -ArrestDB::Serve('POST', '/(#any)', function ($table) { - if (empty($_POST) === true) { +ArrestDB::Serve('POST', '/(#any)', function ($table) +{ + if (empty($_POST) === true) + { $result = ArrestDB::$NO_CONTENT; - } else if (is_array($_POST) === true) { + } + + else if (is_array($_POST) === true) + { $queries = array(); - if (count($_POST) == count($_POST, COUNT_RECURSIVE)) $_POST = array($_POST); + if (count($_POST) == count($_POST, COUNT_RECURSIVE)) + { + $_POST = array($_POST); + } - foreach ($_POST as $row) { + foreach ($_POST as $row) + { $data = array(); - foreach ($row as $key => $value) { + foreach ($row as $key => $value) + { $data[sprintf('"%s"', $key)] = $value; } - $query = array( + $query = array + ( sprintf('INSERT INTO "%s" (%s) VALUES (%s)', $table, implode(', ', array_keys($data)), implode(', ', array_fill(0, count($data), '?'))), ); - $queries[] = array( + $queries[] = array + ( sprintf('%s;', implode(' ', $query)), $data, ); } - if (count($queries) > 1) { + if (count($queries) > 1) + { ArrestDB::Query()->beginTransaction(); - while (is_null($query = array_shift($queries)) !== true) { - if (($result = ArrestDB::Query($query[0], $query[1])) === false) { + while (is_null($query = array_shift($queries)) !== true) + { + if (($result = ArrestDB::Query($query[0], $query[1])) === false) + { ArrestDB::Query()->rollBack(); break; } } - if (($result !== false) && (ArrestDB::Query()->inTransaction() === true)) { + if (($result !== false) && (ArrestDB::Query()->inTransaction() === true)) + { $result = ArrestDB::Query()->commit(); } - } else if (is_null($query = array_shift($queries)) !== true) { + } + + else if (is_null($query = array_shift($queries)) !== true) + { $result = ArrestDB::Query($query[0], $query[1]); } - if ($result === false) { + if ($result === false) + { $result = ArrestDB::$CONFLICT; - } else { + } + + else + { $result = ArrestDB::$CREATED; } } @@ -183,26 +272,37 @@ return ArrestDB::Reply($result); }); -ArrestDB::Serve('PUT', '/(#any)/(#num)', function ($table, $id) { - if (empty($GLOBALS['_PUT']) === true) { +ArrestDB::Serve('PUT', '/(#any)/(#num)', function ($table, $id) +{ + if (empty($GLOBALS['_PUT']) === true) + { $result = ArrestDB::$NO_CONTENT; - } else if (is_array($GLOBALS['_PUT']) === true) { + } + + else if (is_array($GLOBALS['_PUT']) === true) + { $data = array(); - foreach ($GLOBALS['_PUT'] as $key => $value) { + foreach ($GLOBALS['_PUT'] as $key => $value) + { $data[$key] = sprintf('"%s" = ?', $key); } - $query = array( + $query = array + ( sprintf('UPDATE "%s" SET %s WHERE "%s" = ?', $table, implode(', ', $data), 'id'), ); $query = sprintf('%s;', implode(' ', $query)); $result = ArrestDB::Query($query, $GLOBALS['_PUT'], $id); - if ($result === false) { + if ($result === false) + { $result = ArrestDB::$CONFLICT; - } else { + } + + else + { $result = ArrestDB::$OK; } } @@ -214,7 +314,8 @@ exit(ArrestDB::Reply($result)); -class ArrestDB { +class ArrestDB +{ // Result Messages public static $OK = array( @@ -267,34 +368,43 @@ class ArrestDB { ); // Operating Functions - public static function Query($query = null) { + public static function Query($query = null) + { static $db = null; static $result = array(); - try { - if (isset($db, $query) === true) { - if (strncasecmp($db->getAttribute(\PDO::ATTR_DRIVER_NAME), 'mysql', 5) === 0) { + try + { + if (isset($db, $query) === true) + { + if (strncasecmp($db->getAttribute(\PDO::ATTR_DRIVER_NAME), 'mysql', 5) === 0) + { $query = strtr($query, '"', '`'); } - if (empty($result[$hash = crc32($query)]) === true) { + if (empty($result[$hash = crc32($query)]) === true) + { $result[$hash] = $db->prepare($query); } $data = array_slice(func_get_args(), 1); - if (count($data, COUNT_RECURSIVE) > count($data)) { + if (count($data, COUNT_RECURSIVE) > count($data)) + { $data = iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveArrayIterator($data)), false); } - if ($result[$hash]->execute($data) === true) { + if ($result[$hash]->execute($data) === true) + { $sequence = null; - if ((strncmp($db->getAttribute(\PDO::ATTR_DRIVER_NAME), 'pgsql', 5) === 0) && (sscanf($query, 'INSERT INTO %s', $sequence) > 0)) { + if ((strncmp($db->getAttribute(\PDO::ATTR_DRIVER_NAME), 'pgsql', 5) === 0) && (sscanf($query, 'INSERT INTO %s', $sequence) > 0)) + { $sequence = sprintf('%s_id_seq', trim($sequence, '"')); } - switch (strstr($query, ' ', true)) { + switch (strstr($query, ' ', true)) + { case 'INSERT': case 'REPLACE': return $db->lastInsertId($sequence); @@ -314,8 +424,12 @@ public static function Query($query = null) { } return false; - } else if (isset($query) === true) { - $options = array( + } + + else if (isset($query) === true) + { + $options = array + ( \PDO::ATTR_CASE => \PDO::CASE_NATURAL, \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, \PDO::ATTR_EMULATE_PREPARES => false, @@ -324,13 +438,16 @@ public static function Query($query = null) { \PDO::ATTR_STRINGIFY_FETCHES => false, ); - if (preg_match('~^sqlite://([[:print:]]++)$~i', $query, $dsn) > 0) { - $options += array( + if (preg_match('~^sqlite://([[:print:]]++)$~i', $query, $dsn) > 0) + { + $options += array + ( \PDO::ATTR_TIMEOUT => 3, ); $db = new \PDO(sprintf('sqlite:%s', $dsn[1]), null, null, $options); - $pragmas = array( + $pragmas = array + ( 'automatic_index' => 'ON', 'cache_size' => '8192', 'foreign_keys' => 'ON', @@ -345,17 +462,23 @@ public static function Query($query = null) { 'wal_autocheckpoint' => '4096', ); - if (strncasecmp(PHP_OS, 'WIN', 3) !== 0) { + if (strncasecmp(PHP_OS, 'WIN', 3) !== 0) + { $memory = 131072; - if (($page = intval(shell_exec('getconf PAGESIZE'))) > 0) { + if (($page = intval(shell_exec('getconf PAGESIZE'))) > 0) + { $pragmas['page_size'] = $page; } - if (is_readable('/proc/meminfo') === true) { - if (is_resource($handle = fopen('/proc/meminfo', 'rb')) === true) { - while (($line = fgets($handle, 1024)) !== false) { - if (sscanf($line, 'MemTotal: %d kB', $memory) == 1) { + if (is_readable('/proc/meminfo') === true) + { + if (is_resource($handle = fopen('/proc/meminfo', 'rb')) === true) + { + while (($line = fgets($handle, 1024)) !== false) + { + if (sscanf($line, 'MemTotal: %d kB', $memory) == 1) + { $memory = round($memory / 131072) * 131072; break; } } @@ -368,12 +491,18 @@ public static function Query($query = null) { $pragmas['wal_autocheckpoint'] = $pragmas['cache_size'] / 2; } - foreach ($pragmas as $key => $value) { + foreach ($pragmas as $key => $value) + { $db->exec(sprintf('PRAGMA %s=%s;', $key, $value)); } - } else if (preg_match('~^(mysql|pgsql)://(?:(.+?)(?::(.+?))?@)?([^/:@]++)(?::(\d++))?/(\w++)/?$~i', $query, $dsn) > 0) { - if (strncasecmp($query, 'mysql', 5) === 0) { - $options += array( + } + + else if (preg_match('~^(mysql|pgsql)://(?:(.+?)(?::(.+?))?@)?([^/:@]++)(?::(\d++))?/(\w++)/?$~i', $query, $dsn) > 0) + { + if (strncasecmp($query, 'mysql', 5) === 0) + { + $options += array + ( \PDO::ATTR_AUTOCOMMIT => true, \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES "utf8" COLLATE "utf8_general_ci", time_zone = "+00:00";', \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, @@ -383,37 +512,47 @@ public static function Query($query = null) { $db = new \PDO(sprintf('%s:host=%s;port=%s;dbname=%s', $dsn[1], $dsn[4], $dsn[5], $dsn[6]), $dsn[2], $dsn[3], $options); } } - } catch (\Exception $exception) { + } + + catch (\Exception $exception) + { return false; } return (isset($db) === true) ? $db : false; } - public static function Reply($data) { + public static function Reply($data) + { $bitmask = 0; $options = array('UNESCAPED_SLASHES', 'UNESCAPED_UNICODE'); - if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) === true) { + if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) === true) + { $options[] = 'PRETTY_PRINT'; } - foreach ($options as $option) { + foreach ($options as $option) + { $bitmask |= (defined('JSON_' . $option) === true) ? constant('JSON_' . $option) : 0; } - if (($result = json_encode($data, $bitmask)) !== false) { + if (($result = json_encode($data, $bitmask)) !== false) + { $callback = null; - if (array_key_exists('callback', $_GET) === true) { + if (array_key_exists('callback', $_GET) === true) + { $callback = trim(preg_replace('~[^[:alnum:]\[\]_.]~', '', $_GET['callback'])); - if (empty($callback) !== true) { + if (empty($callback) !== true) + { $result = sprintf('%s(%s);', $callback, $result); } } - if (headers_sent() !== true) { + if (headers_sent() !== true) + { header(sprintf('Content-Type: application/%s; charset=utf-8', (empty($callback) === true) ? 'json' : 'javascript')); } } @@ -421,19 +560,24 @@ public static function Reply($data) { return $result; } - public static function Serve($on = null, $route = null, $callback = null) { + public static function Serve($on = null, $route = null, $callback = null) + { static $root = null; - if (isset($_SERVER['REQUEST_METHOD']) !== true) { + if (isset($_SERVER['REQUEST_METHOD']) !== true) + { $_SERVER['REQUEST_METHOD'] = 'CLI'; } - if ((empty($on) === true) || (strcasecmp($_SERVER['REQUEST_METHOD'], $on) === 0)) { - if (is_null($root) === true) { + if ((empty($on) === true) || (strcasecmp($_SERVER['REQUEST_METHOD'], $on) === 0)) + { + if (is_null($root) === true) + { $root = preg_replace('~/++~', '/', substr($_SERVER['PHP_SELF'], strlen($_SERVER['SCRIPT_NAME'])) . '/'); } - if (preg_match('~^' . str_replace(array('#any', '#num'), array('[^/]++', '[0-9]++'), $route) . '~i', $root, $parts) > 0) { + if (preg_match('~^' . str_replace(array('#any', '#num'), array('[^/]++', '[0-9]++'), $route) . '~i', $root, $parts) > 0) + { return (empty($callback) === true) ? true : exit(call_user_func_array($callback, array_slice($parts, 1))); } } From d43022567ad20743b3994accf38902648c710a03 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Tue, 16 Sep 2014 21:48:10 +0000 Subject: [PATCH 39/43] PHP 5.4 short array syntax --- README.md | 3 +- index.php | 165 ++++++++++++++++++++++++++---------------------------- 2 files changed, 80 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index abe36b15..62898608 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Or, if you only want to get one customer, then you would append the customer `id ##Requirements -- PHP 5.3+ & PDO +- PHP 5.4+ & PDO - SQLite / MySQL / PostgreSQL ##Installation @@ -199,6 +199,7 @@ Ajax-like requests will be minified, whereas normal browser requests will be hum - **1.6.0** ~~added support for PostgreSQL~~ - **1.7.0** ~~fixed PostgreSQL connection bug, other minor improvements~~ - **1.8.0** ~~fixed POST / PUT bug introduced in 1.5.0~~ +- **1.9.0** ~~updated to PHP 5.4 short array syntax~~ ##Credits diff --git a/index.php b/index.php index ad2845e7..7d291f18 100644 --- a/index.php +++ b/index.php @@ -1,15 +1,13 @@ **/ @@ -20,16 +18,12 @@ if ((empty($clients) !== true) && (in_array($_SERVER['REMOTE_ADDR'], (array) $clients) !== true)) { - $result = ArrestDB::$FORBIDDEN; - - exit(ArrestDB::Reply($result)); + exit(ArrestDB::Reply(ArrestDB::$HTTP[403])); } else if (ArrestDB::Query($dsn) === false) { - $result = ArrestDB::$SERVICE_UNAVAILABLE; - - exit(ArrestDB::Reply($result)); + exit(ArrestDB::Reply(ArrestDB::$HTTP[503])); } if (array_key_exists('_method', $_GET) === true) @@ -75,12 +69,12 @@ if ($result === false) { - $result = ArrestDB::$NOT_FOUND; + $result = ArrestDB::$HTTP[404]; } else if (empty($result) === true) { - $result = ArrestDB::$NO_CONTENT; + $result = ArrestDB::$HTTP[204]; } return ArrestDB::Reply($result); @@ -126,12 +120,12 @@ if ($result === false) { - $result = ArrestDB::$NOT_FOUND; + $result = ArrestDB::$HTTP[404]; } else if (empty($result) === true) { - $result = ArrestDB::$NO_CONTENT; + $result = ArrestDB::$HTTP[204]; } else if (isset($id) === true) @@ -154,23 +148,23 @@ if ($result === false) { - $result = ArrestDB::$NOT_FOUND; + $result = ArrestDB::$HTTP[404]; } else if (empty($result) === true) { - $result = ArrestDB::$NO_CONTENT; + $result = ArrestDB::$HTTP[204]; } else { - $result = ArrestDB::$OK; + $result = ArrestDB::$HTTP[200]; } return ArrestDB::Reply($result); }); -if (in_array($http = strtoupper($_SERVER['REQUEST_METHOD']), array('POST', 'PUT')) === true) +if (in_array($http = strtoupper($_SERVER['REQUEST_METHOD']), ['POST', 'PUT']) === true) { if (preg_match('~^\x78[\x01\x5E\x9C\xDA]~', $data = file_get_contents('php://input')) > 0) { @@ -184,7 +178,7 @@ $GLOBALS['_' . $http] = json_decode($data, true); } - else if ((strncasecmp($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded', 33) === 0) && (strcasecmp($_SERVER['REQUEST_METHOD'], 'PUT') === 0)) + else if ((strncasecmp($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded', 33) === 0) && (strncasecmp($_SERVER['REQUEST_METHOD'], 'PUT', 3) === 0)) { parse_str($data, $GLOBALS['_' . $http]); } @@ -192,7 +186,7 @@ if ((isset($GLOBALS['_' . $http]) !== true) || (is_array($GLOBALS['_' . $http]) !== true)) { - $GLOBALS['_' . $http] = array(); + $GLOBALS['_' . $http] = []; } unset($data); @@ -202,21 +196,21 @@ { if (empty($_POST) === true) { - $result = ArrestDB::$NO_CONTENT; + $result = ArrestDB::$HTTP[204]; } else if (is_array($_POST) === true) { - $queries = array(); + $queries = []; if (count($_POST) == count($_POST, COUNT_RECURSIVE)) { - $_POST = array($_POST); + $_POST = [$_POST]; } foreach ($_POST as $row) { - $data = array(); + $data = []; foreach ($row as $key => $value) { @@ -260,12 +254,12 @@ if ($result === false) { - $result = ArrestDB::$CONFLICT; + $result = ArrestDB::$HTTP[409]; } else { - $result = ArrestDB::$CREATED; + $result = ArrestDB::$HTTP[201]; } } @@ -276,12 +270,12 @@ { if (empty($GLOBALS['_PUT']) === true) { - $result = ArrestDB::$NO_CONTENT; + $result = ArrestDB::$HTTP[204]; } else if (is_array($GLOBALS['_PUT']) === true) { - $data = array(); + $data = []; foreach ($GLOBALS['_PUT'] as $key => $value) { @@ -298,80 +292,77 @@ if ($result === false) { - $result = ArrestDB::$CONFLICT; + $result = ArrestDB::$HTTP[409]; } else { - $result = ArrestDB::$OK; + $result = ArrestDB::$HTTP[200]; } } return ArrestDB::Reply($result); }); -$result = ArrestDB::$BAD_REQUEST; - -exit(ArrestDB::Reply($result)); +exit(ArrestDB::Reply(ArrestDB::$HTTP[400])); class ArrestDB { + public static $HTTP = [ + 200 => [ + 'success' => [ + 'code' => 200, + 'status' => 'OK', + ], + ], + 201 => [ + 'success' => [ + 'code' => 201, + 'status' => 'Created', + ], + ], + 204 => [ + 'error' => [ + 'code' => 204, + 'status' => 'No Content', + ], + ], + 400 => [ + 'error' => [ + 'code' => 400, + 'status' => 'Bad Request', + ], + ], + 403 => [ + 'error' => [ + 'code' => 403, + 'status' => 'Forbidden', + ], + ], + 404 => [ + 'error' => [ + 'code' => 404, + 'status' => 'Not Found', + ], + ], + 409 => [ + 'error' => [ + 'code' => 409, + 'status' => 'Conflict', + ], + ], + 503 => [ + 'error' => [ + 'code' => 503, + 'status' => 'Service Unavailable', + ], + ], + ]; - // Result Messages - public static $OK = array( - 'success' => array( - 'code' => 200, - 'status' => 'OK', - ), - ); - public static $CREATED = array( - 'success' => array( - 'code' => 201, - 'status' => 'Created', - ), - ); - public static $NO_CONTENT = array( - 'error' => array( - 'code' => 204, - 'status' => 'No Content', - ), - ); - public static $BAD_REQUEST = array( - 'error' => array( - 'code' => 400, - 'status' => 'Bad Request', - ), - ); - public static $FORBIDDEN = array( - 'error' => array( - 'code' => 403, - 'status' => 'Forbidden' - ) - ); - public static $NOT_FOUND = array( - 'error' => array( - 'code' => 404, - 'status' => 'Not Found', - ), - ); - public static $CONFLICT = array( - 'error' => array( - 'code' => 409, - 'status' => 'Conflict', - ), - ); - public static $SERVICE_UNAVAILABLE = array( - 'error' => array( - 'code' => 503, - 'status' => 'Service Unavailable', - ), - ); - - // Operating Functions public static function Query($query = null) { static $db = null; - static $result = array(); + static $result = []; try { @@ -525,7 +516,7 @@ public static function Query($query = null) public static function Reply($data) { $bitmask = 0; - $options = array('UNESCAPED_SLASHES', 'UNESCAPED_UNICODE'); + $options = ['UNESCAPED_SLASHES', 'UNESCAPED_UNICODE']; if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) === true) { @@ -576,7 +567,7 @@ public static function Serve($on = null, $route = null, $callback = null) $root = preg_replace('~/++~', '/', substr($_SERVER['PHP_SELF'], strlen($_SERVER['SCRIPT_NAME'])) . '/'); } - if (preg_match('~^' . str_replace(array('#any', '#num'), array('[^/]++', '[0-9]++'), $route) . '~i', $root, $parts) > 0) + if (preg_match('~^' . str_replace(['#any', '#num'], ['[^/]++', '[0-9]++'], $route) . '~i', $root, $parts) > 0) { return (empty($callback) === true) ? true : exit(call_user_func_array($callback, array_slice($parts, 1))); } From fc92b0aa5a56fd6f97727897127150f893a94e7f Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Tue, 16 Sep 2014 21:58:06 +0000 Subject: [PATCH 40/43] updated README --- README.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 62898608..ef4a8666 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,11 @@ ArrestDB is a "plug-n-play" RESTful API for SQLite, MySQL and PostgreSQL databas ArrestDB provides a REST API that maps directly to your database stucture with no configuation. Lets suppose you have set up ArrestDB at `http://api.example.com/` and a table named `customers`. - To get a list of customers you would simply need to do: GET http://api.example.com/customers/ -Where `customers` is the table name. As a response you would get a JSON formatted list of customers. +As a response, you would get a JSON formatted list of customers. Or, if you only want to get one customer, then you would append the customer `id` to the URL: @@ -101,17 +100,16 @@ Please note that `GET` calls accept the following query string variables: Additionally, `POST` and `PUT` requests accept JSON-encoded and/or zlib-compressed payloads. > `POST` and `PUT` requests are only able to parse data encoded in `application/x-www-form-urlencoded`. - > Support for `multipart/form-data` payloads will be added in the future. -If your REST client does not support certain requests, you can use the `X-HTTP-Method-Override` header: +If your client does not support certain methods, you can use the `X-HTTP-Method-Override` header: - `PUT` = `POST` + `X-HTTP-Method-Override: PUT` - `DELETE` = `GET` + `X-HTTP-Method-Override: DELETE` Alternatively, you can also override the HTTP method by using the `_method` query string parameter. -As of version 1.5.0, it's also possible to atomically `INSERT` a batch of records by POSTing an array of arrays. +Since 1.5.0, it's also possible to atomically `INSERT` a batch of records by POSTing an array of arrays. ##Responses @@ -207,6 +205,4 @@ ArrestDB is a complete rewrite of [Arrest-MySQL](https://github.com/gilbitron/Ar ##License (MIT) -Copyright (c) 2014 Alix Axel (alix.axel@gmail.com). - -[![Donate Bitcoins](https://coinbase.com/assets/buttons/donation_small.png)](https://coinbase.com/checkouts/89e8aa2876ba534f9db3fafa8be4e5fa) +Copyright (c) 2014 Alix Axel (alix.axel+github@gmail.com). \ No newline at end of file From 4267e3238824409dd46313bce4e39c39de2a9403 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Tue, 16 Sep 2014 22:01:04 +0000 Subject: [PATCH 41/43] updated README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ef4a8666..a1ea391e 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ ArrestDB is a "plug-n-play" RESTful API for SQLite, MySQL and PostgreSQL databas ArrestDB provides a REST API that maps directly to your database stucture with no configuation. -Lets suppose you have set up ArrestDB at `http://api.example.com/` and a table named `customers`. -To get a list of customers you would simply need to do: +Lets suppose you have set up ArrestDB at `http://api.example.com/` and that your database has a table named `customers`. +To get a list of all the customers in the table you would simply need to do: GET http://api.example.com/customers/ From c11cc00b665b029c65cdb84891b5f6f6dbc86047 Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Sun, 23 Jun 2019 19:21:34 +0100 Subject: [PATCH 42/43] Create FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..28dca6d7 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: https://paypal.me/alixaxel From a3b89271dec3175a06d2c902867793626e25d42c Mon Sep 17 00:00:00 2001 From: Alix Axel Date: Sun, 5 Jan 2020 18:10:00 +0000 Subject: [PATCH 43/43] Update FUNDING.yml --- .github/FUNDING.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 28dca6d7..0f844cab 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ -custom: https://paypal.me/alixaxel +github: alixaxel +custom: https://paypal.me/alixaxel/10USD