Пишу на заказ и судя по тз, скрипт должен проверять пользовательские загруженные файлы и если там есть sql запросы, то выдавать предупреждение.
Алгоритм на текущий момент следующий:
1) Задаем список началов запроса и список операторов
PHP:
$start = array('select', 'insert', 'delete', 'update', 'alter');
$operator = array('where', 'join', 'using', 'on', 'and', 'in', 'or', 'like', 'values', 'limit', 'group by', 'order by', 'xor', '||', '&&', 'regexp');
2) В тексте ищем все начала и делим текст по ним. Нам неизвестно длины запроса, так что берем отрезок текста от найденного начала до следующего sql начала.
3) У нас имеются куски текста, в которых мы точно знаем, что они начинаются именно с sql запроса. Наша задача сократить объем текста для анализа. Если файл html то ищем первые html теги от начала и все что после них - удаляем.
Потом нужно обрезать текст по символу ;
Вообщем ищем первый попавшийся символ ; который не в ковычках и не в скобках.
4) У нас уже чуть подчищенный кусок текста с началом с sql запросом. Явное мы почистили, теперь осталось самое сложное. Но сейчас нам нужно вообще проверить, является ли выбранный sql начал - вообще sql запросом. Вдруг там просто встретилось слово SELECT в тексте.
Для этого, проверяем зависимости у каждого типа запросов (по документации их может и не быть, пока обдумываю как парсить такие случаи)
Пример код для проверки запросов
PHP:
/*проверяем валидность запросов, у каждого типа запроса должна быть зависимость*/
$tmp = array();
for ($i = 0, $len = sizeof($querys); $i < $len; $i++) {
if (preg_match('%^(SELECT|UPDATE|DELETE|INSERT|REPLACE|ALTER)%i', $querys[$i], $ret)) {
$type = strtolower($ret[1]);
if (($type == 'select' || $type == 'delete') && preg_match('%\bfrom\b%i', $querys[$i])) {
$tmp[] = $querys[$i];
} elseif ($type == 'insert' && preg_match('%values?\s*\(%i', $querys[$i])) {
$tmp[] = $querys[$i];
} elseif ($type == 'update' && preg_match('%\bset\b%i', $querys[$i])) {
$tmp[] = $querys[$i];
} elseif ($type == 'alter' && preg_match('%^alter\s+(DATABASE|TABLE|SERVER|EVENT|VIEW|FUNCTION|PROCEDURE)%i', $querys[$i])) {
$tmp[] = $querys[$i];
}
}
}
$querys = $tmp;
У селекта и делите должен быть FROM
у инстера должны быть VALUES|VALUE
у альтера DATABASE|TABLE|SERVER|EVENT|VIEW|FUNCTION|PROCEDURE
у апдейта должен быть SET
5) Теперь перебираем все sql операторы и выявляем самый последний среди них. Делим текст по нему. В итоге получается у нас есть начало чистого sql запроса и надо выявить его концовку из другой части.
Кстати, из концовки удаляем апострофы, нагрузки не несет, а загоняться лишний раз под регулярки с ним нет никакой надобности.
Концовку немного переписал, прочитав 8 пост, теперь выглядит так
PHP:
if (preg_match('%^(.*?)\s*(=|!=|<>|<|>|>=|<=)\s*([^\s]+)%i', $end, $ret)) { // id=2
$buffer[] = $start.$ret[0];
} elseif (preg_match('%^\((.*?)\)%i', $end, $ret)) { // values,in (1,2,3)
$buffer[] = $start.$ret[0];
} elseif (preg_match('%^[\'"](.*?)[\'"]%i', $end, $ret)) { // like '%%', like "%%", regexp ''
$buffer[] = $start.$ret[0];
} elseif (preg_match('%^(\d+)\s*,\s*(\d+)%i', $end, $ret)) { // limit 1,2
$buffer[] = $start.$ret[0];
} elseif (preg_match('%^(\d+)%i', $end, $ret)) { // where 1
$buffer[] = $start.$ret[0];
} elseif (preg_match('%^(\w+)(.\w+)?%i', $end, $ret)) { // group by id, group by users.id
$buffer[] = $start.$ret[0];
} elseif (preg_match('%^0x(\w+)%i', $end, $ret)) { // where 0x37
$buffer[] = $start.$ret[0];
}
В принципе это весь алгоритм, тестирую его. Работает, глючно конечно, не все запросы обрабатывает, но работает и самые типичные и распространненые запросы выявляет на ура.
С регулярками немного запутался.
Нужно обработать такие случаи
id=2 - до любого пробельного символа
id='2'
id="2"
как-то типо так
PHP:
preg_match('%=([\'"]?)(?(1)[^\'"]+|\s+)[\'"]?%i');
в регуляках есть условия. Если ([\'"]?) вернула значение, значит ищем [^\'"]+ иначе \s+
Решение конечно можно довести до рабочего состояние, но мне кажется, что фигню делаю и можно проще.
И второй момент, нужно обрабатывать такие случаи
VALUES (1,2,3)
VALUES (1,2,3),(4,5,6),(7,8,9)
Но регулярку уже не смог составить. Поможете?