From 7a618e7645d7ee514f9552066db6cf5c9c876186 Mon Sep 17 00:00:00 2001 From: Catfan Date: Sat, 15 Oct 2016 21:26:08 +0800 Subject: [PATCH 01/25] [update] Shorten array syntax --- medoo.php | 64 +++++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/medoo.php b/medoo.php index a57941fc..e59b68ff 100644 --- a/medoo.php +++ b/medoo.php @@ -2,7 +2,7 @@ /*! * Medoo database framework * http://medoo.in - * Version 1.1.3 + * Version 1.2 * * Copyright 2016, Angel Lai * Released under the MIT license @@ -34,17 +34,17 @@ class medoo protected $prefix; - protected $option = array(); + protected $option = []; // Variable - protected $logs = array(); + protected $logs = []; protected $debug_mode = false; public function __construct($options = null) { try { - $commands = array(); + $commands = []; $dsn = ''; if (is_array($options)) @@ -127,7 +127,7 @@ public function __construct($options = null) } if ( - in_array($type, array('mariadb', 'mysql', 'pgsql', 'sybase', 'mssql')) && + in_array($type, ['mariadb', 'mysql', 'pgsql', 'sybase', 'mssql']) && $this->charset ) { @@ -214,10 +214,10 @@ protected function column_push(&$columns) if (is_string($columns)) { - $columns = array($columns); + $columns = [$columns]; } - $stack = array(); + $stack = []; foreach ($columns as $key => $value) { @@ -247,7 +247,7 @@ protected function column_push(&$columns) protected function array_quote($array) { - $temp = array(); + $temp = []; foreach ($array as $value) { @@ -259,7 +259,7 @@ protected function array_quote($array) protected function inner_conjunct($data, $conjunctor, $outer_conjunctor) { - $haystack = array(); + $haystack = []; foreach ($data as $value) { @@ -280,7 +280,7 @@ protected function fn_quote($column, $string) protected function data_implode($data, $conjunctor, $outer_conjunctor = null) { - $wheres = array(); + $wheres = []; foreach ($data as $key => $value) { @@ -355,10 +355,10 @@ protected function data_implode($data, $conjunctor, $outer_conjunctor = null) { if ($type != 'array') { - $value = array($value); + $value = [$value]; } - $like_clauses = array(); + $like_clauses = []; foreach ($value as $item) { @@ -375,7 +375,7 @@ protected function data_implode($data, $conjunctor, $outer_conjunctor = null) $wheres[] = implode(' OR ', $like_clauses); } - if (in_array($operator, array('>', '>=', '<', '<='))) + if (in_array($operator, ['>', '>=', '<', '<='])) { $condition = $column . ' ' . $operator . ' '; @@ -438,10 +438,10 @@ protected function where_clause($where) $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') + ['AND', 'OR', 'GROUP', 'ORDER', 'HAVING', 'LIMIT', 'LIKE', 'MATCH'] )); - if ($single_condition != array()) + if ($single_condition != []) { $condition = $this->data_implode($single_condition, ''); @@ -489,7 +489,7 @@ protected function where_clause($where) if (is_array($ORDER)) { - $stack = array(); + $stack = []; foreach ($ORDER as $column => $value) { @@ -576,14 +576,14 @@ protected function select_context($table, $join, &$columns = null, $where = null strpos($join_key[ 0 ], '[') === 0 ) { - $table_join = array(); + $table_join = []; - $join_array = array( + $join_array = [ '>' => 'LEFT', '<' => 'RIGHT', '<>' => 'FULL', '><' => 'INNER' - ); + ]; foreach($join as $sub_table => $relation) { @@ -605,7 +605,7 @@ protected function select_context($table, $join, &$columns = null, $where = null } else { - $joins = array(); + $joins = []; foreach ($relation as $key => $value) { @@ -705,7 +705,7 @@ protected function data_map($index, $key, $value, $data, &$stack) { if (is_array($value)) { - $sub_stack = array(); + $sub_stack = []; foreach ($value as $sub_key => $sub_value) { @@ -751,7 +751,7 @@ public function select($table, $join, $columns = null, $where = null) $query = $this->query($this->select_context($table, $join, $columns, $where)); - $stack = array(); + $stack = []; $index = 0; @@ -792,18 +792,18 @@ public function select($table, $join, $columns = null, $where = null) public function insert($table, $datas) { - $lastId = array(); + $lastId = []; // Check indexed or associative array if (!isset($datas[ 0 ])) { - $datas = array($datas); + $datas = [$datas]; } foreach ($datas as $data) { - $values = array(); - $columns = array(); + $values = []; + $columns = []; foreach ($data as $key => $value) { @@ -845,7 +845,7 @@ public function insert($table, $datas) public function update($table, $data, $where = null) { - $fields = array(); + $fields = []; foreach ($data as $key => $value) { @@ -901,7 +901,7 @@ public function replace($table, $columns, $search = null, $replace = null, $wher { if (is_array($columns)) { - $replace_query = array(); + $replace_query = []; foreach ($columns as $column => $replacements) { @@ -918,7 +918,7 @@ public function replace($table, $columns, $search = null, $replace = null, $wher { if (is_array($search)) { - $replace_query = array(); + $replace_query = []; foreach ($search as $replace_search => $replace_replacement) { @@ -961,7 +961,7 @@ public function get($table, $join = null, $columns = null, $where = null) return $data[ 0 ]; } - $stack = array(); + $stack = []; foreach ($columns as $key => $value) { @@ -1104,13 +1104,13 @@ public function log() public function info() { - $output = array( + $output = [ 'server' => 'SERVER_INFO', 'driver' => 'DRIVER_NAME', 'client' => 'CLIENT_VERSION', 'version' => 'SERVER_VERSION', 'connection' => 'CONNECTION_STATUS' - ); + ]; foreach ($output as $key => $value) { From 524ce57ec3dc07f86499f139e6e1918d80037578 Mon Sep 17 00:00:00 2001 From: Catfan Date: Sat, 15 Oct 2016 23:34:29 +0800 Subject: [PATCH 02/25] [feature] New initialization for customizable DSN --- medoo.php | 223 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 145 insertions(+), 78 deletions(-) diff --git a/medoo.php b/medoo.php index e59b68ff..e1b22578 100644 --- a/medoo.php +++ b/medoo.php @@ -12,26 +12,7 @@ class medoo // General protected $database_type; - protected $charset; - - protected $database_name; - - // For MySQL, MariaDB, MSSQL, Sybase, PostgreSQL, Oracle - protected $server; - - protected $username; - - protected $password; - - // For SQLite - protected $database_file; - - // For MySQL or MariaDB with unix_socket - protected $socket; - // Optional - protected $port; - protected $prefix; protected $option = []; @@ -45,99 +26,185 @@ public function __construct($options = null) { try { $commands = []; - $dsn = ''; if (is_array($options)) { - foreach ($options as $option => $value) - { - $this->$option = $value; - } + $this->database_type = strtolower($options['database_type']); } else { return false; } - if ( - isset($this->port) && - is_int($this->port * 1) - ) + if (isset($options['prefix'])) { - $port = $this->port; + $this->prefix = $options['prefix']; } - $type = strtolower($this->database_type); - $is_port = isset($port); - - if (isset($options[ 'prefix' ])) + if (isset($options['option'])) { - $this->prefix = $options[ 'prefix' ]; + $this->option = $options['option']; } - switch ($type) + if (isset($options['dsn'])) { - case 'mariadb': - $type = 'mysql'; + if (isset($options['dsn']['driver'])) + { + $attr = $options['dsn']; + } + else + { + return false; + } + } + else + { + if ( + isset($options['port']) && + is_int($options['port'] * 1) + ) + { + $port = $options['port']; + } - 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; - } + $is_port = isset($port); + + switch ($this->database_type) + { + case 'mariadb': + case 'mysql': + $attr = [ + 'driver' => 'mysql', + 'dbname' => $options['database_name'] + ]; + + if (isset($options['socket'])) + { + $attr['unix_socket'] = $options['socket']; + } + else + { + $attr['host'] = $options['server']; + + if ($is_port) + { + $dsn['port'] = $port; + } + } + + // Make MySQL using standard quoted identifier + $commands[] = 'SET SQL_MODE=ANSI_QUOTES'; + break; + + case 'pgsql': + $attr = [ + 'driver' => 'pgsql', + 'host' => $options['server'], + 'dbname' => $options['database_name'] + ]; + + if ($is_port) + { + $dsn['port'] = $port; + } - // Make MySQL using standard quoted identifier - $commands[] = 'SET SQL_MODE=ANSI_QUOTES'; - break; + break; - case 'pgsql': - $dsn = $type . ':host=' . $this->server . ($is_port ? ';port=' . $port : '') . ';dbname=' . $this->database_name; - break; + case 'sybase': + $attr = [ + 'driver' => 'dblib', + 'host' => $options['server'], + 'dbname' => $options['database_name'] + ]; + + if ($is_port) + { + $dsn['port'] = $port; + } - case 'sybase': - $dsn = 'dblib:host=' . $this->server . ($is_port ? ':' . $port : '') . ';dbname=' . $this->database_name; - break; + break; - case 'oracle': - $dbname = $this->server ? - '//' . $this->server . ($is_port ? ':' . $port : ':1521') . '/' . $this->database_name : - $this->database_name; + case 'oracle': + $attr = [ + 'driver' => 'oci', + 'dbname' => $options['server'] ? + '//' . $options['server'] . ($is_port ? ':' . $port : ':1521') . '/' . $options['database_name'] : + $options['database_name'] + ]; - $dsn = 'oci:dbname=' . $dbname . ($this->charset ? ';charset=' . $this->charset : ''); - break; + if (isset($options['charset'])) + { + $dsn['charset'] = $options['charset']; + } - 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; + break; - // Keep MSSQL QUOTED_IDENTIFIER is ON for standard quoting - $commands[] = 'SET QUOTED_IDENTIFIER ON'; - break; + case 'mssql': + if (strstr(PHP_OS, 'WIN')) + { + $attr = [ + 'driver' => 'sqlsrv', + 'server' => $options['server'], + 'database' => $options['database_name'] + ]; + } + else + { + $attr = [ + 'driver' => 'dblib', + 'host' => $options['server'], + 'dbname' => $options['database_name'] + ]; + } - case 'sqlite': - $dsn = $type . ':' . $this->database_file; - $this->username = null; - $this->password = null; - break; + if ($is_port) + { + $dsn['port'] = $port; + } + + // Keep MSSQL QUOTED_IDENTIFIER is ON for standard quoting + $commands[] = 'SET QUOTED_IDENTIFIER ON'; + break; + + case 'sqlite': + $this->pdo = new PDO('sqlite:' . $options['database_file'], null, null, $this->option); + + return $this; + } } + $driver = $attr['driver']; + + unset($attr['driver']); + + $stack = []; + + foreach ($attr as $key => $value) + { + if (is_int($key)) + { + $stack[] = $value; + } + else + { + $stack[] = $key . '=' . $value; + } + } + + $dsn = $driver . ':' . implode($stack, ';'); + if ( - in_array($type, ['mariadb', 'mysql', 'pgsql', 'sybase', 'mssql']) && - $this->charset + in_array($this->database_type, ['mariadb', 'mysql', 'pgsql', 'sybase', 'mssql']) && + $options['charset'] ) { - $commands[] = "SET NAMES '" . $this->charset . "'"; + $commands[] = "SET NAMES '" . $options['charset'] . "'"; } $this->pdo = new PDO( $dsn, - $this->username, - $this->password, + $options['username'], + $options['password'], $this->option ); From cf1a9d887b8e21d6103d0e27cfeb71259ca83df5 Mon Sep 17 00:00:00 2001 From: Catfan Date: Sun, 16 Oct 2016 00:04:06 +0800 Subject: [PATCH 03/25] [update] Update readme file --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fa4dc68d..daa03650 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,22 @@ ## [Medoo](http://medoo.in) +[![Total Downloads](https://poser.pugx.org/catfan/medoo/downloads)](https://packagist.org/packages/catfan/medoo) + +[![Latest Stable Version](https://poser.pugx.org/catfan/medoo/v/stable)](https://packagist.org/packages/catfan/medoo) + +[![License](https://poser.pugx.org/catfan/medoo/license)](https://packagist.org/packages/catfan/medoo) + > The Lightest PHP database framework to accelerate development ## Main Features -* **Lightweight** - 20KB around with only one file. +* **Lightweight** - 25KB 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. From 8d34a2f38dffd7b13c219228f6fbdbde34bb256c Mon Sep 17 00:00:00 2001 From: Catfan Date: Sun, 16 Oct 2016 00:04:53 +0800 Subject: [PATCH 04/25] [update] Update readme file II --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index daa03650..8745e09d 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ * **Powerful** - Support various common and complex SQL queries. +* **Compatible** - Support all SQL databases, including MySQL, MSSQL, SQLite, MariaDB, Sybase, Oracle, PostgreSQL and more. * **Security** - Prevent SQL injection. From 15615b3b89cbfe9e5dd7e74e85ca628760f94ab0 Mon Sep 17 00:00:00 2001 From: Catfan Date: Sun, 16 Oct 2016 00:06:38 +0800 Subject: [PATCH 05/25] [update] Fix badge display for readme --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 8745e09d..a87d37b5 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,7 @@ ## [Medoo](http://medoo.in) [![Total Downloads](https://poser.pugx.org/catfan/medoo/downloads)](https://packagist.org/packages/catfan/medoo) - [![Latest Stable Version](https://poser.pugx.org/catfan/medoo/v/stable)](https://packagist.org/packages/catfan/medoo) - [![License](https://poser.pugx.org/catfan/medoo/license)](https://packagist.org/packages/catfan/medoo) > The Lightest PHP database framework to accelerate development From af806f58dad5427b06ddd5c08e2f42a770a2143c Mon Sep 17 00:00:00 2001 From: Catfan Date: Wed, 19 Oct 2016 18:27:16 +0800 Subject: [PATCH 06/25] [feature] Add namespace --- composer.json | 6 ++++-- medoo.php | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index b9569a88..1ad7ddcc 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ {"name": "Angel Lai", "email": "angel@catfan.me"} ], "require": { - "php": ">=5.1", + "php": ">=5.4", "ext-pdo": "*" }, "suggest": { @@ -21,6 +21,8 @@ "ext-pdo_sqlite": "For SQLite databases" }, "autoload": { - "files": ["medoo.php"] + "psr-4": { + "Medoo\\": "" + } } } \ No newline at end of file diff --git a/medoo.php b/medoo.php index e1b22578..889d94bf 100644 --- a/medoo.php +++ b/medoo.php @@ -1,4 +1,6 @@ Date: Sun, 30 Oct 2016 16:31:31 +0800 Subject: [PATCH 07/25] [update] Update project for PSR-4 specification --- README.md | 7 +++++-- composer.json | 2 +- medoo.php => src/Medoo.php | 0 src/medoo-logo.png | Bin 37776 -> 0 bytes 4 files changed, 6 insertions(+), 3 deletions(-) rename medoo.php => src/Medoo.php (100%) delete mode 100644 src/medoo-logo.png diff --git a/README.md b/README.md index a87d37b5..3f0c6225 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![](https://raw.githubusercontent.com/catfan/Medoo/develop/src/medoo-logo.png) +![](https://cloud.githubusercontent.com/assets/1467904/19835326/ca62bc36-9ebd-11e6-8b37-7240d76319cd.png) ## [Medoo](http://medoo.in) @@ -43,8 +43,11 @@ require 'vendor/autoload.php'; // Or if you just download the medoo.php into directory, require it with the correct path. require_once 'medoo.php'; +// Using Medoo namespace +use Medoo\Medoo; + // Initialize -$database = new medoo([ +$database = new Medoo([ 'database_type' => 'mysql', 'database_name' => 'name', 'server' => 'localhost', diff --git a/composer.json b/composer.json index 1ad7ddcc..a59d4e82 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ }, "autoload": { "psr-4": { - "Medoo\\": "" + "Medoo\\": "/src" } } } \ No newline at end of file diff --git a/medoo.php b/src/Medoo.php similarity index 100% rename from medoo.php rename to src/Medoo.php diff --git a/src/medoo-logo.png b/src/medoo-logo.png deleted file mode 100644 index 914885487fca03651e9cfa9e8d0b06761cc27212..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37776 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 Date: Tue, 15 Nov 2016 02:25:52 +0800 Subject: [PATCH 08/25] [update] Set ANSI_NULLS for MSSQL --- src/Medoo.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Medoo.php b/src/Medoo.php index 889d94bf..30f77c96 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -167,6 +167,9 @@ public function __construct($options = null) // Keep MSSQL QUOTED_IDENTIFIER is ON for standard quoting $commands[] = 'SET QUOTED_IDENTIFIER ON'; + + // Make ANSI_NULLS is ON for NULL value + $commands[] = 'SET ANSI_NULLS ON'; break; case 'sqlite': From a20fdc77167d602875906df4ac98c2a0ca29b892 Mon Sep 17 00:00:00 2001 From: Catfan Date: Fri, 18 Nov 2016 04:33:45 +0800 Subject: [PATCH 09/25] [feature] Add lastInsertId() for improving fetching the last id inserted, and fix the bug for #486 The insert() will not return the last id anymore. Call lastInsertId() after the insertion to get the last id instead. --- src/Medoo.php | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Medoo.php b/src/Medoo.php index 30f77c96..e81b43cd 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -865,7 +865,7 @@ public function select($table, $join, $columns = null, $where = null) public function insert($table, $datas) { - $lastId = []; + $results = []; // Check indexed or associative array if (!isset($datas[ 0 ])) @@ -908,12 +908,10 @@ public function insert($table, $datas) } } - $this->exec('INSERT INTO ' . $this->table_quote($table) . ' (' . implode(', ', $columns) . ') VALUES (' . implode($values, ', ') . ')'); - - $lastId[] = $this->pdo->lastInsertId(); + $results[] = $this->exec('INSERT INTO ' . $this->table_quote($table) . ' (' . implode(', ', $columns) . ') VALUES (' . implode($values, ', ') . ')'); } - return count($lastId) > 1 ? $lastId : $lastId[ 0 ]; + return count($results) > 1 ? $results : $results[ 0 ]; } public function update($table, $data, $where = null) @@ -1153,6 +1151,20 @@ public function action($actions) } } + public function lastInsertId() + { + if ($this->database_type == 'oracle') + { + return 0; + } + elseif ($this->database_type == 'mssql') + { + return $this->pdo->query('SELECT SCOPE_IDENTITY()')->fetchColumn(); + } + + return $this->pdo->lastInsertId(); + } + public function debug() { $this->debug_mode = true; From 154b0cf2b8c57c8c1234b935ada5e1f32044f7b1 Mon Sep 17 00:00:00 2001 From: Catfan Date: Sun, 27 Nov 2016 04:37:56 +0800 Subject: [PATCH 10/25] [update] Add PDO namespace --- src/Medoo.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Medoo.php b/src/Medoo.php index e81b43cd..7c1292dc 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -10,6 +10,8 @@ * Released under the MIT license */ +use PDO; + class Medoo { // General From dc0c8f001c08429bc0ce4754ee8b42a40c108f6d Mon Sep 17 00:00:00 2001 From: Catfan Date: Wed, 7 Dec 2016 16:21:18 +0800 Subject: [PATCH 11/25] [feature] Supoort relationship between two columns Example: ["foo.id" => 10, "foo.age[>]bar.average"] --- src/Medoo.php | 202 ++++++++++++++++++++++++++------------------------ 1 file changed, 107 insertions(+), 95 deletions(-) diff --git a/src/Medoo.php b/src/Medoo.php index 7c1292dc..37025c4d 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -372,128 +372,140 @@ protected function data_implode($data, $conjunctor, $outer_conjunctor = null) } else { - preg_match('/(#?)([\w\.\-]+)(\[(\>|\>\=|\<|\<\=|\!|\<\>|\>\<|\!?~)\])?/i', $key, $match); - $column = $this->column_quote($match[ 2 ]); - - if (isset($match[ 4 ])) + if ( + is_int($key) && + preg_match('/([\w\.\-]+)\[(\>|\>\=|\<|\<\=|\!|\=)\]([\w\.\-]+)/i', $value, $match) + ) + { + $operator = $match[ 2 ]; + + $wheres[] = $this->column_quote($match[ 1 ]) . ' ' . $operator . ' ' . $this->column_quote($match[ 3 ]); + } + else { - $operator = $match[ 4 ]; + preg_match('/(#?)([\w\.\-]+)(\[(\>|\>\=|\<|\<\=|\!|\<\>|\>\<|\!?~)\])?/i', $key, $match); + $column = $this->column_quote($match[ 2 ]); - if ($operator == '!') + if (isset($match[ 4 ])) { - 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; + $operator = $match[ 4 ]; - case 'string': - $wheres[] = $column . ' != ' . $this->fn_quote($key, $value); - break; + 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 == '<>' || $operator == '><') { - if ($operator == '><') + if ($type == 'array') { - $column .= ' NOT'; + 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 (is_numeric($value[ 0 ]) && is_numeric($value[ 1 ])) + if ($operator == '~' || $operator == '!~') + { + if ($type != 'array') { - $wheres[] = '(' . $column . ' BETWEEN ' . $value[ 0 ] . ' AND ' . $value[ 1 ] . ')'; + $value = [$value]; } - else + + $like_clauses = []; + + foreach ($value as $item) { - $wheres[] = '(' . $column . ' BETWEEN ' . $this->quote($value[ 0 ]) . ' AND ' . $this->quote($value[ 1 ]) . ')'; + $item = strval($item); + + if (preg_match('/^(?!(%|\[|_])).+(?fn_quote($key, $item); } - } - } - if ($operator == '~' || $operator == '!~') - { - if ($type != 'array') - { - $value = [$value]; + $wheres[] = implode(' OR ', $like_clauses); } - $like_clauses = []; - - foreach ($value as $item) + if (in_array($operator, ['>', '>=', '<', '<='])) { - $item = strval($item); + $condition = $column . ' ' . $operator . ' '; - if (preg_match('/^(?!(%|\[|_])).+(?fn_quote($key, $value); + } + else { - $item = '%' . $item . '%'; + $condition .= $this->quote($value); } - $like_clauses[] = $column . ($operator === '!~' ? ' NOT' : '') . ' LIKE ' . $this->fn_quote($key, $item); + $wheres[] = $condition; } - - $wheres[] = implode(' OR ', $like_clauses); } - - if (in_array($operator, ['>', '>=', '<', '<='])) + else { - $condition = $column . ' ' . $operator . ' '; - - if (is_numeric($value)) - { - $condition .= $value; - } - elseif (strpos($key, '#') === 0) - { - $condition .= $this->fn_quote($key, $value); - } - else + switch ($type) { - $condition .= $this->quote($value); - } + case 'NULL': + $wheres[] = $column . ' IS NULL'; + break; - $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; + 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; + } } } } From 84ba9bb3d675709b92516790e1e633e419e725b2 Mon Sep 17 00:00:00 2001 From: Catfan Date: Wed, 4 Jan 2017 15:56:19 +0800 Subject: [PATCH 12/25] [fix] Fix SQL wildcards matching --- src/Medoo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Medoo.php b/src/Medoo.php index 37025c4d..3708597c 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -450,7 +450,7 @@ protected function data_implode($data, $conjunctor, $outer_conjunctor = null) { $item = strval($item); - if (preg_match('/^(?!(%|\[|_])).+(? Date: Fri, 13 Jan 2017 06:32:17 +0800 Subject: [PATCH 13/25] [update] Update for 2017 --- LICENSE | 2 +- src/Medoo.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 256b6941..5b1951c9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ [MIT LICENSE] -Copyright (c) 2016 Angel Lai, http://medoo.in +Copyright (c) 2017 Angel Lai, http://medoo.in Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/src/Medoo.php b/src/Medoo.php index 3708597c..8323249a 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -6,7 +6,7 @@ * http://medoo.in * Version 1.2 * - * Copyright 2016, Angel Lai + * Copyright 2017, Angel Lai * Released under the MIT license */ From 91997c7d3b5622c4f219ca40e8d5de2cc5a9ae8a Mon Sep 17 00:00:00 2001 From: Catfan Date: Fri, 13 Jan 2017 06:50:16 +0800 Subject: [PATCH 14/25] [update] Code improvement --- src/Medoo.php | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/Medoo.php b/src/Medoo.php index 8323249a..95489a27 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -556,7 +556,10 @@ protected function where_clause($where) 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' ]) . ')'; + $columns = str_replace('.', '"."', implode($MATCH[ 'columns' ], '", "')); + $keywords = $this->quote($MATCH[ 'keyword' ]); + + $where_clause .= ($where_clause != '' ? ' AND ' : ' WHERE ') . ' MATCH ("' . $columns . '") AGAINST (' . $keywords . ')'; } } @@ -647,7 +650,7 @@ protected function select_context($table, $join, &$columns = null, $where = null { $table = $this->table_quote($table_match[ 1 ]); - $table_query = $this->table_quote($table_match[ 1 ]) . ' AS ' . $this->table_quote($table_match[ 2 ]); + $table_query = $table . ' AS ' . $this->table_quote($table_match[ 2 ]); } else { @@ -861,14 +864,12 @@ public function select($table, $join, $columns = null, $where = null) { foreach ($columns as $key => $value) { - if (is_array($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); + $value = preg_replace('/^[\w]*\./i', "", $value); } + + $this->data_map($index, $key, $value, $row, $stack); } $index++; @@ -1050,14 +1051,12 @@ public function get($table, $join = null, $columns = null, $where = null) foreach ($columns as $key => $value) { - if (is_array($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); + $value = preg_replace('/^[\w]*\./i', "", $value); } + + $this->data_map(0, $key, $value, $data[ 0 ], $stack); } return $stack[ 0 ]; From 161f9d43d5704435d1b21e0878f1122b506230f9 Mon Sep 17 00:00:00 2001 From: Catfan Date: Fri, 13 Jan 2017 07:08:00 +0800 Subject: [PATCH 15/25] [update] Remove require path example from Readme --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 3f0c6225..a622b9c2 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,6 @@ $ composer update // 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'; - // Using Medoo namespace use Medoo\Medoo; From 30c736247a74d9f338461fe22a03110649e01dcd Mon Sep 17 00:00:00 2001 From: Catfan Date: Tue, 24 Jan 2017 04:18:29 +0800 Subject: [PATCH 16/25] [feature] Connect conditions with AND keyword by default --- src/Medoo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Medoo.php b/src/Medoo.php index 95489a27..b6e16908 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -530,7 +530,7 @@ protected function where_clause($where) if ($single_condition != []) { - $condition = $this->data_implode($single_condition, ''); + $condition = $this->data_implode($single_condition, ' AND'); if ($condition != '') { From ed63041165f7f54d2d31d6a308c8333a614f64f1 Mon Sep 17 00:00:00 2001 From: Catfan Date: Sun, 29 Jan 2017 01:54:47 +0800 Subject: [PATCH 17/25] [update] Allow empty database type for connection --- src/Medoo.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Medoo.php b/src/Medoo.php index b6e16908..0592a4d2 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -34,7 +34,10 @@ public function __construct($options = null) if (is_array($options)) { - $this->database_type = strtolower($options['database_type']); + if (isset($options['database_type'])) + { + $this->database_type = strtolower($options['database_type']); + } } else { From a4c8102d1aece102a63d8f408eefe94f8e7c9109 Mon Sep 17 00:00:00 2001 From: Catfan Date: Mon, 30 Jan 2017 04:34:25 +0800 Subject: [PATCH 18/25] [feature] Improve multiple insertion with using one query and allowed empty column --- src/Medoo.php | 77 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/src/Medoo.php b/src/Medoo.php index 0592a4d2..c9b32fa8 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -883,7 +883,9 @@ public function select($table, $join, $columns = null, $where = null) public function insert($table, $datas) { - $results = []; + $stack = []; + $columns = []; + $fields = []; // Check indexed or associative array if (!isset($datas[ 0 ])) @@ -893,43 +895,64 @@ public function insert($table, $datas) foreach ($datas as $data) { - $values = []; - $columns = []; - foreach ($data as $key => $value) { - $columns[] = $this->column_quote(preg_replace("/^(\(JSON\)\s*|#)/i", "", $key)); - - switch (gettype($value)) - { - case 'NULL': - $values[] = 'NULL'; - break; + $columns[] = $key; + } + } - case 'array': - preg_match("/\(JSON\)\s*([\w]+)/i", $key, $column_match); + $columns = array_unique($columns); - $values[] = isset($column_match[ 0 ]) ? - $this->quote(json_encode($value)) : - $this->quote(serialize($value)); - break; + foreach ($datas as $data) + { + $values = []; - case 'boolean': - $values[] = ($value ? '1' : '0'); - break; + foreach ($columns as $key) + { + if (!isset($data[$key])) + { + $values[] = 'NULL'; + } + else + { + $value = $data[$key]; - case 'integer': - case 'double': - case 'string': - $values[] = $this->fn_quote($key, $value); - break; + 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; + } } } - $results[] = $this->exec('INSERT INTO ' . $this->table_quote($table) . ' (' . implode(', ', $columns) . ') VALUES (' . implode($values, ', ') . ')'); + $stack[] = '(' . implode($values, ', ') . ')'; + } + + foreach ($columns as $key) + { + $fields[] = $this->column_quote(preg_replace("/^(\(JSON\)\s*|#)/i", "", $key)); } - return count($results) > 1 ? $results : $results[ 0 ]; + return $this->exec('INSERT INTO ' . $this->table_quote($table) . ' (' . implode(', ', $fields) . ') VALUES ' . implode(', ', $stack)); } public function update($table, $data, $where = null) From f75ce45724e292279dcf1ee36e63eb9359edcff9 Mon Sep 17 00:00:00 2001 From: Catfan Date: Mon, 30 Jan 2017 06:36:45 +0800 Subject: [PATCH 19/25] [update] Change all method name with lower camel case --- src/Medoo.php | 138 +++++++++++++++++++++++++------------------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/src/Medoo.php b/src/Medoo.php index c9b32fa8..617918a6 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -266,12 +266,12 @@ public function quote($string) return $this->pdo->quote($string); } - protected function table_quote($table) + protected function tableQuote($table) { return '"' . $this->prefix . $table . '"'; } - protected function column_quote($string) + protected function columnQuote($string) { preg_match('/(\(JSON\)\s*|^#)?([a-zA-Z0-9_]*)\.([a-zA-Z0-9_]*)/', $string, $column_match); @@ -283,7 +283,7 @@ protected function column_quote($string) return '"' . $string . '"'; } - protected function column_push(&$columns) + protected function columnPush(&$columns) { if ($columns == '*') { @@ -301,7 +301,7 @@ protected function column_push(&$columns) { if (is_array($value)) { - $stack[] = $this->column_push($value); + $stack[] = $this->columnPush($value); } else { @@ -309,13 +309,13 @@ protected function column_push(&$columns) if (isset($match[ 1 ], $match[ 2 ])) { - $stack[] = $this->column_quote( $match[ 1 ] ) . ' AS ' . $this->column_quote( $match[ 2 ] ); + $stack[] = $this->columnQuote( $match[ 1 ] ) . ' AS ' . $this->columnQuote( $match[ 2 ] ); $columns[ $key ] = $match[ 2 ]; } else { - $stack[] = $this->column_quote( $value ); + $stack[] = $this->columnQuote( $value ); } } } @@ -323,7 +323,7 @@ protected function column_push(&$columns) return implode($stack, ','); } - protected function array_quote($array) + protected function arrayQuote($array) { $temp = []; @@ -335,19 +335,19 @@ protected function array_quote($array) return implode($temp, ','); } - protected function inner_conjunct($data, $conjunctor, $outer_conjunctor) + protected function innerConjunct($data, $conjunctor, $outer_conjunctor) { $haystack = []; foreach ($data as $value) { - $haystack[] = '(' . $this->data_implode($value, $conjunctor) . ')'; + $haystack[] = '(' . $this->dataImplode($value, $conjunctor) . ')'; } return implode($outer_conjunctor . ' ', $haystack); } - protected function fn_quote($column, $string) + protected function fnQuote($column, $string) { return (strpos($column, '#') === 0 && preg_match('/^[A-Z0-9\_]*\([^)]*\)$/', $string)) ? @@ -356,7 +356,7 @@ protected function fn_quote($column, $string) $this->quote($string); } - protected function data_implode($data, $conjunctor, $outer_conjunctor = null) + protected function dataImplode($data, $conjunctor, $outer_conjunctor = null) { $wheres = []; @@ -370,8 +370,8 @@ protected function data_implode($data, $conjunctor, $outer_conjunctor = null) ) { $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) . ')'; + '(' . $this->dataImplode($value, ' ' . $relation_match[ 1 ]) . ')' : + '(' . $this->innerConjunct($value, ' ' . $relation_match[ 1 ], $conjunctor) . ')'; } else { @@ -382,12 +382,12 @@ protected function data_implode($data, $conjunctor, $outer_conjunctor = null) { $operator = $match[ 2 ]; - $wheres[] = $this->column_quote($match[ 1 ]) . ' ' . $operator . ' ' . $this->column_quote($match[ 3 ]); + $wheres[] = $this->columnQuote($match[ 1 ]) . ' ' . $operator . ' ' . $this->columnQuote($match[ 3 ]); } else { preg_match('/(#?)([\w\.\-]+)(\[(\>|\>\=|\<|\<\=|\!|\<\>|\>\<|\!?~)\])?/i', $key, $match); - $column = $this->column_quote($match[ 2 ]); + $column = $this->columnQuote($match[ 2 ]); if (isset($match[ 4 ])) { @@ -402,7 +402,7 @@ protected function data_implode($data, $conjunctor, $outer_conjunctor = null) break; case 'array': - $wheres[] = $column . ' NOT IN (' . $this->array_quote($value) . ')'; + $wheres[] = $column . ' NOT IN (' . $this->arrayQuote($value) . ')'; break; case 'integer': @@ -415,7 +415,7 @@ protected function data_implode($data, $conjunctor, $outer_conjunctor = null) break; case 'string': - $wheres[] = $column . ' != ' . $this->fn_quote($key, $value); + $wheres[] = $column . ' != ' . $this->fnQuote($key, $value); break; } } @@ -458,7 +458,7 @@ protected function data_implode($data, $conjunctor, $outer_conjunctor = null) $item = '%' . $item . '%'; } - $like_clauses[] = $column . ($operator === '!~' ? ' NOT' : '') . ' LIKE ' . $this->fn_quote($key, $item); + $like_clauses[] = $column . ($operator === '!~' ? ' NOT' : '') . ' LIKE ' . $this->fnQuote($key, $item); } $wheres[] = implode(' OR ', $like_clauses); @@ -474,7 +474,7 @@ protected function data_implode($data, $conjunctor, $outer_conjunctor = null) } elseif (strpos($key, '#') === 0) { - $condition .= $this->fn_quote($key, $value); + $condition .= $this->fnQuote($key, $value); } else { @@ -493,7 +493,7 @@ protected function data_implode($data, $conjunctor, $outer_conjunctor = null) break; case 'array': - $wheres[] = $column . ' IN (' . $this->array_quote($value) . ')'; + $wheres[] = $column . ' IN (' . $this->arrayQuote($value) . ')'; break; case 'integer': @@ -506,7 +506,7 @@ protected function data_implode($data, $conjunctor, $outer_conjunctor = null) break; case 'string': - $wheres[] = $column . ' = ' . $this->fn_quote($key, $value); + $wheres[] = $column . ' = ' . $this->fnQuote($key, $value); break; } } @@ -517,7 +517,7 @@ protected function data_implode($data, $conjunctor, $outer_conjunctor = null) return implode($conjunctor . ' ', $wheres); } - protected function where_clause($where) + protected function whereClause($where) { $where_clause = ''; @@ -533,7 +533,7 @@ protected function where_clause($where) if ($single_condition != []) { - $condition = $this->data_implode($single_condition, ' AND'); + $condition = $this->dataImplode($single_condition, ' AND'); if ($condition != '') { @@ -544,13 +544,13 @@ protected function where_clause($where) if (!empty($where_AND)) { $value = array_values($where_AND); - $where_clause = ' WHERE ' . $this->data_implode($where[ $value[ 0 ] ], ' AND'); + $where_clause = ' WHERE ' . $this->dataImplode($where[ $value[ 0 ] ], ' AND'); } if (!empty($where_OR)) { $value = array_values($where_OR); - $where_clause = ' WHERE ' . $this->data_implode($where[ $value[ 0 ] ], ' OR'); + $where_clause = ' WHERE ' . $this->dataImplode($where[ $value[ 0 ] ], ' OR'); } if (isset($where[ 'MATCH' ])) @@ -568,11 +568,11 @@ protected function where_clause($where) if (isset($where[ 'GROUP' ])) { - $where_clause .= ' GROUP BY ' . $this->column_quote($where[ 'GROUP' ]); + $where_clause .= ' GROUP BY ' . $this->columnQuote($where[ 'GROUP' ]); if (isset($where[ 'HAVING' ])) { - $where_clause .= ' HAVING ' . $this->data_implode($where[ 'HAVING' ], ' AND'); + $where_clause .= ' HAVING ' . $this->dataImplode($where[ 'HAVING' ], ' AND'); } } @@ -588,15 +588,15 @@ protected function where_clause($where) { if (is_array($value)) { - $stack[] = 'FIELD(' . $this->column_quote($column) . ', ' . $this->array_quote($value) . ')'; + $stack[] = 'FIELD(' . $this->columnQuote($column) . ', ' . $this->arrayQuote($value) . ')'; } else if ($value === 'ASC' || $value === 'DESC') { - $stack[] = $this->column_quote($column) . ' ' . $value; + $stack[] = $this->columnQuote($column) . ' ' . $value; } else if (is_int($column)) { - $stack[] = $this->column_quote($value); + $stack[] = $this->columnQuote($value); } } @@ -604,7 +604,7 @@ protected function where_clause($where) } else { - $where_clause .= ' ORDER BY ' . $this->column_quote($ORDER); + $where_clause .= ' ORDER BY ' . $this->columnQuote($ORDER); } } @@ -645,19 +645,19 @@ protected function where_clause($where) return $where_clause; } - protected function select_context($table, $join, &$columns = null, $where = null, $column_fn = null) + protected function selectContext($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 = $this->tableQuote($table_match[ 1 ]); - $table_query = $table . ' AS ' . $this->table_quote($table_match[ 2 ]); + $table_query = $table . ' AS ' . $this->tableQuote($table_match[ 2 ]); } else { - $table = $this->table_quote($table); + $table = $this->tableQuote($table); $table_query = $table; } @@ -705,24 +705,24 @@ protected function select_context($table, $join, &$columns = null, $where = null $joins[] = ( strpos($key, '.') > 0 ? // For ['tableB.column' => 'column'] - $this->column_quote($key) : + $this->columnQuote($key) : // For ['column1' => 'column2'] $table . '."' . $key . '"' ) . ' = ' . - $this->table_quote(isset($match[ 5 ]) ? $match[ 5 ] : $match[ 3 ]) . '."' . $value . '"'; + $this->tableQuote(isset($match[ 5 ]) ? $match[ 5 ] : $match[ 3 ]) . '."' . $value . '"'; } $relation = 'ON ' . implode($joins, ' AND '); } } - $table_name = $this->table_quote($match[ 3 ]) . ' '; + $table_name = $this->tableQuote($match[ 3 ]) . ' '; if (isset($match[ 5 ])) { - $table_name .= 'AS ' . $this->table_quote($match[ 5 ]) . ' '; + $table_name .= 'AS ' . $this->tableQuote($match[ 5 ]) . ' '; } $table_join[] = $join_array[ $match[ 2 ] ] . ' JOIN ' . $table_name . $relation; @@ -783,18 +783,18 @@ protected function select_context($table, $join, &$columns = null, $where = null $where = $join; } - $column = $column_fn . '(' . $this->column_push($columns) . ')'; + $column = $column_fn . '(' . $this->columnPush($columns) . ')'; } } else { - $column = $this->column_push($columns); + $column = $this->columnPush($columns); } - return 'SELECT ' . $column . ' FROM ' . $table_query . $this->where_clause($where); + return 'SELECT ' . $column . ' FROM ' . $table_query . $this->whereClause($where); } - protected function data_map($index, $key, $value, $data, &$stack) + protected function dataMap($index, $key, $value, $data, &$stack) { if (is_array($value)) { @@ -806,13 +806,13 @@ protected function data_map($index, $key, $value, $data, &$stack) { $current_stack = $stack[ $index ][ $key ]; - $this->data_map(false, $sub_key, $sub_value, $data, $current_stack); + $this->dataMap(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); + $this->dataMap(false, preg_replace('/^[\w]*\./i', "", $sub_value), $sub_key, $data, $sub_stack); $stack[ $index ][ $key ] = $sub_stack; } @@ -842,7 +842,7 @@ public function select($table, $join, $columns = null, $where = null) $is_single_column = (is_string($column) && $column !== '*'); - $query = $this->query($this->select_context($table, $join, $columns, $where)); + $query = $this->query($this->selectContext($table, $join, $columns, $where)); $stack = []; @@ -872,7 +872,7 @@ public function select($table, $join, $columns = null, $where = null) $value = preg_replace('/^[\w]*\./i', "", $value); } - $this->data_map($index, $key, $value, $row, $stack); + $this->dataMap($index, $key, $value, $row, $stack); } $index++; @@ -938,7 +938,7 @@ public function insert($table, $datas) case 'integer': case 'double': case 'string': - $values[] = $this->fn_quote($key, $value); + $values[] = $this->fnQuote($key, $value); break; } } @@ -949,10 +949,10 @@ public function insert($table, $datas) foreach ($columns as $key) { - $fields[] = $this->column_quote(preg_replace("/^(\(JSON\)\s*|#)/i", "", $key)); + $fields[] = $this->columnQuote(preg_replace("/^(\(JSON\)\s*|#)/i", "", $key)); } - return $this->exec('INSERT INTO ' . $this->table_quote($table) . ' (' . implode(', ', $fields) . ') VALUES ' . implode(', ', $stack)); + return $this->exec('INSERT INTO ' . $this->tableQuote($table) . ' (' . implode(', ', $fields) . ') VALUES ' . implode(', ', $stack)); } public function update($table, $data, $where = null) @@ -967,12 +967,12 @@ public function update($table, $data, $where = null) { if (is_numeric($value)) { - $fields[] = $this->column_quote($match[ 1 ]) . ' = ' . $this->column_quote($match[ 1 ]) . ' ' . $match[ 3 ] . ' ' . $value; + $fields[] = $this->columnQuote($match[ 1 ]) . ' = ' . $this->columnQuote($match[ 1 ]) . ' ' . $match[ 3 ] . ' ' . $value; } } else { - $column = $this->column_quote(preg_replace("/^(\(JSON\)\s*|#)/i", "", $key)); + $column = $this->columnQuote(preg_replace("/^(\(JSON\)\s*|#)/i", "", $key)); switch (gettype($value)) { @@ -995,18 +995,18 @@ public function update($table, $data, $where = null) case 'integer': case 'double': case 'string': - $fields[] = $column . ' = ' . $this->fn_quote($key, $value); + $fields[] = $column . ' = ' . $this->fnQuote($key, $value); break; } } } - return $this->exec('UPDATE ' . $this->table_quote($table) . ' SET ' . implode(', ', $fields) . $this->where_clause($where)); + return $this->exec('UPDATE ' . $this->tableQuote($table) . ' SET ' . implode(', ', $fields) . $this->whereClause($where)); } public function delete($table, $where) { - return $this->exec('DELETE FROM ' . $this->table_quote($table) . $this->where_clause($where)); + return $this->exec('DELETE FROM ' . $this->tableQuote($table) . $this->whereClause($where)); } public function replace($table, $columns, $search = null, $replace = null, $where = null) @@ -1019,7 +1019,7 @@ public function replace($table, $columns, $search = null, $replace = null, $wher { 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[] = $column . ' = REPLACE(' . $this->columnQuote($column) . ', ' . $this->quote($replace_search) . ', ' . $this->quote($replace_replacement) . ')'; } } @@ -1034,7 +1034,7 @@ public function replace($table, $columns, $search = null, $replace = null, $wher 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[] = $columns . ' = REPLACE(' . $this->columnQuote($columns) . ', ' . $this->quote($replace_search) . ', ' . $this->quote($replace_replacement) . ')'; } $replace_query = implode(', ', $replace_query); @@ -1042,11 +1042,11 @@ public function replace($table, $columns, $search = null, $replace = null, $wher } else { - $replace_query = $columns . ' = REPLACE(' . $this->column_quote($columns) . ', ' . $this->quote($search) . ', ' . $this->quote($replace) . ')'; + $replace_query = $columns . ' = REPLACE(' . $this->columnQuote($columns) . ', ' . $this->quote($search) . ', ' . $this->quote($replace) . ')'; } } - return $this->exec('UPDATE ' . $this->table_quote($table) . ' SET ' . $replace_query . $this->where_clause($where)); + return $this->exec('UPDATE ' . $this->tableQuote($table) . ' SET ' . $replace_query . $this->whereClause($where)); } public function get($table, $join = null, $columns = null, $where = null) @@ -1055,7 +1055,7 @@ public function get($table, $join = null, $columns = null, $where = null) $is_single_column = (is_string($column) && $column !== '*'); - $query = $this->query($this->select_context($table, $join, $columns, $where) . ' LIMIT 1'); + $query = $this->query($this->selectContext($table, $join, $columns, $where) . ' LIMIT 1'); if ($query) { @@ -1082,7 +1082,7 @@ public function get($table, $join = null, $columns = null, $where = null) $value = preg_replace('/^[\w]*\./i', "", $value); } - $this->data_map(0, $key, $value, $data[ 0 ], $stack); + $this->dataMap(0, $key, $value, $data[ 0 ], $stack); } return $stack[ 0 ]; @@ -1102,7 +1102,7 @@ public function has($table, $join, $where = null) { $column = null; - $query = $this->query('SELECT EXISTS(' . $this->select_context($table, $join, $column, $where, 1) . ')'); + $query = $this->query('SELECT EXISTS(' . $this->selectContext($table, $join, $column, $where, 1) . ')'); if ($query) { @@ -1116,14 +1116,14 @@ public function has($table, $join, $where = null) public function count($table, $join = null, $column = null, $where = null) { - $query = $this->query($this->select_context($table, $join, $column, $where, 'COUNT')); + $query = $this->query($this->selectContext($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')); + $query = $this->query($this->selectContext($table, $join, $column, $where, 'MAX')); if ($query) { @@ -1139,7 +1139,7 @@ public function max($table, $join, $column = null, $where = null) public function min($table, $join, $column = null, $where = null) { - $query = $this->query($this->select_context($table, $join, $column, $where, 'MIN')); + $query = $this->query($this->selectContext($table, $join, $column, $where, 'MIN')); if ($query) { @@ -1155,14 +1155,14 @@ public function min($table, $join, $column = null, $where = null) public function avg($table, $join, $column = null, $where = null) { - $query = $this->query($this->select_context($table, $join, $column, $where, 'AVG')); + $query = $this->query($this->selectContext($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')); + $query = $this->query($this->selectContext($table, $join, $column, $where, 'SUM')); return $query ? 0 + $query->fetchColumn() : false; } @@ -1190,7 +1190,7 @@ public function action($actions) } } - public function lastInsertId() + public function id() { if ($this->database_type == 'oracle') { @@ -1216,7 +1216,7 @@ public function error() return $this->pdo->errorInfo(); } - public function last_query() + public function lastQuery() { return end($this->logs); } From 56085f60098af188c9e6dfca06f7e8be6c29a0ac Mon Sep 17 00:00:00 2001 From: Catfan Date: Mon, 30 Jan 2017 16:53:05 +0800 Subject: [PATCH 20/25] [feature] Add command option for initialization --- src/Medoo.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Medoo.php b/src/Medoo.php index 617918a6..7a6c5d93 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -30,8 +30,6 @@ class Medoo public function __construct($options = null) { try { - $commands = []; - if (is_array($options)) { if (isset($options['database_type'])) @@ -54,6 +52,15 @@ public function __construct($options = null) $this->option = $options['option']; } + if (isset($options['command']) && is_array($options['command'])) + { + $commands = $options['command']; + } + else + { + $commands = []; + } + if (isset($options['dsn'])) { if (isset($options['dsn']['driver'])) From 130e3cd77872152766e8b4adba0a6fdf63ffde05 Mon Sep 17 00:00:00 2001 From: Catfan Date: Tue, 7 Feb 2017 05:09:44 +0800 Subject: [PATCH 21/25] [feature] Enable to use AND and OR for like condition #436 #525 $database->select("account", "id", [ "name[~]" => ["AND" => ["lon", "on"] ]); --- src/Medoo.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Medoo.php b/src/Medoo.php index 7a6c5d93..636e6be2 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -454,6 +454,18 @@ protected function dataImplode($data, $conjunctor, $outer_conjunctor = null) $value = [$value]; } + $connector = ' OR '; + $stack = array_values($value); + + if (is_array($stack[0])) + { + if (isset($value['AND']) || isset($value['OR'])) + { + $connector = ' ' . array_keys($value)[0] . ' '; + $value = $stack[0]; + } + } + $like_clauses = []; foreach ($value as $item) @@ -468,7 +480,7 @@ protected function dataImplode($data, $conjunctor, $outer_conjunctor = null) $like_clauses[] = $column . ($operator === '!~' ? ' NOT' : '') . ' LIKE ' . $this->fnQuote($key, $item); } - $wheres[] = implode(' OR ', $like_clauses); + $wheres[] = implode($connector, $like_clauses); } if (in_array($operator, ['>', '>=', '<', '<='])) From 112521ea7e41fa652951752fa4c0ef436f946f13 Mon Sep 17 00:00:00 2001 From: Catfan Date: Wed, 8 Feb 2017 05:02:53 +0800 Subject: [PATCH 22/25] [fix] Add quote for LIKE condition --- src/Medoo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Medoo.php b/src/Medoo.php index 636e6be2..94591603 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -480,7 +480,7 @@ protected function dataImplode($data, $conjunctor, $outer_conjunctor = null) $like_clauses[] = $column . ($operator === '!~' ? ' NOT' : '') . ' LIKE ' . $this->fnQuote($key, $item); } - $wheres[] = implode($connector, $like_clauses); + $wheres[] = '(' . implode($connector, $like_clauses) . ')'; } if (in_array($operator, ['>', '>=', '<', '<='])) From a9d7d8d78a7155b569799739f215d190b55be94d Mon Sep 17 00:00:00 2001 From: Catfan Date: Wed, 8 Feb 2017 05:52:29 +0800 Subject: [PATCH 23/25] [update] Change lastQuery() to last() --- src/Medoo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Medoo.php b/src/Medoo.php index 94591603..9f2e70ae 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -1235,7 +1235,7 @@ public function error() return $this->pdo->errorInfo(); } - public function lastQuery() + public function last() { return end($this->logs); } From df99bffed0efa3fbc6a56b29bca88eaa3d4d3b76 Mon Sep 17 00:00:00 2001 From: Catfan Date: Wed, 8 Feb 2017 06:34:55 +0800 Subject: [PATCH 24/25] [update] Ignore info warning if attribute not support by driver #241 #205 #130 --- src/Medoo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Medoo.php b/src/Medoo.php index 9f2e70ae..b1cf7b69 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -1257,7 +1257,7 @@ public function info() foreach ($output as $key => $value) { - $output[ $key ] = $this->pdo->getAttribute(constant('PDO::ATTR_' . $value)); + $output[ $key ] = @$this->pdo->getAttribute(constant('PDO::ATTR_' . $value)); } return $output; From 479d064964f7c8f600ba0d4b02fc4c27a7d56acd Mon Sep 17 00:00:00 2001 From: Catfan Date: Wed, 8 Feb 2017 18:01:32 +0800 Subject: [PATCH 25/25] [update] Update readme file --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a622b9c2..d6b58912 100644 --- a/README.md +++ b/README.md @@ -10,18 +10,22 @@ ## Main Features -* **Lightweight** - 25KB around with only one file. +* **Lightweight** - 26KB around with only one file. * **Easy** - Extremely easy to learn and use, friendly construction. -* **Powerful** - Support various common and complex SQL queries. +* **Powerful** - Support various common and complex SQL queries, data mapping, and prevent SQL injection. * **Compatible** - Support all SQL databases, including MySQL, MSSQL, SQLite, MariaDB, Sybase, Oracle, PostgreSQL and more. -* **Security** - Prevent SQL injection. +* **Friendly** - Work well with every PHP frameworks, like Laravel, Codeigniter, Yii, Slim, and framework which supports singleton extension. * **Free** - Under MIT license, you can use it anywhere if you want. +## Requirement + +PHP 5.4+ and PDO extension installed + ## Get Started ### Install via composer