<?php
if (defined('WFWAF_VERSION') && !defined('WFWAF_RUN_COMPLETE')) {
require_once dirname(__FILE__) . '/lexer.php';
class wfWAFSQLiParser extends wfWAFBaseParser {
const FLAG_PARSE_MYSQL_PORTABLE_COMMENTS = wfWAFSQLiLexer::FLAG_TOKENIZE_MYSQL_PORTABLE_COMMENTS;
/**
* @param string $param
* @return bool
*/
public static function testForSQLi($param) {
static $instance;
static $tests;
if (!$instance) {
$instance = new self(new wfWAFSQLiLexer());
}
if (!$tests) {
// SQL statement and token count for lexer
$tests = array(
array('%s', 1),
array('SELECT * FROM t WHERE i = %s ', 7),
array("SELECT * FROM t WHERE i = '%s' ", 7),
array('SELECT * FROM t WHERE i = "%s" ', 7),
array('SELECT * FROM t WHERE i = (%s) ', 9),
array("SELECT * FROM t WHERE i = ('%s') ", 9),
array('SELECT * FROM t WHERE i = ("%s") ', 9),
array('SELECT * FROM t WHERE i = ((%s)) ', 11),
array("SELECT * FROM t WHERE i = (('%s')) ", 11),
array('SELECT * FROM t WHERE i = (("%s")) ', 11),
array('SELECT * FROM t WHERE i = (((%s))) ', 13),
array("SELECT * FROM t WHERE i = ((('%s'))) ", 13),
array('SELECT * FROM t WHERE i = ((("%s"))) ', 13),
array('SELECT * FROM t WHERE i = %s and j = (1
) ', 12),
array("SELECT * FROM t WHERE i = '%s' and j = (1
) ", 12),
array('SELECT * FROM t WHERE i = "%s" and j = (1
) ', 12),
array('SELECT MATCH(t) AGAINST (%s) from t ', 10),
array("SELECT MATCH(t) AGAINST ('%s') from t ", 10),
array('SELECT MATCH(t) AGAINST ("%s") from t ', 10),
// array('SELECT CASE WHEN %s THEN 1 ELSE 0 END from t ', 8),
// array("SELECT CASE WHEN '%s' THEN 1 ELSE 0 END from t ", 8),
// array('SELECT CASE WHEN "%s" THEN 1 ELSE 0 END from t ', 8),
//
// array('SELECT (CASE WHEN (%s) THEN 1 ELSE 0 END) from t ', 12),
// array("SELECT (CASE WHEN ('%s') THEN 1 ELSE 0 END) from t ", 12),
// array('SELECT (CASE WHEN ("%s") THEN 1 ELSE 0 END) from t ', 12),
array('SELECT * FROM (select %s) ', 6),
array("SELECT * FROM (select '%s') ", 6),
array('SELECT * FROM (select "%s") ', 6),
array('SELECT * FROM (select (%s)) ', 8),
array("SELECT * FROM (select ('%s')) ", 8),
array('SELECT * FROM (select ("%s")) ', 8),
array('SELECT * FROM (select ((%s))) ', 10),
array("SELECT * FROM (select (('%s'))) ", 10),
array('SELECT * FROM (select (("%s"))) ', 10),
//
// array('SELECT * FROM t JOIN t2 on i = %s ', 9),
// array("SELECT * FROM t JOIN t2 on i = '%s' ", 9),
// array('SELECT * FROM t JOIN t2 on i = "%s" ', 9),
// array('SELECT * FROM t JOIN t2 on i = (%s) ', 11),
// array("SELECT * FROM t JOIN t2 on i = ('%s') ", 11),
// array('SELECT * FROM t JOIN t2 on i = ("%s") ', 11),
// array('SELECT * FROM t JOIN t2 on i = ((%s)) ', 13),
// array("SELECT * FROM t JOIN t2 on i = (('%s')) ", 13),
// array('SELECT * FROM t JOIN t2 on i = (("%s")) ', 13),
// array('SELECT * FROM t JOIN t2 on i = (((%s))) ', 15),
// array("SELECT * FROM t JOIN t2 on i = ((('%s'))) ", 15),
// array('SELECT * FROM t JOIN t2 on i = ((("%s"))) ', 15),
array('SELECT * FROM %s ', 3),
array('INSERT INTO t (col) VALUES (%s) ', 9),
array("INSERT INTO t (col) VALUES ('%s') ", 9),
array('INSERT INTO t (col) VALUES ("%s") ', 9),
array('UPDATE t1 SET col1 = %s ', 5),
array('UPDATE t1 SET col1 = \'%s\' ', 5),
);
}
$lexerFlags = array(0, wfWAFSQLiLexer::FLAG_TOKENIZE_MYSQL_PORTABLE_COMMENTS);
foreach ($lexerFlags as $flags) {
foreach ($tests as $test) {
// $startTime = microtime(true);
list($sql, $expectedTokenCount) = $test;
try {
$instance->setFlags($flags);
$instance->setSubject(sprintf($sql, $param));
if (($instance->hasMoreThanNumTokens($expectedTokenCount) && $instance->evaluate())
|| $instance->hasMultiplePortableCommentVersions()) {
// printf("%s took %f seconds\n", $sql, microtime(true) - $startTime);
return true;
}
// printf("%s took %f seconds\n", $sql, microtime(true) - $startTime);
} catch (wfWAFParserSyntaxError $e) {
}
}
}
return false;
}
private $subject;
/**
* @var int
*/
private $flags;
/** @var wfWAFSQLiLexer */
protected $lexer;
private $portableCommentVersions = array();
private $intervalUnits = array(
'SECOND',
'MINUTE',
'HOUR',
'DAY_SYM',
'WEEK',
'MONTH',
'QUARTER',
'YEAR',
'SECOND_MICROSECOND',
'MINUTE_MICROSECOND',
'MINUTE_SECOND',
'HOUR_MICROSECOND',
'HOUR_SECOND',
'HOUR_MINUTE',
'DAY_MICROSECOND',
'DAY_SECOND',
'DAY_MINUTE',
'DAY_HOUR',
'YEAR_MONTH',
);
private $reservedWords = array(
"_FILENAME",
"ACCESSIBLE",
"ADD",
"ALL",
"ALTER",
"ANALYZE",
"AND",
"AS",
"ASC",
"ASENSITIVE",
"BEFORE",
"BETWEEN",
"BIGINT",
"BINARY",
"BLOB",
"BOTH",
"BY",
"CALL",
"CASCADE",
"CASE",
"CHANGE",
"CHAR",
"CHARACTER",
"CHECK",
"COLLATE",
"COLUMN",
"CONDITION",
"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",
"DELAYED",
"DELETE",
"DESC",
"DESCRIBE",
"DETERMINISTIC",
"DISTINCT",
"DISTINCTROW",
"DIV",
"DOUBLE",
"DROP",
// "DUAL", // works as a table name ???
"EACH",
"ELSE",
"ELSEIF",
"ENCLOSED",
"ESCAPED",
"EXISTS",
"EXIT",
"EXPLAIN",
"FALSE",
"FETCH",
"FLOAT",
"FLOAT4",
"FLOAT8",
"FOR",
"FORCE",
"FOREIGN",
"FROM",
"FULLTEXT",
"GRANT",
"GROUP",
"HAVING",
"HIGH_PRIORITY",
"HOUR_MICROSECOND",
"HOUR_MINUTE",
"HOUR_SECOND",
"IF",
"IGNORE",
"IN",
"INDEX",
"INFILE",
"INNER",
"INOUT",
"INSENSITIVE",
"INSERT",
"INT",
"INT1",
"INT2",
"INT3",
"INT4",
"INT8",
"INTEGER",
"INTERVAL",
"INTO",
"IS",
"ITERATE",
"JOIN",
"KEY",
"KEYS",
"KILL",
"LEADING",
"LEAVE",
"LEFT",
"LIKE",
"LIMIT",
"LINEAR",
"LINES",
"LOAD",
"LOCALTIME",
"LOCALTIMESTAMP",
"LOCK",
"LONG",
"LONGBLOB",
"LONGTEXT",
"LOOP",
"LOW_PRIORITY",
"MASTER_SSL_VERIFY_SERVER_CERT",
"MATCH",
"MAXVALUE",
"MEDIUMBLOB",
"MEDIUMINT",
"MEDIUMTEXT",
"MIDDLEINT",
"MINUTE_MICROSECOND",
"MINUTE_SECOND",
"MOD",
"MODIFIES",
"NATURAL",
"NOT",
"NO_WRITE_TO_BINLOG",
"NULL",
"NUMERIC",
"ON",
"OPTIMIZE",
"OPTION",
"OPTIONALLY",
"OR",
"ORDER",
"OUT",
"OUTER",
"OUTFILE",
"PRECISION",
"PRIMARY",
"PROCEDURE",
"PURGE",
"RANGE",
"READ",
"READS",
"READ_WRITE",
"REAL",
"REFERENCES",
"REGEXP",
"RELEASE",
"RENAME",
"REPEAT",
"REPLACE",
"REQUIRE",
"RESIGNAL",
"RESTRICT",
"RETURN",
"REVOKE",
"RIGHT",
"RLIKE",
"SCHEMA",
"SCHEMAS",
"SECOND_MICROSECOND",
"SELECT",
"SENSITIVE",
"SEPARATOR",
"SET",
"SHOW",
"SIGNAL",
"SMALLINT",
"SPATIAL",
"SPECIFIC",
"SQL",
"SQLEXCEPTION",
"SQLSTATE",
"SQLWARNING",
"SQL_BIG_RESULT",
"SQL_CALC_FOUND_ROWS",
"SQL_SMALL_RESULT",
"SSL",
"STARTING",
"STRAIGHT_JOIN",
"TABLE",
"TERMINATED",
"THEN",
"TINYBLOB",
"TINYINT",
"TINYTEXT",
"TO",
"TRAILING",
"TRIGGER",
"TRUE",
"UNDO",
"UNION",
"UNIQUE",
"UNLOCK",
"UNSIGNED",
"UPDATE",
"USAGE",
"USE",
"USING",
"UTC_DATE",
"UTC_TIME",
"UTC_TIMESTAMP",
"VALUES",
"VARBINARY",
"VARCHAR",
"VARCHARACTER",
"VARYING",
"WHEN",
"WHERE",
"WHILE",
"WITH",
"WRITE",
"XOR",
"YEAR_MONTH",
"ZEROFILL",
);
private $keywords = array(
"ACCESSIBLE",
"ACTION",
"ADD",
"AFTER",
"AGAINST",
"AGGREGATE",
"ALGORITHM",
"ALL",
"ALTER",
"ANALYZE",
"AND",
"ANY",
"AS",
"ASC",
"ASCII",
"ASENSITIVE",
"AT",
"AUTHORS",
"AUTOEXTEND_SIZE",
"AUTO_INCREMENT",
"AVG",
"AVG_ROW_LENGTH",
"BACKUP",
"BEFORE",
"BEGIN",
"BETWEEN",
"BIGINT",
"BINARY",
"BINLOG",
"BIT",
"BLOB",
"BLOCK",
"BOOL",
"BOOLEAN",
"BOTH",
"BTREE",
"BY",
"BYTE",
"CACHE",
"CALL",
"CASCADE",
"CASCADED",
"CASE",
"CATALOG_NAME",
"CHAIN",
"CHANGE",
"CHANGED",
"CHAR",
"CHARACTER",
"CHARSET",
"CHECK",
"CHECKSUM",
"CIPHER",
"CLASS_ORIGIN",
"CLIENT",
"CLOSE",
"COALESCE",
"CODE",
"COLLATE",
"COLLATION",
"COLUMN",
"COLUMNS",
"COLUMN_NAME",
"COMMENT",
"COMMIT",
"COMMITTED",
"COMPACT",
"COMPLETION",
"COMPRESSED",
"CONCURRENT",
"CONDITION",
"CONNECTION",
"CONSISTENT",
"CONSTRAINT",
"CONSTRAINT_CATALOG",
"CONSTRAINT_NAME",
"CONSTRAINT_SCHEMA",
"CONTAINS",
"CONTEXT",
"CONTINUE",
"CONTRIBUTORS",
"CONVERT",
"CPU",
"CREATE",
"CROSS",
"CUBE",
"CURRENT_DATE",
"CURRENT_TIME",
"CURRENT_TIMESTAMP",
"CURRENT_USER",
"CURSOR",
"CURSOR_NAME",
"DATA",
"DATABASE",
"DATABASES",
"DATAFILE",
"DATE",
"DATETIME",
"DAY",
"DAY_HOUR",
"DAY_MICROSECOND",
"DAY_MINUTE",
"DAY_SECOND",
"DEALLOCATE",
"DEC",
"DECIMAL",
"DECLARE",
"DEFAULT",
"DEFINER",
"DELAYED",
"DELAY_KEY_WRITE",
"DELETE",
"DESC",
"DESCRIBE",
"DES_KEY_FILE",
"DETERMINISTIC",
"DIRECTORY",
"DISABLE",
"DISCARD",
"DISK",
"DISTINCT",
"DISTINCTROW",
"DIV",
"DO",
"DOUBLE",
"DROP",
"DUAL",
"DUMPFILE",
"DUPLICATE",
"DYNAMIC",
"EACH",
"ELSE",
"ELSEIF",
"ENABLE",
"ENCLOSED",
"END",
"ENDS",
"ENGINE",
"ENGINES",
"ENUM",
"ERROR",
"ERRORS",
"ESCAPE",
"ESCAPED",
"EVENT",
"EVENTS",
"EVERY",
"EXECUTE",
"EXISTS",
"EXIT",
"EXPANSION",
"EXPLAIN",
"EXTENDED",
"EXTENT_SIZE",
"FALSE",
"FAST",
"FAULTS",
"FETCH",
"FIELDS",
"FILE",
"FIRST",
"FIXED",
"FLOAT",
"FLOAT4",
"FLOAT8",
"FLUSH",
"FOR",
"FORCE",
"FOREIGN",
"FOUND",
"FRAC_SECOND",
"FROM",
"FULL",
"FULLTEXT",
"FUNCTION",
"GENERAL",
"GEOMETRY",
"GEOMETRYCOLLECTION",
"GET_FORMAT",
"GLOBAL",
"GRANT",
"GRANTS",
"GROUP",
"HANDLER",
"HASH",
"HAVING",
"HELP",
"HIGH_PRIORITY",
"HOST",
"HOSTS",
"HOUR",
"HOUR_MICROSECOND",
"HOUR_MINUTE",
"HOUR_SECOND",
"IDENTIFIED",
"IF",
"IGNORE",
"IGNORE_SERVER_IDS",
"IMPORT",
"IN",
"INDEX",
"INDEXES",
"INFILE",
"INITIAL_SIZE",
"INNER",
"INNOBASE",
"INNODB",
"INOUT",
"INSENSITIVE",
"INSERT",
"INSERT_METHOD",
"INSTALL",
"INT",
"INT1",
"INT2",
"INT3",
"INT4",
"INT8",
"INTEGER",
"INTERVAL",
"INTO",
"INVOKER",
"IO",
"IO_THREAD",
"IPC",
"IS",
"ISOLATION",
"ISSUER",
"ITERATE",
"JOIN",
"KEY",
"KEYS",
"KEY_BLOCK_SIZE",
"KILL",
"LANGUAGE",
"LAST",
"LEADING",
"LEAVE",
"LEAVES",
"LEFT",
"LESS",
"LEVEL",
"LIKE",
"LIMIT",
"LINEAR",
"LINES",
"LINESTRING",
"LIST",
"LOAD",
"LOCAL",
"LOCALTIME",
"LOCALTIMESTAMP",
"LOCK",
"LOCKS",
"LOGFILE",
"LOGS",
"LONG",
"LONGBLOB",
"LONGTEXT",
"LOOP",
"LOW_PRIORITY",
"MASTER",
"MASTER_CONNECT_RETRY",
"MASTER_HEARTBEAT_PERIOD",
"MASTER_HOST",
"MASTER_LOG_FILE",
"MASTER_LOG_POS",
"MASTER_PASSWORD",
"MASTER_PORT",
"MASTER_SERVER_ID",
"MASTER_SSL",
"MASTER_SSL_CA",
"MASTER_SSL_CAPATH",
"MASTER_SSL_CERT",
"MASTER_SSL_CIPHER",
"MASTER_SSL_KEY",
"MASTER_SSL_VERIFY_SERVER_CERT",
"MASTER_USER",
"MATCH",
"MAXVALUE",
"MAX_CONNECTIONS_PER_HOUR",
"MAX_QUERIES_PER_HOUR",
"MAX_ROWS",
"MAX_SIZE",
"MAX_UPDATES_PER_HOUR",
"MAX_USER_CONNECTIONS",
"MEDIUM",
"MEDIUMBLOB",
"MEDIUMINT",
"MEDIUMTEXT",
"MEMORY",
"MERGE",
"MESSAGE_TEXT",
"MICROSECOND",
"MIDDLEINT",
"MIGRATE",
"MINUTE",
"MINUTE_MICROSECOND",
"MINUTE_SECOND",
"MIN_ROWS",
"MOD",
"MODE",
"MODIFIES",
"MODIFY",
"MONTH",
"MULTILINESTRING",
"MULTIPOINT",
"MULTIPOLYGON",
"MUTEX",
"MYSQL_ERRNO",
"NAME",
"NAMES",
"NATIONAL",
"NATURAL",
"NCHAR",
"NDB",
"NDBCLUSTER",
"NEW",
"NEXT",
"NO",
"NODEGROUP",
"NONE",
"NOT",
"NO_WAIT",
"NO_WRITE_TO_BINLOG",
"NULL",
"NUMERIC",
"NVARCHAR",
"OFFSET",
"OLD_PASSWORD",
"ON",
"ONE",
"ONE_SHOT",
"OPEN",
"OPTIMIZE",
"OPTION",
"OPTIONALLY",
"OPTIONS",
"OR",
"ORDER",
"OUT",
"OUTER",
"OUTFILE",
"OWNER",
"PACK_KEYS",
"PAGE",
"PARSER",
"PARTIAL",
"PARTITION",
"PARTITIONING",
"PARTITIONS",
"PASSWORD",
"PHASE",
"PLUGIN",
"PLUGINS",
"POINT",
"POLYGON",
"PORT",
"PRECISION",
"PREPARE",
"PRESERVE",
"PREV",
"PRIMARY",
"PRIVILEGES",
"PROCEDURE",
"PROCESSLIST",
"PROFILE",
"PROFILES",
"PROXY",
"PURGE",
"QUARTER",
"QUERY",
"QUICK",
"RANGE",
"READ",
"READS",
"READ_ONLY",
"READ_WRITE",
"REAL",
"REBUILD",
"RECOVER",
"REDOFILE",
"REDO_BUFFER_SIZE",
"REDUNDANT",
"REFERENCES",
"REGEXP",
"RELAY",
"RELAYLOG",
"RELAY_LOG_FILE",
"RELAY_LOG_POS",
"RELAY_THREAD",
"RELEASE",
"RELOAD",
"REMOVE",
"RENAME",
"REORGANIZE",
"REPAIR",
"REPEAT",
"REPEATABLE",
"REPLACE",
"REPLICATION",
"REQUIRE",
"RESET",
"RESIGNAL",
"RESTORE",
"RESTRICT",
"RESUME",
"RETURN",
"RETURNS",
"REVOKE",
"RIGHT",
"RLIKE",
"ROLLBACK",
"ROLLUP",
"ROUTINE",
"ROW",
"ROWS",
"ROW_FORMAT",
"RTREE",
"SAVEPOINT",
"SCHEDULE",
"SCHEMA",
"SCHEMAS",
"SCHEMA_NAME",
"SECOND",
"SECOND_MICROSECOND",
"SECURITY",
"SELECT",
"SENSITIVE",
"SEPARATOR",
"SERIAL",
"SERIALIZABLE",
"SERVER",
"SESSION",
"SET",
"SHARE",
"SHOW",
"SHUTDOWN",
"SIGNAL",
"SIGNED",
"SIMPLE",
"SLAVE",
"SLOW",
"SMALLINT",
"SNAPSHOT",
"SOCKET",
"SOME",
"SONAME",
"SOUNDS",
"SOURCE",
"SPATIAL",
"SPECIFIC",
"SQL",
"SQLEXCEPTION",
"SQLSTATE",
"SQLWARNING",
"SQL_BIG_RESULT",
"SQL_BUFFER_RESULT",
"SQL_CACHE",
"SQL_CALC_FOUND_ROWS",
"SQL_NO_CACHE",
"SQL_SMALL_RESULT",
"SQL_THREAD",
"SQL_TSI_DAY",
"SQL_TSI_FRAC_SECOND",
"SQL_TSI_HOUR",
"SQL_TSI_MINUTE",
"SQL_TSI_MONTH",
"SQL_TSI_QUARTER",
"SQL_TSI_SECOND",
"SQL_TSI_WEEK",
"SQL_TSI_YEAR",
"SSL",
"START",
"STARTING",
"STARTS",
"STATUS",
"STOP",
"STORAGE",
"STRAIGHT_JOIN",
"STRING",
"SUBCLASS_ORIGIN",
"SUBJECT",
"SUBPARTITION",
"SUBPARTITIONS",
"SUPER",
"SUSPEND",
"SWAPS",
"SWITCHES",
"TABLE",
"TABLES",
"TABLESPACE",
"TABLE_CHECKSUM",
"TABLE_NAME",
"TEMPORARY",
"TEMPTABLE",
"TERMINATED",
"TEXT",
"THAN",
"THEN",
"TIME",
"TIMESTAMP",
"TIMESTAMPADD",
"TIMESTAMPDIFF",
"TINYBLOB",
"TINYINT",
"TINYTEXT",
"TO",
"TRAILING",
"TRANSACTION",
"TRIGGER",
"TRIGGERS",
"TRUE",
"TRUNCATE",
"TYPE",
"TYPES",
"UNCOMMITTED",
"UNDEFINED",
"UNDO",
"UNDOFILE",
"UNDO_BUFFER_SIZE",
"UNICODE",
"UNINSTALL",
"UNION",
"UNIQUE",
"UNKNOWN",
"UNLOCK",
"UNSIGNED",
"UNTIL",
"UPDATE",
"UPGRADE",
"USAGE",
"USE",
"USER",
"USER_RESOURCES",
"USE_FRM",
"USING",
"UTC_DATE",
"UTC_TIME",
"UTC_TIMESTAMP",
"VALUE",
"VALUES",
"VARBINARY",
"VARCHAR",
"VARCHARACTER",
"VARIABLES",
"VARYING",
"VIEW",
"WAIT",
"WARNINGS",
"WEEK",
"WHEN",
"WHERE",
"WHILE",
"WITH",
"WORK",
"WRAPPER",
"WRITE",
"X509",
"XA",
"XML",
"XOR",
"YEAR",
"YEAR_MONTH",
"ZEROFILL",
);
private $numberFunctions = array(
'ABS',
'ACOS',
'ASIN',
'ATAN2',
'ATAN',
'CEIL',
'CEILING',
'CONV',
'COS',
'COT',
'CRC32',
'DEGREES',
'EXP',
'FLOOR',
'LN',
'LOG10',
'LOG2',
'LOG',
'MOD',
'PI',
'POW',
'POWER',
'RADIANS',
'RAND',
'ROUND',
'SIGN',
'SIN',
'SQRT',
'TAN',
'TRUNCATE',
);
private $charFunctions = array(
'ASCII_SYM',
'BIN',
'BIT_LENGTH',
'CHAR_LENGTH',
'CHAR',
'CONCAT_WS',
'CONCAT',
'ELT',
'EXPORT_SET',
'FIELD',
'FIND_IN_SET',
'FORMAT',
'FROM_BASE64',
'HEX',
'INSERT',
'INSTR',
'LEFT',
'LENGTH',
'LOAD_FILE',
'LOCATE',
'LOWER',
'LPAD',
'LTRIM',
'MAKE_SET',
'MID',
'OCT',
'ORD',
'QUOTE',
'REPEAT',
'REPLACE',
'REVERSE',
'RIGHT',
'RPAD',
'RTRIM',
'SOUNDEX',
'SPACE',
'STRCMP',
'SUBSTRING_INDEX',
'SUBSTRING',
'TO_BASE64',
'TRIM',
'UNHEX',
'UPPER',
'WEIGHT_STRING',
);
private $timeFunctions = array(
'ADDDATE',
'ADDTIME',
'CONVERT_TZ',
'CURDATE',
'CURTIME',
'DATE_ADD',
'DATE_FORMAT',
'DATE_SUB',
'DATE_SYM',
'DATEDIFF',
'DAYNAME',
'DAYOFMONTH',
'DAYOFWEEK',
'DAYOFYEAR',
'EXTRACT',
'FROM_DAYS',
'FROM_UNIXTIME',
'GET_FORMAT',
'HOUR',
'LAST_DAY ',
'MAKEDATE',
'MAKETIME ',
'MICROSECOND',
'MINUTE',
'MONTH',
'MONTHNAME',
'NOW',
'PERIOD_ADD',
'PERIOD_DIFF',
'QUARTER',
'SEC_TO_TIME',
'SECOND',
'STR_TO_DATE',
'SUBTIME',
'SYSDATE',
'TIME_FORMAT',
'TIME_TO_SEC',
'TIME_SYM',
'TIMEDIFF',
'TIMESTAMP',
'TIMESTAMPADD',
'TIMESTAMPDIFF',
'TO_DAYS',
'TO_SECONDS',
'UNIX_TIMESTAMP',
'UTC_DATE',
'UTC_TIME',
'UTC_TIMESTAMP',
'WEEK',
'WEEKDAY',
'WEEKOFYEAR',
'YEAR',
'YEARWEEK',
);
private $otherFunctions = array(
'MAKE_SET', 'LOAD_FILE',
'IF', 'IFNULL',
'AES_ENCRYPT', 'AES_DECRYPT',
'DECODE', 'ENCODE',
'DES_DECRYPT', 'DES_ENCRYPT',
'ENCRYPT', 'MD5',
'OLD_PASSWORD', 'PASSWORD',
'BENCHMARK', 'CHARSET', 'COERCIBILITY', 'COLLATION', 'CONNECTION_ID',
'CURRENT_USER', 'DATABASE', 'SCHEMA', 'USER', 'SESSION_USER', 'SYSTEM_USER',
'VERSION_SYM',
'FOUND_ROWS', 'LAST_INSERT_ID', 'DEFAULT',
'GET_LOCK', 'RELEASE_LOCK', 'IS_FREE_LOCK', 'IS_USED_LOCK', 'MASTER_POS_WAIT',
'INET_ATON', 'INET_NTOA',
'NAME_CONST',
'SLEEP',
'UUID',
'VALUES',
);
private $groupFunctions = array(
'AVG', 'COUNT', 'MAX_SYM', 'MIN_SYM', 'SUM',
'BIT_AND', 'BIT_OR', 'BIT_XOR',
'GROUP_CONCAT',
'STD', 'STDDEV', 'STDDEV_POP', 'STDDEV_SAMP',
'VAR_POP', 'VAR_SAMP', 'VARIANCE',
);
/**
* @param wfWAFSQLiLexer $lexer
* @param string $subject
* @param int $flags
*/
public function __construct($lexer, $subject = null, $flags = 0) {
parent::__construct($lexer);
$this->setSubject($subject);
$this->setFlags($flags);
}
protected function _init() {
$this->portableCommentVersions = array();
$this->index = -1;
}
/**
* @param int $num
* @return bool
*/
public function hasMoreThanNumTokens($num) {
$this->_init();
$savePoint = $this->index;
for ($i = 0; $i <= $num;) {
$token=$this->nextToken();
if($token){
if(!$this->lexer->isValueLiteral($token->getType()))
$i++;
}
else{
$this->index = $savePoint;
return false;
}
}
$this->index = $savePoint;
return true;
}
/**
* @return bool
*/
public function evaluate() {
try {
$this->parse();
return true;
} catch (wfWAFParserSyntaxError $e) {
return false;
}
}
public function parse() {
$this->_init();
if (
$this->parseSelectStatement()
|| $this->parseInsertStatement()
|| $this->parseUpdateStatement()
// || $this->parseDeleteStatement()
// || $this->parseReplaceStatement()
) {
$token = $this->nextToken();
if ($token && !$this->isTokenOfType($token, wfWAFSQLiLexer::SEMICOLON)) {
$this->triggerSyntaxError($this->currentToken());
}
} else {
$this->triggerSyntaxError($this->expectNextToken());
}
}
/**
* @param int $index
* @return bool
*/
protected function getToken($index) {
if (array_key_exists($index, $this->tokens)) {
return $this->tokens[$index];
}
while ($token = $this->getLexer()->nextToken()) {
if (!$this->isCommentToken($token)) {
$this->tokens[$index] = $token;
return $this->tokens[$index];
}
}
return false;
}
/**
* @param wfWAFLexerToken $token
* @return bool
*/
public function isCommentToken($token) {
if ($this->isTokenOfType($token, wfWAFSQLiLexer::MYSQL_PORTABLE_COMMENT_START)) {
$this->portableCommentVersions[(int) preg_replace('/[^\d]/', '', $token->getValue())] = 1;
}
return $this->isTokenOfType($token, array(
wfWAFSQLiLexer::SINGLE_LINE_COMMENT,
wfWAFSQLiLexer::MULTI_LINE_COMMENT,
wfWAFSQLiLexer::MYSQL_PORTABLE_COMMENT_START,
wfWAFSQLiLexer::MYSQL_PORTABLE_COMMENT_END,
));
}
public function hasMultiplePortableCommentVersions() {
return count($this->portableCommentVersions) > 1;
}
/**
* Expects the next token to be an identifier with the supplied case-insensitive value
*
* @param $keyword
* @return wfWAFLexerToken
* @throws wfWAFParserSyntaxError
*/
protected function expectNextIdentifierEquals($keyword) {
$nextToken = $this->expectNextToken();
$this->expectTokenTypeEquals($nextToken, wfWAFSQLiLexer::UNQUOTED_IDENTIFIER);
if ($nextToken->getLowerCaseValue() !== wfWAFUtils::strtolower($keyword)) {
$this->triggerSyntaxError($nextToken);
}
return $nextToken;
}
private function parseSelectStatement() {
$startIndex = $this->index;
$hasSelect = false;
while ($this->parseSelectExpression()) {
$hasSelect = true;
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'union')) {
$hasSelect = false;
if (!$this->isIdentifierWithValue($this->nextToken(), 'all')) {
$this->index--;
}
continue;
}
$this->index = $savePoint;
break;
}
if ($hasSelect) {
return true;
}
$this->index = $startIndex;
return false;
}
private function parseSelectExpression() {
$savePoint = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
$this->parseSelectExpression() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
) {
return true;
}
$this->index = $savePoint;
if ($this->parseSelect()) {
if ($this->parseFrom()) {
$this->parseWhere();
$this->parseProcedure();
$this->parseGroupBy();
$this->parseHaving();
}
$this->parseOrderBy();
$this->parseLimit();
return true;
}
return false;
}
/**
* @throws wfWAFParserSyntaxError
*/
private function parseSelect() {
$startPoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'select')) {
$optionalSelectParamsRegex = '/ALL|DISTINCT(?:ROW)?|HIGH_PRIORITY|MAX_STATEMENT_TIME|STRAIGHT_JOIN|SQL_SMALL_RESULT|SQL_BIG_RESULT|SQL_BUFFER_RESULT|SQL_CACHE|SQL_NO_CACHE|SQL_CALC_FOUND_ROWS/i';
while (true) {
$savePoint = $this->index;
$token = $this->nextToken();
if ($token) {
$value = $token->getLowerCaseValue();
if (preg_match($optionalSelectParamsRegex, $value)) {
if ($value == 'max_statement_time') {
$this->expectTokenTypeEquals($this->expectNextToken(), wfWAFSQLiLexer::EQUALS_SYMBOL);
$this->expectTokenTypeInArray($this->expectNextToken(), array(
wfWAFSQLiLexer::INTEGER_LITERAL,
wfWAFSQLiLexer::BINARY_NUMBER_LITERAL,
wfWAFSQLiLexer::HEX_NUMBER_LITERAL,
wfWAFSQLiLexer::BINARY_NUMBER_LITERAL,
));
}
continue;
}
}
$this->index = $savePoint;
break;
}
return $this->parseSelectList();
}
$this->index = $startPoint;
return false;
}
/**
* @throws wfWAFParserSyntaxError
*/
private function parseSelectList() {
$startPoint = $this->index;
$hasSelects = false;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::ASTERISK)) {
$hasSelects = true;
if (!$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
// Just SELECT * [FROM ...]
$this->index--;
return true;
}
} else {
$this->index = $startPoint;
}
while ($this->parseDisplayedColumn()) {
$hasSelects = true;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
continue;
}
$this->index--;
break;
}
if ($hasSelects) {
return true;
}
$this->index = $startPoint;
return false;
}
private function parseDisplayedColumn() {
/*
( table_spec DOT ASTERISK )
|
( column_spec (alias)? )
|
( bit_expr (alias)? )
*/
$savePoint = $this->index;
if ($this->parseTableSpec() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::DOT) &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::ASTERISK)
) {
return true;
}
$this->index = $savePoint;
$savePoint = $this->index;
if ($this->parseExpression()) {
$this->parseAlias();
return true;
}
$this->index = $savePoint;
$savePoint = $this->index;
if ($this->parseColumnSpec()) {
$this->parseAlias();
return true;
}
$this->index = $savePoint;
return false;
}
/**
* @return bool
*/
private function parseExpressionList() {
$startIndex = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS)) {
$hasExpressions = false;
while ($this->parseExpression()) {
$hasExpressions = true;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
continue;
}
$this->index--;
break;
}
if ($hasExpressions && $this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
return true;
}
}
$this->index = $startIndex;
return false;
}
private function parseExpression() {
// Combines these:
// exp_factor3 ( AND_SYM exp_factor3 )* ;
// expression: exp_factor1 ( OR_SYM exp_factor1 )* ;
// exp_factor1: exp_factor2 ( XOR exp_factor2 )* ;
// exp_factor2: exp_factor3 ( AND_SYM exp_factor3 )* ;
$savePoint = $this->index;
$hasExpression = false;
while ($this->parseExpressionFactor3()) {
$hasExpression = true;
$savePoint2 = $this->index;
$token = $this->nextToken();
if ($this->isOrToken($token) || $this->isAndToken($token) || $this->isIdentifierWithValue($token, 'xor')) {
continue;
}
$this->index = $savePoint2;
break;
}
if ($hasExpression) {
return true;
}
$this->index = $savePoint;
return false;
}
private function parseExpressionFactor3() {
// (NOT_SYM)? exp_factor4 ;
$savePoint = $this->index;
if (!$this->isNotSymbolToken($this->nextToken())) {
$this->index--;
}
if ($this->parseExpressionFactor4()) {
return true;
}
$this->index = $savePoint;
return false;
}
private function parseExpressionFactor4() {
// bool_primary ( IS_SYM (NOT_SYM)? (boolean_literal|NULL_SYM) )? ;
$savePoint = $this->index;
if ($this->parseBoolPrimary()) {
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'is')) {
if (!$this->isNotSymbolToken($this->nextToken())) {
$this->index--;
}
if ($this->isIdentifierWithValue($this->nextToken(), array(
'true', 'false', 'null',
))
) {
return true;
}
}
$this->index = $savePoint;
return true;
}
$this->index = $savePoint;
return false;
}
/**
* @return bool
*/
private function parseBoolPrimary() {
$startIndex = $this->index;
$token = $this->nextToken();
if ($token) {
$hasNot = false;
if ($this->isNotSymbolToken($token)) {
$hasNot = true;
$token = $this->nextToken();
}
$val = $token->getLowerCaseValue();
if ($token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER) {
if ($val === 'exists' && $this->parseSubquery()) {
return true;
} else if ($hasNot) {
$this->index = $startIndex;
return false;
}
}
if (!$hasNot) {
$this->index = $startIndex;
}
}
if ($this->parsePredicate()) {
$savePoint = $this->index;
$opToken = $this->nextToken();
if ($opToken) {
switch ($opToken->getType()) {
case wfWAFSQLiLexer::EQUALS_SYMBOL:
case wfWAFSQLiLexer::LESS_THAN:
case wfWAFSQLiLexer::GREATER_THAN:
case wfWAFSQLiLexer::LESS_THAN_EQUAL_TO:
case wfWAFSQLiLexer::GREATER_THAN_EQUAL_TO:
case wfWAFSQLiLexer::NOT_EQUALS:
case wfWAFSQLiLexer::SET_VAR:
$savePoint2 = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), array(
'any', 'all'
)) &&
$this->parseSubquery()
) {
return true;
}
$this->index = $savePoint2;
$savePoint2 = $this->index;
if ($this->testForSubquery() && $this->parseSubquery()) {
return true;
}
$this->index = $savePoint2;
if ($this->parsePredicate()) {
return true;
}
$this->index = $startIndex;
return false;
}
}
$this->index = $savePoint;
return true;
}
$this->index = $startIndex;
return false;
}
private function parsePredicate() {
$startIndex = $this->index;
if ($this->parseBitExpression()) {
$savePoint = $this->index;
$token = $this->nextToken();
if ($token) {
if ($hasNot = $this->isNotSymbolToken($token)) {
$token = $this->nextToken();
if (!$token) {
$this->index = $startIndex;
return false;
}
}
$val = $token->getLowerCaseValue();
if ($token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER) {
switch ($val) {
case 'in':
if ($this->parseSubquery() || $this->parseExpressionList()) {
return true;
}
break;
case 'between':
if ($this->parseBitExpression() && $this->isIdentifierWithValue($this->nextToken(), 'and') &&
$this->parsePredicate()
) {
return true;
}
break;
case 'sounds':
if ($this->isIdentifierWithValue($this->nextToken(), 'like') &&
$this->parseBitExpression()
) {
return true;
}
break;
case 'like':
case 'rlike':
if ($this->parseSimpleExpression()) {
// We've got a LIKE statement at this point
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'escape') &&
$this->parseSimpleExpression()
) {
return true;
}
$this->index = $savePoint;
return true;
}
break;
case 'regexp':
if ($this->parseBitExpression()) {
return true;
}
break;
default:
if ($hasNot) {
$this->index = $startIndex;
return false;
}
break;
}
}
}
$this->index = $savePoint;
return true;
}
$this->index = $startIndex;
return false;
}
/**
* @return bool
*/
private function parseBitExpression() {
// factor1 ( VERTBAR factor1 )? ;
$savePoint = $this->index;
if ($this->parseBitExprFactor5()) {
$savePoint = $this->index;
$token = $this->nextToken();
if (($this->isTokenOfType($token, array(
wfWAFSQLiLexer::BIT_OR,
wfWAFSQLiLexer::BIT_AND,
wfWAFSQLiLexer::BIT_XOR,
wfWAFSQLiLexer::BIT_LEFT_SHIFT,
wfWAFSQLiLexer::BIT_RIGHT_SHIFT,
wfWAFSQLiLexer::BIT_INVERSION,
wfWAFSQLiLexer::PLUS,
wfWAFSQLiLexer::MINUS,
wfWAFSQLiLexer::ASTERISK,
wfWAFSQLiLexer::DIVISION,
wfWAFSQLiLexer::MOD,
))
||
$this->isIdentifierWithValue($token, array(
'div', 'mod'
))) &&
$this->parseBitExpression()
) {
return true;
}
$this->index = $savePoint;
return true;
}
$this->index = $savePoint;
return false;
}
private function parseBitExprFactor5() {
// factor6 ( (PLUS|MINUS) interval_expr )? ;
$savePoint = $this->index;
if ($this->parseBitExprFactor6()) {
$savePoint = $this->index;
if ($this->isTokenOfType($this->nextToken(), array(
wfWAFSQLiLexer::PLUS,
wfWAFSQLiLexer::MINUS,
)) && $this->parseIntervalExpression()
) {
return true;
}
$this->index = $savePoint;
return true;
}
$this->index = $savePoint;
return false;
}
private function parseBitExprFactor6() {
// (PLUS | MINUS | NEGATION | BINARY) simple_expr
// | simple_expr ;
$startPoint = $this->index;
$savePoint = $this->index;
while (
($token = $this->nextToken()) &&
(
$this->isTokenOfType($token, array(
wfWAFSQLiLexer::PLUS,
wfWAFSQLiLexer::MINUS,
)) ||
($this->isTokenOfType($token, wfWAFSQLiLexer::BIT_INVERSION)) ||
($this->isIdentifierWithValue($token, 'BINARY'))
)
) {
$savePoint = $this->index;
}
$this->index = $savePoint;
if ($this->parseSimpleExpression()) {
return true;
}
$this->index = $startPoint;
return false;
}
/**
* literal_value
* | column_spec
* | function_call
* | USER_VAR
* | expression_list
* | (ROW_SYM expression_list)
* | subquery
* | EXISTS subquery
* | {identifier expr}
* | match_against_statement
* | case_when_statement
* | interval_expr
*
* @return bool
*/
private function parseSimpleExpression() {
$startPoint = $this->index;
$simple = ($parseLiteral = $this->parseLiteral()) ||
($parseMatchAgainst = $this->parseMatchAgainst()) ||
($parseFunctionCall = $this->parseFunctionCall()) ||
($parseVariable = $this->parseVariable()) ||
($parseExpressionList = $this->parseExpressionList()) ||
($parseSubquery = $this->parseSubquery()) ||
($parseExistsSubquery = $this->parseExistsSubquery()) ||
($parseCaseWhen = $this->parseCaseWhen()) ||
($parseODBCExpression = $this->parseODBCExpression()) ||
($parseIntervalExpression = $this->parseIntervalExpression()) ||
($parseColumnSpec = $this->parseColumnSpec());
if ($simple) {
$token = $this->nextToken();
if ($token && $token->getLowerCaseValue() == 'collate') {
$savePoint = $this->index;
if ($this->parseCollationName()) {
return true;
}
$this->index = $savePoint;
} else {
$this->index--;
}
return true;
}
$this->index = $startPoint;
return false;
}
/**
* @return bool
*/
private function parseLiteral() {
$startIndex = $this->index;
$savePoint = $this->index;
while ($this->isTokenOfType($this->nextToken(), array(
wfWAFSQLiLexer::PLUS,
wfWAFSQLiLexer::MINUS,
))) {
$savePoint = $this->index;
}
$this->index = $savePoint;
// Check if this is a Character Set Introducer
$nextToken = $this->nextToken();
$hasCharacterSetIntroducer = $this->isTokenOfType($nextToken, wfWAFSQLiLexer::UNQUOTED_IDENTIFIER) &&
substr($nextToken->getValue(), 0, 1) === '_';
if (!$hasCharacterSetIntroducer) {
$this->index--;
}
$validLiteral = false;
$nextToken = $this->nextToken();
if ($nextToken) {
switch ($nextToken->getType()) {
case wfWAFSQLiLexer::INTEGER_LITERAL:
case wfWAFSQLiLexer::BINARY_NUMBER_LITERAL:
case wfWAFSQLiLexer::HEX_NUMBER_LITERAL:
case wfWAFSQLiLexer::REAL_NUMBER_LITERAL:
$validLiteral = true;
break;
// Allow concatenation: 'test' 'test' is valid
case wfWAFSQLiLexer::DOUBLE_STRING_LITERAL:
case wfWAFSQLiLexer::SINGLE_STRING_LITERAL:
$savePoint = $this->index;
while ($this->isTokenOfType($this->nextToken(), array(
wfWAFSQLiLexer::DOUBLE_STRING_LITERAL,
wfWAFSQLiLexer::SINGLE_STRING_LITERAL
))) {
$savePoint = $this->index;
}
$this->index = $savePoint;
$validLiteral = true;
break;
case wfWAFSQLiLexer::UNQUOTED_IDENTIFIER:
if ($nextToken->getLowerCaseValue() === 'null') {
$validLiteral = true;
}
break;
}
}
if ($validLiteral) {
if ($hasCharacterSetIntroducer) {
// Check for and parse collation
$savePoint = $this->index;
$hasCollation = $this->isIdentifierWithValue($this->nextToken(), 'collation') &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::UNQUOTED_IDENTIFIER);
if (!$hasCollation) {
$this->index = $savePoint;
}
}
return true;
}
$this->index = $startIndex;
return false;
}
/**
* @return bool
*/
private function parseColumnSpec() {
$savePoint = $this->index;
if ($this->parseTableSpec()) {
$savePoint = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::DOT)) {
$nextToken = $this->nextToken();
if ($nextToken && ($nextToken->getType() == wfWAFSQLiLexer::UNQUOTED_IDENTIFIER ||
$nextToken->getType() == wfWAFSQLiLexer::QUOTED_IDENTIFIER)
) {
return true;
}
$this->index = $savePoint;
return false;
}
$this->index = $savePoint;
return true;
}
$this->index = $savePoint;
return false;
}
/**
* CAST_SYM LPAREN expression AS_SYM cast_data_type RPAREN )
* | ( CONVERT_SYM LPAREN expression COMMA cast_data_type RPAREN )
* | ( CONVERT_SYM LPAREN expression USING_SYM transcoding_name RPAREN )
* | ( group_functions LPAREN ( ASTERISK | ALL | DISTINCT )? bit_expr RPAREN )
*
* @return bool
*/
private function parseFunctionCall() {
$startPoint = $this->index;
$functionToken = $this->nextToken();
if ($functionToken && $functionToken->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER) {
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS)) {
switch ($functionToken->getLowerCaseValue()) {
case 'cast':
if ($this->parseExpression() &&
$this->isIdentifierWithValue($this->nextToken(), 'as') &&
$this->parseCastDataType() &&
$this->parseOptionalCharacterSet() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
) {
return true;
}
break;
case 'convert':
if ($this->parseExpression()) {
$savePoint = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA) &&
$this->parseCastDataType() &&
$this->parseOptionalCharacterSet() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
) {
return true;
}
$this->index = $savePoint;
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'using') &&
$this->parseTranscodingName() &&
$this->parseOptionalCharacterSet() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
) {
return true;
}
$this->index = $savePoint;
}
break;
case 'trim':
if (!$this->isIdentifierWithValue($this->nextToken(), array(
'leading',
'both',
'trailing',
))) {
$this->index--;
}
while ($this->parseExpression()) {
$nextToken = $this->nextToken();
if (
$this->isTokenOfType($nextToken, wfWAFSQLiLexer::COMMA) ||
$this->isIdentifierWithValue($nextToken, array(
'from',
'for',
'in',
))
) {
continue;
}
$this->index--;
break;
}
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
return true;
}
break;
case 'weight_string':
if ($this->parseExpression()) {
$savePoint = $this->index;
if (!(
$this->isIdentifierWithValue($this->nextToken(), 'as') &&
$this->parseCastDataType() &&
$this->parseOptionalCharacterSet()
)) {
$this->index = $savePoint;
}
if ($this->isIdentifierWithValue($this->nextToken(), 'level')) {
while ($this->parseExpression()) {
$nextToken = $this->nextToken();
if (
$this->isTokenOfType($nextToken, wfWAFSQLiLexer::COMMA) ||
$this->isTokenOfType($nextToken, wfWAFSQLiLexer::MINUS)
) {
continue;
}
$this->index--;
break;
}
while ($this->isIdentifierWithValue($this->nextToken(), array(
'asc',
'desc',
'reverse',
))) {
continue;
}
$this->index--;
} else {
$this->index--;
}
}
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
return true;
}
break;
default:
$savePoint = $this->index;
if (in_array($functionToken->getUpperCaseValue(), $this->groupFunctions)) {
$token = $this->nextToken();
if (!$this->isIdentifierWithValue($token, array(
'all', 'distinct',
)) && !$this->isTokenOfType($token, wfWAFSQLiLexer::ASTERISK)
) {
$this->index--;
}
$this->parseBitExpression();
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
return true;
}
}
$this->index = $savePoint;
while ($this->parseExpression()) {
$nextToken = $this->nextToken();
if (
$this->isTokenOfType($nextToken, wfWAFSQLiLexer::COMMA) ||
$this->isIdentifierWithValue($nextToken, array(
'from',
'for',
'in',
))
) {
continue;
}
$this->index--;
break;
}
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
return true;
}
break;
}
}
}
$this->index = $startPoint;
return false;
}
/**
* BINARY (INTEGER_NUM)?
* | CHAR (INTEGER_NUM)?
* | DATE_SYM
* | DATETIME
* | DECIMAL_SYM ( INTEGER_NUM (COMMA INTEGER_NUM)? )?
* | SIGNED_SYM (INTEGER_SYM)?
* | TIME_SYM
* | UNSIGNED_SYM (INTEGER_SYM)?
*
* @return bool
*/
private function parseCastDataType() {
$startPoint = $this->index;
$token = $this->nextToken();
if ($this->isKeywordToken($token)) {
switch ($token->getLowerCaseValue()) {
case 'binary':
case 'char':
case 'nchar':
case 'varchar':
case 'character':
$savePoint = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::INTEGER_LITERAL) &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
) {
return true;
}
$this->index = $savePoint;
return true;
case 'date':
case 'datetime':
case 'time':
return true;
case 'signed':
case 'unsigned':
if (!$this->isIdentifierWithValue($this->nextToken(), array(
'int',
'integer',
))) {
$this->index--;
}
return true;
case 'decimal':
$savePoint = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::INTEGER_LITERAL)) {
$savePoint2 = $this->index;
if (!($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA) &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::INTEGER_LITERAL)
)) {
$this->index = $savePoint2;
}
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
return true;
}
}
$this->index = $savePoint;
return true;
}
}
$this->index = $startPoint;
return false;
}
private function parseTranscodingName() {
$savePoint = $this->index;
$token = $this->nextToken();
if ($token && $token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER) {
return true;
}
$this->index = $savePoint;
return false;
}
private function parseOptionalCharacterSet() {
$savePoint = $this->index;
if (!(
$this->nextToken()->getLowerCaseValue() === 'character' &&
$this->nextToken()->getLowerCaseValue() === 'set' &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::UNQUOTED_IDENTIFIER)
)) {
$this->index = $savePoint;
}
return true;
}
private function parseVariable() {
$nextToken = $this->nextToken();
if ($nextToken && $nextToken->getType() === wfWAFSQLiLexer::VARIABLE) {
return true;
}
$this->index--;
return false;
}
/**
* @return bool
*/
private function parseSubquery() {
$startIndex = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
$this->parseSelectStatement() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
) {
return true;
}
$this->index = $startIndex;
return false;
}
private function testForSubquery() {
$startIndex = $this->index;
$nextToken = $this->nextToken();
if ($nextToken && $nextToken->getType() === wfWAFSQLiLexer::OPEN_PARENTHESIS) {
$selectToken = $this->nextToken();
if ($this->isIdentifierWithValue($selectToken, 'select')) {
$this->index = $startIndex;
return true;
}
}
$this->index = $startIndex;
return false;
}
/**
*
*
* @return bool
*/
private function parseExistsSubquery() {
$startIndex = $this->index;
$existsToken = $this->nextToken();
if ($this->isIdentifierWithValue($existsToken, 'exists')) {
if ($this->parseSubquery()) {
return true;
}
}
$this->index = $startIndex;
return false;
}
/**
* MATCH (column_spec (COMMA column_spec)* ) AGAINST (expression (search_modifier)? )
*
* @return bool
*/
private function parseMatchAgainst() {
$startIndex = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'match')) {
$savePoint = $this->index;
if (!$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS)) {
$this->index = $savePoint;
}
$hasColumns = false;
while ($this->parseColumnSpec()) {
$hasColumns = true;
$savePoint = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
continue;
}
$this->index = $savePoint;
break;
}
$savePoint = $this->index;
if (!$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
$this->index = $savePoint;
}
if ($hasColumns && $this->isIdentifierWithValue($this->nextToken(), 'against') &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
$this->parseExpression() &&
($this->parseSearchModifier() || true) &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
) {
return true;
}
}
$this->index = $startIndex;
return false;
}
/**
* Used in match/against
*
* @link https://dev.mysql.com/doc/refman/5.6/en/fulltext-search.html
* @return bool
*/
private function parseSearchModifier() {
$startIndex = $this->index;
$startToken = $this->nextToken();
if ($this->isIdentifierWithValue($startToken, 'in')) {
$next = $this->nextToken();
if ($this->isIdentifierWithValue($next, 'natural') &&
$this->isIdentifierWithValue($this->nextToken(), 'language') &&
$this->isIdentifierWithValue($this->nextToken(), 'mode')
) {
$saveIndex = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'with') &&
$this->isIdentifierWithValue($this->nextToken(), 'query') &&
$this->isIdentifierWithValue($this->nextToken(), 'expansion')
) {
return true;
}
$this->index = $saveIndex;
return true;
} else if ($this->isIdentifierWithValue($next, 'boolean') &&
$this->isIdentifierWithValue($this->nextToken(), 'mode')
) {
return true;
}
} else if ($this->isIdentifierWithValue($startToken, 'with')) {
if ($this->isIdentifierWithValue($this->nextToken(), 'query') &&
$this->isIdentifierWithValue($this->nextToken(), 'expansion')
) {
return true;
}
}
$this->index = $startIndex;
return false;
}
/**
* @return bool
*/
private function parseCaseWhen() {
$startIndex = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'case')) {
$hasWhen = false;
while (true) {
if (!$this->isIdentifierWithValue($this->nextToken(), 'when')) {
$this->index--;
break;
}
if ($this->parseExpression()) {
if ($this->isIdentifierWithValue($this->nextToken(), 'then') && $this->parseBitExpression()) {
$hasWhen = true;
continue;
}
$this->index--;
}
$this->index--;
break;
}
if ($hasWhen) {
$endToken = $this->nextToken();
if ($this->isIdentifierWithValue($endToken, 'else')) {
if (!$this->parseBitExpression()) {
$this->index = $startIndex;
return false;
}
$endToken = $this->nextToken();
}
if ($this->isIdentifierWithValue($endToken, 'end')) {
return true;
}
}
}
$this->index = $startIndex;
return false;
}
/**
* @return bool
*/
private function parseODBCExpression() {
$startIndex = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_BRACKET) && $this->isIdentifier($this->nextToken()) && $this->parseExpression() && $this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_BRACKET)) {
return true;
}
$this->index = $startIndex;
return false;
}
/**
* @return bool
*/
private function parseIntervalExpression() {
$startIndex = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'interval') && $this->parseExpression()) {
$intervalUnitToken = $this->nextToken();
if ($intervalUnitToken && in_array($intervalUnitToken->getType(), $this->intervalUnits)) {
return true;
}
}
$this->index = $startIndex;
return false;
}
/**
* @return bool
*/
public function parseCollationName() {
$startIndex = $this->index;
$token = $this->nextToken();
if ($token && $token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER) {
return true;
}
$this->index = $startIndex;
return false;
}
/**
* @return bool
*/
private function parseFrom() {
$startIndex = $this->index;
$token = $this->nextToken();
if ($this->isIdentifierWithValue($token, 'from')) {
return $this->parseTableReferences();
}
$this->index = $startIndex;
return false;
}
/**
* @link http://dev.mysql.com/doc/refman/5.6/en/join.html
* @return bool
*/
private function parseTableReferences() {
$startPoint = $this->index;
$hasReferences = false;
while ($this->parseEscapedTableReference()) {
$hasReferences = true;
$savePoint = $this->index;
$token = $this->nextToken();
if ($this->isTokenOfType($token, wfWAFSQLiLexer::COMMA)) {
continue;
}
$this->index = $savePoint;
break;
}
if ($hasReferences) {
return true;
}
$this->index = $startPoint;
return false;
}
/**
* @return bool
*/
private function parseEscapedTableReference() {
$startPoint = $this->index;
if ($this->parseTableReference() ||
(
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_BRACKET) &&
$this->isIdentifierWithValue($this->nextToken(), 'oj') &&
$this->parseTableReference() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_BRACKET)
)
) {
return true;
}
$this->index = $startPoint;
return false;
}
/**
* @return bool
*/
private function parseTableReference() {
$savePoint = $this->index;
$hasTables = false;
if ($this->parseTableFactor()) {
$hasTables = true;
while ($this->parseJoinTable()) {
}
}
if ($hasTables) {
return true;
}
$this->index = $savePoint;
return false;
}
/**
* table_factor:
* tbl_name [PARTITION (partition_names)] [[AS] alias] [index_hint_list]
* | table_subquery [AS] alias
* | ( table_references )
*/
private function parseTableFactor() {
$savePoint = $this->index;
if ($this->parseTableSpec()) {
$savePoint2 = $this->index;
if (!$this->parsePartitionClause()) {
$this->index = $savePoint2;
}
$this->parseAlias();
$this->parseIndexHintList();
return true;
}
$this->index = $savePoint;
$savePoint = $this->index;
if ($this->parseSubquery() && $this->parseAlias()) {
return true;
}
$this->index = $savePoint;
$savePoint = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
$this->parseTableReferences() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
) {
return true;
}
$this->index = $savePoint;
return false;
}
/**
* PARTITION (partition_names)
*
* @return bool
*/
private function parsePartitionClause() {
$startIndex = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'partition') &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
$this->parsePartitionNames() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
) {
return true;
}
$this->index = $startIndex;
return false;
}
/**
* @return bool
*/
private function parsePartitionNames() {
$startPoint = $this->index;
$hasPartition = false;
while ($this->parsePartitionName()) {
$hasPartition = true;
$savePoint = $this->index;
if (!$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
$this->index = $savePoint;
break;
}
}
if ($hasPartition) {
return true;
}
$this->index = $startPoint;
return false;
}
/**
* @return bool
*/
private function parsePartitionName() {
$startPoint = $this->index;
$token = $this->nextToken();
if ($this->isValidNonReservedWordIdentifier($token)) {
return true;
}
$this->index = $startPoint;
return false;
}
/**
* join_table:
* table_reference [INNER | CROSS] JOIN table_factor [join_condition]
* | table_reference STRAIGHT_JOIN table_factor
* | table_reference STRAIGHT_JOIN table_factor ON conditional_expr
* | table_reference {LEFT|RIGHT} [OUTER] JOIN table_reference join_condition
* | table_reference NATURAL [{LEFT|RIGHT} [OUTER]] JOIN table_factor
*
* @return bool
*/
private function parseJoinTable() {
$savePoint = $this->index;
if (!$this->isIdentifierWithValue($this->nextToken(), array(
'inner', 'cross',
))
) {
$this->index = $savePoint;
}
if ($this->isIdentifierWithValue($this->nextToken(), 'join') && $this->parseTableFactor()) {
$this->parseJoinCondition();
return true;
}
$this->index = $savePoint;
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'straight_join') &&
$this->parseTableFactor()
) {
$savePoint = $this->index;
if (!($this->isIdentifierWithValue($this->nextToken(), 'on') && $this->parseExpression())) {
$this->index = $savePoint;
}
return true;
}
$this->index = $savePoint;
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), array(
'left', 'right',
))
) {
$savePoint2 = $this->index;
if (!$this->isIdentifierWithValue($this->nextToken(), array(
'outer',
))
) {
$this->index = $savePoint2;
}
} else {
$this->index = $savePoint;
}
if ($this->isIdentifierWithValue($this->nextToken(), 'join') &&
$this->parseTableReference() &&
$this->parseJoinCondition()
) {
return true;
}
$this->index = $savePoint;
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), array(
'natural',
))
) {
if ($this->isIdentifierWithValue($this->nextToken(), array(
'left', 'right',
))
) {
$savePoint2 = $this->index;
if (!$this->isIdentifierWithValue($this->nextToken(), array(
'outer',
))
) {
$this->index = $savePoint2;
}
} else {
$this->index = $savePoint;
}
if ($this->isIdentifierWithValue($this->nextToken(), 'join') &&
$this->parseTableFactor()
) {
return true;
}
}
$this->index = $savePoint;
return false;
}
/**
* (ON expression) | (USING_SYM column_list)
*
* @return bool
*/
private function parseJoinCondition() {
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'on') && $this->parseExpression()) {
return true;
}
$this->index = $savePoint;
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'using') && $this->parseColumnList()) {
return true;
}
$this->index = $savePoint;
return false;
}
/**
* @return bool
*/
private function parseTableSpec() {
$savePoint = $this->index;
$lastComponent = $savePoint;
$components = 0;
while (($token = $this->nextToken()) !== false) {
if ($this->isValidIdentifier($token)) {
$lastComponent = $this->index;
$next = $this->nextToken();
if ($this->isTokenOfType($next, wfWAFSQLiLexer::DOT)) {
$components++;
}
elseif ($this->isTokenOfType($next, wfWAFSQLiLexer::REAL_NUMBER_LITERAL)) {
$next = $this->nextToken();
if ($this->isTokenOfType($next, wfWAFSQLiLexer::UNQUOTED_IDENTIFIER) && in_array($next->getValue(), array('e', 'E')) && $this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::DOT)) {
$components++;
}
else {
break;
}
}
elseif ($components > 0 || $this->isValidNonReservedWordIdentifier($token)) {
$this->index = $lastComponent;
return true;
}
else {
break;
}
}
else if ($this->isTokenOfType($token, wfWAFSQLiLexer::ASTERISK)) {
$this->index = $lastComponent;
return true;
}
else {
break;
}
}
$this->index = $savePoint;
return false;
}
/**
* @return bool
*/
private function parseAlias() {
$savePoint = $this->index;
$token = $this->nextToken();
if ($this->isIdentifierWithValue($token, 'as')) {
$token = $this->nextToken();
}
if ($this->isValidNonReservedWordIdentifier($token)) {
return true;
}
$this->index = $savePoint;
return false;
}
/**
* @return bool
*/
private function parseIndexHintList() {
$startPoint = $this->index;
$hasHints = false;
while ($this->parseIndexHint()) {
$hasHints = true;
$savePoint = $this->index;
if (!$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
$this->index = $savePoint;
break;
}
}
if ($hasHints) {
return true;
}
$this->index = $startPoint;
return false;
}
/**
* @return bool
*/
private function parseIndexHint() {
// USE_SYM index_options LPAREN (index_list)? RPAREN
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'use') &&
$this->parseIndexOptions() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS)
) {
$this->parseIndexList();
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
return true;
}
}
$this->index = $savePoint;
// IGNORE_SYM index_options LPAREN index_list RPAREN
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'ignore') &&
$this->parseIndexOptions() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
$this->parseIndexList() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
) {
return true;
}
$this->index = $savePoint;
// FORCE_SYM index_options LPAREN index_list RPAREN
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'force') &&
$this->parseIndexOptions() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS) &&
$this->parseIndexList() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
) {
return true;
}
$this->index = $savePoint;
return false;
}
/**
* (INDEX_SYM | KEY_SYM) ( FOR_SYM ((JOIN_SYM) | (ORDER_SYM BY_SYM) | (GROUP_SYM BY_SYM)) )?
*
* @return bool
*/
private function parseIndexOptions() {
$savePoint = $this->index;
$token = $this->nextToken();
if ($this->isIdentifierWithValue($token, 'index') ||
$this->isIdentifierWithValue($token, 'key')
) {
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'for')) {
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'join')) {
return true;
}
$this->index = $savePoint;
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'order') &&
$this->isIdentifierWithValue($this->nextToken(), 'by')
) {
return true;
}
$this->index = $savePoint;
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'group') &&
$this->isIdentifierWithValue($this->nextToken(), 'by')
) {
return true;
}
$this->index = $savePoint;
return true;
}
$this->index = $savePoint;
return true;
}
$this->index = $savePoint;
return false;
}
private function parseIndexList() {
$startPoint = $this->index;
$hasIndex = false;
while ($this->parseIndexName()) {
$hasIndex = true;
$savePoint = $this->index;
if (!$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
$this->index = $savePoint;
break;
}
}
if ($hasIndex) {
return true;
}
$this->index = $startPoint;
return false;
}
private function parseIndexName() {
$startPoint = $this->index;
$token = $this->nextToken();
if ($this->isValidNonReservedWordIdentifier($token)) {
return true;
}
$this->index = $startPoint;
return false;
}
/**
* LPAREN column_spec (COMMA column_spec)* RPAREN
*
* @return bool
*/
private function parseColumnList() {
$startPoint = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS)) {
$hasColumn = false;
while ($this->parseColumnSpec()) {
$hasColumn = true;
$savePoint = $this->index;
if (!$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
$this->index = $savePoint;
break;
}
}
if ($hasColumn && $this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
return true;
}
}
$this->index = $startPoint;
return false;
}
private function parseWhere() {
$startIndex = $this->index;
$token = $this->nextToken();
if ($this->isIdentifierWithValue($token, 'where')) {
if ($this->parseExpression()) {
return true;
}
}
$this->index = $startIndex;
return false;
}
private function parseProcedure() {
$startIndex = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'PROCEDURE') &&
$this->isIdentifierWithValue($this->nextToken(), 'ANALYSE') &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS)
) {
$savePoint = $this->index;
if ($this->parseExpression()) {
$savePoint = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA) &&
$this->parseExpression() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)
) {
return true;
}
$this->index = $savePoint;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
return true;
}
}
$this->index = $savePoint;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
return true;
}
}
$this->index = $startIndex;
return false;
}
/**
* GROUP_SYM BY_SYM groupby_item (COMMA groupby_item)* (WITH ROLLUP_SYM)?
*
* @return bool
*/
private function parseGroupBy() {
$startIndex = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'group') &&
$this->isIdentifierWithValue($this->nextToken(), 'by')
) {
$hasItems = false;
while ($this->parseGroupByItem()) {
$hasItems = true;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
continue;
}
$this->index--;
break;
}
if ($hasItems) {
$savePoint = $this->index;
if (!($this->isIdentifierWithValue($this->nextToken(), 'with') &&
$this->isIdentifierWithValue($this->nextToken(), 'rollup'))
) {
$this->index = $savePoint;
}
return true;
}
}
$this->index = $startIndex;
return false;
}
/**
* column_spec | INTEGER_NUM | bit_expr ;
*
* @return bool
*/
private function parseGroupByItem() {
$startIndex = $this->index;
if ($this->parseBitExpression()) {
return true;
}
$this->index = $startIndex;
return false;
}
/**
* HAVING expression
*
* @return bool
*/
private function parseHaving() {
$startIndex = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'having')
&& $this->parseExpression()
) {
return true;
}
$this->index = $startIndex;
return false;
}
/**
* ORDER_SYM BY_SYM orderby_item (COMMA orderby_item)*
*
* @return bool
*/
private function parseOrderBy() {
$startIndex = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'order') &&
$this->isIdentifierWithValue($this->nextToken(), 'by')
) {
$hasItems = false;
while ($this->parseOrderByItem()) {
$hasItems = true;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
continue;
}
$this->index--;
break;
}
if ($hasItems) {
return true;
}
}
$this->index = $startIndex;
return false;
}
/**
* groupby_item (ASC | DESC)? ;
*
* @return bool
*/
private function parseOrderByItem() {
$startIndex = $this->index;
if ($this->parseGroupByItem()) {
$savePoint = $this->index;
if (!$this->isIdentifierWithValue($this->nextToken(), array(
'asc', 'desc',
))
) {
$this->index = $savePoint;
}
return true;
}
$this->index = $startIndex;
return false;
}
private function parseLimit() {
// LIMIT ((offset COMMA)? row_count) | (row_count OFFSET_SYM offset)
$startIndex = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'limit') &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::INTEGER_LITERAL)
) {
$savePoint = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA) &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::INTEGER_LITERAL)
) {
return true;
}
$this->index = $savePoint;
$savePoint = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'offset') &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::INTEGER_LITERAL)
) {
return true;
}
$this->index = $savePoint;
return true;
}
$this->index = $startIndex;
return false;
}
/**
* @link http://dev.mysql.com/doc/refman/5.6/en/insert.html
* @return bool
*/
private function parseInsertStatement() {
$startIndex = $this->index;
if ($this->parseInsertStatement1() ||
$this->parseInsertStatement2() ||
$this->parseInsertStatement3()
) {
return true;
}
$this->index = $startIndex;
return false;
}
/**
* insert_header
* (column_list)?
* value_list_clause
* ( insert_subfix )?
*
* @return bool
*/
private function parseInsertStatement1() {
$startIndex = $this->index;
if ($this->parseInsertHeader()) {
$this->parseColumnList();
if ($this->parseValueListClause()) {
$this->parseInsertSubfix();
return true;
}
}
$this->index = $startIndex;
return false;
}
/**
* insert_header
* set_columns_cluase
* ( insert_subfix )?
*
* @return bool
*/
private function parseInsertStatement2() {
$startIndex = $this->index;
if ($this->parseInsertHeader() && $this->parseSetColumnsClause()) {
$this->parseInsertSubfix();
return true;
}
$this->index = $startIndex;
return false;
}
/**
* insert_header
* (column_list)?
* select_expression
* ( insert_subfix )?
*
* @return bool
*/
private function parseInsertStatement3() {
$startIndex = $this->index;
if ($this->parseInsertHeader()) {
$this->parseColumnList();
if ($this->parseSelectStatement()) {
$this->parseInsertSubfix();
return true;
}
}
$this->index = $startIndex;
return false;
}
/**
* INSERT (LOW_PRIORITY | HIGH_PRIORITY)? (IGNORE_SYM)?
* (INTO)? table_spec
* (partition_clause)?
*
* @return bool
*/
private function parseInsertHeader() {
$startIndex = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'insert')) {
$savePoint = $this->index;
// (LOW_PRIORITY | HIGH_PRIORITY)?
if (!$this->isIdentifierWithValue($this->nextToken(), array(
'LOW_PRIORITY', 'HIGH_PRIORITY'
))
) {
$this->index = $savePoint;
}
// (IGNORE_SYM)?
$savePoint = $this->index;
if (!$this->isIdentifierWithValue($this->nextToken(), array(
'IGNORE'
))
) {
$this->index = $savePoint;
}
// (INTO)?
$savePoint = $this->index;
if (!$this->isIdentifierWithValue($this->nextToken(), array(
'into'
))
) {
$this->index = $savePoint;
}
// table_spec
if ($this->parseTableSpec()) {
$savePoint = $this->index;
// (partition_clause)?
if (!$this->parsePartitionClause()) {
$this->index = $savePoint;
}
return true;
}
}
$this->index = $startIndex;
return false;
}
/**
* (VALUES | VALUE_SYM) column_value_list (COMMA column_value_list)*;
*
* @return bool
*/
private function parseValueListClause() {
$startIndex = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), array(
'value', 'values',
))
) {
$hasValues = false;
while ($this->parseColumnValueList()) {
$hasValues = true;
$savePoint = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
$hasValues = false;
continue;
}
$this->index = $savePoint;
break;
}
if ($hasValues) {
return true;
}
}
$this->index = $startIndex;
return false;
}
/**
* LPAREN (bit_expr|DEFAULT) (COMMA (bit_expr|DEFAULT) )* RPAREN ;
*
* @return bool
*/
private function parseColumnValueList() {
$startIndex = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::OPEN_PARENTHESIS)) {
$hasValues = false;
while (true) {
$savePoint = $this->index;
if (!$this->parseBitExpression() && !$this->isIdentifierWithValue($this->nextToken(), 'DEFAULT')) {
$this->index = $savePoint;
break;
}
$hasValues = true;
$savePoint = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
$hasValues = false;
continue;
}
$this->index = $savePoint;
break;
}
if ($hasValues && $this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::CLOSE_PARENTHESIS)) {
return true;
}
}
$this->index = $startIndex;
return false;
}
private function parseInsertSubfix() {
// ON DUPLICATE_SYM KEY_SYM UPDATE column_spec EQ_SYM expression (COMMA column_spec EQ_SYM expression)*
$startIndex = $this->index;
if (
$this->isIdentifierWithValue($this->nextToken(), 'on') &&
$this->isIdentifierWithValue($this->nextToken(), 'duplicate') &&
$this->isIdentifierWithValue($this->nextToken(), 'key') &&
$this->isIdentifierWithValue($this->nextToken(), 'update')
) {
$hasValues = false;
while (true) {
$savePoint = $this->index;
if ($this->parseColumnSpec() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::EQUALS_SYMBOL) &&
$this->parseExpression()
) {
$hasValues = true;
$savePoint = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
$hasValues = false;
continue;
}
$this->index = $savePoint;
break;
}
$this->index = $savePoint;
break;
}
if ($hasValues) {
return true;
}
}
$this->index = $startIndex;
return false;
}
/**
* SET_SYM set_column_cluase ( COMMA set_column_cluase )*;
*
* @return bool
*/
private function parseSetColumnsClause() {
$startIndex = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'set')) {
$hasValues = true;
while ($this->parseSetColumnClause()) {
$hasValues = true;
$savePoint = $this->index;
if ($this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::COMMA)) {
$hasValues = false;
continue;
}
$this->index = $savePoint;
break;
}
if ($hasValues) {
return true;
}
}
$this->index = $startIndex;
return false;
}
/**
* column_spec EQ_SYM (expression|DEFAULT) ;
*
* @return bool
*/
private function parseSetColumnClause() {
$startIndex = $this->index;
if ($this->parseColumnSpec() &&
$this->isTokenOfType($this->nextToken(), wfWAFSQLiLexer::EQUALS_SYMBOL) &&
($this->parseExpression() || $this->isIdentifierWithValue($this->nextToken(), 'default'))
) {
return true;
}
$this->index = $startIndex;
return false;
}
/**
* UPDATE (LOW_PRIORITY)? (IGNORE_SYM)? table_reference
* set_columns_cluase
* (where_clause)?
* (orderby_clause)?
* (limit_clause)?
* |
* UPDATE (LOW_PRIORITY)? (IGNORE_SYM)? table_references
* set_columns_cluase
* (where_clause)?
*
* @return bool
*/
private function parseUpdateStatement() {
$startIndex = $this->index;
if ($this->isIdentifierWithValue($this->nextToken(), 'update')) {
$savePoint = $this->index;
if (!$this->isIdentifierWithValue($this->nextToken(), 'LOW_PRIORITY')) {
$this->index = $savePoint;
}
$savePoint = $this->index;
if (!$this->isIdentifierWithValue($this->nextToken(), 'ignore')) {
$this->index = $savePoint;
}
if ($this->parseTableReferences() && $this->parseSetColumnsClause()) {
$this->parseWhere();
$this->parseOrderBy();
$this->parseLimit();
return true;
}
}
$this->index = $startIndex;
return false;
}
/**
* @param wfWAFLexerToken $token
* @return bool
*/
private function isIdentifier($token) {
return $token && ($token->getType() === wfWAFSQLiLexer::QUOTED_IDENTIFIER || $token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER);
}
/**
* @param wfWAFLexerToken $token
* @param string|array $value
* @return bool
*/
private function isIdentifierWithValue($token, $value) {
return $token && $token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER &&
(is_array($value) ? in_array($token->getLowerCaseValue(), array_map('wfWAFUtils::strtolower', $value)) :
$token->getLowerCaseValue() === wfWAFUtils::strtolower($value));
}
/**
* @param wfWAFLexerToken $token
* @param mixed $type
* @return bool
*/
protected function isTokenOfType($token, $type) {
if (is_array($type)) {
return $token && in_array($token->getType(), $type);
}
return $token && $token->getType() === $type;
}
/**
* @param wfWAFLexerToken $token
* @return bool
*/
private function isNotSymbolToken($token) {
return $token &&
(
($token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER && $token->getLowerCaseValue() === 'not') ||
($token->getType() === wfWAFSQLiLexer::EXPR_NOT)
);
}
/**
* @param wfWAFLexerToken $token
* @return bool
*/
private function isKeywordToken($token) {
return $token && $token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER &&
in_array($token->getUpperCaseValue(), $this->keywords);
}
/**
* @param wfWAFLexerToken $token
* @return bool
*/
private function isReservedWordToken($token) {
return $token && $token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER &&
in_array($token->getUpperCaseValue(), $this->reservedWords);
}
/**
* @param wfWAFLexerToken $token
* @return bool
*/
private function isValidNonKeywordIdentifier($token) {
return $token && (
$token->getType() === wfWAFSQLiLexer::QUOTED_IDENTIFIER ||
($token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER && !$this->isKeywordToken($token))
);
}
/**
* @param wfWAFLexerToken $token
* @return bool
*/
private function isValidNonReservedWordIdentifier($token) {
return $token && (
$token->getType() === wfWAFSQLiLexer::QUOTED_IDENTIFIER ||
($token->getType() === wfWAFSQLiLexer::UNQUOTED_IDENTIFIER && !$this->isReservedWordToken($token))
);
}
/**
* @param wfWAFLexerToken $token
* @return bool
*/
private function isValidIdentifier($token) {
return $this->isTokenOfType($token, array(
wfWAFSQLiLexer::QUOTED_IDENTIFIER,
wfWAFSQLiLexer::UNQUOTED_IDENTIFIER
));
}
/**
* @param wfWAFLexerToken $token
* @return bool
*/
private function isOrToken($token) {
return $token && ($this->isIdentifierWithValue($token, 'or') || $this->isTokenOfType($token, wfWAFSQLiLexer::EXPR_OR));
}
/**
* @param wfWAFLexerToken $token
* @return bool
*/
private function isAndToken($token) {
return $token && ($this->isIdentifierWithValue($token, 'and') || $this->isTokenOfType($token, wfWAFSQLiLexer::EXPR_AND));
}
/**
* @return string
*/
public function getSubject() {
return $this->subject;
}
/**
* @param string $subject
*/
public function setSubject($subject) {
$this->subject = $subject;
$this->setTokens(array());
$this->lexer->setSQL($this->subject);
}
/**
* @return int
*/
public function getFlags() {
return $this->flags;
}
/**
* @param int $flags
*/
public function setFlags($flags) {
$this->flags = $flags;
$this->lexer->setFlags($this->flags);
}
}
class wfWAFSQLiLexer implements wfWAFLexerInterface {
const FLAG_TOKENIZE_MYSQL_PORTABLE_COMMENTS = 0x1;
const UNQUOTED_IDENTIFIER = 'UNQUOTED_IDENTIFIER';
const VARIABLE = 'VARIABLE';
const QUOTED_IDENTIFIER = 'QUOTED_IDENTIFIER';
const DOUBLE_STRING_LITERAL = 'DOUBLE_STRING_LITERAL';
const SINGLE_STRING_LITERAL = 'SINGLE_STRING_LITERAL';
const INTEGER_LITERAL = 'INTEGER_LITERAL';
const REAL_NUMBER_LITERAL = 'REAL_NUMBER_LITERAL';
const BINARY_NUMBER_LITERAL = 'BINARY_NUMBER_LITERAL';
const HEX_NUMBER_LITERAL = 'HEX_NUMBER_LITERAL';
const DOT = 'DOT';
const OPEN_PARENTHESIS = 'OPEN_PARENTHESIS';
const CLOSE_PARENTHESIS = 'CLOSE_PARENTHESIS';
const OPEN_BRACKET = 'OPEN_BRACKET';
const CLOSE_BRACKET = 'CLOSE_BRACKET';
const COMMA = 'COMMA';
const EXPR_OR = 'EXPR_OR';
const EXPR_AND = 'EXPR_AND';
const EXPR_NOT = 'EXPR_NOT';
const BIT_AND = 'BIT_AND';
const BIT_LEFT_SHIFT = 'BIT_LEFT_SHIFT';
const BIT_RIGHT_SHIFT = 'BIT_RIGHT_SHIFT';
const BIT_XOR = 'BIT_XOR';
const BIT_INVERSION = 'BIT_INVERSION';
const BIT_OR = 'BIT_OR';
const PLUS = 'PLUS';
const MINUS = 'MINUS';
const ASTERISK = 'ASTERISK';
const DIVISION = 'DIVISION';
const MOD = 'MOD';
const ARROW = 'ARROW';
const EQUALS_SYMBOL = 'EQUALS_SYMBOL';
const NOT_EQUALS = 'NOT_EQUALS';
const LESS_THAN = 'LESS_THAN';
const GREATER_THAN = 'GREATER_THAN';
const LESS_THAN_EQUAL_TO = 'LESS_THAN_EQUAL_TO';
const GREATER_THAN_EQUAL_TO = 'GREATER_THAN_EQUAL_TO';
const SET_VAR = 'SET_VAR';
const RIGHT_BRACKET = 'RIGHT_BRACKET';
const LEFT_BRACKET = 'LEFT_BRACKET';
const SEMICOLON = 'SEMICOLON';
const COLON = 'COLON';
const MYSQL_PORTABLE_COMMENT_START = 'MYSQL_PORTABLE_COMMENT_START';
const MYSQL_PORTABLE_COMMENT_END = 'MYSQL_PORTABLE_COMMENT_END';
const SINGLE_LINE_COMMENT = 'SINGLE_LINE_COMMENT';
const MULTI_LINE_COMMENT = 'MULTI_LINE_COMMENT';
/**
* @var int
*/
private $flags;
private $tokenMatchers;
private $hasPortableCommentStart = false;
public static function getLexerTokenMatchers() {
static $tokenMatchers;
if ($tokenMatchers === null) {
$tokenMatchers = array(
new wfWAFLexerTokenMatcher(self::REAL_NUMBER_LITERAL, '/^(?:[0-9]+\\.[0-9]+|[0-9]+\\.|\\.[0-9]+|[Ee][\\+\\-][0-9]+)/'),
new wfWAFLexerTokenMatcher(self::BINARY_NUMBER_LITERAL, '/^(?:0b[01]+|[bB]\'[01]+\')/', true),
new wfWAFLexerTokenMatcher(self::HEX_NUMBER_LITERAL, '/^(?:0x[0-9a-fA-F]+|[xX]\'[0-9a-fA-F]+\')/', true),
new wfWAFLexerTokenMatcher(self::INTEGER_LITERAL, '/^[0-9]+/', true),
new wfWAFLexerTokenMatcher(self::VARIABLE, '/^(?:@(?:`(?:[^`]*(?:``[^`]*)*)`|
"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|
\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'|
[a-zA-Z_\\$\\.]+|
@[a-zA-Z_\\$][a-zA-Z_\\$0-9]{0,256}){0,1})
/Asx'),
new wfWAFLexerTokenMatcher(self::QUOTED_IDENTIFIER, '/^`(?:[^`]*(?:``[^`]*)*)`/As'),
new wfWAFLexerTokenMatcher(self::DOUBLE_STRING_LITERAL, '/^(?:[nN]|_[0-9a-zA-Z\\$_]{0,256})?"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"/As'),
new wfWAFLexerTokenMatcher(self::SINGLE_STRING_LITERAL, '/^(?:[nN]|_[0-9a-zA-Z\\$_]{0,256})?\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As'),
// U+0080 .. U+FFFF
new wfWAFLexerTokenMatcher(self::UNQUOTED_IDENTIFIER, '/^[0-9a-zA-Z\\$_\\x{0080}-\\x{FFFF}]{1,256}/u'),
new wfWAFLexerTokenMatcher(self::MYSQL_PORTABLE_COMMENT_START, '/^\\/\\*\\![0-9]{0,5}/s'),
new wfWAFLexerTokenMatcher(self::MYSQL_PORTABLE_COMMENT_END, '/^\\*\\//s'),
new wfWAFLexerTokenMatcher(self::SINGLE_LINE_COMMENT, '/^(?:#[^\n]*|--(?:[ \t\r][^\n]*|[\n]))/'),
new wfWAFLexerTokenMatcher(self::MULTI_LINE_COMMENT, '/^\\/\\*.*?\\*\\//s'),
new wfWAFLexerTokenMatcher(self::DOT, '/^\\./'),
new wfWAFLexerTokenMatcher(self::OPEN_PARENTHESIS, '/^\\(/'),
new wfWAFLexerTokenMatcher(self::CLOSE_PARENTHESIS, '/^\\)/'),
new wfWAFLexerTokenMatcher(self::OPEN_BRACKET, '/^\\{/'),
new wfWAFLexerTokenMatcher(self::CLOSE_BRACKET, '/^\\}/'),
new wfWAFLexerTokenMatcher(self::COMMA, '/^,/'),
new wfWAFLexerTokenMatcher(self::EXPR_OR, '/^\\|\\|/'),
new wfWAFLexerTokenMatcher(self::EXPR_AND, '/^&&/'),
new wfWAFLexerTokenMatcher(self::BIT_LEFT_SHIFT, '/^\\<\\</'),
new wfWAFLexerTokenMatcher(self::BIT_RIGHT_SHIFT, '/^\\>\\>/'),
new wfWAFLexerTokenMatcher(self::EQUALS_SYMBOL, '/^(?:\\=|\\<\\=\\>)/'),
new wfWAFLexerTokenMatcher(self::ARROW, '/^\\=\\>/'),
new wfWAFLexerTokenMatcher(self::LESS_THAN_EQUAL_TO, '/^\\<\\=/'),
new wfWAFLexerTokenMatcher(self::GREATER_THAN_EQUAL_TO, '/^\\>\\=/'),
new wfWAFLexerTokenMatcher(self::NOT_EQUALS, '/^(?:\\<\\>|(?:\\!|\\~|\\^)\\=)/'),
new wfWAFLexerTokenMatcher(self::LESS_THAN, '/^\\</'),
new wfWAFLexerTokenMatcher(self::GREATER_THAN, '/^\\>/'),
new wfWAFLexerTokenMatcher(self::SET_VAR, '/^:\\=/'),
new wfWAFLexerTokenMatcher(self::BIT_XOR, '/^\\^/'),
new wfWAFLexerTokenMatcher(self::BIT_INVERSION, '/^\\~/'),
new wfWAFLexerTokenMatcher(self::BIT_OR, '/^\\|/'),
new wfWAFLexerTokenMatcher(self::PLUS, '/^\\+/'),
new wfWAFLexerTokenMatcher(self::MINUS, '/^\\-/'),
new wfWAFLexerTokenMatcher(self::ASTERISK, '/^\\*/'),
new wfWAFLexerTokenMatcher(self::DIVISION, '/^\\//'),
new wfWAFLexerTokenMatcher(self::MOD, '/^%/'),
new wfWAFLexerTokenMatcher(self::EXPR_NOT, '/^\\!/'),
new wfWAFLexerTokenMatcher(self::BIT_AND, '/^&/'),
new wfWAFLexerTokenMatcher(self::RIGHT_BRACKET, '/^\\]/'),
new wfWAFLexerTokenMatcher(self::LEFT_BRACKET, '/^\\[/'),
new wfWAFLexerTokenMatcher(self::SEMICOLON, '/^;/'),
new wfWAFLexerTokenMatcher(self::COLON, '/^:/'),
);
}
return $tokenMatchers;
}
/**
* @var string
*/
private $sql;
/**
* @var wfWAFStringScanner
*/
private $scanner;
/**
* wfWAFRuleLexer constructor.
* @param $sql
* @param int $flags
*/
public function __construct($sql = null, $flags = 0) {
$this->scanner = new wfWAFStringScanner();
$this->tokenMatchers = self::getLexerTokenMatchers();
$this->setSQL($sql);
$this->setFlags($flags);
}
/**
* @return array
* @throws wfWAFParserSyntaxError
*/
public function tokenize() {
$tokens = array();
while ($token = $this->nextToken()) {
$tokens[] = $token;
}
return $tokens;
}
/**
* @return bool|wfWAFLexerToken
* @throws wfWAFParserSyntaxError
*/
public function nextToken() {
if (!$this->scanner->eos()) {
/** @var wfWAFLexerTokenMatcher $tokenMatcher */
foreach ($this->tokenMatchers as $tokenMatcher) {
$this->scanner->skip('/^\s+/s');
if ($this->scanner->eos()) {
return false;
}
if (($this->flags & self::FLAG_TOKENIZE_MYSQL_PORTABLE_COMMENTS) === 0 &&
($tokenMatcher->getTokenID() === self::MYSQL_PORTABLE_COMMENT_START ||
$tokenMatcher->getTokenID() === self::MYSQL_PORTABLE_COMMENT_END)
) {
continue;
}
if (!$this->hasPortableCommentStart && $tokenMatcher->getTokenID() === self::MYSQL_PORTABLE_COMMENT_END) {
continue;
}
if ($tokenMatcher->useMaximalMunch() && ($match = $this->scanner->check($tokenMatcher->getMatch())) !== null) {
$biggestToken = $this->createToken($tokenMatcher->getTokenID(), $match);
/** @var wfWAFLexerTokenMatcher $tokenMatcher2 */
foreach ($this->tokenMatchers as $tokenMatcher2) {
if ($tokenMatcher === $tokenMatcher2) {
continue;
}
if (($match2 = $this->scanner->check($tokenMatcher2->getMatch())) !== null) {
$biggestToken2 = $this->createToken($tokenMatcher2->getTokenID(), $match2);
if (wfWAFUtils::strlen($biggestToken2->getValue()) > wfWAFUtils::strlen($biggestToken->getValue())) {
$biggestToken = $biggestToken2;
}
}
}
$this->scanner->advancePointer(wfWAFUtils::strlen($biggestToken->getValue()));
return $biggestToken;
} else if (($match = $this->scanner->scan($tokenMatcher->getMatch())) !== null) {
$token = $this->createToken($tokenMatcher->getTokenID(), $match);
if ($tokenMatcher->getTokenID() === self::MYSQL_PORTABLE_COMMENT_START) {
$this->hasPortableCommentStart = true;
} else if ($tokenMatcher->getTokenID() === self::MYSQL_PORTABLE_COMMENT_END) {
$this->hasPortableCommentStart = false;
}
return $token;
}
}
$char = $this->scanner->scanChar();
$e = new wfWAFParserSyntaxError(sprintf('Invalid character "%s" (\x%02x) found on line %d, column %d',
$char, ord($char), $this->scanner->getLine(), $this->scanner->getColumn()));
$e->setParseLine($this->scanner->getLine());
$e->setParseColumn($this->scanner->getColumn());
throw $e;
}
return false;
}
public function hasMoreTokens() {
}
/**
* @param $type
* @param $value
* @return wfWAFLexerToken
*/
protected function createToken($type, $value) {
return new wfWAFLexerToken($type, $value, $this->scanner->getLine(), $this->scanner->getColumn());
}
/**
* @return string
*/
public function getSQL() {
return $this->sql;
}
/**
* @param string $sql
*/
public function setSQL($sql) {
if (is_string($sql)) {
$this->scanner->setString($sql);
}
$this->sql = $sql;
}
/**
* @return int
*/
public function getFlags() {
return $this->flags;
}
/**
* @param int $flags
*/
public function setFlags($flags) {
$this->flags = $flags;
}
/**
* Check if the given token type represents a literal value
* @param $type the token type
* @return bool true if the provided type is a value literal, false otherwise
*/
public function isValueLiteral($type){
switch($type){
case self::DOUBLE_STRING_LITERAL:
case self::SINGLE_STRING_LITERAL:
case self::INTEGER_LITERAL:
case self::REAL_NUMBER_LITERAL:
case self::BINARY_NUMBER_LITERAL:
case self::HEX_NUMBER_LITERAL:
case self::COMMA:
return true;
default:
return false;
}
}
}
class wfWAFLexerTokenMatcher {
/**
* @var mixed
*/
private $tokenID;
/**
* @var string
*/
private $match;
/**
* @var bool
*/
private $useMaximalMunch;
/**
* @param mixed $tokenID
* @param string $match
* @param bool $useMaximalMunch
*/
public function __construct($tokenID, $match, $useMaximalMunch = false) {
$this->tokenID = $tokenID;
$this->match = $match;
$this->useMaximalMunch = $useMaximalMunch;
}
/**
* @return bool
*/
public function useMaximalMunch() {
return $this->useMaximalMunch;
}
/**
* @return mixed
*/
public function getTokenID() {
return $this->tokenID;
}
/**
* @param mixed $tokenID
*/
public function setTokenID($tokenID) {
$this->tokenID = $tokenID;
}
/**
* @return string
*/
public function getMatch() {
return $this->match;
}
/**
* @param string $match
*/
public function setMatch($match) {
$this->match = $match;
}
}
}
|