PDO支持多个查询(PDO_MYSQL,PDO_MYSQLND)

| 我确实知道PDO不支持在一条语句中执行多个查询。我一直在Google搜索,发现很少有关于PDO_MYSQL和PDO_MYSQLND的帖子。   PDO_MySQL更危险   比其他任何传统应用   MySQL应用程序。传统MySQL   只允许一个SQL查询。在   PDO_MySQL没有这样的限制,   但您可能会被注入   多个查询。 来自:使用PDO和Zend Framework防止SQL注入(2010年6月;朱利安(Julian)) 似乎PDO_MYSQL和PDO_MYSQLND确实提供了对多个查询的支持,但我无法找到有关它们的更多信息。这些项目被中止了吗?现在有什么方法可以使用PDO运行多个查询。     
已邀请:
        据我所知,PHP 5.3中的“ 0”代替了“ 1”。令人困惑的是该名称仍然是“ 1”。因此,现在ND是MySQL + PDO的默认驱动程序。 总体而言,一次执行多个查询需要: PHP 5.3以上 mysqlnd 模拟准备好的语句。确保将
PDO::ATTR_EMULATE_PREPARES
设置为to4ѭ(默认)。另外,您可以避免使用准备好的语句,而直接使用
$pdo->exec
。 使用执行
$db = new PDO(\"mysql:host=localhost;dbname=test\", \'root\', \'\');

// works regardless of statements emulation
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);

$sql = \"
DELETE FROM car; 
INSERT INTO car(name, type) VALUES (\'car1\', \'coupe\'); 
INSERT INTO car(name, type) VALUES (\'car2\', \'coupe\');
\";

try {
    $db->exec($sql);
}
catch (PDOException $e)
{
    echo $e->getMessage();
    die();
}
使用语句
$db = new PDO(\"mysql:host=localhost;dbname=test\", \'root\', \'\');

// works not with the following set to 0. You can comment this line as 1 is default
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1);

$sql = \"
DELETE FROM car; 
INSERT INTO car(name, type) VALUES (\'car1\', \'coupe\'); 
INSERT INTO car(name, type) VALUES (\'car2\', \'coupe\');
\";

try {
    $stmt = $db->prepare($sql);
    $stmt->execute();
}
catch (PDOException $e)
{
    echo $e->getMessage();
    die();
}
一张纸条: 使用模拟的准备好的语句时,请确保已在DSN中设置了正确的编码(反映了实际的数据编码)(自5.3.6起可用)。否则,如果使用某种奇数编码,则SQL注入的可能性很小。     
        经过半天的摆弄之后,发现PDO有一个错误,它... -
//This would run as expected:
$pdo->exec(\"valid-stmt1; valid-stmt2;\");
-
//This would error out, as expected:
$pdo->exec(\"non-sense; valid-stmt1;\");
-
//Here is the bug:
$pdo->exec(\"valid-stmt1; non-sense; valid-stmt3;\");
它会执行
\"valid-stmt1;\"
,停在
\"non-sense;\"
,并且永远不会抛出错误。将不会运行
\"valid-stmt3;\"
,返回true并认为一切运行良好。 我希望它会在ѭ12to上出错,但事实并非如此。 这是我找到此信息的地方: 无效的PDO查询不会返回错误 错误如下: https://bugs.php.net/bug.php?id=61613 因此,我尝试使用mysqli进行此操作,但尚未真正找到有关其工作原理的可靠答案,因此我认为我只是将它留在这里供那些想要使用它的人使用。
try{
    // db connection
    $mysqli = new mysqli(\"host\", \"user\" , \"password\", \"database\");
    if($mysqli->connect_errno){
        throw new Exception(\"Connection Failed: [\".$mysqli->connect_errno. \"] : \".$mysqli->connect_error );
        exit();
    }

    // read file.
    // This file has multiple sql statements.
    $file_sql = file_get_contents(\"filename.sql\");

    if($file_sql == \"null\" || empty($file_sql) || strlen($file_sql) <= 0){
        throw new Exception(\"File is empty. I wont run it..\");
    }

    //run the sql file contents through the mysqli\'s multi_query function.
    // here is where it gets complicated...
    // if the first query has errors, here is where you get it.
    $sqlFileResult = $mysqli->multi_query($file_sql);
    // this returns false only if there are errros on first sql statement, it doesn\'t care about the rest of the sql statements.

    $sqlCount = 1;
    if( $sqlFileResult == false ){
        throw new Exception(\"File: \'\".$fullpath.\"\' , Query#[\".$sqlCount.\"], [\".$mysqli->errno.\"]: \'\".$mysqli->error.\"\' }\");
    }

    // so handle the errors on the subsequent statements like this.
    // while I have more results. This will start from the second sql statement. The first statement errors are thrown above on the $mysqli->multi_query(\"SQL\"); line
    while($mysqli->more_results()){
        $sqlCount++;
        // load the next result set into mysqli\'s active buffer. if this fails the $mysqli->error, $mysqli->errno will have appropriate error info.
        if($mysqli->next_result() == false){
            throw new Exception(\"File: \'\".$fullpath.\"\' , Query#[\".$sqlCount.\"], Error No: [\".$mysqli->errno.\"]: \'\".$mysqli->error.\"\' }\");
        }
    }
}
catch(Exception $e){
    echo $e->getMessage(). \" <pre>\".$e->getTraceAsString().\"</pre>\";
}
    
        一种快速而肮脏的方法:
function exec_sql_from_file($path, PDO $pdo) {
    if (! preg_match_all(\"/(\'(\\\\\\\\.|.)*?\'|[^;])+/s\", file_get_contents($path), $m))
        return;

    foreach ($m[0] as $sql) {
        if (strlen(trim($sql)))
            $pdo->exec($sql);
    }
}
在合理的SQL语句终点分割。没有错误检查,没有注入保护。使用前了解您的用法。我个人使用它为原始迁移文件播种以进行集成测试。     
        就像成千上万的人一样,我正在寻找这个问题: 可以同时运行多个查询,并且如果有一个错误,则不会运行 我到处都去过这个页面 但是尽管这里的朋友给出了很好的答案,但是这些答案对我的问题不利 因此,我编写了一个运行良好的函数,并且使用sql Injection几乎没有问题。 对于那些正在寻找类似问题的人可能会有所帮助,因此我将其放在此处以供使用
function arrayOfQuerys($arrayQuery)
{
    $mx = true;
    $conn->beginTransaction();
    try {
        foreach ($arrayQuery AS $item) {
            $stmt = $conn->prepare($item[\"query\"]);
            $stmt->execute($item[\"params\"]);
            $result = $stmt->rowCount();
            if($result == 0)
                $mx = false;
         }
         if($mx == true)
             $conn->commit();
         else
             $conn->rollBack();
    } catch (Exception $e) {
        $conn->rollBack();
        echo \"Failed: \" . $e->getMessage();
    }
    return $mx;
}
使用(示例):
 $arrayQuery = Array(
    Array(
        \"query\" => \"UPDATE test SET title = ? WHERE test.id = ?\",
        \"params\" => Array(\"aa1\", 1)
    ),
    Array(
        \"query\" => \"UPDATE test SET title = ? WHERE test.id = ?\",
        \"params\" => Array(\"bb1\", 2)
    )
);
arrayOfQuerys($arrayQuery);
和我的联系:
    try {
        $options = array(
            //For updates where newvalue = oldvalue PDOStatement::rowCount()   returns zero. You can use this:
            PDO::MYSQL_ATTR_FOUND_ROWS => true
        );
        $conn = new PDO(\"mysql:host=$servername;dbname=$database\", $username, $password, $options);
        $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    } catch (PDOException $e) {
        echo \"Error connecting to SQL Server: \" . $e->getMessage();
    }
注意: 此解决方案可帮助您一起运行多个语句, 如果出现不正确的语句,则不会执行任何其他语句     
        尝试以下代码
 $db = new PDO(\"mysql:host={$dbhost};dbname={$dbname};charset=utf8\", $dbuser, $dbpass, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
然后
 try {
 $db->query(\'SET NAMES gbk\');
 $stmt = $db->prepare(\'SELECT * FROM 2_1_paidused WHERE NumberRenamed = ? LIMIT 1\');
 $stmt->execute(array(\"\\xbf\\x27 OR 1=1 /*\"));
 }
 catch (PDOException $e){
 echo \"DataBase Errorz: \" .$e->getMessage() .\'<br>\';
 }
 catch (Exception $e) {
 echo \"General Errorz: \".$e->getMessage() .\'<br>\';
 }
并得到
DataBase Errorz: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'/*\' LIMIT 1\' at line 1
如果在
$db = ...
之后加上
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
然后得到空白页 如果取而代之的是
SELECT
尝试then26ѭ,则在两种情况下都会出现类似
 DataBase Errorz: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'* FROM 2_1_paidused WHERE NumberRenamed = \'¿\\\' OR 1=1 /*\' LIMIT 1\' at line 1
所以我的结论是不可能注射...     
        试试这个功能:多查询和多值插入。
function employmentStatus($Status) {
$pdo = PDO2::getInstance();

$sql_parts = array(); 
for($i=0; $i<count($Status); $i++){
    $sql_parts[] = \"(:userID, :val$i)\";
}

$requete = $pdo->dbh->prepare(\"DELETE FROM employment_status WHERE userid = :userID; INSERT INTO employment_status (userid, status) VALUES \".implode(\",\", $sql_parts));
$requete->bindParam(\":userID\", $_SESSION[\'userID\'],PDO::PARAM_INT);
for($i=0; $i<count($Status); $i++){
    $requete->bindParam(\":val$i\", $Status[$i],PDO::PARAM_STR);
}
if ($requete->execute()) {
    return true;
}
return $requete->errorInfo();
}
    

要回复问题请先登录注册