Класс Parser
Объект $Parser:
Объект для разбора SQL-запроса
Исходный код
class Parser extends \Cms\Site\Base { … }
Свойства
$reserved
$Parser->reserved = array(
'ABORT', 'ACCESSIBLE', 'ACTION', 'ADD', 'AFTER', 'ALL', 'ALTER', 'ANALYZE', 'AND', 'AS', 'ASC', 'ASENSITIVE', 'ATTACH', 'AUTOINCREMENT', 'BEFORE', 'BEGIN', 'BETWEEN', 'BIGINT', 'BINARY', 'BLOB', 'BOTH', 'BY', 'CALL', 'CASCADE', 'CASE', 'CAST', 'CHANGE', 'CHAR', 'CHARACTER', 'CHECK', 'COLLATE', 'COLUMN', 'COMMIT', 'CONDITION', 'CONFLICT', 'CONSTRAINT', 'CONTINUE', 'CONVERT', 'CREATE', 'CROSS', 'CURRENT_DATE', 'CURRENT_TIME', 'CURRENT_TIMESTAMP', 'CURRENT_USER', 'CURSOR', 'DATABASE', 'DATABASES', 'DAY_HOUR', 'DAY_MICROSECOND', 'DAY_MINUTE', 'DAY_SECOND', 'DEC', 'DECIMAL', 'DECLARE', 'DEFAULT', 'DEFERRABLE', 'DEFERRED', 'DELAYED', 'DELETE', 'DESC', 'DESCRIBE', 'DETACH', 'DETERMINISTIC', 'DISTINCT', 'DISTINCTROW', 'DIV', 'DOUBLE', 'DROP', 'DUAL', 'EACH', 'ELSE', 'ELSEIF', 'ENCLOSED', 'END', 'ESCAPE', 'ESCAPED', 'EXCEPT', 'EXCLUSIVE', 'EXISTS', 'EXIT', 'EXPLAIN', 'FAIL', 'FALSE', 'FETCH', 'FLOAT', 'FLOAT4', 'FLOAT8', 'FOR', 'FORCE', 'FOREIGN', 'FROM', 'FULL', 'FULLTEXT', 'GENERAL', 'GLOB', 'GRANT', 'GROUP', 'HAVING', 'HIGH_PRIORITY', 'HOUR_MICROSECOND', 'HOUR_MINUTE', 'HOUR_SECOND', 'IF', 'IGNORE', 'IGNORE_SERVER_IDS', 'IMMEDIATE', 'IN', 'INDEX', 'INDEXED', 'INFILE', 'INITIALLY', 'INNER', 'INOUT', 'INSENSITIVE', 'INSERT', 'INSTEAD', 'INT', 'INT1', 'INT2', 'INT3', 'INT4', 'INT8', 'INTEGER', 'INTERSECT', 'INTERVAL', 'INTO', 'IS', 'ISNULL', 'ITERATE', 'JOIN', 'KEY', 'KEYS', 'KILL', 'LEADING', 'LEAVE', 'LEFT', 'LIKE', 'LIMIT', 'LINEAR', 'LINES', 'LOAD', 'LOCALTIME', 'LOCALTIMESTAMP', 'LOCK', 'LONG', 'LONGBLOB', 'LONGTEXT', 'LOOP', 'LOW_PRIORITY', 'MASTER_HEARTBEAT_PERIOD', 'MASTER_SSL_VERIFY_SERVER_CERT', 'MATCH', 'MAXVALUE', 'MEDIUMBLOB', 'MEDIUMINT', 'MEDIUMTEXT', 'MIDDLEINT', 'MINUTE_MICROSECOND', 'MINUTE_SECOND', 'MOD', 'MODIFIES', 'NATURAL', 'NO', 'NOT', 'NOTNULL', 'NO_WRITE_TO_BINLOG', 'NULL', 'NUMERIC', 'OF', 'OFFSET', 'ON', 'OPTIMIZE', 'OPTION', 'OPTIONALLY', 'OR', 'ORDER', 'OUT', 'OUTER', 'OUTFILE', 'PLAN', 'PRAGMA', 'PRECISION', 'PRIMARY', 'PROCEDURE', 'PURGE', 'QUERY', 'RAISE', 'RANGE', 'READ', 'READS', 'READ_WRITE', 'REAL', 'REFERENCES', 'REGEXP', 'REINDEX', 'RELEASE', 'RENAME', 'REPEAT', 'REPLACE', 'REQUIRE', 'RESIGNAL', 'RESTRICT', 'RETURN', 'REVOKE', 'RIGHT', 'RLIKE', 'ROLLBACK', 'ROW', 'SAVEPOINT', 'SCHEMA', 'SCHEMAS', 'SECOND_MICROSECOND', 'SELECT', 'SENSITIVE', 'SEPARATOR', 'SET', 'SHOW', 'SIGNAL', 'SLOW', 'SMALLINT', 'SPATIAL', 'SPECIFIC', 'SQL', 'SQLEXCEPTION', 'SQLSTATE', 'SQLWARNING', 'SQL_BIG_RESULT', 'SQL_CALC_FOUND_ROWS', 'SQL_SMALL_RESULT', 'SSL', 'STARTING', 'STRAIGHT_JOIN', 'TABLE', 'TEMP', 'TEMPORARY', 'TERMINATED', 'THEN', 'TINYBLOB', 'TINYINT', 'TINYTEXT', 'TO', 'TRAILING', 'TRANSACTION', 'TRIGGER', 'TRUE', 'UNDO', 'UNION', 'UNIQUE', 'UNLOCK', 'UNSIGNED', 'UPDATE', 'USAGE', 'USE', 'USING', 'UTC_DATE', 'UTC_TIME', 'UTC_TIMESTAMP', 'VACUUM', 'VALUES', 'VARBINARY', 'VARCHAR', 'VARCHARACTER', 'VARYING', 'VIEW', 'VIRTUAL', 'WHEN', 'WHERE', 'WHILE', 'WITH', 'WRITE', 'XOR', 'YEAR_MONTH', 'ZEROFILL',
);
Исходный код
static $reserved = array( … );
$reserved_select
$Parser->reserved_select = array(
'SELECT', 'FROM', 'JOIN', 'LEFT JOIN', 'WHERE', 'GROUP BY', 'HAVING', 'ORDER BY', 'LIMIT',
);
Исходный код
static $reserved_select = array( … );
$sql
$Parser->sql = null;
Исходный код
protected $sql = null;
$tokens
$Parser->tokens = array();
Исходный код
protected $tokens = array();
$keywords
$Parser->keywords = array();
Исходный код
protected $keywords = array();
$sql_type
$Parser->sql_type = "sqlite";
Исходный код
protected $sql_type = "sqlite";
Методы
reset()
$Parser->reset();
Исходный код
function reset() {
$this->setSql(null);
}
parse()
$Parser->parse($sql=null, $r=null);
Исходный код
function parse($sql = null, $r = null) {
if (isset($this)) {
$self = $this;
$self->reset();
} else {
$class = __CLASS__;
$self = new $class;
}
if ($sql === null) {
$sql = $this->getSql();
} else {
$self->setSql($sql);
}
if (!is_object($r)) {
$App = $self->App();
$class = $App->getClass("sql");
$r = new $class;
}
$self->tokenize();
$tokens = $self->getTokens();
$keywords = $self->getKeywords();
$keywords[count($tokens)] = '';
if ($keywords[0] == "SELECT") {
$e = new \Cms\Root\Error("SQL error: $sql");
$r->setQuery("SELECT");
$prev_index = $next_index = 0;
$prev_keyword = $next_keyword = null;
foreach ($keywords as $next_index => $next_keyword) if (!strlen($next_keyword) || in_array($next_keyword, self::$reserved_select)) {
if ($prev_keyword == "SELECT") {
$what = $self->getExpressionList($prev_index, $next_index, ",");
$r->setWhat($what);
} elseif ($prev_keyword == "FROM") {
$table = $self->getExpression($prev_index, $next_index);
$r->setTable($table);
} elseif ($prev_keyword == "JOIN") {
$join = $self->getExpressionList($prev_index, $next_index, "ON");
$r->addJoin(array_shift($join), array_shift($join));
} elseif ($prev_keyword == "LEFT JOIN") {
$join = $self->getExpressionList($prev_index, $next_index, "ON");
$r->addJoin(array_shift($join), array_shift($join), array( "left" => true ));
} elseif ($prev_keyword == "WHERE") {
$where = $self->getExpressionList($prev_index, $next_index, "AND");
$r->setWhere($where);
} elseif ($prev_keyword == "GROUP BY") {
$group = $self->getExpressionList($prev_index, $next_index, ",");
$r->setGroup($group);
} elseif ($prev_keyword == "HAVING") {
$having = $self->getExpressionList($prev_index, $next_index, "AND");
$r->setHaving($having);
} elseif ($prev_keyword == "ORDER BY") {
$order = $self->getExpressionList($prev_index, $next_index, ",");
$r->setOrder($order);
} elseif ($prev_keyword == "LIMIT") {
$limit = $self->getExpression($prev_index, $next_index);
$r->setLimit($limit);
}
$prev_index = $next_index + 1;
$prev_keyword = $next_keyword;
}
}
return $r;
}
tokenize()
$Parser->tokenize();
Исходный код
function tokenize() {
$this->tokens = array();
$this->keywords = array();
$sql = $this->sql;
$position = 0;
$token = "";
$type = 0;
$level = 0;
$is_string = false;
$is_variable = false;
# T_STRING, T_VARIABLE, T_LIST, T_CONST; T_CHARACTER, T_WHITESPACE
while (($c = $sql[$position ++]) !== "") {
if ($c == "\\" && $this->sql_type == "mysql") {
if ($is_string || $is_variable) { # || $type == T_STRING || $type == T_VARIABLE
# continue
$token .= $c;
# continue
if (($c = $sql[$position ++]) !== "") $token .= $c;
} elseif ($type == T_CONST) {
# break T_CONST
$this->addToken($token, $type);
$token = "";
$type = 0;
# add T_CHARACTER
$this->addToken($c, T_CHARACTER);
} elseif ($type) {
# continue T_* - T_LIST
$token .= $c;
} else {
# add T_CHARACTER
$this->addToken($c, T_CHARACTER);
}
} elseif ($c == " " || $c == "\t" || $c == "\n" || $c == "\r") {
if ($type == T_CONST) {
# break T_CONST
$this->addToken($token, $type);
$token = "";
$type = 0;
# add T_WHITESPACE
$this->addToken($c, T_WHITESPACE);
} elseif ($type) {
# continue T_*
$token .= $c;
} else {
# add T_WHITESPACE
$this->addToken($c, T_WHITESPACE);
}
} elseif ($c == "'") {
if ($is_variable) { # || $type == T_VARIABLE
# continue T_VARIABLE
$token .= $c;
} elseif ($type == T_STRING) {
$token .= $c;
if (($c = $sql[$position ++]) == "'") {
$token .= $c;
} else {
# break T_STRING
$this->addToken($token, $type);
$token = "";
$type = 0;
$is_string = !$is_string;
}
} elseif ($type == T_LIST) {
# continue T_LIST
$token .= $c;
$is_string = !$is_string;
} elseif ($type) {
# break T_* - T_CONST
$this->addToken($token, $type);
# begin T_STRING
$token = $c;
$type = T_STRING;
$is_string = !$is_string;
} else {
# begin T_STRING
$token = $c;
$type = T_STRING;
$is_string = !$is_string;
}
} elseif ($c == "`") {
if ($is_string) { # || $type == T_STRING
# continue T_STRING
$token .= $c;
} elseif ($type == T_VARIABLE) {
# break T_VARIABLE
$token .= $c;
$this->addToken($token, $type);
$token = "";
$type = 0;
$is_variable = !$is_variable;
} elseif ($type == T_LIST) {
# continue T_LIST
$token .= $c;
$is_variable = !$is_variable;
} elseif ($type) {
# break T_*
$this->addToken($token, $type);
# begin T_VARIABLE
$token = $c;
$type = T_VARIABLE;
$is_variable = !$is_variable;
} else {
# begin T_VARIABLE
$token = $c;
$type = T_VARIABLE;
$is_variable = !$is_variable;
}
} elseif ($c == "(") {
if ($is_string || $is_variable) { # || $type == T_STRING || $type == T_VARIABLE
# continue T_STRING | T_VARIABLE
$token .= $c;
} elseif ($type == T_LIST) {
# continue T_LIST
$token .= $c;
$level ++;
} elseif ($type) {
# break T_*
$this->addToken($token, $type);
# begin T_LIST
$token = $c;
$type = T_LIST;
$level ++;
} else {
# begin T_LIST
$token = $c;
$type = T_LIST;
$level ++;
}
} elseif ($c == ")") {
if ($is_string || $is_variable) { # || $type == T_STRING || $type == T_VARIABLE
# continue T_STRING | T_VARIABLE
$token .= $c;
} elseif ($type == T_LIST) {
# continue T_LIST
$token .= $c;
$level --;
if (!$level) {
# break T_LIST
$this->addToken($token, $type);
$token = "";
$type = 0;
}
} elseif ($type) {
# break T_*
$this->addToken($token, $type);
$token = "";
$type = 0;
# add T_CHARACTER
$this->addToken($c, T_CHARACTER);
} else {
# add T_CHARACTER
$this->addToken($c, T_CHARACTER);
}
} elseif ($c >= "0" && $c <= "9") {
if ($type) {
# continue T_*
$token .= $c;
} else {
# add T_CHARACTER
$this->addToken($c, T_CHARACTER);
}
} elseif (($c >= "a" && $c <= "z") || ($c >= "A" && $c <= "Z")) {
if ($type) {
# continue T_*
$token .= $c;
} else {
# begin T_CONST
$token = $c;
$type = T_CONST;
}
} else {
if ($type == T_CONST) {
# break T_CONST
$this->addToken($token, $type);
$token = "";
$type = 0;
# add T_CHARACTER
$this->addToken($c, T_CHARACTER);
} elseif ($type) {
# continue T_*
$token .= $c;
} else {
# add T_CHARACTER
$this->addToken($c, T_CHARACTER);
}
}
}
if ($type) {
# break T_*
$this->addToken($token, $type);
}
}
addToken()
$Parser->addToken($token, $type);
Исходный код
function addToken($token, $type) {
$this->tokens[] = array($token, $type);
if ($type == T_CONST) {
if (in_array($keyword = strtoupper($token), self::$reserved)) {
end($this->tokens);
$index = key($this->tokens);
if ($keyword == "BY") {
while (($a = prev($this->tokens)) && $a[1] == T_WHITESPACE) { }
if ($a && $a[1] == T_CONST) {
$prev_keyword = strtoupper($a[0]);
if ($prev_keyword == "GROUP" || $prev_keyword == "ORDER") {
# GROUP BY | ORDER BY
$keyword = "$prev_keyword $keyword";
$prev_index = key($this->tokens);
for ($i = $prev_index; $i <= $index; $i ++) array_pop($this->tokens);
$this->tokens[$index = $prev_index] = array($keyword, T_CONST);
}
}
}
if ($keyword == "JOIN") {
while (($a = prev($this->tokens)) && $a[1] == T_WHITESPACE) { }
if ($a && $a[1] == T_CONST) {
$prev_keyword = strtoupper($a[0]);
if ($prev_keyword == "LEFT") {
# LEFT JOIN
$keyword = "$prev_keyword $keyword";
$prev_index = key($this->tokens);
for ($i = $prev_index; $i <= $index; $i ++) array_pop($this->tokens);
$this->tokens[$index = $prev_index] = array($keyword, T_CONST);
}
}
}
$this->keywords[$index] = $keyword;
}
}
}
getExpression()
$Parser->getExpression($index1, $index2);
Исходный код
function getExpression($index1, $index2) {
$expression = "";
for ($i = $index1; $i < $index2; $i ++) $expression .= $this->tokens[$i][0];
return trim($expression);
}
getExpressionList()
$Parser->getExpressionList($index1, $index2, $separator=",");
Исходный код
function getExpressionList($index1, $index2, $separator = ",") {
$r = array();
if (strlen($separator) == 1) {
$separator_type = T_CHARACTER;
} else {
$separator_type = T_CONST;
$separator = strtoupper($separator);
}
$expression = "";
for ($i = $index1; $i < $index2; $i ++) {
list($token, $type) = $this->tokens[$i];
if ($type == $separator_type && strtoupper($token) == $separator) {
if (strlen($expression = trim($expression))) $r[] = $expression;
$expression = "";
} else {
$expression .= $token;
}
}
if (strlen($expression = trim($expression))) $r[] = $expression;
return $r;
}
getTokens()
$Parser->getTokens();
Исходный код
function getTokens() {
return $this->tokens;
}
getKeywords()
$Parser->getKeywords();
Исходный код
function getKeywords() {
return $this->keywords;
}
getSql()
$Parser->getSql();
Исходный код
function getSql() {
return $this->sql;
}
setSql()
$Parser->setSql($sql);
Исходный код
function setSql($sql) {
$this->sql = trim($sql);
$this->tokens = array();
$this->indexes = array();
$this->keywords = array();
}