Prise en charge par AOP pour les requêtes multiples (AOP MYSQL, AOP MYSQLND)

je sais que PDO ne supporte pas les requêtes multiples exécutées en une seule instruction. J'ai cherché sur Google et j'ai trouvé quelques billets parlant de PDO_MYSQL et PDO_MYSQLND.

PDO_MySQL est plus dangereux l'application de toute autre traditionnelles MySQL applications. MySQL Traditionnel permet seulement une seule requête SQL. Dans PDO_MySQL il n'y a pas de telle limitation, mais on risque d'être injecté avec plusieurs requêtes.

De: Protection contre les injections SQL en utilisant PDO et Zend Framework (juin 2010, par Julien)

il semble que PDO_MYSQL et PDO_MYSQLND fournissent un support pour les requêtes multiples, mais je ne suis pas en mesure de trouver plus d'informations à leur sujet. Ces projets abandonnées? Y a-t-il un moyen d'exécuter des requêtes multiples en utilisant PDO?

83
demandé sur Matt 2011-06-14 20:15:30

5 réponses

Que je sache, PDO_MYSQLND remplacé PDO_MYSQL en PHP 5.3. La partie confuse est que le nom est toujours PDO_MYSQL . Donc maintenant ND est le pilote par défaut pour MySQL+PDO.

dans l'ensemble, pour exécuter plusieurs requêtes à la fois vous avez besoin:

  • PHP 5.3+
  • mysqlnd
  • Émulé déclarations préparées à l'avance. Assurez-vous que PDO::ATTR_EMULATE_PREPARES est défini à 1 (par défaut). Sinon, vous pouvez éviter d'utiliser préparé instructions et utiliser $pdo->exec directement.

utilisant 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();
}

utilisant les déclarations

$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();
}

Une note:

lorsque vous utilisez des déclarations préparées émulées, assurez-vous que vous avez défini le codage approprié (qui reflète le codage réel des données) dans DSN (disponible depuis 5.3.6). Sinon il peut y avoir une légère possibilité pour l'injection SQL si un codage Impair est utilisé .

123
répondu Sam Dark 2017-05-23 10:31:20

après une demi-journée de bricolage, a découvert que PDO avait un bug où...

--

//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;");

il exécuterait le "valid-stmt1;" , s'arrêterait sur "non-sense;" et ne lancerait jamais une erreur. Ne lancera pas le "valid-stmt3;" , revenir vrai et mentir que tout s'est bien passé.

Je m'attendrais à ce qu'il se trompe sur le "non-sense;" mais ça ne l'est pas.

voici où j'ai trouvé cette information: la requête AOP invalide ne renvoie pas d'erreur

voici le bug: https://bugs.php.net/bug.php?id=61613


donc, j'ai essayé de le faire avec mysqli et n'ai pas vraiment trouvé de réponse solide sur la façon dont cela fonctionne donc j'ai pensé que je suis juste le laisser ici pour ceux qui veulent l'utiliser..

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>";
}
15
répondu Sai Phaninder Reddy J 2017-05-23 12:25:52

rapide et sale approche:

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);
    }
}

se divise aux points d'extrémité raisonnables de L'énoncé SQL. Il n'y a pas de contrôle d'erreur, Pas de protection contre l'injection. Comprenez votre usage avant de l'utiliser. Personnellement, je l'utilise pour l'ensemencement des fichiers de migration bruts pour les tests d'intégration.

4
répondu bishop 2015-05-06 16:26:48

essayez cette fonction : requêtes multiples et insertion de valeurs multiples.

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();
}
0
répondu hassan b. 2016-11-17 16:00:59

essayé de suivre le code

 $db = new PDO("mysql:host={$dbhost};dbname={$dbname};charset=utf8", $dbuser, $dbpass, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

puis

 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>';
 }

Et a obtenu

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

Si ajouté $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); après $db = ...

puis une page blanche

si à la place SELECT essayé DELETE , puis dans les deux cas obtenu erreur comme

 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

donc ma conclusion qu'aucune injection n'est possible...

-1
répondu Andris 2014-04-30 17:04:57