День добрый, не подскажите класс, который поможет справится с задачей: вход queryString = 'a="12" || b="22" && (c="11" || d="12") ' например такая строка, я задаю, что a - pole1Name b - pole2Name c - pole3Name d - pole4Name в БД mysql. а на выходе нужно получить валидный mysql запрос к БД (вернее все что после WHERE) например, в данном случаи pole1Name="12" OR pole2Name="22" AND (pole3Name="11" OR pole4Name="12") или посоветуйте куда копать в подобном случаи, задача составить из строки, введенной пользователя mysql запрос, который не содержит инъекции....
пока никто ничего не подсказывает сам пишу)) Подскажите, как aa||bb&&cc по регуляркам на два варианта разделить aa - || - bb&&cc aa||bb - && - cc preg_match_all('/^(?P<left>.*)(?P<logic>(\|\||&&))(?P<right>.*)$/', $this->query, $data, PREG_SET_ORDER); только один почему то получается, не разобрался до конца
это можно сделать простым str_replace, правда нужно будет пройтись несколько раз по результату. а защиту от инъекции можно сделать только через плейсхолдеры с экранированием данных. если получаешь уже готовый запрос, его экранировать бесполезно (или очень сложно).
А зачем разделять? Просто замените || на OR, && на AND. Было: a="12" || b="22" && (c="11" || d="12") Стало: a="12" OR b="22" AND (c="11" OR d="12")
Это все к инъекции приведет либо к невалидному запросу, сейчас уже на середине написания анализатора, мб кто второй вопрос подскажет
ещё как приведёт. готовый запрос нет смысла экранировать. а по второму вопросу тут недавно тема поднималась preg_split с сохранением разделителя
Пример был дан под запрос вида aa||bb&&cc. Зачем тут регулярками разделять логику, если любой язык самостоятельно может определить старшинство операторов?
Jampire строку eval-ом? к тому же эту строку нужно преобразовать в SQL вид, если я правильно понял поставленную задачу
Нифига не врублюсь. Допустим строка уже почти преобразована, на предпоследнем этапе все выглядит так: Код (Text): SELECT * FROM `table` WHERE `aa`= 1 || `bb` = 2 && `cc` = 3; Ну и в чем тут смысл строчить мегарегулярки? Что мешает тупо заменить || на OR и && на AND??? Последовательность разбора условия мускул возьмет на себя.
Данные приходят ОТ ПОЛЬЗОВАТЕЛЯ, почитай про sql - инъекцию, класс кстати написал в итоге, мб чуть позже выложу
Костян ))) Слышать тебя больше не хочу, постоянно пытаешься докопаться, при этом не можешь аргументировать практически ни единого слова, о твоих знаниях судить не буду ибо не с чего, но лучше не порти мне настроение, не пиши ерунды))
Еще раз. Как стоял вопрос? Как разделить логику разбора a&&b||c. Так зачем ее разделять??? Вы уже отфильтровали все данные, пришедшие от пользователя, вы уже выяснили, что a, b и с корректные данные, на предпоследнем этапе преобразования ваш запрос выглядит так: Код (Text): SELECT * FROM `table` WHERE `aa`= 1 || `bb` = 2 && `cc` = 3; Внимание вопрос! Зачем тут выдумывать логику разбора операторов? Какая тут может быть инъекция, если я || заменю на OR, а && на AND? Не существует инъекции в логических операторах. Инъекции могут быть только в значениях. OR 1=1 OR ... Тут инъекция не в OR, как вы ложно предполагаете, а инъекция в 1=1 ... . Эти лишние OR вы должны были отсечь на ранних этапах преобразования. И все сводиться к банальной str_replace на последнем шаге.
karlozzz так ты гонишь, ты какую то хрень придумал опять... если уж ты сделал такую хрень, то надо разбирать конечным автоматом если регуляркой не получается сделать надёжно...
Jampire от пользователя приходит строка, которую он вписывает в поле, все это нужно для более сложного отсева данных Зачем? Так надо значит, мне деньги платят, значит без проблем сделаю Костян Разбирал на лексемы и углублялся в них, но меня уже не это интересует, почему бл*** хрень то? Ну где ты тут хоть что то нелогичное видишь? Задача вполне понятная igordata 20 минут? Напиши ради интереса, каким хотя бы алгоритмом будешь разбирать?
Примерно так. Есть конечно много направлений для улучшения... PHP: <?php function makeSql($queryString){ $db=SQLDBFactory::getDB(); $rez=preg_split('#(\|\|)|(&&)|(\()|(\))#u',$queryString,null, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY); $asIs=array('(', ')'); $replacement=array('a'=>'pole1Name','b'=>'pole2Name','c'=>'pole3Name','d'=>'pole4Name'); $operations=array('>=','<=','=','>','<'); $sql=''; foreach($rez as $token){ $add=false; if (!trim($token)) continue; if (in_array($token, $asIs)){ $sql.=$token.' '; continue; } if ($token=='||'){ $sql.='or '; continue; } if ($token=='&&'){ $sql.='and '; continue; } foreach($operations as $o){ if(strpos($token,$o)!==false){ $po=false; $p1=false; $parts=explode($o,$token); if(count($parts)!=2) throw new Exception('Запрос составлен неверно.'); $parts[0]=trim($parts[0]); $parts[1]=trim($parts[1]); $pos[0]=strpos($parts[0],'"'); $pos[1]=strpos($parts[0],"'"); $pos[2]=strpos($parts[1],'"'); $pos[3]=strpos($parts[1],"'"); logVar($pos, 'pos'); if($pos[0]===0 || $pos[1]===0){ $parts[0]=$db->escapeString(trim($parts[0],'"\' ')); $p0=true; } if($pos[2]===0 || $pos[3]===0){ $parts[1]=$db->escapeString(trim($parts[1],'"\' ')); $p1=true; } if (isset($replacement[$parts[0]])){ $parts[0]=$db->escapeKeys($replacement[$parts[0]]); $p0=true; } if (isset($replacement[$parts[1]])){ $parts[1]=$db->escapeKeys($replacement[$parts[1]]); $p1=true; } logVar($parts, 'parts'); if(!$p0 || !$p1) throw new Exception('Не могу разобрать на части кусок => '.$token); $sql.=$parts[0].' '.$o.' '.$parts[1].' '; $add=true; break; } } if ($add) continue; throw new Exception('Не могу разобрать часть запроса => '.htmlspecialchars($token)); } return $sql; } $queryString[] = 'a="12" || b="22" && (c="11" || d<"12") '; $queryString[] = 'a="12"'; $queryString[] = 'a="12" || b>="22" && c="11"'; $queryString[] = '"12"=a || b=c'; $queryString[] = '"12"=a || b="абра кадабра \' or true"'; foreach($queryString as $query){ echo $query,"<br>", makeSql($query), "<br><br>"; } Для MSSQL с настройкой "текст в базе хранится в UTF" выводит:
Volt(220) Спасибо большое, как появится время разберусь в Вашем примере, как и обещал, выкладываю свой, делал достаточно спешно, так что строго не судить)))) PHP: <?php class booleanParser_element{ protected $bp; protected $isNormal = false; protected $query; protected $type; protected $data = Array(); public function razbiv($str, $delimiter){ $return = Array(); $start = 0; $nCount = 0; while(true){ $now = strpos($str, $delimiter, $start); if($now != false){ $return[$nCount][0] = substr($str, 0, $now); $return[$nCount][1] = substr($str, $now + strlen($delimiter)); $nCount++; $start = $now + 1; }else{ break; } } return $return; } public function __construct(&$bp, $query, $type = "logic") { $this->type = $type; $this->bp = &$bp; $this->query = trim($query); switch($type){ case('logic'): if(isset($this->query[0], $this->query[strlen($this->query)-1]) AND ($this->query[0] == '(' AND $this->query[strlen($this->query)-1] == ')')){ $a = new booleanParser_element($bp, substr($this->query, 1, strlen($this->query)-2)); if($a->isNormal()){ $this->isNormal = true; $this->data[0] = '()'; $this->data[1] = $a; return true; } } if(isset($this->query[0]) AND ($this->query[0] == '!')){ $a = new booleanParser_element($bp, substr($this->query, 1)); if($a->isNormal()){ $this->isNormal = true; $this->data[0] = '!l'; $this->data[1] = $a; return true; } } $lOpers = Array('OR'=>'||', 'AND'=>'&&'); foreach ($lOpers as $koper=>$loper){ $razbiv = $this->razbiv($this->query, $loper); foreach ($razbiv as $val){ $a = new booleanParser_element($bp, $val[0]); $b = new booleanParser_element($bp, $val[1]); if($a->isNormal() AND $b->isNormal()){ $this->isNormal = true; $this->data[0] = 'l-l'; $this->data[1] = $koper; $this->data[2] = $a; $this->data[3] = $b; return true; } } } $sravOpers = Array('='=>'==', '!='=>'!=', '>'=>'>', '<'=>'<'); foreach($sravOpers as $koper=>$soper){ $razbiv = $this->razbiv($this->query, $soper); foreach ($razbiv as $val){ $a = new booleanParser_element($bp, $val[0], 'operator'); $b = new booleanParser_element($bp, $val[1], 'value'); if($a->isNormal() AND $b->isNormal()){ $this->isNormal = true; $this->data[0] = 'o-v'; $this->data[1] = $koper; $this->data[2] = $a; $this->data[3] = $b; return true; } } } break; case('value'): if(preg_match('/(^"([0-9A-Z a-z-_]|\\\")*"$)|(^[0-9]+$)/i', $this->query)){ $this->isNormal = true; $this->data[0] = $this->query; } break; case('operator'): if($bp->__get($this->query) != false){ $this->isNormal = true; $this->data[0] = $bp->__get($this->query); } break; default: $this->isNormal = false; } } public function isNormal(){ return $this->isNormal; } public function getQuery(){ if($this->isNormal()){ switch ($this->type){ case('logic'): switch($this->data[0]){ case('()'): return '(' . $this->data[1]->getQuery() . ')'; break; case('!l'): return ' NOT (' . $this->data[1]->getQuery() . ')'; break; case('l-l'): return $this->data[2]->getQuery() . ' ' . $this->data[1] . ' ' . $this->data[3]->getQuery(); break; case('o-v'): return $this->data[2]->getQuery() . $this->data[1] . $this->data[3]->getQuery(); break; } break; case('operator'): case('value'): return $this->data[0]; break; } }else{ return false; } } } class booleanParser{ protected $vars = Array(); public function __construct() {} public function __set($name, $value) { $this->vars[$name] = $value; } public function __get($name){ if(isset($this->vars[$name])){ return $this->vars[$name]; }else{ return false; } } public function run($str){ $logic = new booleanParser_element($this, $str); return $logic->getQuery(); } } /* $test = new booleanParser(); $test->nm = 'testName'; $test->nm1 = 'testName2'; var_dump($test->run('!(nm=="1\"0" || nm1 != 23)')); */ ?>
/queryString = '{a}="12" || {b}="22" && ({c}="11" || {d}="12") ' например такая строка, я задаю, что a - pole1Name b - pole2Name c - pole3Name d - pole4Name / работа с шаблонами, почитайте в теме для новичков хе-хе Код (Text): // frame ------------------------------------------------------------------ $query_='{a}="12" OR {b}="22" AND ({c}="11" OR {d}="12")'; // main ------------------------------------------------------------------- $a = 'pole1Name'; $b = 'pole2Name'; $c = 'pole3Name'; $d = 'pole4Name'; // constructor ------------------------------------------------------------ $query = Parse($query_); Код (Text): $query_1='{a}={avalue} or {b}={bvalue} {and}'; // default ----------------------------------------------------------------- $and='AND ({c}="{cvalue}" OR {d}="{dvalue}")'; // main ------------------------------------------------------------------- $a = 'pole1Name'; $b = 'pole2Name'; $c = 'pole3Name'; $d = 'pole4Name'; $avalue = '12'; $bvalue = 22; // без разницы string/int $cvalue = 11; $dvalue = 12; // constructor ------------------------------------------------------------ $query = Parse($query_1); * с помощью секции "and" можно добиться маштабируемости запроса "$and='luboi vid zaprosa po prinatoi sheme';" и потом "Parse($query_1);" * можно загружать переменные из массива главное чтобы объявления переменных были в след. порядке: от старших к младшим (сначала $query_ потом $and а потом $a..$d; $avalue..)
kadet Если честно ничего не понял, если не сложно объясните еще раз, со ссылками на материал, чтобы сразу же почитать
Прошу прощения что долго не отвечал, всё дело в функции Parse(); Я её немного усовершенствовал ( Код (Text): // frame ------------------------------------------------------------------ $query_='{a}="12" OR {b}="22" AND ({c}="11" OR {d}="12")'; // main ------------------------------------------------------------------- $a = 'pole1Name'; $b = 'pole2Name'; $c = 'pole3Name'; $d = 'pole4Name'; // constructor ------------------------------------------------------------ $query = Parse($query_); вот функция Parse(); Код (Text): function Parse($st) { if($st=='') return ''; # внимание! использование такого подхода НЕ безопасно # модифицируйте сами этот метод, для работы с массивами. reset($GLOBALS); while( $pv=each( $GLOBALS )) { if( strpos( $st , '{' .$pv[0]. '}')>0) { $st=str_replace('{'.$pv[0].'}',$pv[1],$st); } } return $st; } Теперь вам должно быть понятно Работа с шаблонами описана в теме "Буферизация и быстрые шаблоны" http://www.php.ru/forum/viewtopic.php?t=1372