From fe6245798169e3cd01c4a12b6e9438a3cd73a7a5 Mon Sep 17 00:00:00 2001 From: Angel Date: Mon, 12 Apr 2021 04:46:34 +0800 Subject: [PATCH] [update] Code style update --- .php_cs.dist | 5 +- src/Medoo.php | 301 +++++++++++++++++++++++++++------------- tests/AggregateTest.php | 28 ++-- tests/CreateTest.php | 11 +- tests/DeleteTest.php | 8 +- tests/DropTest.php | 8 +- tests/GetTest.php | 5 +- tests/HasTest.php | 5 +- tests/InsertTest.php | 17 ++- tests/MedooTestCase.php | 14 +- tests/QuoteTest.php | 13 +- tests/RandTest.php | 5 +- tests/ReplaceTest.php | 8 +- tests/SelectTest.php | 18 ++- tests/UpdateTest.php | 5 +- tests/WhereTest.php | 83 +++++++---- 16 files changed, 346 insertions(+), 188 deletions(-) diff --git a/.php_cs.dist b/.php_cs.dist index 39128c3c..74c6f0f6 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -3,9 +3,12 @@ return PhpCsFixer\Config::create() ->setRules([ '@PSR2' => true, + '@PSR12' => true, 'array_syntax' => ['syntax' => 'short'] ]) ->setFinder( - PhpCsFixer\Finder::create()->in(__DIR__ . '/src') + PhpCsFixer\Finder::create() + ->in(__DIR__ . '/src') + ->in(__DIR__ . '/tests') ) ; \ No newline at end of file diff --git a/src/Medoo.php b/src/Medoo.php index 0799ac48..c8783706 100644 --- a/src/Medoo.php +++ b/src/Medoo.php @@ -1,9 +1,10 @@ 'localhost', * 'username' => 'your_username', * 'password' => 'your_password', - * + * * // [optional] * 'charset' => 'utf8mb4', * 'port' => 3306, * 'prefix' => 'PREFIX_' * ]); * ``` - * + * * @param array $options Connection options * @return Medoo * @throws PDOException @@ -213,6 +214,7 @@ public function __construct(array $options) $options['host'] = $options['host'] ?? $options['server']; if (isset($options['type'])) { + $this->type = strtolower($options['type']); if ($this->type === 'mariadb') { @@ -225,9 +227,12 @@ public function __construct(array $options) } $option = $options['option'] ?? []; - $commands = (isset($options['command']) && is_array($options['command'])) ? $options['command'] : []; + $commands = (isset($options['command']) && is_array($options['command'])) ? + $options['command'] : + []; switch ($this->type) { + case 'mysql': // Make MySQL using standard quoted identifier $commands[] = 'SET SQL_MODE=ANSI_QUOTES'; @@ -245,6 +250,7 @@ public function __construct(array $options) } if (isset($options['pdo'])) { + if (!$options['pdo'] instanceof PDO) { throw new InvalidArgumentException('Invalid PDO object supplied.'); } @@ -259,12 +265,14 @@ public function __construct(array $options) } if (isset($options['dsn'])) { + if (is_array($options['dsn']) && isset($options['dsn']['driver'])) { $attr = $options['dsn']; } else { throw new InvalidArgumentException('Invalid DSN option supplied.'); } } else { + if ( isset($options['port']) && is_int($options['port'] * 1) @@ -275,6 +283,7 @@ public function __construct(array $options) $isPort = isset($port); switch ($this->type) { + case 'mysql': $attr = [ 'driver' => 'mysql', @@ -335,6 +344,7 @@ public function __construct(array $options) case 'mssql': if (isset($options['driver']) && $options['driver'] === 'dblib') { + $attr = [ 'driver' => 'dblib', 'host' => $options['host'] . ($isPort ? ':' . $port : ''), @@ -349,6 +359,7 @@ public function __construct(array $options) $attr['charset'] = $options['charset']; } } else { + $attr = [ 'driver' => 'sqlsrv', 'Server' => $options['host'] . ($isPort ? ',' . $port : ''), @@ -383,6 +394,7 @@ public function __construct(array $options) ]; foreach ($config as $value) { + $keyname = strtolower(preg_replace(['/([a-z\d])([A-Z])/', '/([^_])([A-Z][a-z])/'], '$1_$2', $value)); if (isset($options[$keyname])) { @@ -444,6 +456,7 @@ public function __construct(array $options) ); if (isset($options['error'])) { + $modes = [ PDO::ERRMODE_SILENT, PDO::ERRMODE_WARNING, @@ -471,7 +484,7 @@ public function __construct(array $options) * * @return string */ - protected function mapKey() : string + protected function mapKey(): string { return ':MeD' . $this->guid++ . '_mK'; } @@ -483,10 +496,9 @@ protected function mapKey() : string * @param array $map The array of input parameters value for prepared statement. * @return \PDOStatement|null */ - public function query(string $statement, array $map = []) : ?PDOStatement + public function query(string $statement, array $map = []): ?PDOStatement { $raw = $this->raw($statement, $map); - $statement = $this->buildRaw($raw, $map); return $this->exec($statement, $map); @@ -499,7 +511,7 @@ public function query(string $statement, array $map = []) : ?PDOStatement * @param array $map The array of input parameters value for prepared statement. * @return \PDOStatement|null */ - public function exec(string $statement, array $map = [], callable $callback = null) : ?PDOStatement + public function exec(string $statement, array $map = [], callable $callback = null): ?PDOStatement { $this->statement = null; $this->errorInfo = null; @@ -531,10 +543,10 @@ public function exec(string $statement, array $map = [], callable $callback = nu } $statement = $this->pdo->prepare($statement); - $errorInfo = $this->pdo->errorInfo(); if ($errorInfo[0] !== '00000') { + $this->errorInfo = $errorInfo; $this->error = $errorInfo[2]; @@ -550,10 +562,10 @@ public function exec(string $statement, array $map = [], callable $callback = nu } $execute = $statement->execute(); - $errorInfo = $statement->errorInfo(); if ($errorInfo[0] !== '00000') { + $this->errorInfo = $errorInfo; $this->error = $errorInfo[2]; @@ -574,7 +586,7 @@ public function exec(string $statement, array $map = [], callable $callback = nu * @param array $map * @return string */ - protected function generate(string $statement, array $map) : string + protected function generate(string $statement, array $map): string { $identifier = [ 'mysql' => '`$1`', @@ -588,6 +600,7 @@ protected function generate(string $statement, array $map) : string ); foreach ($map as $key => $value) { + if ($value[1] === PDO::PARAM_STR) { $replace = $this->quote($value[0]); } elseif ($value[1] === PDO::PARAM_NULL) { @@ -611,7 +624,7 @@ protected function generate(string $statement, array $map) : string * @param array $map The array of mapping data for the raw string. * @return Medoo::raw */ - public static function raw(string $string, array $map = []) : Raw + public static function raw(string $string, array $map = []): Raw { $raw = new Raw(); @@ -627,7 +640,7 @@ public static function raw(string $string, array $map = []) : Raw * @param object $object * @return bool */ - protected function isRaw($object) : bool + protected function isRaw($object): bool { return $object instanceof Raw; } @@ -639,7 +652,7 @@ protected function isRaw($object) : bool * @param array $map * @return string|null */ - protected function buildRaw($raw, array &$map) : ?string + protected function buildRaw($raw, array &$map): ?string { if (!$this->isRaw($raw)) { return null; @@ -648,6 +661,7 @@ protected function buildRaw($raw, array &$map) : ?string $query = preg_replace_callback( '/(([`\']).*?)?((FROM|TABLE|INTO|UPDATE|JOIN)\s*)?\<(((?![_\d])[\p{N}\p{L}_]+)(\.(?![_\d])[\p{N}\p{L}_]+)?)\>(.*?\2)?/u', function ($matches) { + if (!empty($matches[2]) && isset($matches[8])) { return $matches[0]; } @@ -664,6 +678,7 @@ function ($matches) { $rawMap = $raw->map; if (!empty($rawMap)) { + foreach ($rawMap as $key => $value) { $map[$key] = $this->typeMap($value, gettype($value)); } @@ -678,7 +693,7 @@ function ($matches) { * @param string $string * @return string */ - public function quote(string $string) : string + public function quote(string $string): string { if ($this->type === 'mysql') { return "'" . preg_replace(['/([\'"])/', '/(\\\\\\\")/'], ["\\\\\${1}", '\\\${1}'], $string) . "'"; @@ -693,7 +708,7 @@ public function quote(string $string) : string * @param string $table * @return string */ - public function tableQuote(string $table) : string + public function tableQuote(string $table): string { if (preg_match('/^(?![_\d])[\p{N}\p{L}_]+$/u', $table)) { return '"' . $this->prefix . $table . '"'; @@ -708,7 +723,7 @@ public function tableQuote(string $table) : string * @param string $column * @return string */ - public function columnQuote(string $column) : string + public function columnQuote(string $column): string { if (preg_match('/^(?![_\d])[\p{N}\p{L}_]+(\.?(?![_\d])[\p{N}\p{L}_]+)?$/u', $column)) { return strpos($column, '.') !== false ? @@ -726,7 +741,7 @@ public function columnQuote(string $column) : string * @param string $type * @return array */ - protected function typeMap($value, string $type) : array + protected function typeMap($value, string $type): array { $map = [ 'NULL' => PDO::PARAM_NULL, @@ -756,7 +771,7 @@ protected function typeMap($value, string $type) : array * @param bool $isJoin * @return string */ - protected function columnPush(&$columns, array &$map, bool $root, bool $isJoin = false) : string + protected function columnPush(&$columns, array &$map, bool $root, bool $isJoin = false): string { if ($columns === '*') { return $columns; @@ -769,17 +784,23 @@ protected function columnPush(&$columns, array &$map, bool $root, bool $isJoin = } foreach ($columns as $key => $value) { + if (!is_int($key) && is_array($value) && $root && count(array_keys($columns)) === 1) { - $stack[] = $this->columnQuote($key); + $stack[] = $this->columnQuote($key); $stack[] = $this->columnPush($value, $map, false, $isJoin); + } elseif (is_array($value)) { + $stack[] = $this->columnPush($value, $map, false, $isJoin); + } elseif (!is_int($key) && $raw = $this->buildRaw($value, $map)) { - preg_match('/(?[\p{N}\p{L}_\.]+)(\s*\[(?(String|Bool|Int|Number))\])?/u', $key, $match); + preg_match('/(?[\p{N}\p{L}_\.]+)(\s*\[(?(String|Bool|Int|Number))\])?/u', $key, $match); $stack[] = "{$raw} AS {$this->columnQuote($match['column'])}"; + } elseif (is_int($key) && is_string($value)) { + if ($isJoin && strpos($value, '*') !== false) { throw new InvalidArgumentException('Cannot use table.* to select all columns while joining table.'); } @@ -787,8 +808,8 @@ protected function columnPush(&$columns, array &$map, bool $root, bool $isJoin = preg_match('/(?[^(]+)(?:\s*\((?.+)\))?(?:\s*\[(?(?:String|Bool|Int|Number|Object|JSON))\])?/u', $value, $match); if (!empty($match['alias'])) { - $stack[] = "{$this->columnQuote($match['column'])} AS {$this->columnQuote($match['alias'])}"; + $stack[] = "{$this->columnQuote($match['column'])} AS {$this->columnQuote($match['alias'])}"; $columns[$key] = $match['alias']; if (!empty($match['type'])) { @@ -812,7 +833,7 @@ protected function columnPush(&$columns, array &$map, bool $root, bool $isJoin = * @param string $outerConjunctor * @return string */ - protected function innerConjunct(array $data, array $map, string $conjunctor, string $outerConjunctor) : string + protected function innerConjunct(array $data, array $map, string $conjunctor, string $outerConjunctor): string { $stack = []; @@ -831,11 +852,12 @@ protected function innerConjunct(array $data, array $map, string $conjunctor, st * @param string $conjunctor * @return string */ - protected function dataImplode(array $data, array &$map, string $conjunctor) : string + protected function dataImplode(array $data, array &$map, string $conjunctor): string { $stack = []; foreach ($data as $key => $value) { + $type = gettype($value); if ( @@ -843,7 +865,6 @@ protected function dataImplode(array $data, array &$map, string $conjunctor) : s preg_match("/^(AND|OR)(\s+#.*)?$/", $key, $relationMatch) ) { $relationship = $relationMatch[1]; - $stack[] = $value !== array_keys(array_keys($value)) ? '(' . $this->dataImplode($value, $map, ' ' . $relationship) . ')' : '(' . $this->innerConjunct($value, $map, ' ' . $relationship, $conjunctor) . ')'; @@ -859,28 +880,39 @@ protected function dataImplode(array $data, array &$map, string $conjunctor) : s ) { $stack[] = $this->columnQuote($match[1]) . ' ' . $match['operator'] . ' ' . $this->columnQuote($match[3]); } else { + preg_match('/([\p{N}\p{L}_\.]+)(\[(?\>\=?|\<\=?|\!|\<\>|\>\<|\!?~|REGEXP)\])?/u', $key, $match); $column = $this->columnQuote($match[1]); if (isset($match['operator'])) { + $operator = $match['operator']; if (in_array($operator, ['>', '>=', '<', '<='])) { + $condition = "{$column} {$operator} "; if (is_numeric($value)) { + $condition .= $mapKey; $map[$mapKey] = [$value, is_float($value) ? PDO::PARAM_STR : PDO::PARAM_INT]; + } elseif ($raw = $this->buildRaw($value, $map)) { + $condition .= $raw; + } else { + $condition .= $mapKey; $map[$mapKey] = [$value, PDO::PARAM_STR]; } $stack[] = $condition; + } elseif ($operator === '!') { + switch ($type) { + case 'NULL': $stack[] = $column . ' IS NOT NULL'; break; @@ -889,8 +921,8 @@ protected function dataImplode(array $data, array &$map, string $conjunctor) : s $placeholders = []; foreach ($value as $index => $item) { - $stackKey = $mapKey . $index . '_i'; + $stackKey = $mapKey . $index . '_i'; $placeholders[] = $stackKey; $map[$stackKey] = $this->typeMap($item, gettype($item)); } @@ -912,7 +944,9 @@ protected function dataImplode(array $data, array &$map, string $conjunctor) : s $map[$mapKey] = $this->typeMap($value, $type); break; } + } elseif ($operator === '~' || $operator === '!~') { + if ($type !== 'array') { $value = [$value]; } @@ -921,7 +955,9 @@ protected function dataImplode(array $data, array &$map, string $conjunctor) : s $data = array_values($value); if (is_array($data[0])) { + if (isset($value['AND']) || isset($value['OR'])) { + $connector = ' ' . array_keys($value)[0] . ' '; $value = $data[0]; } @@ -930,6 +966,7 @@ protected function dataImplode(array $data, array &$map, string $conjunctor) : s $likeClauses = []; foreach ($value as $index => $item) { + $item = strval($item); if (!preg_match('/(\[.+\]|[\*\?\!\%\-#^_]|%.+|.+%)/', $item)) { @@ -941,8 +978,11 @@ protected function dataImplode(array $data, array &$map, string $conjunctor) : s } $stack[] = '(' . implode($connector, $likeClauses) . ')'; + } elseif ($operator === '<>' || $operator === '><') { + if ($type === 'array') { + if ($operator === '><') { $column .= ' NOT'; } @@ -954,12 +994,17 @@ protected function dataImplode(array $data, array &$map, string $conjunctor) : s $map[$mapKey . 'a'] = [$value[0], $dataType]; $map[$mapKey . 'b'] = [$value[1], $dataType]; } + } elseif ($operator === 'REGEXP') { + $stack[] = "{$column} REGEXP {$mapKey}"; $map[$mapKey] = [$value, PDO::PARAM_STR]; } + } else { + switch ($type) { + case 'NULL': $stack[] = $column . ' IS NULL'; break; @@ -1005,11 +1050,12 @@ protected function dataImplode(array $data, array &$map, string $conjunctor) : s * @param array $map * @return string */ - protected function whereClause($where, array &$map) : string + protected function whereClause($where, array &$map): string { $whereClause = ''; if (is_array($where)) { + $conditions = array_diff_key($where, array_flip( ['GROUP', 'ORDER', 'HAVING', 'LIMIT', 'LIKE', 'MATCH'] )); @@ -1019,9 +1065,11 @@ protected function whereClause($where, array &$map) : string } if (isset($where['MATCH']) && $this->type === 'mysql') { + $match = $where['MATCH']; if (is_array($match) && isset($match['columns'], $match['keyword'])) { + $mode = ''; $modeMap = [ @@ -1038,15 +1086,16 @@ protected function whereClause($where, array &$map) : string $columns = implode(', ', array_map([$this, 'columnQuote'], $match['columns'])); $mapKey = $this->mapKey(); $map[$mapKey] = [$match['keyword'], PDO::PARAM_STR]; - $whereClause .= ($whereClause !== '' ? ' AND ' : ' WHERE') . ' MATCH (' . $columns . ') AGAINST (' . $mapKey . $mode . ')'; } } if (isset($where['GROUP'])) { + $group = $where['GROUP']; if (is_array($group)) { + $stack = []; foreach ($group as $column => $value) { @@ -1054,13 +1103,18 @@ protected function whereClause($where, array &$map) : string } $whereClause .= ' GROUP BY ' . implode(',', $stack); + } elseif ($raw = $this->buildRaw($group, $map)) { + $whereClause .= ' GROUP BY ' . $raw; + } else { + $whereClause .= ' GROUP BY ' . $this->columnQuote($group); } if (isset($where['HAVING'])) { + if ($raw = $this->buildRaw($where['HAVING'], $map)) { $whereClause .= ' HAVING ' . $raw; } else { @@ -1070,12 +1124,15 @@ protected function whereClause($where, array &$map) : string } if (isset($where['ORDER'])) { + $order = $where['ORDER']; if (is_array($order)) { + $stack = []; foreach ($order as $column => $value) { + if (is_array($value)) { $valueStack = []; @@ -1085,27 +1142,36 @@ protected function whereClause($where, array &$map) : string } $valueString = implode(',', $valueStack); - $stack[] = "FIELD({$this->columnQuote($column)}, {$valueString})"; + } elseif ($value === 'ASC' || $value === 'DESC') { + $stack[] = $this->columnQuote($column) . ' ' . $value; + } elseif (is_int($column)) { + $stack[] = $this->columnQuote($value); } } $whereClause .= ' ORDER BY ' . implode(',', $stack); + } elseif ($raw = $this->buildRaw($order, $map)) { + $whereClause .= ' ORDER BY ' . $raw; + } else { + $whereClause .= ' ORDER BY ' . $this->columnQuote($order); } } if (isset($where['LIMIT'])) { + $limit = $where['LIMIT']; if (in_array($this->type, ['oracle', 'mssql'])) { + if ($this->type === 'mssql' && !isset($where['ORDER'])) { $whereClause .= ' ORDER BY (SELECT 0)'; } @@ -1121,8 +1187,9 @@ protected function whereClause($where, array &$map) : string ) { $whereClause .= " OFFSET {$limit[0]} ROWS FETCH NEXT {$limit[1]} ROWS ONLY"; } - } - else { + + } else { + if (is_numeric($limit)) { $whereClause .= ' LIMIT ' . $limit; } elseif ( @@ -1159,18 +1226,19 @@ protected function selectContext( &$columns = null, array $where = null, $columnFn = null - ) : string - { + ): string { + preg_match('/(?(?![_\d])[\p{N}\p{L}_]+)\s*\((?(?![_\d])[\p{N}\p{L}_]+)\)/u', $table, $tableMatch); if (isset($tableMatch['table'], $tableMatch['alias'])) { + $table = $this->tableQuote($tableMatch['table']); $tableAlias = $this->tableQuote($tableMatch['alias']); - $tableQuery = "{$table} AS {$tableAlias}"; + } else { - $table = $this->tableQuote($table); + $table = $this->tableQuote($table); $tableQuery = $table; } @@ -1179,7 +1247,9 @@ protected function selectContext( if ($isJoin) { $tableQuery .= ' ' . $this->buildJoin($tableAlias ?? $table, $join); } else { + if (is_null($columns)) { + if ( !is_null($where) || (is_array($join) && isset($columnFn)) @@ -1197,15 +1267,20 @@ protected function selectContext( } if (isset($columnFn)) { + if ($columnFn === 1) { + $column = '1'; if (is_null($where)) { $where = $columns; } } elseif ($raw = $this->buildRaw($columnFn, $map)) { + $column = $raw; + } else { + if (empty($columns) || $this->isRaw($columns)) { $columns = '*'; $where = $join; @@ -1226,7 +1301,7 @@ protected function selectContext( * @param mixed $join * @return string */ - protected function isJoin($join) : bool + protected function isJoin($join): bool { if (!is_array($join)) { return false; @@ -1252,10 +1327,9 @@ protected function isJoin($join) : bool * @param array $join * @return string */ - protected function buildJoin(string $table, array $join) : string + protected function buildJoin(string $table, array $join): string { $tableJoin = []; - $joinMap = [ '>' => 'LEFT', '<' => 'RIGHT', @@ -1264,9 +1338,11 @@ protected function buildJoin(string $table, array $join) : string ]; foreach ($join as $subTable => $relation) { + preg_match('/(\[(?\<\>?|\>\(?![_\d])[\p{N}\p{L}_]+)\s?(\((?(?![_\d])[\p{N}\p{L}_]+)\))?/u', $subTable, $match); if ($match['join'] !== '' && $match['table'] !== '') { + if (is_string($relation)) { $relation = 'USING ("' . $relation . '")'; } @@ -1274,11 +1350,15 @@ protected function buildJoin(string $table, array $join) : string if (is_array($relation)) { // For ['column1', 'column2'] if (isset($relation[0])) { + $relation = 'USING ("' . implode('", "', $relation) . '")'; + } else { + $joins = []; foreach ($relation as $key => $value) { + $joins[] = ( strpos($key, '.') > 0 ? // For ['tableB.column' => 'column'] @@ -1316,14 +1396,16 @@ protected function buildJoin(string $table, array $join) : string * @param bool $root * @return array */ - protected function columnMap($columns, array &$stack, bool $root) : array + protected function columnMap($columns, array &$stack, bool $root): array { if ($columns === '*') { return $stack; } foreach ($columns as $key => $value) { + if (is_int($key)) { + preg_match('/((?![_\d])[\p{N}\p{L}_]+\.)?(?(?![_\d])[\p{N}\p{L}_]+)(?:\s*\((?(?![_\d])[\p{N}\p{L}_]+)\))?(?:\s*\[(?(?:String|Bool|Int|Number|Object|JSON))\])?/u', $value, $keyMatch); $columnKey = !empty($keyMatch['alias']) ? @@ -1335,15 +1417,16 @@ protected function columnMap($columns, array &$stack, bool $root) : array [$columnKey, 'String']; } elseif ($this->isRaw($value)) { - preg_match('/((?![_\d])[\p{N}\p{L}_]+\.)?(?(?![_\d])[\p{N}\p{L}_]+)(\s*\[(?(String|Bool|Int|Number))\])?/u', $key, $keyMatch); + preg_match('/((?![_\d])[\p{N}\p{L}_]+\.)?(?(?![_\d])[\p{N}\p{L}_]+)(\s*\[(?(String|Bool|Int|Number))\])?/u', $key, $keyMatch); $columnKey = $keyMatch['column']; $stack[$key] = isset($keyMatch['type']) ? [$columnKey, $keyMatch['type']] : [$columnKey, 'String']; - + } elseif (!is_int($key) && is_array($value)) { + if ($root && count(array_keys($columns)) === 1) { $stack[$key] = [$key, 'String']; } @@ -1366,20 +1449,28 @@ protected function columnMap($columns, array &$stack, bool $root) : array * @param array $result * @return void */ - protected function dataMap(array $data, array $columns, array $columnMap, array &$stack, bool $root, array &$result = null) : void - { + protected function dataMap( + array $data, + array $columns, + array $columnMap, + array &$stack, + bool $root, + array &$result = null + ): void { + if ($root) { + $columnsKey = array_keys($columns); if (count($columnsKey) === 1 && is_array($columns[$columnsKey[0]])) { + $indexKey = array_keys($columns)[0]; $dataKey = preg_replace("/^(?![_\d])[\p{N}\p{L}_]+\./u", "", $indexKey); - $currentStack = []; foreach ($data as $item) { - $this->dataMap($data, $columns[$indexKey], $columnMap, $currentStack, false, $result); + $this->dataMap($data, $columns[$indexKey], $columnMap, $currentStack, false, $result); $index = $data[$dataKey]; if (isset($result)) { @@ -1389,8 +1480,8 @@ protected function dataMap(array $data, array $columns, array $columnMap, array } } } else { + $currentStack = []; - $this->dataMap($data, $columns, $columnMap, $currentStack, false, $result); if (isset($result)) { @@ -1404,16 +1495,17 @@ protected function dataMap(array $data, array $columns, array $columnMap, array } foreach ($columns as $key => $value) { + $isRaw = $this->isRaw($value); if (is_int($key) || $isRaw) { - $map = $columnMap[$isRaw ? $key : $value]; + $map = $columnMap[$isRaw ? $key : $value]; $columnKey = $map[0]; - $item = $data[$columnKey]; if (isset($map[1])) { + if ($isRaw && in_array($map[1], ['Object', 'JSON'])) { continue; } @@ -1424,6 +1516,7 @@ protected function dataMap(array $data, array $columns, array $columnMap, array } switch ($map[1]) { + case 'Number': $stack[$columnKey] = (double) $item; break; @@ -1452,8 +1545,8 @@ protected function dataMap(array $data, array $columns, array $columnMap, array $stack[$columnKey] = $item; } } else { - $currentStack = []; + $currentStack = []; $this->dataMap($data, $value, $columnMap, $currentStack, false, $result); $stack[$key] = $currentStack; @@ -1469,13 +1562,14 @@ protected function dataMap(array $data, array $columns, array $columnMap, array * @param array $options Additional table options for creating a table. * @return \PDOStatement|null */ - public function create(string $table, $columns, $options = null) : ?PDOStatement + public function create(string $table, $columns, $options = null): ?PDOStatement { $stack = []; - + $tableOption = ''; $tableName = $this->tableQuote($this->prefix . $table); foreach ($columns as $name => $definition) { + if (is_int($name)) { $stack[] = preg_replace('/\<((?![_\d])[\p{N}\p{L}_]+)\>/u', '"$1"', $definition); } elseif (is_array($definition)) { @@ -1485,18 +1579,19 @@ public function create(string $table, $columns, $options = null) : ?PDOStatement } } - $tableOption = ''; - if (is_array($options)) { + $optionStack = []; foreach ($options as $key => $value) { + if (is_string($value) || is_int($value)) { $optionStack[] = "{$key} = {$value}"; } } $tableOption = ' ' . implode(', ', $optionStack); + } elseif (is_string($options)) { $tableOption = ' ' . $options; } @@ -1516,7 +1611,7 @@ public function create(string $table, $columns, $options = null) : ?PDOStatement * @param string $table * @return \PDOStatement|null */ - public function drop(string $table) : ?PDOStatement + public function drop(string $table): ?PDOStatement { $tableName = $this->prefix . $table; @@ -1532,7 +1627,7 @@ public function drop(string $table) : ?PDOStatement * @param array $where * @return array|null */ - public function select(string $table, $join, $columns = null, $where = null) : ?array + public function select(string $table, $join, $columns = null, $where = null): ?array { $map = []; $result = []; @@ -1558,6 +1653,7 @@ public function select(string $table, $join, $columns = null, $where = null) : ? if ($columns === '*') { if (isset($callback)) { + while ($data = $statement->fetch(PDO::FETCH_ASSOC)) { $callback($data); } @@ -1573,15 +1669,15 @@ public function select(string $table, $join, $columns = null, $where = null) : ? $currentStack = []; if (isset($callback)) { - + $this->dataMap($data, $columns, $columnMap, $currentStack, true); - $callback($isSingle ? + $callback( + $isSingle ? $currentStack[$columnMap[$column][0]] : $currentStack ); - } - else { + } else { $this->dataMap($data, $columns, $columnMap, $currentStack, true, $result); } } @@ -1591,6 +1687,7 @@ public function select(string $table, $join, $columns = null, $where = null) : ? } if ($isSingle) { + $singleResult = []; $resultKey = $columnMap[$column][0]; @@ -1612,7 +1709,7 @@ public function select(string $table, $join, $columns = null, $where = null) : ? * @param string $primaryKey * @return \PDOStatement|null */ - public function insert(string $table, array $datas, string $primaryKey = null) : ?PDOStatement + public function insert(string $table, array $datas, string $primaryKey = null): ?PDOStatement { $stack = []; $columns = []; @@ -1624,6 +1721,7 @@ public function insert(string $table, array $datas, string $primaryKey = null) : } foreach ($datas as $data) { + foreach ($data as $key => $value) { $columns[] = $key; } @@ -1632,26 +1730,28 @@ public function insert(string $table, array $datas, string $primaryKey = null) : $columns = array_unique($columns); foreach ($datas as $data) { + $values = []; foreach ($columns as $key) { + if ($raw = $this->buildRaw($data[$key], $map)) { $values[] = $raw; continue; } $mapKey = $this->mapKey(); - $values[] = $mapKey; if (!isset($data[$key])) { $map[$mapKey] = [null, PDO::PARAM_NULL]; } else { - $value = $data[$key]; + $value = $data[$key]; $type = gettype($value); switch ($type) { + case 'array': $map[$mapKey] = [ strpos($key, '[JSON]') === strlen($key) - 6 ? @@ -1687,9 +1787,10 @@ public function insert(string $table, array $datas, string $primaryKey = null) : $query = 'INSERT INTO ' . $this->tableQuote($table) . ' (' . implode(', ', $fields) . ') VALUES ' . implode(', ', $stack); if ($this->type === 'oracle') { + if ($primaryKey) { - $returning = ""; + $returning = ""; $query .= ' RETURNING ' . $this->columnQuote($primaryKey) . ' INTO :RETURNID'; $statement = $this->exec($query, $map, function ($statement) use (&$returning) { @@ -1713,12 +1814,13 @@ public function insert(string $table, array $datas, string $primaryKey = null) : * @param array $where * @return \PDOStatement|null */ - public function update(string $table, $data, $where = null) : ?PDOStatement + public function update(string $table, $data, $where = null): ?PDOStatement { $fields = []; $map = []; foreach ($data as $key => $value) { + $column = $this->columnQuote(preg_replace("/(\s*\[(JSON|\+|\-|\*|\/)\]$)/", '', $key)); if ($raw = $this->buildRaw($value, $map)) { @@ -1727,19 +1829,20 @@ public function update(string $table, $data, $where = null) : ?PDOStatement } $mapKey = $this->mapKey(); - preg_match('/(?(?![_\d])[\p{N}\p{L}_]+)(\[(?\+|\-|\*|\/)\])?/u', $key, $match); if (isset($match['operator'])) { + if (is_numeric($value)) { $fields[] = "{$column} = {$column} {$match['operator']} {$value}"; } } else { - $fields[] = "{$column} = {$mapKey}"; + $fields[] = "{$column} = {$mapKey}"; $type = gettype($value); switch ($type) { + case 'array': $map[$mapKey] = [ strpos($key, '[JSON]') === strlen($key) - 6 ? @@ -1775,7 +1878,7 @@ public function update(string $table, $data, $where = null) : ?PDOStatement * @param array $where * @return \PDOStatement|null */ - public function delete(string $table, array $where) : ?PDOStatement + public function delete(string $table, array $where): ?PDOStatement { $map = []; @@ -1790,18 +1893,19 @@ public function delete(string $table, array $where) : ?PDOStatement * @param array $where * @return \PDOStatement|null */ - public function replace(string $table, array $columns, $where = null) : ?PDOStatement + public function replace(string $table, array $columns, $where = null): ?PDOStatement { $map = []; $stack = []; foreach ($columns as $column => $replacements) { + if (is_array($replacements)) { + foreach ($replacements as $old => $new) { - $mapKey = $this->mapKey(); + $mapKey = $this->mapKey(); $columnName = $this->columnQuote($column); - $stack[] = "{$columnName} = REPLACE({$columnName}, {$mapKey}a, {$mapKey}b)"; $map[$mapKey . 'a'] = [$old, PDO::PARAM_STR]; @@ -1834,7 +1938,7 @@ public function get(string $table, $join = null, $columns = null, $where = null) $currentStack = []; if ($where === null) { - + if ($this->isJoin($join)) { $where['LIMIT'] = 1; } else { @@ -1848,7 +1952,6 @@ public function get(string $table, $join = null, $columns = null, $where = null) } $isSingle = (is_string($column) && $column !== '*'); - $query = $this->exec($this->selectContext($table, $map, $join, $columns, $where), $map); if (!$this->statement) { @@ -1858,12 +1961,12 @@ public function get(string $table, $join = null, $columns = null, $where = null) $data = $query->fetchAll(PDO::FETCH_ASSOC); if (isset($data[0])) { + if ($column === '*') { return $data[0]; } $this->columnMap($columns, $columnMap, true); - $this->dataMap($data[0], $columns, $columnMap, $currentStack, true, $result); if ($isSingle) { @@ -1882,7 +1985,7 @@ public function get(string $table, $join = null, $columns = null, $where = null) * @param array $where * @return bool */ - public function has(string $table, $join, $where = null) : bool + public function has(string $table, $join, $where = null): bool { $map = []; $column = null; @@ -1911,7 +2014,7 @@ public function has(string $table, $join, $where = null) : bool * @param array $where * @return array */ - public function rand(string $table, $join = null, $columns = null, $where = null) : array + public function rand(string $table, $join = null, $columns = null, $where = null): array { $order = ($this->type === 'mysql' ? 'RAND()' : $this->type === 'mssql') ? 'NEWID()' @@ -1920,19 +2023,20 @@ public function rand(string $table, $join = null, $columns = null, $where = null $orderRaw = $this->raw($order); if ($where === null) { + if ($columns === null) { - $columns = [ - 'ORDER' => $orderRaw - ]; + + $columns = ['ORDER' => $orderRaw]; + } else { + $columns = $join; - unset($columns['ORDER']); + unset($columns['ORDER']); $columns['ORDER'] = $orderRaw; } } else { unset($where['ORDER']); - $where['ORDER'] = $orderRaw; } @@ -1949,7 +2053,7 @@ public function rand(string $table, $join = null, $columns = null, $where = null * @param array $where * @return string|null */ - private function aggregate(string $type, string $table, $join = null, $column = null, $where = null) : ?string + private function aggregate(string $type, string $table, $join = null, $column = null, $where = null): ?string { $map = []; @@ -1971,7 +2075,7 @@ private function aggregate(string $type, string $table, $join = null, $column = * @param array $where * @return int|null */ - public function count(string $table, $join = null, $column = null, $where = null) : ?int + public function count(string $table, $join = null, $column = null, $where = null): ?int { return (int) $this->aggregate('count', $table, $join, $column, $where); } @@ -1985,7 +2089,7 @@ public function count(string $table, $join = null, $column = null, $where = null * @param array $where * @return string|null */ - public function avg(string $table, $join, $column = null, $where = null) : ?string + public function avg(string $table, $join, $column = null, $where = null): ?string { return $this->aggregate('avg', $table, $join, $column, $where); } @@ -1999,7 +2103,7 @@ public function avg(string $table, $join, $column = null, $where = null) : ?stri * @param array $where * @return string|null */ - public function max(string $table, $join, $column = null, $where = null) : ?string + public function max(string $table, $join, $column = null, $where = null): ?string { return $this->aggregate('max', $table, $join, $column, $where); } @@ -2013,7 +2117,7 @@ public function max(string $table, $join, $column = null, $where = null) : ?stri * @param array $where * @return string|null */ - public function min(string $table, $join, $column = null, $where = null) : ?string + public function min(string $table, $join, $column = null, $where = null): ?string { return $this->aggregate('min', $table, $join, $column, $where); } @@ -2027,7 +2131,7 @@ public function min(string $table, $join, $column = null, $where = null) : ?stri * @param array $where * @return string|null */ - public function sum(string $table, $join, $column = null, $where = null) : ?string + public function sum(string $table, $join, $column = null, $where = null): ?string { return $this->aggregate('sum', $table, $join, $column, $where); } @@ -2038,9 +2142,10 @@ public function sum(string $table, $join, $column = null, $where = null) : ?stri * @param callable $actions * @return void */ - public function action(callable $actions) : void + public function action(callable $actions): void { if (is_callable($actions)) { + $this->pdo->beginTransaction(); try { @@ -2053,7 +2158,6 @@ public function action(callable $actions) : void } } catch (Exception $e) { $this->pdo->rollBack(); - throw $e; } } @@ -2065,13 +2169,14 @@ public function action(callable $actions) : void * @param string $name * @return string|null */ - public function id(string $name = null) : ?string + public function id(string $name = null): ?string { $type = $this->type; if ($type === 'oracle') { return $this->returnId; } elseif ($type === 'pgsql') { + $id = $this->pdo->query('SELECT LASTVAL()')->fetchColumn(); return (string) $id ?: null; @@ -2085,7 +2190,7 @@ public function id(string $name = null) : ?string * * @return Medoo */ - public function debug() : self + public function debug(): self { $this->debugMode = true; @@ -2097,7 +2202,7 @@ public function debug() : self * * @return void */ - public function beginDebug() : void + public function beginDebug(): void { $this->debugMode = true; $this->debugLogging = true; @@ -2108,7 +2213,7 @@ public function beginDebug() : void * * @return void */ - public function debugLog() : array + public function debugLog(): array { $this->debugMode = false; $this->debugLogging = false; @@ -2121,7 +2226,7 @@ public function debugLog() : array * * @return string|null */ - public function last() : ?string + public function last(): ?string { if (empty($this->logs)) { return null; @@ -2137,7 +2242,7 @@ public function last() : ?string * * @return string[] */ - public function log() : array + public function log(): array { return array_map( function ($log) { @@ -2152,7 +2257,7 @@ function ($log) { * * @return array */ - public function info() : array + public function info(): array { $output = [ 'server' => 'SERVER_INFO', diff --git a/tests/AggregateTest.php b/tests/AggregateTest.php index d49b3b73..a25b72dd 100644 --- a/tests/AggregateTest.php +++ b/tests/AggregateTest.php @@ -1,4 +1,5 @@ setType($type); - + $this->database->count("account", [ "gender" => "female" ]); - $this->assertQuery(<<assertQuery( + <<setType($type); - + $this->database->max("account", "age"); - $this->assertQuery(<<assertQuery( + <<setType($type); - + $this->database->min("account", "age"); - $this->assertQuery(<<assertQuery( + <<setType($type); - + $this->database->avg("account", "age"); - $this->assertQuery(<<assertQuery( + <<setType($type); - + $this->database->sum("account", "money"); - $this->assertQuery(<<assertQuery( + <<database->queryString ); } -} \ No newline at end of file +} diff --git a/tests/CreateTest.php b/tests/CreateTest.php index a0b5300d..cb4d2fbe 100644 --- a/tests/CreateTest.php +++ b/tests/CreateTest.php @@ -1,4 +1,5 @@ setType($type); - + $this->database->create("account", [ "id" => [ "INT", @@ -27,7 +28,8 @@ public function testCreate($type) "AUTO_INCREMENT" => 200 ]); - $this->assertQuery([ + $this->assertQuery( + [ 'default' => <<database->queryString + ], + $this->database->queryString ); } -} \ No newline at end of file +} diff --git a/tests/DeleteTest.php b/tests/DeleteTest.php index 3fff03dd..4d8c4a3b 100644 --- a/tests/DeleteTest.php +++ b/tests/DeleteTest.php @@ -1,4 +1,5 @@ setType($type); - + $this->database->delete("account", [ "AND" => [ "type" => "business", @@ -18,11 +19,12 @@ public function testDelete($type) ] ]); - $this->assertQuery(<<assertQuery( + <<database->queryString ); } -} \ No newline at end of file +} diff --git a/tests/DropTest.php b/tests/DropTest.php index 8a452471..6abdc2e5 100644 --- a/tests/DropTest.php +++ b/tests/DropTest.php @@ -1,4 +1,5 @@ setType($type); - + $this->database->drop("account"); - $this->assertQuery(<<assertQuery( + <<database->queryString ); } -} \ No newline at end of file +} diff --git a/tests/GetTest.php b/tests/GetTest.php index 2690eb61..f66e99e4 100644 --- a/tests/GetTest.php +++ b/tests/GetTest.php @@ -1,4 +1,5 @@ setType($type); - + $this->database->get("account", "email", [ "user_id" => 1234 ]); @@ -156,4 +157,4 @@ public function testGetWithJoinAndWhere($type) EOD, ], $this->database->queryString); } -} \ No newline at end of file +} diff --git a/tests/HasTest.php b/tests/HasTest.php index 6b86cd81..83915017 100644 --- a/tests/HasTest.php +++ b/tests/HasTest.php @@ -1,4 +1,5 @@ setType($type); - + $this->database->has("account", [ "user_name" => "foo" ]); @@ -24,4 +25,4 @@ public function testHas($type) EOD ], $this->database->queryString); } -} \ No newline at end of file +} diff --git a/tests/InsertTest.php b/tests/InsertTest.php index 1bd4aebf..97a2f233 100644 --- a/tests/InsertTest.php +++ b/tests/InsertTest.php @@ -1,4 +1,5 @@ setType($type); - + $this->database->insert("account", [ "user_name" => "foo", "email" => "foo@bar.com" ]); - $this->assertQuery(<<assertQuery( + <<setType($type); - + $this->database->insert("account", [ "user_name" => "foo", "lang" => ["en", "fr"] @@ -56,7 +58,7 @@ public function testInsertWithArray($type) public function testInsertWithJSON($type) { $this->setType($type); - + $this->database->insert("account", [ "user_name" => "foo", "lang [JSON]" => ["en", "fr"] @@ -81,7 +83,7 @@ public function testInsertWithJSON($type) public function testMultiInsert($type) { $this->setType($type); - + $this->database->insert("account", [ [ "user_name" => "foo", @@ -93,11 +95,12 @@ public function testMultiInsert($type) ] ]); - $this->assertQuery(<<assertQuery( + <<database->queryString ); } -} \ No newline at end of file +} diff --git a/tests/MedooTestCase.php b/tests/MedooTestCase.php index e6eb9a8d..ac2772e4 100644 --- a/tests/MedooTestCase.php +++ b/tests/MedooTestCase.php @@ -9,7 +9,7 @@ class MedooTestCase extends TestCase { protected Medoo $database; - public function setUp() : void + public function setUp(): void { $this->database = new Medoo([ 'testMode' => true @@ -27,12 +27,12 @@ public function typesProvider(): array ]; } - public function setType($type) : void + public function setType($type): void { $this->database->type = $type; } - public function expectedQuery($expected) : string + public function expectedQuery($expected): string { $identifier = [ 'mysql' => '`$1`', @@ -46,17 +46,15 @@ public function expectedQuery($expected) : string ); } - public function assertQuery($expected, $query) : void + public function assertQuery($expected, $query): void { if (is_array($expected)) { - $this->assertEquals( $this->expectedQuery($expected[$this->database->type] ?? $expected['default']), $query ); - } - else { + } else { $this->assertEquals($this->expectedQuery($expected), $query); } } -} \ No newline at end of file +} diff --git a/tests/QuoteTest.php b/tests/QuoteTest.php index 3c6d7cc3..fdc2b85f 100644 --- a/tests/QuoteTest.php +++ b/tests/QuoteTest.php @@ -1,4 +1,5 @@ setType($type); - + $quotedString = $this->database->quote("Co'mpl''ex \"st'\"ring"); $expected = [ @@ -47,8 +48,8 @@ public function testColumnQuote() $this->assertEquals('"Column"."Name"', $this->database->columnQuote("Column.Name")); } - public function columnNamesProvider() : array - { + public function columnNamesProvider(): array + { return [ ["9ColumnName"], ["@ColumnName"], @@ -91,8 +92,8 @@ public function testPrefixTableQuote() $this->assertEquals('"PREFIX_TableName"', $database->tableQuote("TableName")); } - public function tableNamesProvider() : array - { + public function tableNamesProvider(): array + { return [ ["9TableName"], ["@TableName"], @@ -113,4 +114,4 @@ public function testIncorrectTableQuote($table) $this->database->tableQuote($table); } -} \ No newline at end of file +} diff --git a/tests/RandTest.php b/tests/RandTest.php index 08fd130d..21f5170b 100644 --- a/tests/RandTest.php +++ b/tests/RandTest.php @@ -1,4 +1,5 @@ setType($type); - + $this->database->rand("account", [ "user_name" ]); @@ -33,4 +34,4 @@ public function testRand($type) EOD ], $this->database->queryString); } -} \ No newline at end of file +} diff --git a/tests/ReplaceTest.php b/tests/ReplaceTest.php index b5825b9a..d3bf6579 100644 --- a/tests/ReplaceTest.php +++ b/tests/ReplaceTest.php @@ -1,4 +1,5 @@ setType($type); - + $this->database->replace("account", [ "type" => [ "user" => "new_user", @@ -23,7 +24,8 @@ public function testReplace($type) "user_id[>]" => 1000 ]); - $this->assertQuery(<<assertQuery( + <<database->queryString ); } -} \ No newline at end of file +} diff --git a/tests/SelectTest.php b/tests/SelectTest.php index 95d5f100..5cb57384 100644 --- a/tests/SelectTest.php +++ b/tests/SelectTest.php @@ -1,4 +1,5 @@ setType($type); - + $this->database->select("account", "*"); - $this->assertQuery(<<assertQuery( + <<database->queryString @@ -27,10 +29,11 @@ public function testSelectAll($type) public function testSelectColumns($type) { $this->setType($type); - + $this->database->select("account", ["name", "id"]); - $this->assertQuery(<<assertQuery( + <<setType($type); - + $this->database->select("account", ["name(nickname)", "id"]); - $this->assertQuery(<<assertQuery( + <<setType($type); - + $this->database->select("account", [ "name", "id" diff --git a/tests/UpdateTest.php b/tests/UpdateTest.php index 21b47eb3..c7e709d4 100644 --- a/tests/UpdateTest.php +++ b/tests/UpdateTest.php @@ -1,4 +1,5 @@ setType($type); - + $this->database->update("account", [ "type" => "user", @@ -60,4 +61,4 @@ public function testUpdate($type) EOD, ], $this->database->queryString); } -} \ No newline at end of file +} diff --git a/tests/WhereTest.php b/tests/WhereTest.php index 3fd9d6d3..a4c401a1 100644 --- a/tests/WhereTest.php +++ b/tests/WhereTest.php @@ -1,4 +1,5 @@ <]" => [200, 500] ]); - $this->assertQuery(<<assertQuery( + <<]" => [date("Y-m-d", mktime(0, 0, 0, 1, 1, 2015)), date("Y-m-d", mktime(0, 0, 0, 1, 1, 2045))] ]); - $this->assertQuery(<<assertQuery( + <<<]" => [date("Y-m-d", mktime(0, 0, 0, 1, 1, 2015)), date("Y-m-d", mktime(0, 0, 0, 1, 1, 2045))] ]); - $this->assertQuery(<<assertQuery( + << [2, 123, 234, 54] ]); - $this->assertQuery(<<assertQuery( + << ["foo@bar.com", "cat@dog.com", "admin@medoo.in"] ]); - $this->assertQuery(<<assertQuery( + <<assertQuery(<<assertQuery( + <<assertQuery(<<assertQuery( + << "female" ]); - $this->assertQuery(<<assertQuery( + <<assertQuery(<<assertQuery( + <<assertQuery(<<assertQuery( + <<assertQuery(<<assertQuery( + << "lon" ]); - $this->assertQuery(<<assertQuery( + << ["lon", "foo", "bar"] ]); - $this->assertQuery(<<assertQuery( + << "lon" ]); - $this->assertQuery(<<assertQuery( + << ["OR" => ["lon", "on"]] ]); - $this->assertQuery(<<assertQuery( + << "[!BCR]at" ]); - $this->assertQuery(<<assertQuery( + << "user_id" ]); - $this->assertQuery(<<assertQuery( + << [ // Order by column with sorting by customized order. "user_id" => [43, 12, 57, 98, 144, 1], - + // Order by column. "register_date", - + // Order by column with descending sorting. "profile_id" => "DESC", - + // Order by column with ascending sorting. "date" => "ASC" ] ]); - $this->assertQuery(<<assertQuery( + <<assertQuery(<<assertQuery( + << '[a-z0-9]*' ]); - $this->assertQuery(<<assertQuery( + << Medoo::raw('NOW()') ]); - $this->assertQuery(<<assertQuery( + <<database->queryString); } - + /** * @covers Medoo::where() * @dataProvider typesProvider @@ -612,7 +634,8 @@ public function testGroupWhere($type) 'GROUP' => 'type', ]); - $this->assertQuery(<<assertQuery( + <<assertQuery(<<assertQuery( + <<assertQuery(<<assertQuery( + <<database->queryString ); } -} \ No newline at end of file +}