За последние 24 часа нас посетили 23674 программиста и 1545 роботов. Сейчас ищут 1000 программистов ...

составлятель запросов

Тема в разделе "Прочие вопросы по PHP", создана пользователем karlozzz, 9 фев 2011.

  1. karlozzz

    karlozzz Активный пользователь

    С нами с:
    24 окт 2010
    Сообщения:
    430
    Симпатии:
    0
    Адрес:
    Y-OLA
    День добрый, не подскажите класс, который поможет справится с задачей:
    вход
    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 запрос, который не содержит инъекции....
     
  2. karlozzz

    karlozzz Активный пользователь

    С нами с:
    24 окт 2010
    Сообщения:
    430
    Симпатии:
    0
    Адрес:
    Y-OLA
    пока никто ничего не подсказывает сам пишу))
    Подскажите, как
    aa||bb&&cc

    по регуляркам на два варианта разделить
    aa - || - bb&&cc
    aa||bb - && - cc

    preg_match_all('/^(?P<left>.*)(?P<logic>(\|\||&&))(?P<right>.*)$/', $this->query, $data, PREG_SET_ORDER);
    только один почему то получается, не разобрался до конца
     
  3. Gromo

    Gromo Активный пользователь

    С нами с:
    24 май 2010
    Сообщения:
    2.786
    Симпатии:
    2
    Адрес:
    Ташкент
    это можно сделать простым str_replace, правда нужно будет пройтись несколько раз по результату.
    а защиту от инъекции можно сделать только через плейсхолдеры с экранированием данных.
    если получаешь уже готовый запрос, его экранировать бесполезно (или очень сложно).
     
  4. Jampire

    Jampire Активный пользователь

    С нами с:
    22 авг 2009
    Сообщения:
    181
    Симпатии:
    0
    Адрес:
    Гомель
    А зачем разделять? Просто замените || на OR, && на AND.
    Было:
    a="12" || b="22" && (c="11" || d="12")
    Стало:
    a="12" OR b="22" AND (c="11" OR d="12")
     
  5. karlozzz

    karlozzz Активный пользователь

    С нами с:
    24 окт 2010
    Сообщения:
    430
    Симпатии:
    0
    Адрес:
    Y-OLA
    Это все к инъекции приведет либо к невалидному запросу, сейчас уже на середине написания анализатора, мб кто второй вопрос подскажет
     
  6. Gromo

    Gromo Активный пользователь

    С нами с:
    24 май 2010
    Сообщения:
    2.786
    Симпатии:
    2
    Адрес:
    Ташкент
    ещё как приведёт. готовый запрос нет смысла экранировать.
    а по второму вопросу тут недавно тема поднималась preg_split с сохранением разделителя
     
  7. Jampire

    Jampire Активный пользователь

    С нами с:
    22 авг 2009
    Сообщения:
    181
    Симпатии:
    0
    Адрес:
    Гомель
    Пример был дан под запрос вида aa||bb&&cc. Зачем тут регулярками разделять логику, если любой язык самостоятельно может определить старшинство операторов?
     
  8. Gromo

    Gromo Активный пользователь

    С нами с:
    24 май 2010
    Сообщения:
    2.786
    Симпатии:
    2
    Адрес:
    Ташкент
    Jampire
    строку eval-ом? к тому же эту строку нужно преобразовать в SQL вид, если я правильно понял поставленную задачу
     
  9. karlozzz

    karlozzz Активный пользователь

    С нами с:
    24 окт 2010
    Сообщения:
    430
    Симпатии:
    0
    Адрес:
    Y-OLA
    Gromo
    Да, понял все правильно) Заканчиваю уже класс, мб даже выложу)
     
  10. Jampire

    Jampire Активный пользователь

    С нами с:
    22 авг 2009
    Сообщения:
    181
    Симпатии:
    0
    Адрес:
    Гомель
    Нифига не врублюсь. Допустим строка уже почти преобразована, на предпоследнем этапе все выглядит так:
    Код (Text):
    1. SELECT * FROM `table` WHERE `aa`= 1 || `bb` = 2 && `cc` = 3;
    Ну и в чем тут смысл строчить мегарегулярки? Что мешает тупо заменить || на OR и && на AND??? Последовательность разбора условия мускул возьмет на себя.
     
  11. karlozzz

    karlozzz Активный пользователь

    С нами с:
    24 окт 2010
    Сообщения:
    430
    Симпатии:
    0
    Адрес:
    Y-OLA
    Данные приходят ОТ ПОЛЬЗОВАТЕЛЯ, почитай про sql - инъекцию, класс кстати написал в итоге, мб чуть позже выложу
     
  12. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    очередной бред
     
  13. karlozzz

    karlozzz Активный пользователь

    С нами с:
    24 окт 2010
    Сообщения:
    430
    Симпатии:
    0
    Адрес:
    Y-OLA
    Костян
    ))) Слышать тебя больше не хочу, постоянно пытаешься докопаться, при этом не можешь аргументировать практически ни единого слова, о твоих знаниях судить не буду ибо не с чего, но лучше не порти мне настроение, не пиши ерунды))
     
  14. Jampire

    Jampire Активный пользователь

    С нами с:
    22 авг 2009
    Сообщения:
    181
    Симпатии:
    0
    Адрес:
    Гомель
    Еще раз. Как стоял вопрос? Как разделить логику разбора a&&b||c. Так зачем ее разделять??? Вы уже отфильтровали все данные, пришедшие от пользователя, вы уже выяснили, что a, b и с корректные данные, на предпоследнем этапе преобразования ваш запрос выглядит так:
    Код (Text):
    1. SELECT * FROM `table` WHERE `aa`= 1 || `bb` = 2 && `cc` = 3;
    Внимание вопрос! Зачем тут выдумывать логику разбора операторов? Какая тут может быть инъекция, если я || заменю на OR, а && на AND? Не существует инъекции в логических операторах. Инъекции могут быть только в значениях.
    OR 1=1 OR ...
    Тут инъекция не в OR, как вы ложно предполагаете, а инъекция в 1=1 ... . Эти лишние OR вы должны были отсечь на ранних этапах преобразования. И все сводиться к банальной str_replace на последнем шаге.
     
  15. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    karlozzz
    так ты гонишь, ты какую то хрень придумал опять... если уж ты сделал такую хрень, то надо разбирать конечным автоматом если регуляркой не получается сделать надёжно...
     
  16. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    тут работы на 20 минут. как вы можете это мусолить так долго?
     
  17. karlozzz

    karlozzz Активный пользователь

    С нами с:
    24 окт 2010
    Сообщения:
    430
    Симпатии:
    0
    Адрес:
    Y-OLA
    Jampire
    от пользователя приходит строка, которую он вписывает в поле, все это нужно для более сложного отсева данных
    Зачем? Так надо значит, мне деньги платят, значит без проблем сделаю
    Костян
    Разбирал на лексемы и углублялся в них, но меня уже не это интересует, почему бл*** хрень то? Ну где ты тут хоть что то нелогичное видишь? Задача вполне понятная
    igordata
    20 минут? Напиши ради интереса, каким хотя бы алгоритмом будешь разбирать?
     
  18. Volt(220)

    Volt(220) Активный пользователь

    С нами с:
    11 июн 2009
    Сообщения:
    1.640
    Симпатии:
    1
    Примерно так. Есть конечно много направлений для улучшения...
    PHP:
    1. <?php
    2. function makeSql($queryString){
    3.     $db=SQLDBFactory::getDB();
    4.     $rez=preg_split('#(\|\|)|(&&)|(\()|(\))#u',$queryString,null, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
    5.     $asIs=array('(', ')');
    6.     $replacement=array('a'=>'pole1Name','b'=>'pole2Name','c'=>'pole3Name','d'=>'pole4Name');
    7.     $operations=array('>=','<=','=','>','<');
    8.     $sql='';
    9.     foreach($rez as $token){
    10.         $add=false;
    11.         if (!trim($token)) continue;
    12.         if (in_array($token, $asIs)){
    13.             $sql.=$token.' ';
    14.             continue;
    15.         }
    16.         if ($token=='||'){
    17.             $sql.='or ';
    18.             continue;
    19.         }
    20.         if ($token=='&&'){
    21.             $sql.='and ';
    22.             continue;
    23.         }
    24.         foreach($operations as $o){
    25.             if(strpos($token,$o)!==false){
    26.                 $po=false;
    27.                 $p1=false;
    28.                 $parts=explode($o,$token);
    29.                 if(count($parts)!=2) throw new Exception('Запрос составлен неверно.');
    30.                 $parts[0]=trim($parts[0]);
    31.                 $parts[1]=trim($parts[1]);
    32.                 $pos[0]=strpos($parts[0],'"');
    33.                 $pos[1]=strpos($parts[0],"'");
    34.                 $pos[2]=strpos($parts[1],'"');
    35.                 $pos[3]=strpos($parts[1],"'");
    36.                 logVar($pos, 'pos');
    37.                 if($pos[0]===0 || $pos[1]===0){
    38.                     $parts[0]=$db->escapeString(trim($parts[0],'"\' '));
    39.                     $p0=true;
    40.                 }
    41.                 if($pos[2]===0 || $pos[3]===0){
    42.                     $parts[1]=$db->escapeString(trim($parts[1],'"\' '));
    43.                     $p1=true;
    44.                 }
    45.                 if (isset($replacement[$parts[0]])){
    46.                     $parts[0]=$db->escapeKeys($replacement[$parts[0]]);
    47.                     $p0=true;
    48.                 }
    49.                 if (isset($replacement[$parts[1]])){
    50.                     $parts[1]=$db->escapeKeys($replacement[$parts[1]]);
    51.                     $p1=true;
    52.                 }
    53.                 logVar($parts, 'parts');
    54.                 if(!$p0 || !$p1) throw new Exception('Не могу разобрать на части кусок => '.$token);
    55.                 $sql.=$parts[0].' '.$o.' '.$parts[1].' ';
    56.                 $add=true;
    57.                 break;
    58.             }
    59.         }
    60.         if ($add) continue;
    61.  
    62.         throw new Exception('Не могу разобрать часть запроса => '.htmlspecialchars($token));
    63.     }
    64.     return $sql;
    65. }
    66.  
    67. $queryString[] = 'a="12" || b="22" && (c="11" || d<"12") ';
    68. $queryString[] = 'a="12"';
    69. $queryString[] = 'a="12" || b>="22" && c="11"';
    70. $queryString[] = '"12"=a || b=c';
    71. $queryString[] = '"12"=a || b="абра кадабра \' or  true"';
    72. foreach($queryString as $query){
    73.     echo $query,"<br>", makeSql($query), "<br><br>";
    74. }
    75.  
    Для MSSQL с настройкой "текст в базе хранится в UTF" выводит:
     
  19. karlozzz

    karlozzz Активный пользователь

    С нами с:
    24 окт 2010
    Сообщения:
    430
    Симпатии:
    0
    Адрес:
    Y-OLA
    Volt(220)
    Спасибо большое, как появится время разберусь в Вашем примере, как и обещал, выкладываю свой, делал достаточно спешно, так что строго не судить))))


    PHP:
    1. <?php
    2. class booleanParser_element{
    3.     protected $bp;
    4.     protected $isNormal = false;
    5.     protected $query;
    6.     protected $type;
    7.     protected $data = Array();
    8.  
    9.     public function razbiv($str, $delimiter){
    10.         $return = Array();
    11.         $start = 0;
    12.         $nCount = 0;
    13.  
    14.         while(true){
    15.             $now = strpos($str, $delimiter, $start);
    16.             if($now != false){
    17.                 $return[$nCount][0] = substr($str, 0, $now);
    18.                 $return[$nCount][1] = substr($str, $now + strlen($delimiter));
    19.                 $nCount++;
    20.                 $start = $now + 1;
    21.             }else{
    22.                 break;
    23.             }
    24.         }
    25.         return $return;
    26.  
    27.     }
    28.     public function  __construct(&$bp, $query, $type = "logic") {
    29.         $this->type = $type;
    30.         $this->bp = &$bp;
    31.         $this->query = trim($query);
    32.  
    33.         switch($type){
    34.             case('logic'):
    35.                 if(isset($this->query[0], $this->query[strlen($this->query)-1]) AND ($this->query[0] == '(' AND $this->query[strlen($this->query)-1] == ')')){
    36.                     $a = new booleanParser_element($bp, substr($this->query, 1, strlen($this->query)-2));
    37.                     if($a->isNormal()){
    38.                         $this->isNormal = true;
    39.                         $this->data[0] = '()';
    40.                         $this->data[1] = $a;
    41.                         return true;
    42.                     }
    43.                 }
    44.  
    45.                 if(isset($this->query[0]) AND ($this->query[0] == '!')){
    46.                     $a = new booleanParser_element($bp, substr($this->query, 1));
    47.                     if($a->isNormal()){
    48.                         $this->isNormal = true;
    49.                         $this->data[0] = '!l';
    50.                         $this->data[1] = $a;
    51.                         return true;
    52.                     }
    53.                 }
    54.  
    55.                 $lOpers = Array('OR'=>'||', 'AND'=>'&&');
    56.                 foreach ($lOpers as $koper=>$loper){
    57.                     $razbiv = $this->razbiv($this->query, $loper);
    58.                     foreach ($razbiv as $val){
    59.                         $a = new booleanParser_element($bp, $val[0]);
    60.                         $b = new booleanParser_element($bp, $val[1]);
    61.                         if($a->isNormal() AND $b->isNormal()){
    62.                             $this->isNormal = true;
    63.                             $this->data[0] = 'l-l';
    64.                             $this->data[1] = $koper;
    65.                             $this->data[2] = $a;
    66.                             $this->data[3] = $b;
    67.                             return true;
    68.                         }
    69.                     }
    70.                 }
    71.  
    72.  
    73.                 $sravOpers = Array('='=>'==', '!='=>'!=', '>'=>'>', '<'=>'<');
    74.                 foreach($sravOpers as $koper=>$soper){
    75.                     $razbiv = $this->razbiv($this->query, $soper);
    76.                     foreach ($razbiv as $val){
    77.                         $a = new booleanParser_element($bp, $val[0], 'operator');
    78.                         $b = new booleanParser_element($bp, $val[1], 'value');
    79.                         if($a->isNormal() AND $b->isNormal()){
    80.                             $this->isNormal = true;
    81.                             $this->data[0] = 'o-v';
    82.                             $this->data[1] = $koper;
    83.                             $this->data[2] = $a;
    84.                             $this->data[3] = $b;
    85.                             return true;
    86.                         }
    87.                     }
    88.                 }
    89.                 break;
    90.             case('value'):
    91.                 if(preg_match('/(^"([0-9A-Z a-z-_]|\\\")*"$)|(^[0-9]+$)/i', $this->query)){
    92.                     $this->isNormal = true;
    93.                     $this->data[0] = $this->query;
    94.                 }
    95.                 break;
    96.             case('operator'):
    97.                 if($bp->__get($this->query) != false){
    98.                     $this->isNormal = true;
    99.                     $this->data[0] = $bp->__get($this->query);
    100.                 }
    101.                 break;
    102.             default:
    103.                 $this->isNormal = false;
    104.         }
    105.     }
    106.  
    107.     public function isNormal(){
    108.         return $this->isNormal;
    109.     }
    110.  
    111.     public function getQuery(){
    112.         if($this->isNormal()){
    113.             switch ($this->type){
    114.                 case('logic'):
    115.                     switch($this->data[0]){
    116.                         case('()'):
    117.                             return '(' . $this->data[1]->getQuery() . ')';
    118.                             break;
    119.                         case('!l'):
    120.                             return ' NOT (' . $this->data[1]->getQuery() . ')';
    121.                             break;
    122.                         case('l-l'):
    123.                             return $this->data[2]->getQuery() . ' ' . $this->data[1] . ' ' . $this->data[3]->getQuery();
    124.                             break;
    125.                         case('o-v'):
    126.                             return $this->data[2]->getQuery() . $this->data[1] . $this->data[3]->getQuery();
    127.                             break;
    128.                     }
    129.                     break;
    130.                 case('operator'):
    131.                 case('value'):
    132.                     return $this->data[0];
    133.                     break;
    134.             }
    135.         }else{
    136.             return false;
    137.         }
    138.     }
    139. }
    140.  
    141. class booleanParser{
    142.     protected $vars = Array();
    143.  
    144.     public function  __construct() {}
    145.  
    146.     public function  __set($name, $value) {
    147.         $this->vars[$name] = $value;
    148.     }
    149.     public function __get($name){
    150.         if(isset($this->vars[$name])){
    151.             return $this->vars[$name];
    152.         }else{
    153.             return false;
    154.         }
    155.     }
    156.  
    157.  
    158.     public function run($str){
    159.         $logic = new booleanParser_element($this, $str);
    160.         return $logic->getQuery();
    161.     }
    162. }
    163.  
    164. /*
    165. $test = new booleanParser();
    166. $test->nm = 'testName';
    167. $test->nm1 = 'testName2';
    168. var_dump($test->run('!(nm=="1\"0"  ||  nm1 != 23)'));
    169. */
    170. ?>
    171.  
     
  20. kadet

    kadet Активный пользователь

    С нами с:
    2 авг 2010
    Сообщения:
    79
    Симпатии:
    0
    /queryString = '{a}="12" || {b}="22" && ({c}="11" || {d}="12") '
    например такая строка, я задаю, что

    a - pole1Name
    b - pole2Name
    c - pole3Name
    d - pole4Name /

    работа с шаблонами, почитайте в теме для новичков хе-хе

    Код (Text):
    1.  
    2. // frame ------------------------------------------------------------------
    3. $query_='{a}="12" OR {b}="22" AND ({c}="11" OR {d}="12")';
    4.  
    5. // main -------------------------------------------------------------------
    6. $a = 'pole1Name';
    7. $b = 'pole2Name';
    8. $c = 'pole3Name';
    9. $d = 'pole4Name';
    10.  
    11. // constructor ------------------------------------------------------------
    12. $query = Parse($query_);
    Код (Text):
    1.  
    2. $query_1='{a}={avalue} or {b}={bvalue} {and}';
    3.  
    4. // default -----------------------------------------------------------------
    5. $and='AND ({c}="{cvalue}" OR {d}="{dvalue}")';
    6.  
    7. // main -------------------------------------------------------------------
    8. $a = 'pole1Name';
    9. $b = 'pole2Name';
    10. $c = 'pole3Name';
    11. $d = 'pole4Name';
    12.  
    13. $avalue = '12';
    14. $bvalue = 22; // без разницы string/int
    15. $cvalue = 11;
    16. $dvalue = 12;
    17.  
    18. // constructor ------------------------------------------------------------
    19. $query = Parse($query_1);
    * с помощью секции "and" можно добиться маштабируемости запроса
    "$and='luboi vid zaprosa po prinatoi sheme';"
    и потом "Parse($query_1);"

    * можно загружать переменные из массива

    главное чтобы объявления переменных были в след. порядке: от старших к младшим (сначала $query_ потом $and а потом $a..$d; $avalue..)
     
  21. karlozzz

    karlozzz Активный пользователь

    С нами с:
    24 окт 2010
    Сообщения:
    430
    Симпатии:
    0
    Адрес:
    Y-OLA
    kadet
    Если честно ничего не понял, если не сложно объясните еще раз, со ссылками на материал, чтобы сразу же почитать
     
  22. kadet

    kadet Активный пользователь

    С нами с:
    2 авг 2010
    Сообщения:
    79
    Симпатии:
    0
    Прошу прощения что долго не отвечал, всё дело в функции Parse();
    Я её немного усовершенствовал :)(


    Код (Text):
    1.  
    2. // frame ------------------------------------------------------------------
    3. $query_='{a}="12" OR {b}="22" AND ({c}="11" OR {d}="12")';
    4.  
    5. // main -------------------------------------------------------------------
    6. $a = 'pole1Name';
    7. $b = 'pole2Name';
    8. $c = 'pole3Name';
    9. $d = 'pole4Name';
    10.  
    11. // constructor ------------------------------------------------------------
    12. $query = Parse($query_);
    вот функция Parse();

    Код (Text):
    1.  
    2. function Parse($st) {
    3.  
    4.          if($st=='') return '';
    5.  
    6.          # внимание! использование такого подхода НЕ безопасно
    7.          # модифицируйте сами этот метод, для работы с массивами.
    8.  
    9.  
    10.          reset($GLOBALS);
    11.  
    12.          while( $pv=each( $GLOBALS )) {
    13.                  if( strpos( $st , '{' .$pv[0]. '}')>0) {
    14.                          $st=str_replace('{'.$pv[0].'}',$pv[1],$st);
    15.                  }
    16.          }
    17.          
    18.          return $st;  
    19.  }
    Теперь вам должно быть понятно

    Работа с шаблонами описана в теме "Буферизация и быстрые шаблоны" http://www.php.ru/forum/viewtopic.php?t=1372
     
  23. karlozzz

    karlozzz Активный пользователь

    С нами с:
    24 окт 2010
    Сообщения:
    430
    Симпатии:
    0
    Адрес:
    Y-OLA
    kadet
    ужс, вопрос другой, но "парсер" убивает...
     
  24. kadet

    kadet Активный пользователь

    С нами с:
    2 авг 2010
    Сообщения:
    79
    Симпатии:
    0
    кого?
     
  25. Gromo

    Gromo Активный пользователь

    С нами с:
    24 май 2010
    Сообщения:
    2.786
    Симпатии:
    2
    Адрес:
    Ташкент
    лучше этим не пользоваться