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?
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é .
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>";
}
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.
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();
}
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...