so I recently switched to use pdo instead of mysqli. now I have a question about binding key values with mysqli. I looped through it escaped the key's and values and used them in my queries now I want to do the same thing in pdo but this isn't working and I don't know why this is my code:
foreach($userdata as $key => $value){
$sql = $this->db->prepare("UPDATE `users` SET :key = :value WHERE `id` = :userid");
$sql->execute(
array(
'key' => $key,
'value' => $value,
'userid' => $userid
)
);
}
ofcourse there's more code to see if it needs update and other type of inputs that need more validation but this is the main query i used but without binding. is this possible with pdo and binding parameter's and values?
this is the error i'm getting:
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 'username' = 'sjerdus' WHERE `id` = '2''
You have this error because you tried to update a field named dynamically. The SET :key = ... can't work because when the parameter will be replaced by its value, it will be escaped (and quoted) by PDO.
If you want to put a variable field name that will be updated, you have to manually concatenate the field name, but you'll have to check for the security yourself.
Try something like this :
//Security checks for $field variable...
$sql = $this->db->prepare("UPDATE `users` SET " . $field . " = :value WHERE `id` = :userid");
$sql->execute(
array(
'value' => $value,
'userid' => $userid
)
);
Here is you could do. I assume that the $userid you have provided is an integer. Where as when you use params in execute() directly. They are considered as string.
foreach($userdata as $key => $value){
$sql = $this->db->prepare("UPDATE `users` SET :key = :value WHERE `id` = :userid");
$sql->bindParam(':key', $key);
$sql->bindParam(':value', $value);
$sql->bindParam(':userid', $userid);
$sql->execute()
);
}
http://php.net/manual/en/pdostatement.bindparam.php
Related
I'm working with two identical tables from SQL Server and MySQL and my end-goal is to be able to sync their contents through PHP. I made a previous post about this and I found out that the reason my code wasn't working was because of single quotes messing up my SQL Syntax. I then converted my table to use PDO instead because I heard preparing statements/binding param through it is more efficient. However, my code is still not escaping single quotes properly. I've already look into past posts but none of them solved my problem. Here is the code:
<?php
$serverName = "<servername>";
$connectionInfo_mssql = array("Database"=>"<dbname>", "CharacterSet"=>"UTF-8");
try
{
$conn_mssql = new PDO("sqlsrv:Server=$serverName;Database=<dbname>");
$conn_mssql->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$conn_mysql = new PDO("mysql:host=localhost;dbname=<dbname>", "", "");
$conn_mysql->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//SELECT FROM SQL SERVER DB
$mssql_array = array();
$mssql_query = $conn_mssql->prepare("SELECT * FROM Customers");
$mssql_query->execute();
while($row = $mssql_query->fetch(PDO::FETCH_BOTH))
{
$mssql_array[] = array('ID' => $row['ID'],
'Name' => $row["Name"],
'Address' => $row['Address'],
'Email' => $row['Email']);
}
foreach($mssql_array as $key => $value)
{
//SELECT FROM MySQL DB
$mysql_query = $conn_mysql->prepare("SELECT COUNT(*) FROM Customers WHERE ID ='".$value['ID']."'
AND Name = '".$value["Name"]."'
AND Address = '".$value['Address']."'
AND Email = '".$value['Email']."' ");
$mysql_query->execute();
$num_rows = $mysql_query->fetchColumn();
if ($num_rows == 0)
{
//INSERT INTO MySQL DB
$sql = $conn_mysql->prepare("INSERT INTO Customers VALUES (:ID, :Name, :Address, :Email)");
$params = array(':ID' => $value["ID"], ':Name' => $value["Name"], ':Address' => $value["Address"], ':Email' => $value["Email"]);
$sql->execute($params); //this is where the error occurs
}
}
echo 'Table Customers from MS SQL DB and table Customers from MySQL DB are now synced!'."<br>";
echo "<a href='table_updater.php'>Go back to updater</a>";
}
catch(PDOException $e)
{
echo "Error: " . $e->getMessage();
}
?>
What this basically does is it selects all of the SQL Server table's contents and puts it in MySQL, but since one of my rows has the value "Jojo's" it just gives me an error because of the single quote. The error I'm getting is
Error: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 's' AND Address = 'IDK''
which pretty much tells me that I'm not escaping the single quote.
Any ideas are much appreciated!
The issue is not with the INSERT statement but with the SELECT one. From the error you can see that it fails to interpret a part of the select query. So the correct code would look like this:
//SELECT FROM MySQL DB
$mysql_query = $conn_mysql->prepare("SELECT COUNT(*) FROM Customers WHERE ID = :ID
AND Name = :Name
AND Address = :Address
AND Email = :Email ");
$params = [':ID' => $value['ID'], ':Name' => $value['Name'], ':Address' => $value['Address'], ':Email' => $value['Email']];
$mysql_query->execute($params);
To further explain, without the placeholders, your select query could look like the following if for example the name had a quote in it:
SELECT COUNT(*) FROM Customers WHERE ID = '123'
AND Name = 'As'd, dfg' # <- Problem here
AND Address = 'some address'
AND Email = 'email#example.com'
For the record, you should always use placeholders for any value that you do not control in code. Even if that's for a SELECT statement or a data source you trust. This prevents unintended injection by accident and handles any type of character.
I would like to create a query that may or may not have more than one part. This means, I will be including an array and looping through it, and appending a query to the main SQL query, then finally prepare it.
First of all, I have defined the sql query
$sql = '';
then, I defined a foreach looping value
$arrayLoopValue = 0;
after that, I have created a foreach loop. In which I increased the arrayLoopValue, appended the sql with a new query based on the array's index.
foreach($questionsArray as $questionAnswerRow){
$arrayLoopValue = $arrayLoopValue + 1;
$sql = $sql .
'INSERT INTO gosurveys_surveys_questions_answers
SET survey_id = :survey_id_' . $arrayLoopValue .
', question_id = :question_id_' . $arrayLoopValue .
', user_email = :user_email_' . $arrayLoopValue .
', answer_type = :answer_type_' . $arrayLoopValue .
', question_answer = :question_answer_' . $arrayLoopValue .
', question_answer_creation_date = UTC_TIMESTAMP(); ';
}
The database / example for this query is NOT important, as all fields match and it's already empty. Only the structure, which is provided above, is required.
This fails at the following line.
$query = $this->conn->prepare($sql);
I tried to echo the query and see if there's something wrong. I got the following output:
INSERT INTO gosurveys_surveys_questions_answers
SET survey_id = :survey_id_1,
question_id = :question_id_1,
user_email = :user_email_1,
answer_type = :answer_type_1,
question_answer = :question_answer_1,
question_answer_creation_date = UTC_TIMESTAMP();
INSERT INTO gosurveys_surveys_questions_answers
SET survey_id = :survey_id_2,
question_id = :question_id_2,
user_email = :user_email_2,
answer_type = :answer_type_2,
question_answer = :question_answer_2,
question_answer_creation_date = UTC_TIMESTAMP();
Which is correct. After this prepare, there's a second foreach loop. But the function does NOT reach after the prepare statement.
I would like to know the reason. MYSQL says the following:
Uncaught exception 'PDOException' with message '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 'INSERT INTO gosurveys_surveys_questions_answers
SET survey_id = ?, ques'
The name of the parameter is not the important thing.
If you want to run a query more than once then preparing it is a great idea but the parameter names are almost irrelevant. the important thing is the binding of new values to the paramters.
So lets assume this is your query
$sql = "INSERT INTO gosurveys_surveys_questions_answers
SET survey_id = :a,
question_id = :b,
user_email = :c,
answer_type = :d,
question_answer = :e,
question_answer_creation_date = UTC_TIMESTAMP()";
Notice the parameter names are irrelevant as long as they are something unique in the query string.
So now you prepare that query. This passes the basic query to the database where it is compiled and optimized, but is not actually run.
$stmt = $this->conn->prepare($sql);
Now within a loop that gets you the parameter you can run that prepared query 1000, 1,000,000 times if you like, all you have to do is bind new values to the parameters and execute the query, which passes the parameter values to the already prepared (compiled and optimized query) and runs it with the data you pass on the execute()
foreach($inArray as $array) {
// build the array of parameters and values
$params = [ ':a' => $array['field1']
':b' => $array['field2']
':c' => $array['field3']
':d' => $array['field4']
':e' => $array['field5']
];
// execute the prepared query using the new parameters
$stmt->execute($params);
}
here is the way to insert multiple rows of data, you prepare once and insert execute multiple times one prepared statement
$dbh = new PDO($dsn, $user, $pass, $options);
$arrayOfData = [...];
$stmt = $dbh->prepare('INSERT INTO table SET col = :val');
$dbh->beginTransaction();
foreach($arrayOfData as $data) {
$stmt->bindValue(':val', $data);
$stmt->execute();
}
$dbh->commit();
The idea of a prepared statement is that the statement is prepared once and then executed multiple times. Something like this:
$sql = 'INSERT INTO gosurveys_surveys_questions_answers
SET survey_id = :survey_id,
question_id = :question_id,
user_email = :user_email,
answer_type = :answer_type,
question_answer = :question_answer,
question_answer_creation_date = UTC_TIMESTAMP()';
$query = $this->conn->prepare($sql);
foreach($questionsArray as $questionAnswerRow) {
$query->execute([
":survey_id" => $questionAnswerRow["survey_id"],
// etc.
]);
}
I'm trying to set up a way for users to set settings, i'm saving the settings in a json format in the databse. When I try to update the user though I get this syntax error:
Warning: PDOStatement::execute(): SQLSTATE[42000]: Syntax error or
access violation: 1064 You have an error in your SQL syntax; check the
manual that corresponds to your MariaDB server version for the right
syntax to use near ''settings' = '{\"background-color\":\"050505\"}'
WHERE ID = '2'' at line 1 in
C:...\htdocs\app\model\model.database.php on line 29
Here is the code that I have.
public function setColor(){
$modUser = $this->model('user');
$modInput = $this->model('input');
$modViewData = $this->model('viewData');
$modUser->setSetting("background-color",str_replace('#', "", $modInput->returnPost("color")));
$this->view('profile/view.profile', $modViewData->getData());
}
//in User model
public function setSetting($name, $value){
$settings = $this->getSetting();
$settings[$name] = $value;
$settings = json_encode($settings);
$this->update("settings", $settings);
}
public function update($field, $value){
$sql = "UPDATE `users` SET :field = :value WHERE `ID` = :id";
$params = [":field" => $field, ":value" => $value, ":id" => $this->_data->ID];
$database = $this->model('database');
$database->query($sql,$params);
}
You cannot a parameterize table and column names. You need to insert those directly into the query string. One method is:
$sql = "UPDATE `users` SET $field = :value WHERE `ID` = :id";
trying to make a delete function which will delete from any table using a field and value.
problem is its not working and im not getting any errors from it, I can add stuff to the database but not delete them.
if (isset($_REQUEST['delete']))
{
$table = $_POST['table'];
$field = $_POST['field'];
$value = $_POST['value'];
deleteFromTable($database, $table, $field, $value);
}
function deleteFromTable($pdo, $table, $field, $value)
{
$stmt = $pdo->prepare('DELETE FROM :table WHERE :field = :value');
$criteria = [
'table' => $table,
'field' => $field,
'value' => $value
];
$stmt->execute($criteria);
}
Tables cannot be used as a parameter. While insecure, you're better off just placing it into the query:
$stmt = $pdo->prepare("DELETE FROM `$table` WHERE $field = :value");
or
$stmt = $pdo->prepare('DELETE FROM `'.$table.'` WHERE `'.$field.'` = :value');
You really should check to make sure that $table is an acceptable value before you proceed with the query.
You also need colons on your parameters, like so:
$criteria = [
':value' => $value
];
You cannot use parameters for table names, and you also cannot use parameters for column names. As it is currently written, you ARE generating a syntax error when you execute your prepared statement, whether or not you can see it. If you do this:
$stmt = $pdo->prepare("DELETE FROM `$table` WHERE :field = :value");
you will not generate a syntax error, but it will still not be doing what you think it is. It will not treat :field as a column identifier, but as a value! So if the user posts something like
$_POST = ['table' => 'table1','field' => 1, 'value' => 1]
then in effect, the query will be
DELETE FROM `table1` WHERE '1' = '1'
This will delete every row from your table, which I assume you would not want.
The statement you need is
$stmt = $pdo->prepare("DELETE FROM `$table` WHERE `$field` = :value");
But concatenating user input into your query like this is obviously a dangerous thing to do.
If you want to safely create a query like this, you can use a whitelist approach, where you define which tables can be deleted from, and by which keys, and check your user input against that before running your delete. Here is a basic example:
$allowed_delete_keys = array(
'table1' => array('columnA', 'columnB'),
'table2' => array('columnX', 'columnY')
);
if (!isset($allowed_delete_keys[$_POST['table']])) {
echo "You can't delete from this table.";
exit;
} else {
if (!in_array($_POST['field'], $allowed_delete_keys[$_POST['table']])) {
echo "You can't delete records based on this key";
exit;
} else {
deleteFromTable($database, $table, $field, $value);
}
}
The below sql UPDATE statement returns an error but I'm unable to see why:
Failed to run query: 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 ')' at line 6
I already did a vardump of the array that I pass to bind the parameters but I see nothing unusual. The correct values are passed and I double checked for typos.
What I try to accomplish is to auto-generate a username based on firstname - lastname and user_id after insertion into the database.
Perhaps additional question: do you see any harm in that and if so, what is your suggestion?
I'm still in PHP learning phase.
Thanks.
...
//Autogenerate user_name based on first name, last name and user_id (auto-increment)
$query_username = "
UPDATE user_tbl
SET
user_name = :username
WHERE
user_id = :userid
)
";
// The parameter values
$query_params_username = array(
':username' => $_SESSION['user']['first_name'].".".$_SESSION['user']['last_name'].$_SESSION['user']['user_id'],
':userid' => $_SESSION['user']['user_id']
);
try
{
// Execute the query against the database
$stmt_username = $db->prepare($query_username);
$stmt_username->execute($query_params_username);
}
catch(PDOException $ex)
{
//Not to be used in production
die("Failed to run query: " . $ex->getMessage());
}
$_SESSION['user']['username'] = $_SESSION['user']['first_name'].".".$_SESSION['user']['last_name'].$_SESSION['user']['user_id'];
You had a closing parentheses after user_id = :userid
Try the following:
$query_username = "
UPDATE user_tbl
SET
user_name = :username
WHERE
user_id = :userid
";
Try doing this:
$query_username = "
UPDATE `user_tbl`
SET `user_name` = :username
WHERE `user_id` = :userid
";
There seems to be a lost ) character in your code.