commit efd6d39baa3aa5c99c7b055de7e6cc3ec6b3d6b2 Author: Skylar Ittner Date: Mon Oct 3 12:23:42 2016 -0600 Create terrain API diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..df108cf --- /dev/null +++ b/composer.json @@ -0,0 +1,13 @@ +{ + "name": "vendor/g-i-s-a-p-i", + "description": "Description of project GIS API.", + "authors": [ + { + "name": "skylar", + "email": "your@email.here" + } + ], + "require": { + "catfan/medoo": "^1.1" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..44311ef --- /dev/null +++ b/composer.lock @@ -0,0 +1,74 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "14739f64042726cd1da2fc3588dd655d", + "content-hash": "a04c7422846ed42285920bcc09babfd3", + "packages": [ + { + "name": "catfan/medoo", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/catfan/Medoo.git", + "reference": "2601ffd53971866695c544c955d580d3d36b3848" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/catfan/Medoo/zipball/2601ffd53971866695c544c955d580d3d36b3848", + "reference": "2601ffd53971866695c544c955d580d3d36b3848", + "shasum": "" + }, + "require": { + "ext-pdo": "*", + "php": ">=5.1" + }, + "suggest": { + "ext-pdo_dblib": "For MSSQL or Sybase databases on Linux/UNIX platform", + "ext-pdo_mysql": "For MySQL or MariaDB databases", + "ext-pdo_oci": "For Oracle databases", + "ext-pdo_pqsql": "For PostgreSQL databases", + "ext-pdo_sqlite": "For SQLite databases", + "ext-pdo_sqlsrv": "For MSSQL databases on Windows platform" + }, + "type": "framework", + "autoload": { + "files": [ + "medoo.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Angel Lai", + "email": "angel@catfan.me" + } + ], + "description": "The Lightest PHP database framework to accelerate development", + "homepage": "http://medoo.in", + "keywords": [ + "database", + "lightweight", + "mssql", + "mysql", + "php framework", + "sql", + "sqlite" + ], + "time": "2016-09-05 07:18:35" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/index.php b/index.php new file mode 100644 index 0000000..a14d38f --- /dev/null +++ b/index.php @@ -0,0 +1,20 @@ + + + + + + GIS Server + + +

TerranQuest GIS Server

+

+ This server handles the global terrain data for TerranQuest. + To request an API key for your server, go here: + https://netsyms.com/#contact +

+ + diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..76f6f91 --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,7 @@ +include.path=${php.global.include.path} +php.version=PHP_56 +source.encoding=UTF-8 +src.dir=. +tags.asp=false +tags.short=false +web.root=. diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..ab194dd --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,9 @@ + + + org.netbeans.modules.php.project + + + GIS API + + + diff --git a/required.php b/required.php new file mode 100644 index 0000000..dcc5620 --- /dev/null +++ b/required.php @@ -0,0 +1,67 @@ + DB_TYPE, + 'database_name' => DB_NAME, + 'server' => DB_SERVER, + 'username' => DB_USER, + 'password' => DB_PASS, + 'charset' => DB_CHARSET + ]); +} catch (Exception $ex) { + header('HTTP/1.1 500 Internal Server Error'); + sendError('Database error. Try again later.', true); +} + +if (!DEBUG) { + error_reporting(0); +} else { + error_reporting(E_ALL); + ini_set('display_errors', 'On'); +} + + +$VARS; +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $VARS = $_POST; + define("GET", false); +} else { + $VARS = $_GET; + define("GET", true); +} + +/** + * Checks if a string or whatever is empty. + * @param $str The thingy to check + * @return boolean True if it's empty or whatever. + */ +function is_empty($str) { + return (!isset($str) || $str == '' || $str == null); +} + +if (is_empty($VARS['key'])) { + sendError('Please supply an API key (?key=abc)', true); +} + +if (!$database->has('apikeys', ["AND" => ['apikey' => $VARS['key'], "active" => 1]])) { + sendError('Invalid or inactive API key.', true); +} \ No newline at end of file diff --git a/response.php b/response.php new file mode 100644 index 0000000..ec26082 --- /dev/null +++ b/response.php @@ -0,0 +1,27 @@ +select('terrain', 'type', ["AND" => ["latitude" => $lat, "longitude" => $long]])[0]; + +$out = ["status" => "OK", "type" => $terrainid]; +echo json_encode($out); \ No newline at end of file diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 0000000..2f24043 --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,7 @@ + The Lightest PHP database framework to accelerate development + +## Main Features + +* **Lightweight** - 20KB around with only one file. + +* **Easy** - Extremely easy to learn and use, friendly construction. + +* **Powerful** - Support various common and complex SQL queries. + +* **Compatible** - Support various SQL database, including MySQL, MSSQL, SQLite, MariaDB, Sybase, Oracle, PostgreSQL and more. + +* **Security** - Prevent SQL injection. + +* **Free** - Under MIT license, you can use it anywhere if you want. + +## Get Started + +### Install via composer + +Add Medoo to composer.json configuration file. +``` +$ composer require catfan/Medoo +``` + +And update the composer +``` +$ composer update +``` + +```php +// If you installed via composer, just use this code to requrie autoloader on the top of your projects. +require 'vendor/autoload.php'; + +// Or if you just download the medoo.php into directory, require it with the correct path. +require_once 'medoo.php'; + +// Initialize +$database = new medoo([ + 'database_type' => 'mysql', + 'database_name' => 'name', + 'server' => 'localhost', + 'username' => 'your_username', + 'password' => 'your_password', + 'charset' => 'utf8' +]); + +// Enjoy +$database->insert('account', [ + 'user_name' => 'foo', + 'email' => 'foo@bar.com', + 'age' => 25, + 'lang' => ['en', 'fr', 'jp', 'cn'] +]); +``` + +## Contribution Guides + +For most of time, Medoo is using develop branch for adding feature and fixing bug, and the branch will be merged into master branch while releasing a public version. For contribution, submit your code to the develop branch, and start a pull request into it. + +On develop branch, each commits are started with `[fix]`, `[feature]` or `[update]` tag to indicate the change. + +Keep it simple and keep it clear. + +## License + +Medoo is under the MIT license. + +## Links + +* Official website: [http://medoo.in](http://medoo.in) + +* Documentation: [http://medoo.in/doc](http://medoo.in/doc) \ No newline at end of file diff --git a/vendor/catfan/medoo/composer.json b/vendor/catfan/medoo/composer.json new file mode 100644 index 0000000..b9569a8 --- /dev/null +++ b/vendor/catfan/medoo/composer.json @@ -0,0 +1,26 @@ +{ + "name": "catfan/medoo", + "type": "framework", + "description": "The Lightest PHP database framework to accelerate development", + "keywords": ["database", "lightweight", "PHP framework", "SQL", "MySQL", "MSSQL", "SQLite"], + "homepage": "http://medoo.in", + "license": "MIT", + "authors": [ + {"name": "Angel Lai", "email": "angel@catfan.me"} + ], + "require": { + "php": ">=5.1", + "ext-pdo": "*" + }, + "suggest": { + "ext-pdo_mysql": "For MySQL or MariaDB databases", + "ext-pdo_sqlsrv": "For MSSQL databases on Windows platform", + "ext-pdo_dblib": "For MSSQL or Sybase databases on Linux/UNIX platform", + "ext-pdo_oci": "For Oracle databases", + "ext-pdo_pqsql": "For PostgreSQL databases", + "ext-pdo_sqlite": "For SQLite databases" + }, + "autoload": { + "files": ["medoo.php"] + } +} \ No newline at end of file diff --git a/vendor/catfan/medoo/medoo.php b/vendor/catfan/medoo/medoo.php new file mode 100644 index 0000000..a57941f --- /dev/null +++ b/vendor/catfan/medoo/medoo.php @@ -0,0 +1,1123 @@ + $value) + { + $this->$option = $value; + } + } + else + { + return false; + } + + if ( + isset($this->port) && + is_int($this->port * 1) + ) + { + $port = $this->port; + } + + $type = strtolower($this->database_type); + $is_port = isset($port); + + if (isset($options[ 'prefix' ])) + { + $this->prefix = $options[ 'prefix' ]; + } + + switch ($type) + { + case 'mariadb': + $type = 'mysql'; + + case 'mysql': + if ($this->socket) + { + $dsn = $type . ':unix_socket=' . $this->socket . ';dbname=' . $this->database_name; + } + else + { + $dsn = $type . ':host=' . $this->server . ($is_port ? ';port=' . $port : '') . ';dbname=' . $this->database_name; + } + + // Make MySQL using standard quoted identifier + $commands[] = 'SET SQL_MODE=ANSI_QUOTES'; + break; + + case 'pgsql': + $dsn = $type . ':host=' . $this->server . ($is_port ? ';port=' . $port : '') . ';dbname=' . $this->database_name; + break; + + case 'sybase': + $dsn = 'dblib:host=' . $this->server . ($is_port ? ':' . $port : '') . ';dbname=' . $this->database_name; + break; + + case 'oracle': + $dbname = $this->server ? + '//' . $this->server . ($is_port ? ':' . $port : ':1521') . '/' . $this->database_name : + $this->database_name; + + $dsn = 'oci:dbname=' . $dbname . ($this->charset ? ';charset=' . $this->charset : ''); + break; + + case 'mssql': + $dsn = strstr(PHP_OS, 'WIN') ? + 'sqlsrv:server=' . $this->server . ($is_port ? ',' . $port : '') . ';database=' . $this->database_name : + 'dblib:host=' . $this->server . ($is_port ? ':' . $port : '') . ';dbname=' . $this->database_name; + + // Keep MSSQL QUOTED_IDENTIFIER is ON for standard quoting + $commands[] = 'SET QUOTED_IDENTIFIER ON'; + break; + + case 'sqlite': + $dsn = $type . ':' . $this->database_file; + $this->username = null; + $this->password = null; + break; + } + + if ( + in_array($type, array('mariadb', 'mysql', 'pgsql', 'sybase', 'mssql')) && + $this->charset + ) + { + $commands[] = "SET NAMES '" . $this->charset . "'"; + } + + $this->pdo = new PDO( + $dsn, + $this->username, + $this->password, + $this->option + ); + + foreach ($commands as $value) + { + $this->pdo->exec($value); + } + } + catch (PDOException $e) { + throw new Exception($e->getMessage()); + } + } + + public function query($query) + { + if ($this->debug_mode) + { + echo $query; + + $this->debug_mode = false; + + return false; + } + + $this->logs[] = $query; + + return $this->pdo->query($query); + } + + public function exec($query) + { + if ($this->debug_mode) + { + echo $query; + + $this->debug_mode = false; + + return false; + } + + $this->logs[] = $query; + + return $this->pdo->exec($query); + } + + public function quote($string) + { + return $this->pdo->quote($string); + } + + protected function table_quote($table) + { + return '"' . $this->prefix . $table . '"'; + } + + protected function column_quote($string) + { + preg_match('/(\(JSON\)\s*|^#)?([a-zA-Z0-9_]*)\.([a-zA-Z0-9_]*)/', $string, $column_match); + + if (isset($column_match[ 2 ], $column_match[ 3 ])) + { + return '"' . $this->prefix . $column_match[ 2 ] . '"."' . $column_match[ 3 ] . '"'; + } + + return '"' . $string . '"'; + } + + protected function column_push(&$columns) + { + if ($columns == '*') + { + return $columns; + } + + if (is_string($columns)) + { + $columns = array($columns); + } + + $stack = array(); + + foreach ($columns as $key => $value) + { + if (is_array($value)) + { + $stack[] = $this->column_push($value); + } + else + { + preg_match('/([a-zA-Z0-9_\-\.]*)\s*\(([a-zA-Z0-9_\-]*)\)/i', $value, $match); + + if (isset($match[ 1 ], $match[ 2 ])) + { + $stack[] = $this->column_quote( $match[ 1 ] ) . ' AS ' . $this->column_quote( $match[ 2 ] ); + + $columns[ $key ] = $match[ 2 ]; + } + else + { + $stack[] = $this->column_quote( $value ); + } + } + } + + return implode($stack, ','); + } + + protected function array_quote($array) + { + $temp = array(); + + foreach ($array as $value) + { + $temp[] = is_int($value) ? $value : $this->pdo->quote($value); + } + + return implode($temp, ','); + } + + protected function inner_conjunct($data, $conjunctor, $outer_conjunctor) + { + $haystack = array(); + + foreach ($data as $value) + { + $haystack[] = '(' . $this->data_implode($value, $conjunctor) . ')'; + } + + return implode($outer_conjunctor . ' ', $haystack); + } + + protected function fn_quote($column, $string) + { + return (strpos($column, '#') === 0 && preg_match('/^[A-Z0-9\_]*\([^)]*\)$/', $string)) ? + + $string : + + $this->quote($string); + } + + protected function data_implode($data, $conjunctor, $outer_conjunctor = null) + { + $wheres = array(); + + foreach ($data as $key => $value) + { + $type = gettype($value); + + if ( + preg_match("/^(AND|OR)(\s+#.*)?$/i", $key, $relation_match) && + $type == 'array' + ) + { + $wheres[] = 0 !== count(array_diff_key($value, array_keys(array_keys($value)))) ? + '(' . $this->data_implode($value, ' ' . $relation_match[ 1 ]) . ')' : + '(' . $this->inner_conjunct($value, ' ' . $relation_match[ 1 ], $conjunctor) . ')'; + } + else + { + preg_match('/(#?)([\w\.\-]+)(\[(\>|\>\=|\<|\<\=|\!|\<\>|\>\<|\!?~)\])?/i', $key, $match); + $column = $this->column_quote($match[ 2 ]); + + if (isset($match[ 4 ])) + { + $operator = $match[ 4 ]; + + if ($operator == '!') + { + switch ($type) + { + case 'NULL': + $wheres[] = $column . ' IS NOT NULL'; + break; + + case 'array': + $wheres[] = $column . ' NOT IN (' . $this->array_quote($value) . ')'; + break; + + case 'integer': + case 'double': + $wheres[] = $column . ' != ' . $value; + break; + + case 'boolean': + $wheres[] = $column . ' != ' . ($value ? '1' : '0'); + break; + + case 'string': + $wheres[] = $column . ' != ' . $this->fn_quote($key, $value); + break; + } + } + + if ($operator == '<>' || $operator == '><') + { + if ($type == 'array') + { + if ($operator == '><') + { + $column .= ' NOT'; + } + + if (is_numeric($value[ 0 ]) && is_numeric($value[ 1 ])) + { + $wheres[] = '(' . $column . ' BETWEEN ' . $value[ 0 ] . ' AND ' . $value[ 1 ] . ')'; + } + else + { + $wheres[] = '(' . $column . ' BETWEEN ' . $this->quote($value[ 0 ]) . ' AND ' . $this->quote($value[ 1 ]) . ')'; + } + } + } + + if ($operator == '~' || $operator == '!~') + { + if ($type != 'array') + { + $value = array($value); + } + + $like_clauses = array(); + + foreach ($value as $item) + { + $item = strval($item); + + if (preg_match('/^(?!(%|\[|_])).+(?fn_quote($key, $item); + } + + $wheres[] = implode(' OR ', $like_clauses); + } + + if (in_array($operator, array('>', '>=', '<', '<='))) + { + $condition = $column . ' ' . $operator . ' '; + + if (is_numeric($value)) + { + $condition .= $value; + } + elseif (strpos($key, '#') === 0) + { + $condition .= $this->fn_quote($key, $value); + } + else + { + $condition .= $this->quote($value); + } + + $wheres[] = $condition; + } + } + else + { + switch ($type) + { + case 'NULL': + $wheres[] = $column . ' IS NULL'; + break; + + case 'array': + $wheres[] = $column . ' IN (' . $this->array_quote($value) . ')'; + break; + + case 'integer': + case 'double': + $wheres[] = $column . ' = ' . $value; + break; + + case 'boolean': + $wheres[] = $column . ' = ' . ($value ? '1' : '0'); + break; + + case 'string': + $wheres[] = $column . ' = ' . $this->fn_quote($key, $value); + break; + } + } + } + } + + return implode($conjunctor . ' ', $wheres); + } + + protected function where_clause($where) + { + $where_clause = ''; + + if (is_array($where)) + { + $where_keys = array_keys($where); + $where_AND = preg_grep("/^AND\s*#?$/i", $where_keys); + $where_OR = preg_grep("/^OR\s*#?$/i", $where_keys); + + $single_condition = array_diff_key($where, array_flip( + array('AND', 'OR', 'GROUP', 'ORDER', 'HAVING', 'LIMIT', 'LIKE', 'MATCH') + )); + + if ($single_condition != array()) + { + $condition = $this->data_implode($single_condition, ''); + + if ($condition != '') + { + $where_clause = ' WHERE ' . $condition; + } + } + + if (!empty($where_AND)) + { + $value = array_values($where_AND); + $where_clause = ' WHERE ' . $this->data_implode($where[ $value[ 0 ] ], ' AND'); + } + + if (!empty($where_OR)) + { + $value = array_values($where_OR); + $where_clause = ' WHERE ' . $this->data_implode($where[ $value[ 0 ] ], ' OR'); + } + + if (isset($where[ 'MATCH' ])) + { + $MATCH = $where[ 'MATCH' ]; + + if (is_array($MATCH) && isset($MATCH[ 'columns' ], $MATCH[ 'keyword' ])) + { + $where_clause .= ($where_clause != '' ? ' AND ' : ' WHERE ') . ' MATCH ("' . str_replace('.', '"."', implode($MATCH[ 'columns' ], '", "')) . '") AGAINST (' . $this->quote($MATCH[ 'keyword' ]) . ')'; + } + } + + if (isset($where[ 'GROUP' ])) + { + $where_clause .= ' GROUP BY ' . $this->column_quote($where[ 'GROUP' ]); + + if (isset($where[ 'HAVING' ])) + { + $where_clause .= ' HAVING ' . $this->data_implode($where[ 'HAVING' ], ' AND'); + } + } + + if (isset($where[ 'ORDER' ])) + { + $ORDER = $where[ 'ORDER' ]; + + if (is_array($ORDER)) + { + $stack = array(); + + foreach ($ORDER as $column => $value) + { + if (is_array($value)) + { + $stack[] = 'FIELD(' . $this->column_quote($column) . ', ' . $this->array_quote($value) . ')'; + } + else if ($value === 'ASC' || $value === 'DESC') + { + $stack[] = $this->column_quote($column) . ' ' . $value; + } + else if (is_int($column)) + { + $stack[] = $this->column_quote($value); + } + } + + $where_clause .= ' ORDER BY ' . implode($stack, ','); + } + else + { + $where_clause .= ' ORDER BY ' . $this->column_quote($ORDER); + } + } + + if (isset($where[ 'LIMIT' ])) + { + $LIMIT = $where[ 'LIMIT' ]; + + if (is_numeric($LIMIT)) + { + $where_clause .= ' LIMIT ' . $LIMIT; + } + + if ( + is_array($LIMIT) && + is_numeric($LIMIT[ 0 ]) && + is_numeric($LIMIT[ 1 ]) + ) + { + if ($this->database_type === 'pgsql') + { + $where_clause .= ' OFFSET ' . $LIMIT[ 0 ] . ' LIMIT ' . $LIMIT[ 1 ]; + } + else + { + $where_clause .= ' LIMIT ' . $LIMIT[ 0 ] . ',' . $LIMIT[ 1 ]; + } + } + } + } + else + { + if ($where != null) + { + $where_clause .= ' ' . $where; + } + } + + return $where_clause; + } + + protected function select_context($table, $join, &$columns = null, $where = null, $column_fn = null) + { + preg_match('/([a-zA-Z0-9_\-]*)\s*\(([a-zA-Z0-9_\-]*)\)/i', $table, $table_match); + + if (isset($table_match[ 1 ], $table_match[ 2 ])) + { + $table = $this->table_quote($table_match[ 1 ]); + + $table_query = $this->table_quote($table_match[ 1 ]) . ' AS ' . $this->table_quote($table_match[ 2 ]); + } + else + { + $table = $this->table_quote($table); + + $table_query = $table; + } + + $join_key = is_array($join) ? array_keys($join) : null; + + if ( + isset($join_key[ 0 ]) && + strpos($join_key[ 0 ], '[') === 0 + ) + { + $table_join = array(); + + $join_array = array( + '>' => 'LEFT', + '<' => 'RIGHT', + '<>' => 'FULL', + '><' => 'INNER' + ); + + foreach($join as $sub_table => $relation) + { + preg_match('/(\[(\<|\>|\>\<|\<\>)\])?([a-zA-Z0-9_\-]*)\s?(\(([a-zA-Z0-9_\-]*)\))?/', $sub_table, $match); + + if ($match[ 2 ] != '' && $match[ 3 ] != '') + { + if (is_string($relation)) + { + $relation = 'USING ("' . $relation . '")'; + } + + if (is_array($relation)) + { + // For ['column1', 'column2'] + if (isset($relation[ 0 ])) + { + $relation = 'USING ("' . implode($relation, '", "') . '")'; + } + else + { + $joins = array(); + + foreach ($relation as $key => $value) + { + $joins[] = ( + strpos($key, '.') > 0 ? + // For ['tableB.column' => 'column'] + $this->column_quote($key) : + + // For ['column1' => 'column2'] + $table . '."' . $key . '"' + ) . + ' = ' . + $this->table_quote(isset($match[ 5 ]) ? $match[ 5 ] : $match[ 3 ]) . '."' . $value . '"'; + } + + $relation = 'ON ' . implode($joins, ' AND '); + } + } + + $table_name = $this->table_quote($match[ 3 ]) . ' '; + + if (isset($match[ 5 ])) + { + $table_name .= 'AS ' . $this->table_quote($match[ 5 ]) . ' '; + } + + $table_join[] = $join_array[ $match[ 2 ] ] . ' JOIN ' . $table_name . $relation; + } + } + + $table_query .= ' ' . implode($table_join, ' '); + } + else + { + if (is_null($columns)) + { + if (is_null($where)) + { + if ( + is_array($join) && + isset($column_fn) + ) + { + $where = $join; + $columns = null; + } + else + { + $where = null; + $columns = $join; + } + } + else + { + $where = $join; + $columns = null; + } + } + else + { + $where = $columns; + $columns = $join; + } + } + + if (isset($column_fn)) + { + if ($column_fn == 1) + { + $column = '1'; + + if (is_null($where)) + { + $where = $columns; + } + } + else + { + if (empty($columns)) + { + $columns = '*'; + $where = $join; + } + + $column = $column_fn . '(' . $this->column_push($columns) . ')'; + } + } + else + { + $column = $this->column_push($columns); + } + + return 'SELECT ' . $column . ' FROM ' . $table_query . $this->where_clause($where); + } + + protected function data_map($index, $key, $value, $data, &$stack) + { + if (is_array($value)) + { + $sub_stack = array(); + + foreach ($value as $sub_key => $sub_value) + { + if (is_array($sub_value)) + { + $current_stack = $stack[ $index ][ $key ]; + + $this->data_map(false, $sub_key, $sub_value, $data, $current_stack); + + $stack[ $index ][ $key ][ $sub_key ] = $current_stack[ 0 ][ $sub_key ]; + } + else + { + $this->data_map(false, preg_replace('/^[\w]*\./i', "", $sub_value), $sub_key, $data, $sub_stack); + + $stack[ $index ][ $key ] = $sub_stack; + } + } + } + else + { + if ($index !== false) + { + $stack[ $index ][ $value ] = $data[ $value ]; + } + else + { + if (preg_match('/[a-zA-Z0-9_\-\.]*\s*\(([a-zA-Z0-9_\-]*)\)/i', $key, $key_match)) + { + $key = $key_match[ 1 ]; + } + + $stack[ $key ] = $data[ $key ]; + } + } + } + + public function select($table, $join, $columns = null, $where = null) + { + $column = $where == null ? $join : $columns; + + $is_single_column = (is_string($column) && $column !== '*'); + + $query = $this->query($this->select_context($table, $join, $columns, $where)); + + $stack = array(); + + $index = 0; + + if (!$query) + { + return false; + } + + if ($columns === '*') + { + return $query->fetchAll(PDO::FETCH_ASSOC); + } + + if ($is_single_column) + { + return $query->fetchAll(PDO::FETCH_COLUMN); + } + + while ($row = $query->fetch(PDO::FETCH_ASSOC)) + { + foreach ($columns as $key => $value) + { + if (is_array($value)) + { + $this->data_map($index, $key, $value, $row, $stack); + } + else + { + $this->data_map($index, $key, preg_replace('/^[\w]*\./i', "", $value), $row, $stack); + } + } + + $index++; + } + + return $stack; + } + + public function insert($table, $datas) + { + $lastId = array(); + + // Check indexed or associative array + if (!isset($datas[ 0 ])) + { + $datas = array($datas); + } + + foreach ($datas as $data) + { + $values = array(); + $columns = array(); + + foreach ($data as $key => $value) + { + $columns[] = $this->column_quote(preg_replace("/^(\(JSON\)\s*|#)/i", "", $key)); + + switch (gettype($value)) + { + case 'NULL': + $values[] = 'NULL'; + break; + + case 'array': + preg_match("/\(JSON\)\s*([\w]+)/i", $key, $column_match); + + $values[] = isset($column_match[ 0 ]) ? + $this->quote(json_encode($value)) : + $this->quote(serialize($value)); + break; + + case 'boolean': + $values[] = ($value ? '1' : '0'); + break; + + case 'integer': + case 'double': + case 'string': + $values[] = $this->fn_quote($key, $value); + break; + } + } + + $this->exec('INSERT INTO ' . $this->table_quote($table) . ' (' . implode(', ', $columns) . ') VALUES (' . implode($values, ', ') . ')'); + + $lastId[] = $this->pdo->lastInsertId(); + } + + return count($lastId) > 1 ? $lastId : $lastId[ 0 ]; + } + + public function update($table, $data, $where = null) + { + $fields = array(); + + foreach ($data as $key => $value) + { + preg_match('/([\w]+)(\[(\+|\-|\*|\/)\])?/i', $key, $match); + + if (isset($match[ 3 ])) + { + if (is_numeric($value)) + { + $fields[] = $this->column_quote($match[ 1 ]) . ' = ' . $this->column_quote($match[ 1 ]) . ' ' . $match[ 3 ] . ' ' . $value; + } + } + else + { + $column = $this->column_quote(preg_replace("/^(\(JSON\)\s*|#)/i", "", $key)); + + switch (gettype($value)) + { + case 'NULL': + $fields[] = $column . ' = NULL'; + break; + + case 'array': + preg_match("/\(JSON\)\s*([\w]+)/i", $key, $column_match); + + $fields[] = $column . ' = ' . $this->quote( + isset($column_match[ 0 ]) ? json_encode($value) : serialize($value) + ); + break; + + case 'boolean': + $fields[] = $column . ' = ' . ($value ? '1' : '0'); + break; + + case 'integer': + case 'double': + case 'string': + $fields[] = $column . ' = ' . $this->fn_quote($key, $value); + break; + } + } + } + + return $this->exec('UPDATE ' . $this->table_quote($table) . ' SET ' . implode(', ', $fields) . $this->where_clause($where)); + } + + public function delete($table, $where) + { + return $this->exec('DELETE FROM ' . $this->table_quote($table) . $this->where_clause($where)); + } + + public function replace($table, $columns, $search = null, $replace = null, $where = null) + { + if (is_array($columns)) + { + $replace_query = array(); + + foreach ($columns as $column => $replacements) + { + foreach ($replacements as $replace_search => $replace_replacement) + { + $replace_query[] = $column . ' = REPLACE(' . $this->column_quote($column) . ', ' . $this->quote($replace_search) . ', ' . $this->quote($replace_replacement) . ')'; + } + } + + $replace_query = implode(', ', $replace_query); + $where = $search; + } + else + { + if (is_array($search)) + { + $replace_query = array(); + + foreach ($search as $replace_search => $replace_replacement) + { + $replace_query[] = $columns . ' = REPLACE(' . $this->column_quote($columns) . ', ' . $this->quote($replace_search) . ', ' . $this->quote($replace_replacement) . ')'; + } + + $replace_query = implode(', ', $replace_query); + $where = $replace; + } + else + { + $replace_query = $columns . ' = REPLACE(' . $this->column_quote($columns) . ', ' . $this->quote($search) . ', ' . $this->quote($replace) . ')'; + } + } + + return $this->exec('UPDATE ' . $this->table_quote($table) . ' SET ' . $replace_query . $this->where_clause($where)); + } + + public function get($table, $join = null, $columns = null, $where = null) + { + $column = $where == null ? $join : $columns; + + $is_single_column = (is_string($column) && $column !== '*'); + + $query = $this->query($this->select_context($table, $join, $columns, $where) . ' LIMIT 1'); + + if ($query) + { + $data = $query->fetchAll(PDO::FETCH_ASSOC); + + if (isset($data[ 0 ])) + { + if ($is_single_column) + { + return $data[ 0 ][ preg_replace('/^[\w]*\./i', "", $column) ]; + } + + if ($column === '*') + { + return $data[ 0 ]; + } + + $stack = array(); + + foreach ($columns as $key => $value) + { + if (is_array($value)) + { + $this->data_map(0, $key, $value, $data[ 0 ], $stack); + } + else + { + $this->data_map(0, $key, preg_replace('/^[\w]*\./i', "", $value), $data[ 0 ], $stack); + } + } + + return $stack[ 0 ]; + } + else + { + return false; + } + } + else + { + return false; + } + } + + public function has($table, $join, $where = null) + { + $column = null; + + $query = $this->query('SELECT EXISTS(' . $this->select_context($table, $join, $column, $where, 1) . ')'); + + if ($query) + { + return $query->fetchColumn() === '1'; + } + else + { + return false; + } + } + + public function count($table, $join = null, $column = null, $where = null) + { + $query = $this->query($this->select_context($table, $join, $column, $where, 'COUNT')); + + return $query ? 0 + $query->fetchColumn() : false; + } + + public function max($table, $join, $column = null, $where = null) + { + $query = $this->query($this->select_context($table, $join, $column, $where, 'MAX')); + + if ($query) + { + $max = $query->fetchColumn(); + + return is_numeric($max) ? $max + 0 : $max; + } + else + { + return false; + } + } + + public function min($table, $join, $column = null, $where = null) + { + $query = $this->query($this->select_context($table, $join, $column, $where, 'MIN')); + + if ($query) + { + $min = $query->fetchColumn(); + + return is_numeric($min) ? $min + 0 : $min; + } + else + { + return false; + } + } + + public function avg($table, $join, $column = null, $where = null) + { + $query = $this->query($this->select_context($table, $join, $column, $where, 'AVG')); + + return $query ? 0 + $query->fetchColumn() : false; + } + + public function sum($table, $join, $column = null, $where = null) + { + $query = $this->query($this->select_context($table, $join, $column, $where, 'SUM')); + + return $query ? 0 + $query->fetchColumn() : false; + } + + public function action($actions) + { + if (is_callable($actions)) + { + $this->pdo->beginTransaction(); + + $result = $actions($this); + + if ($result === false) + { + $this->pdo->rollBack(); + } + else + { + $this->pdo->commit(); + } + } + else + { + return false; + } + } + + public function debug() + { + $this->debug_mode = true; + + return $this; + } + + public function error() + { + return $this->pdo->errorInfo(); + } + + public function last_query() + { + return end($this->logs); + } + + public function log() + { + return $this->logs; + } + + public function info() + { + $output = array( + 'server' => 'SERVER_INFO', + 'driver' => 'DRIVER_NAME', + 'client' => 'CLIENT_VERSION', + 'version' => 'SERVER_VERSION', + 'connection' => 'CONNECTION_STATUS' + ); + + foreach ($output as $key => $value) + { + $output[ $key ] = $this->pdo->getAttribute(constant('PDO::ATTR_' . $value)); + } + + return $output; + } +} +?> \ No newline at end of file diff --git a/vendor/catfan/medoo/src/medoo-logo.png b/vendor/catfan/medoo/src/medoo-logo.png new file mode 100644 index 0000000..9148854 Binary files /dev/null and b/vendor/catfan/medoo/src/medoo-logo.png differ diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php new file mode 100644 index 0000000..ff6ecfb --- /dev/null +++ b/vendor/composer/ClassLoader.php @@ -0,0 +1,413 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see http://www.php-fig.org/psr/psr-0/ + * @see http://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + + private $classMapAuthoritative = false; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative) { + return false; + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if ($file === null && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if ($file === null) { + // Remember that this class does not exist. + return $this->classMap[$class] = false; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { + if (0 === strpos($class, $prefix)) { + foreach ($this->prefixDirsPsr4[$prefix] as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE new file mode 100644 index 0000000..1a28124 --- /dev/null +++ b/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) 2016 Nils Adermann, Jordi Boggiano + +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/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000..7a91153 --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,9 @@ + $vendorDir . '/catfan/medoo/medoo.php', +); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000..b7fc012 --- /dev/null +++ b/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ += 50600 && !defined('HHVM_VERSION'); + if ($useStaticLoader) { + require_once __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInitced6a9042f97dd8b342a978ca0b1aac0::getInitializer($loader)); + } else { + $map = require __DIR__ . '/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->register(true); + + if ($useStaticLoader) { + $includeFiles = Composer\Autoload\ComposerStaticInitced6a9042f97dd8b342a978ca0b1aac0::$files; + } else { + $includeFiles = require __DIR__ . '/autoload_files.php'; + } + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequireced6a9042f97dd8b342a978ca0b1aac0($fileIdentifier, $file); + } + + return $loader; + } +} + +function composerRequireced6a9042f97dd8b342a978ca0b1aac0($fileIdentifier, $file) +{ + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } +} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php new file mode 100644 index 0000000..7cf9e96 --- /dev/null +++ b/vendor/composer/autoload_static.php @@ -0,0 +1,19 @@ + __DIR__ . '/..' . '/catfan/medoo/medoo.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + + }, null, ClassLoader::class); + } +} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json new file mode 100644 index 0000000..f96b005 --- /dev/null +++ b/vendor/composer/installed.json @@ -0,0 +1,59 @@ +[ + { + "name": "catfan/medoo", + "version": "v1.1.3", + "version_normalized": "1.1.3.0", + "source": { + "type": "git", + "url": "https://github.com/catfan/Medoo.git", + "reference": "2601ffd53971866695c544c955d580d3d36b3848" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/catfan/Medoo/zipball/2601ffd53971866695c544c955d580d3d36b3848", + "reference": "2601ffd53971866695c544c955d580d3d36b3848", + "shasum": "" + }, + "require": { + "ext-pdo": "*", + "php": ">=5.1" + }, + "suggest": { + "ext-pdo_dblib": "For MSSQL or Sybase databases on Linux/UNIX platform", + "ext-pdo_mysql": "For MySQL or MariaDB databases", + "ext-pdo_oci": "For Oracle databases", + "ext-pdo_pqsql": "For PostgreSQL databases", + "ext-pdo_sqlite": "For SQLite databases", + "ext-pdo_sqlsrv": "For MSSQL databases on Windows platform" + }, + "time": "2016-09-05 07:18:35", + "type": "framework", + "installation-source": "dist", + "autoload": { + "files": [ + "medoo.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Angel Lai", + "email": "angel@catfan.me" + } + ], + "description": "The Lightest PHP database framework to accelerate development", + "homepage": "http://medoo.in", + "keywords": [ + "database", + "lightweight", + "mssql", + "mysql", + "php framework", + "sql", + "sqlite" + ] + } +]