Advertisement
Guest User

replacer/sql_assignment.php

a guest
Jan 21st, 2013
260
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 5.37 KB | None | 0 0
  1. <?php
  2. require(__DIR__ . '/rewrite.php');
  3.  
  4. // Для начала, оставим только те файлы, которые содержат паттерн, который мы собираемся заменять
  5. $files = exec('grep -RF \'$db->sql_query($sql\' * | awk -F: \'{print $1;}\'', $out, $retval);
  6. if ($retval) exit(1);
  7.  
  8. $num_lines = 0;
  9.  
  10. foreach (array_unique($out) as $filename) {
  11.     foreach ($excludes as $excl) {
  12.         if (strpos($filename, $excl) === 0) continue(2);
  13.     }
  14.    
  15.     echo "$filename\n";
  16.    
  17.     $contents = file_get_contents($filename);
  18.     if ($contents === false) exit(1);
  19.    
  20.     $lines = explode("\n", $contents);
  21.  
  22.     // Получаем массив всех токенов в файле
  23.     $tokens = token_get_all($contents);
  24.     $num = count($tokens);
  25.     $line = 1;
  26.     $type = $text = '';
  27.  
  28.     $result_tokens = $tokens;
  29.  
  30.     // Нам нужно найти 5 идущих подряд токенов: '$db', '->', 'sql_query', '(' и '$sql'
  31.     foreach ($tokens as $cur_idx => $tok) {
  32.         parse_token($tok, $type, $text, $line);
  33.  
  34.         // Пропускаем токены в самом конце, где точно не могут уместиться наши 5 токенов
  35.         if ($cur_idx >= $num - 5) continue;
  36.        
  37.         $ln = $line;
  38.         $i = $cur_idx + 1;
  39.  
  40.         // Список токенов доступен в документации (http://php.net/manual/tokens.php)
  41.         if ($type !== T_VARIABLE || $text !== '$db') continue;
  42.         parse_token($tokens[$i++], $type, $text, $ln);
  43.         if ($type !== T_OBJECT_OPERATOR || $text !== '->') continue;
  44.         parse_token($tokens[$i++], $type, $text, $ln);
  45.         if ($type !== T_STRING || $text !== 'sql_query') continue;
  46.         $sql_query_idx = $i - 1;
  47.         parse_token($tokens[$i++], $type, $text, $ln);
  48.         if ($type !== '(') continue;
  49.         parse_token($tokens[$i++], $type, $text, $ln);
  50.         if ($type !== T_VARIABLE || $text !== '$sql') continue;
  51.         $dollar_sql_idx = $i - 1;
  52.         $positions = find_dollar_sql_assignment($tokens, $cur_idx);
  53.        
  54.         if (!is_array($positions)) {
  55.             echo "       $positions\n";
  56.             continue;
  57.         }
  58.        
  59.         $query_tokens = array();
  60.         for ($i = $positions['begin']; $i <= $positions['end']; $i++) {
  61.             parse_token($tokens[$i], $type, $text, $ln);
  62.             $query_tokens[] = $tokens[$i];
  63.         }
  64.  
  65.         $rewrite_result = rewrite_tokens($query_tokens);
  66.         if (!is_array($rewrite_result)) {
  67.             echo "       Error: $rewrite_result\n";
  68.         } else {
  69.             for ($i = $positions['begin']; $i <= $positions['end']; $i++) {
  70.                 parse_token($tokens[$i], $type, $text, $ln);
  71.                 if ($i > $positions['begin']) unset($result_tokens[$i]);
  72.             }
  73.  
  74.             $result_tokens[$sql_query_idx] = 'sql_query_escaped';
  75.             $result_tokens[$positions['begin']] = tokens_to_string($rewrite_result['tokens'], true);
  76.             $result_tokens[$dollar_sql_idx] = '$sql';
  77.             foreach ($rewrite_result['params'] as $par) $result_tokens[$dollar_sql_idx] .= ', ' . trim($par);
  78.  
  79.             $num_lines++;
  80.         }
  81.     }
  82.    
  83.     file_put_contents($filename, tokens_to_string($result_tokens, true));
  84.  
  85.     exec("php -l " . escapeshellarg($filename), $out, $retval);
  86.     if ($retval) {
  87.         fwrite(STDERR, "PHP Syntax error in $filename\n");
  88.         exit(1);
  89.     }
  90. }
  91.  
  92. echo "Total lines recognized: $num_lines\n";
  93.  
  94. // Найти $sql = '...'; перед $db->sql_query($sql
  95. function find_dollar_sql_assignment($tokens, $db_idx)
  96. {
  97.     $depth = 0;
  98.     $line = 1;
  99.     $type = $text = '';
  100.  
  101.     // Пройдемся по предыдущим токенам и найдем упоминания $sql
  102.     for ($idx = $db_idx - 1; $idx > 0; $idx--) {
  103.         parse_token($tokens[$idx], $type, $text, $line);
  104.        
  105.         // учет вложенности скобок
  106.         if ($type === ')') $depth++;
  107.         else if ($type === '(') $depth--;
  108.        
  109.         if ($depth < 0) {
  110.             return "depth < 0 on line " . __LINE__;
  111.         }
  112.        
  113.         /*
  114.         Мы ничего не можем сделать в случаях следующего вида, поэтому
  115.         их нужно определять и пропускать:
  116.              
  117.             if (...) {
  118.                 $sql = 'SELECT * FROM Table1 WHERE user_id = $user_id';
  119.             } else {
  120.                 $sql = 'SELECT * FROM Table2';
  121.             }
  122.        
  123.             $result = $db->sql_query($sql);
  124.         */
  125.         if ($type === '{' || $type === '}') {
  126.             return "open/close brace found on line " . __LINE__;
  127.         }
  128.        
  129.         if ($type === T_VARIABLE && $text === '$sql') {
  130.             $i = $idx + 1;
  131.             parse_token($tokens[$i++], $type, $text, $line);
  132.             if ($type === T_WHITESPACE) parse_token($tokens[$i++], $type, $text, $line);
  133.             if ($type !== '=') continue;
  134.             // мы нашли "$sql = " !
  135.            
  136.             $begin_pos = $i;
  137.             break;
  138.         }
  139.     }
  140.    
  141.     // Мы уже нашли начало присваивания "$sql = ", теперь нужно найти конец
  142.     // Будем считать, что выражение у нас всегда оканчивается на ";"
  143.    
  144.     $depth = 0;
  145.     for ($idx = $begin_pos; $idx < $db_idx; $idx++) {
  146.         parse_token($tokens[$idx], $type, $text, $line);
  147.        
  148.         if ($type === '(') $depth++;
  149.         else if ($type === ')') $depth--;
  150.        
  151.         if ($depth < 0) {
  152.             return "depth < 0 on line " . __LINE__;
  153.         }
  154.        
  155.         if ($depth === 0 && $type === ';') {
  156.             return array('begin' => $begin_pos, 'end' => $idx - 1);
  157.         }
  158.     }
  159.  
  160.     // Если цикл закончился, значит между найденным нами началом и $db->sql_query нет ";"
  161.     // То есть, наше предположение было неверно
  162.    
  163.     return "Statement does not terminate on line " . __LINE__;
  164. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement