如果MySQL函数用于自定义类,MySQL将决定是否在字符串周围加单引号


MySQL determine whether to put single quotes around a string or not if MySQL function for custom class

我正在创建一个自定义类,以使连接和使用mysql变得更容易。我创建了许多函数,除了一个部分外,其他部分都很好。我似乎不知道如何确定一个字符串是一个实际的字符串,还是一个mysql函数,以便在它周围加引号。我把一个函数放在一起,我可以传递两个参数,它将插入数据库。第一个参数是要插入的表,而第二个参数是列、值对的数组。也许这个代码示例将帮助您进一步理解。注意:我已经对所有字符串进行了转义,并删除了该代码以显示问题的简单性。

function insert($table,array $array){
    $table = "`".$table."`";
    $columns = "";
    $values = "";
    foreach($array as $key => $value){
        $columns = ($columns=="") ? "`".$key."`" : $columns.", `".$key."`";
        if($value===NULL || $value=="NULL"){
            $value = "NULL";
        } else {
            $value = "'".$value."'";
        }
        $values = ($values=="") ? "".$value."" : $values.", ".$value."";
    }
    $query = "INSERT INTO $table ($columns) VALUES ($values)";
    return $this->query($query);
}

以下是两种不同用途的示例以及每个的预期结果

$data = array(
  "first_name"=>"John",
  "last_name"=>"Doe",
  "entry_date"=>"2013-08-26"
);
$MyClass->insert("people",$data);

将执行

INSERT INTO `people` (`first_name`,`last_name`,`entry_date`) VALUES ('John','Doe','2013-08-26')

但是以下用途:

$data = array(
  "first_name"=>"John",
  "last_name"=>"Doe",
  "entry_date"=>"NOW()"
);
$MyClass->insert("people",$data);

将作为执行

INSERT INTO `people` (`first_name`,`last_name`,`entry_date`) VALUES ('John','Doe','NOW()')

这将不起作用,因为MySQL函数如果在引号内将不会运行。我知道这可能是一个小的安全风险,因为如果没有被捕获,它将允许用户执行功能。有没有更好的方法可以做到这一点,或者在这样的类插入函数中不允许使用MySQL函数?如果有人组合了一个regex来查找并允许mysql函数,那么这可能是一个解决方案。如果不存在这样的正则表达式,那么一种可能性是从头开始写一个我不太擅长的正则表达式…

编辑:

在此期间,我将使用的另一个解决方案是在形成数组时使用PHP获取日期。如果可能的话,我还是想使用MySQL函数。这是我目前的代码,它解决了这个问题,但不是我喜欢的方式:

$data = array(
  "first_name"=>"John",
  "last_name"=>"Doe",
  "entry_date"=>date('Y-m-d H:i:s'),
);
$MyClass->insert("people",$data);

几年前,我实现了一个功能来实现您为Zend Framework 1.0所描述的功能。

我通过区分纯字符串标量和包含字符串的对象来解决这个问题。

$data = array(
  "first_name"=>"John",
  "last_name"=>"Doe",
  "entry_date"=>new Zend_Db_Expr("NOW()")
);
$MyClass->insert("people",$data);

这样,我的框架就可以区分我想要处理的PHP字符串作为SQL字符串(即引号),而不是在对象中汇总的字符串。

if($value===NULL || $value=="NULL"){
    $value = "NULL";
} else if ($value instanceof Zend_Db_Expr) {
    $value = $value->__toString(); // no quotes
} else {
    $value = "'".$value."'";
}

PS:有些人可能会建议您使用查询参数,而不是像现在这样连接转义变量。我同意这个建议,但它与您最初的问题几乎没有关系,所以我不会深入讨论。还有许多其他StackOverflow问题涉及使用查询参数。


回复您的评论:

您可以使用正则表达式来匹配NOW()和其他所有MySQL函数,并对它们进行特殊处理,但如何插入文字字符串"NOW()"(而不是该名称函数的结果)?

答:你不能——就像你不能在当前代码中插入文本字符串"NULL"一样,因为你已经把这个词当作了一个特例。

您需要一些方法来区分SQL表达式NOW()和"NOW()"。现在不需要将该字符串插入数据库并不意味着您永远不需要该功能。请记住,您设计这个insert()函数是为了针对任何表(当前或将来)进行通用。

你实际上是在设计一个框架。

感谢@phpisuber1提供的regex if(preg_match("/^[A-Z_]+'(.*?')/", $value)) { // Mysql function },感谢@BillKarwin,我和你们两个一起用你们的输入创建了一个解决方案。

对我来说,解决方案是在我的类函数中添加一些默认值的参数,告诉函数是否作为MySQL函数或字符串插入,以及是否作为NULL或字符串"NULL"插入。请参阅下面的新函数:

/**
 * @param string $table the table to insert array into
 * @param array $array contains column => value pairs values may be MySQL Function
 * @param bool $mysql_functions true to insert mysql functions as function, false to insert mysql functions as string
 * @param bool $null_string_is_null true to insert string "NULL" as NULL; false to insert string "NULL" as string "NULL"
 * @param bool $empty_string_is_null true to insert string "" as NULL; false to insert string "" as string ""
 * @return bool Returns FALSE on failure. For successful queries will return TRUE.
 */
function insert($table,array $array,$mysql_functions = false,$null_string_is_null = true,$empty_string_is_null = false){
    $table = "`".$table."`";
    $columns = "";
    $values = "";
    foreach($array as $key => $value){
        $columns = ($columns=="") ? "`".$key."`" : $columns.", `".$key."`";
        if($value===NULL || ($value=="NULL" && $null_string_is_null) || ($value=="" && $empty_string_is_null)){
            $value = "NULL";
        } else if(preg_match("/^[A-Z_]+'(.*?')/", $value) && $mysql_functions) {
            $value = "".$value."";
        } else {
            $value = "'".$value."'";
        }
        $values = ($values=="") ? "".$value."" : $values.", ".$value."";
    }
    $query = "INSERT INTO $table ($columns) VALUES ($values)";
    return $this->query($query);
}

这也不是完整的功能,因为出于安全原因,我省略了部分,但这是一个正常运行的版本。我将研究查询参数,看看它是否会提高此函数和其他函数的安全性,而不是连接转义字符串。此函数的默认参数适用于我的默认用途,并且可以更改以供将来使用。总的来说,我认为这是最好的解决方案。如果你不这么想的话,我很乐意阅读你的评论。