Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/260.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 回溯MySQL实体_Php_Sql_Mysql_String_Escaping - Fatal编程技术网

Php 回溯MySQL实体

Php 回溯MySQL实体,php,sql,mysql,string,escaping,Php,Sql,Mysql,String,Escaping,我有以下方法可以保护MySQL实体: public function Tick($string) { $string = explode('.', str_replace('`', '', $string)); foreach ($string as $key => $value) { if ($value != '*') { $string[$key] = '`' . trim($value) . '`';

我有以下方法可以保护MySQL实体:

public function Tick($string)
{
    $string = explode('.', str_replace('`', '', $string));

    foreach ($string as $key => $value)
    {
        if ($value != '*')
        {
            $string[$key] = '`' . trim($value) . '`';
        }
    }

    return implode('.', $string);
}
这对我使用它来说相当有效

它保护数据库、表、字段名,甚至*运算符,但现在我还希望它保护函数调用,即:

AVG(database.employees.salary)
应成为:

AVG(`database`.`employees`.`salary`) and not `AVG(database`.`employees`.`salary)`
我该怎么做?我应该使用正则表达式吗

另外,我如何支持更高级的东西,从:

MAX(AVG(database.table.field1), MAX(database.table.field2))
MAX(AVG(`database`.`table`.`field1`), MAX(`database`.`table`.`field2`))
至:

MAX(AVG(database.table.field1), MAX(database.table.field2))
MAX(AVG(`database`.`table`.`field1`), MAX(`database`.`table`.`field2`))

请记住,我希望此方法尽可能简单/快速,因为它几乎会迭代我数据库中的所有实体名称。

您可以使用
preg\u replace\u callback()
Tick()
方法结合使用,以跳过至少一个级别的参数:

public function tick($str) 
{
  return preg_replace_callback('/[^()]*/', array($this, '_tick_replace_callback'), $str);
}

protected function _tick_replace_callback($str) {
  $string = explode('.', str_replace('`', '', $string));
  foreach ($string as $key => $value)
  {
    if ($value != '*')
    {
      $string[$key] = '`' . trim($value) . '`';
    }
  }
  return implode('.', $string);
}

如果这是引用SQL语句的一部分,并且它们只有您描述的复杂性,那么正则表达式是一种很好的方法。另一方面,如果需要对完整的SQL语句或更复杂的语句组件(如“MAX(AVG(val)、MAX(val2)))执行此操作,则需要对字符串进行标记化或解析,并对其有更深入的理解,以便准确地进行此引用

如果使用正则表达式方法,您可能会发现将函数名分解为一个步骤更容易,然后使用当前代码引用数据库/表/列名。这可以在一次重做中完成,但要做到正确,这将是一个骗局

不管怎样,我强烈建议编写几个单元测试用例。事实上,对于这种方法来说,这是一种理想的情况:编写测试很容易,您有一些可用的现有案例(您不想破坏这些案例),并且您只需要再添加一个案例

您的测试可以简单地开始,如下所示:

assert '`ticked`' == Tick('ticked');
assert '`table`.`ticked`' == Tick('table.ticked');
assert 'db`.`table`.`ticked`' == Tick('db.table.ticked');
然后添加:

assert 'FN(`ticked`)' == Tick('FN(ticked)');
etc.

在按句点分解字符串之前,请检查最后一个字符是否为括号。如果是,则此调用是一个函数

<?php
$string = str_replace('`', '', $string)
$function = "";
if (substr($string,-1) == ")") {
  // Strip off function call first
  $opening = strpos($string, "(");
  $function = substr($string, 0, $opening+1);
  $string = substr($string, $opening+1, -1);
}

// Do your existing parsing to $string

if ($function == "") {
  // Put function back on string
  $string = $function . $string . ")";
}
?>

如果您需要涵盖更高级的情况,例如使用嵌套函数,或者在一个“$string”变量中按顺序使用多个函数,那么这将成为一个更高级的函数,您最好问问自己,为什么这些元素没有首先正确地勾选,并且不需要任何进一步的解析

编辑:根据原始后期编辑更新嵌套函数 要让上面的函数处理多个嵌套函数,您可能需要一些可以“展开”嵌套函数的东西。我还没有对此进行测试,但是下面的函数可能会让您走上正确的道路

<?php
function unwrap($str) {
  $pos = strpos($str, "(");
  if ($pos === false) return $str; // There's no function call here

  $last_close = 0;
  $cur_offset = 0; // Start at the beginning
  while ($cur_offset <= strlen($str)) {
    $first_close = strpos($str, ")", $offset); // Find first deep function
    $pos = strrpos($str, "(", $first_close-1); // Find associated opening
    if ($pos > $last_close) {
      // This function is entirely after the previous function
      $ticked = Tick(substr($str, $pos+1, $first_close-$pos)); // Tick the string inside
      $str = substr($str, 0, $pos)."{".$ticked."}".substr($str,$first_close); // Replace parenthesis by curly braces temporarily
      $first_close += strlen($ticked)-($first_close-$pos); // Shift parenthesis location due to new ticks being added
    } else {
      // This function wraps other functions; don't tick it
      $str = substr($str, 0, $pos)."{".substr($str,$pos+1, $first_close-$pos)."}".substr($str,$first_close);
    }
    $last_close = $first_close;
    $offset = $first_close+1;
  }
  // Replace the curly braces with parenthesis again
  $str = str_replace(array("{","}"), array("(",")"), $str);
}

将整个SQL传递给函数通常是个坏主意。这样,除非您完全解析SQL语法,否则当它不起作用时,您总会发现一个例子


在前面某个抽象级别的名称上打勾,该抽象级别构成SQL。

您是在生成SQL查询还是正在将其传递给您?如果生成查询,我不会传递整个查询字符串,而只传递要在backticks中包装的参数/值或其他需要的内容

例如:

   function addTick($var) {
      return '`' . $var . '`';
   }

   $condition = addTick($condition);

   $SQL = 'SELECT' . $what . ' 
      FROM ' . $table . ' 
      WHERE ' . $condition . ' = ' . $constraint;

这只是一个模拟,但您可以传递或循环代码并构建查询字符串,而不是解析查询字符串并添加反勾号

如果要在代码中添加函数调用,而不是通过纯字符串接口传入,则可以用类型检查替换字符串解析:

function Tick($value) {
    if (is_object($value)) {
        $result = $value->value;
    } else {
        $result = '`'.str_replace(array('`', '.'), array('', '`.`'), $value).'`';
    }

    return $result;
}

class SqlFunction {
    var $value;
    function SqlFunction($function, $params) {
        $sane = implode(', ', array_map('Tick', $params));
        $this->value = "$function($sane)";
    }
}

function Maximum($column) {
    return new SqlFunction('MAX', array($column));
}

function Avg($column) {
    return new SqlFunction('AVG', array($column));
}

function Greatest() {
    $params = func_get_args();
    return new SqlFunction('GREATEST', $params);
}

$cases = array(
    "'simple'" => Tick('simple'),
    "'table.field'" => Tick('table.field'),
    "'table.*'" => Tick('table.*'),
    "'evil`hack'" => Tick('evil`hack'),
    "Avg('database.table.field')" => Tick(Avg('database.table.field')),
    "Greatest(Avg('table.field1'), Maximum('table.field2'))" => Tick(Greatest(Avg('table.field1'), Maximum('table.field2'))),
);

echo "<table>";
foreach ($cases as $case => $result) {
    echo "<tr><td>$case</td><td>$result</td></tr>";
}

echo "</table>";
函数勾选($value){
如果(是对象($value)){
$result=$value->value;
}否则{
$result='''''.str_replace(数组('''','.')、数组(''''.''''')、$value)。''''';
}
返回$result;
}
类SqlFunction{
var$价值;
函数SqlFunction($function,$params){
$sane=内爆(',',数组映射('Tick',$params));
$this->value=“$function($sane)”;
}
}
函数最大值($列){
返回新的SqlFunction('MAX',数组($column));
}
函数平均值($列){
返回新的SqlFunction('AVG',数组($column));
}
函数(){
$params=func_get_args();
返回新的SqlFunction('magest',$params);
}
$cases=数组(
“'simple'”=>勾选('simple'),
“'table.field'”=>勾选('table.field'),
“'table.*'”=>勾选('table.*'),
“'evil'hack'”=>勾选('evil'hack'),
“Avg('database.table.field')”=>勾选(Avg('database.table.field')),
“最大值(平均值('table.field1')、最大值('table.field2')”=>勾选(最大值(平均值('table.field1')、最大值('table.field2')),
);
回声“;
foreach($cases as$case=>$result){
回显“$case$result”;
}
回声“;
这避免了任何可能的SQL注入,同时对代码的未来读者保持清晰。

使用给出的测试用例,我创建了一个正则表达式来为您完成这项艰巨的工作。下面的正则表达式将替换单词周围未后跟左括号的所有单词边界

\b(\w+)\b(?!\()
Tick()功能将在PHP中实现,如下所示:

function Tick($string) 
{
    return preg_replace( '/\b(\w+)\b(?!\()/', '`\1`', $string );
}

返回“`Array``Array`)`Array`”=\我不会传递整个SQL,通常只传递字段名,但是我需要检测字段名何时也包含函数。我没有别的办法,相信我。它会永远是一个函数吗?表达方式呢?当
db
tab
col
db
othertab
col2
之间时,10 ELSE 20A定制ORM确实是一个强大的对手。我正在生成SQL,但不是像你的例子那样,tick函数通常只处理列名,但我也需要它来处理作用于列名的函数。可以理解,但如果生成查询,这应该没有问题。我正在使用类似于生成SQL的东西。为什么要这样做?倒勾是为了减少保留字标识符(和特殊字符等)。倒勾所有内容都是浪费时间我有类似的情况:我无法指定需要/想要倒勾哪些列。@Alix Axel我想你做得不对。你应该定义你的目标