| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679 |
- <?php
- require __DIR__ . '/vendor/autoload.php';
- class sqlparser
- {
- /**
- * Parser object of SqlParser.
- *
- * @var object
- * @access public
- */
- public $parser;
- /**
- * Statements of parser.
- *
- * @var array
- * @access public
- */
- public $statements;
- /**
- * First statement.
- *
- * @var object
- * @access public
- */
- public $statement;
- /**
- * Count statements.
- *
- * @var int
- * @access public
- */
- public $statementsCount = 0;
- /**
- * Is first statemnt select type.
- *
- * @var bool
- * @access public
- */
- public $isSelect = false;
- /**
- * DAO
- *
- * @var object
- * @access public
- */
- public $dao = null;
- /**
- * Origin tables.
- *
- * @var array
- * @access public
- */
- public $originTables = array();
- /**
- * Columns.
- *
- * @var array
- * @access public
- */
- public $columns = array();
- /**
- * Tables.
- *
- * @var array
- * @access public
- */
- public $tables = array();
- public function __construct($query = '')
- {
- $query = $this->skipLineBreak($query);
- if(empty($query)) return;
- $this->parser = new PhpMyAdmin\SqlParser\Parser($query);
- $this->statements = $this->parser->statements;
- $this->statementsCount = count($this->statements);
- $this->statement = $this->statementsCount > 0 ? current($this->statements) : null;
- $this->isSelect = $this->statement instanceof PhpMyAdmin\SqlParser\Statements\SelectStatement === true;
- }
- /**
- * Set dao.
- *
- * @param object $dao
- * @access public
- * @return void
- */
- public function setDAO($dao)
- {
- $this->dao = $dao;
- }
- /**
- * Parse statement.
- *
- * @access public
- * @return void
- */
- public function parseStatement()
- {
- if(empty($this->statement)) return;
- $this->columns = $this->parseColumns();
- $this->tables = $this->parseTables();
- }
- /**
- * Create statement.
- *
- * @access public
- * @return void
- */
- public function createStatement()
- {
- $this->statement = new PhpMyAdmin\SqlParser\Statements\SelectStatement();
- }
- /**
- * Set from.
- *
- * @param object $from
- * @access public
- * @return void
- */
- public function setFrom($from)
- {
- $this->statement->from = array($from);
- }
- /**
- * Add select.
- *
- * @param array|object $exprs
- * @access public
- * @return void
- */
- public function addSelect($exprs)
- {
- if(empty($exprs)) return;
- if($exprs instanceof PhpMyAdmin\SqlParser\Components\Expression)
- {
- $this->statement->expr[] = $exprs;
- return;
- }
- foreach($exprs as $expr) $this->addSelect($expr);
- }
- /**
- * Add join.
- *
- * @param array|object $joins
- * @access public
- * @return void
- */
- public function addJoin($joins)
- {
- if(empty($joins)) return;
- if($joins instanceof PhpMyAdmin\SqlParser\Components\JoinKeyword)
- {
- $this->statement->join[] = $joins;
- return;
- }
- foreach($joins as $join) $this->addJoin($join);
- }
- /**
- * Add where.
- *
- * @param array|object $wheres
- * @access public
- * @return void
- */
- public function addWhere($wheres)
- {
- if(empty($wheres)) return;
- if($wheres instanceof PhpMyAdmin\SqlParser\Components\Condition)
- {
- $this->statement->where[] = $wheres;
- return;
- }
- foreach($wheres as $where) $this->addWhere($where);
- }
- /**
- * Add group.
- *
- * @param array|object $groups
- * @access public
- * @return void
- */
- public function addGroup($groups)
- {
- if(empty($groups)) return;
- if($groups instanceof PhpMyAdmin\SqlParser\Components\GroupKeyword)
- {
- $this->statement->group[] = $groups;
- return;
- }
- foreach($groups as $group) $this->addGroup($group);
- }
- /**
- * Get function.
- *
- * @param string $name
- * @param mixed $args
- * @access public
- * @return string
- */
- public function getFunction($name, ...$args)
- {
- $name = strtoupper($name);
- $argStr = implode(', ', $args);
- $argStr = str_replace(PHP_EOL, '', $argStr);
- return "$name($argStr)";
- }
- /**
- * Get expression.
- *
- * @param string|array $table
- * @param string $column
- * @param string $alias
- * @param string $function
- * @access public
- * @return PhpMyAdmin\SqlParser\Components\Expression
- */
- public function getExpression($table = null, $column = null, $alias = null, $function = null)
- {
- if(is_array($table)) return call_user_func_array(array($this, 'getExpression'), $table);
- $expression = new PhpMyAdmin\SqlParser\Components\Expression();
- if(!empty($function))
- {
- $expression->function = $function;
- $expression->expr = $this->getFunction($function, $expression->build($this->getExpression($table, $column)));
- }
- else
- {
- if($column === '*')
- {
- $table = $this->trimExpr($table);
- $expression->expr = empty($table) ? '*' : "`$table`.*";
- }
- else
- {
- $expression->table = $table;
- $expression->column = $column;
- }
- }
- $expression->alias = $alias;
- return $expression;
- }
- /**
- * operatorCondition
- *
- * @param string $type
- * @access public
- * @return void
- */
- public function operatorCondition($type)
- {
- $condition = new PhpMyAdmin\SqlParser\Components\Condition();
- $condition->isOperator = true;
- $condition->expr = strtoupper($type);
- return $condition;
- }
- /**
- * Get condition.
- *
- * @param mixed $tableA
- * @param mixed $columnA
- * @param string $operator
- * @param mixed $tableB
- * @param mixed $columnB
- * @param mixed $group
- * @access public
- * @return object
- */
- public function getCondition($tableA = null, $columnA = null, $operator = '', $tableB = null, $columnB = null, $group = 1, $quote = true)
- {
- if(is_array($tableA)) return call_user_func_array(array($this, 'getCondition'), $tableA);
- $condition = new PhpMyAdmin\SqlParser\Components\Condition();
- $tableA = $this->trimExpr($tableA);
- $columnA = $this->trimExpr($columnA);
- $tableB = $this->trimExpr($tableB);
- $expr = '';
- if(empty($operator))
- {
- $expr = $columnA;
- }
- else
- {
- /* 如果tableB不为空,那么columnB就是字段,需要trim('`')。*/
- if(!empty($tableB)) $columnB = $this->trimExpr($columnB);
- /* 如果tableB为空,那么columnB是值,需要trim("'")。*/
- if(empty($tableB)) $columnB = $this->trimExpr($columnB, "'");
- $columnB = empty($tableB) && $quote ? "'$columnB'" : $columnB;
- $exprA = empty($tableA) ? "`$columnA`" : "`$tableA`.`$columnA`";
- $exprB = empty($tableB) ? $columnB : "`$tableB`.`$columnB`";
- $operator = strtoupper($operator);
- $expr = "$exprA $operator $exprB";
- }
- $condition->expr = $expr;
- $condition->group = $group;
- return $condition;
- }
- /**
- * Get conditions from array.
- *
- * @param array $conditions
- * @access public
- * @return array
- */
- public function getConditionsFromArray($conditions)
- {
- if(is_string($conditions)) return $this->operatorCondition($conditions);
- $first = current($conditions);
- if(!is_array($first) && !in_array($first, array('and', 'or'))) return $this->getCondition($conditions);
- $conditionExprs = array();
- foreach($conditions as $condition) $conditionExprs[] = $this->getConditionsFromArray($condition);
- return $conditionExprs;
- }
- /**
- * Get left join.
- *
- * @param string $table
- * @param string $alias
- * @param array $on
- * @access public
- * @return object
- */
- public function getLeftJoin($table, $alias, $on)
- {
- $left = new PhpMyAdmin\SqlParser\Components\JoinKeyword();
- $left->type = 'LEFT';
- $left->expr = $this->getExpression($table, null, $alias);
- $left->on = $this->combineConditions($on);
- return $left;
- }
- /**
- * Get group.
- *
- * @param object $expr
- * @access public
- * @return void
- */
- public function getGroup($expr)
- {
- $group = new PhpMyAdmin\SqlParser\Components\GroupKeyword();
- $group->expr = $expr;
- return $group;
- }
- /**
- * Combine conditions.
- *
- * @param array|object $conditions
- * @access public
- * @return array
- */
- public function combineConditions($conditions, $quote = false)
- {
- if(empty($conditions)) return array();
- if($conditions instanceof PhpMyAdmin\SqlParser\Components\Condition === true) return $conditions;
- $first = reset($conditions);
- $last = end($conditions);
- if($quote)
- {
- $first = $this->addQuote($first, 'left');
- $last = $this->addQuote($last, 'right');
- }
- $quote = true;
- if(is_array($first)) $first = $this->combineConditions($first, $quote);
- if(is_array($last)) $last = $this->combineConditions($last, $quote);
- $conditions[0] = $first;
- $conditions[count($conditions) - 1] = $last;
- return $conditions;
- }
- public function addQuote($conditions, $side)
- {
- if($conditions instanceof PhpMyAdmin\SqlParser\Components\Condition === true)
- {
- $expr = $conditions->expr;
- $conditions->expr = $side == 'left' ? '(' . $expr : $expr . ')';
- return $conditions;
- }
- $condition = $side == 'left' ? current($conditions) : end($conditions);
- $index = $side == 'left' ? 0 : count($conditions) - 1;
- $conditions[$index] = $this->addQuote($condition, $side);
- return $conditions;
- }
- /**
- * Match columns with table.
- *
- * @access public
- * @return array
- */
- public function matchColumnsWithTable()
- {
- if(empty($this->statement)) return array();
- if(count($this->tables) == 1) return $this->combineSingleTable();
- return $this->combineMultipleTable();
- }
- /**
- * Trim expr.
- *
- * @param string $expr
- * @access private
- * @return string
- */
- private function trimExpr($expr, $char = '`')
- {
- if(empty($expr)) return $expr;
- return trim(trim($expr), $char);
- }
- /**
- * Combine single table to columns.
- *
- * @access private
- * @return array
- */
- private function combineSingleTable()
- {
- $combineColumns = array();
- $fromTable = current($this->tables);
- foreach($this->columns as $columnName => $column)
- {
- $column['table'] = array_merge($fromTable, array('column' => $column['origin']));
- $combineColumns[$columnName] = $column;
- }
- return $combineColumns;
- }
- /**
- * Combine multiple table to columns.
- *
- * @access private
- * @return array
- */
- private function combineMultipleTable()
- {
- foreach($this->columns as $columnName => $column)
- {
- $column['table'] = $this->searchTables($column['table'], $this->tables, $column['origin']);
- $combineColumns[$columnName] = $column;
- }
- return $combineColumns;
- }
- /**
- * Search table from origin tables.
- *
- * @param string $tableName
- * @param string $tables
- * @param string $column
- * @access private
- * @return string|false
- */
- private function searchTables($tableName, $tables, $column)
- {
- /* 如果能使用别名匹配上,那么直接返回。*/
- /* If it can be matched using an alias, then it returns. */
- foreach($tables as $table) if($tableName == $table['alias']) return array_merge($table, array('column' => $column));
- /* 如果匹配不上,则字段没有使用别名进行限制,那么需要通过字段去遍历所有表。*/
- /* If it doesn't match, then the field is not aliased, and you need to iterate over all tables using the field. */
- foreach($tables as $table)
- {
- $isTable = $table['isTable'];
- $originTable = $table['originTable'];
- /* 如果是原始表,并且列在原始表中存在,那么返回这个表。*/
- /* If it is the original table and the column exists in the original table, then the table is returned. */
- if($isTable && $this->columnExistInOriginTable($originTable, $column)) return array_merge($table, array('column' => $column));
- /* 如果不是原始表,并且列在子句中存在,那么返回子句中这个字段对应的表。*/
- /* If it is not the original table and the column exists in the clause, then the table corresponding to the field in the clause is returned. */
- if(!$isTable && isset($originTable[$column])) return array_merge($originTable[$column]['table'], array('column' => $originTable[$column]['origin']));
- }
- return false;
- }
- /**
- * Parse columns.
- *
- * @access private
- * @return void
- */
- private function parseColumns()
- {
- $fields = array();
- foreach($this->statement->expr as $expr)
- {
- /* 获取查询数据后真正展示出来的列名 */
- $columnName = empty($expr->alias) ? $expr->column : $expr->alias;
- $fields[$columnName] = array('origin' => $expr->column, 'table' => $expr->table);
- }
- return $fields;
- }
- /**
- * Parse tables.
- *
- * @access private
- * @return void
- */
- private function parseTables()
- {
- $from = current($this->statement->from);
- $joins = $this->statement->join;
- $tables = array();
- $tables[] = $this->parseTable($from, 'from');
- if(!empty($joins)) foreach($joins as $join) $tables[] = $this->parseTable($join->expr, 'join');
- return $tables;
- }
- /**
- * Parse table.
- *
- * @param object $expr
- * @param string $type
- * @access private
- * @return void
- */
- private function parseTable($expr, $type)
- {
- $isTable = empty($expr->subquery);
- $table = array('alias' => $expr->alias, 'isTable' => $isTable, 'type' => $type);
- $table['originTable'] = $this->getOriginTable($expr->expr, $isTable);
- return $table;
- }
- /**
- * Get origin table from table name or expr.
- *
- * @param string $table
- * @param bool $isTable
- * @access private
- * @return string|array
- */
- private function getOriginTable($table, $isTable)
- {
- if(!$isTable)
- {
- $parser = new sqlparser($table);
- $parser->setDAO($this->dao);
- $parser->parseStatement();
- return $parser->matchColumnsWithTable();
- }
- $this->storeOriginTable($table);
- return $table;
- }
- /**
- * Judge column exist in origin table or not.
- *
- * @param string $table
- * @param string $column
- * @access private
- * @return bool
- */
- private function columnExistInOriginTable($table, $column)
- {
- $originTable = $this->getOriginTableColumns($table);
- if(!$originTable) return false;
- return isset($originTable[$column]);
- }
- /**
- * Get origin table columns.
- *
- * @param string $table
- * @access private
- * @return array|null
- */
- private function getOriginTableColumns($table)
- {
- $originTables = $this->originTables;
- return isset($originTables[$table]) ? $originTables[$table] : null;
- }
- /**
- * Store origin table.
- *
- * @param string $table
- * @access private
- * @return void
- */
- private function storeOriginTable($table)
- {
- if(!isset($this->originTables[$table])) $this->originTables[$table] = $this->dao->descTable($table);
- }
- /**
- * Skip line break in sql.
- *
- * @param string $sql
- * @access private
- * @return string
- */
- private function skipLineBreak($sql)
- {
- if(empty($sql)) return $sql;
- $sql = str_replace("\n\t", " ", $sql);
- $sql = str_replace("\t\n", " ", $sql);
- $sql = str_replace("\n\r", " ", $sql);
- $sql = str_replace("\r\n", " ", $sql);
- $sql = str_replace("\r", " ", $sql);
- $sql = str_replace("\n", " ", $sql);
- return $sql;
- }
- }
|