Just changed my previous question to reflect PDO changes everyone told me to make. Am I doing this right? Error reporting right? Is everything secure?
Just changed my previous question to reflect PDO changes everyone told me to make. Am I doing this right? Error reporting right? Is everything secure?
try{
$connection = new PDO('mysql:host=supertopsecret;dbname=supertopsecret;charset=utf8mb4',
'supertopsecret', 'supertopsecret');
$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
//Query 1 - Insert Provider's Name
//if(isset($_POST['submit'])){ delete this? do I still use this? halp
$stmt1 = $connection->prepare("INSERT INTO
`providers`(provider_first_name,provider_last_name,date_added)
VALUES (:providerfirstname, :providerlastname, NOW())");
//bind parameters:
$stmt1->bindParam(':providerfirstname', $providerfirstname);
$stmt1->bindParam(':providerlastname', $providerlastname);
//insert row
$providerfirstname = $_POST['providerfirstname'];
$providerlastname = $_POST['providerlastname'];
$stmt1->execute();
//Query 2 - Insert Practices
$prov_id = $connection->lastInsertId();
/*Get all values of practice_name[]:*/
$practicename = $_POST['practice_name'];
for ($i = 0; $i < count($practicename); $i++) {
if ($practicename[$i]) {
$practice_name_data = $practicename[$i];
$stmt2 = $connection->prepare("INSERT INTO
practices(prov_id,practice_name) VALUES (:prov_id,:practice_name)");
$stmt2->bindParam(':prov_id', $prov_id);
$stmt2->bindParam(':practice_name', $practice_name_data);
$stmt2->execute();
}
}
echo '<center><h3><br><br><br>Thank you! Your provider has
successfully been submitted to the database!</center></h3></br>';
} catch(PDOException $e){
echo "Sorry, there was an problem submitting your provider to the
database. Please try again or copy and paste the error code below to
the \"Report a Problem\" page and we will try to correct the problem.
</b></br></br> Error: " . $e->getMessage();
die();
}
$connection = null;
You should use prepared statements instead of escaping yourself, see How can I prevent SQL injection in PHP?. But it's probably '$practicename[$i]'. It would be '{$practicename[$i]}', but easier:
foreach($practicename as $value){
if($value!=""){
$value = mysqli_real_escape_string($connection, $value);
$query2 = mysqli_query($connection,
"INSERT INTO `practices`(prov_id,practice_name)
VALUES ('$prov_id','$value')");
}
}
But again, abandon this and use Prepared Statements!
Check this it may help you. Use PDO for insert.
$connection = new PDO("mysql:host=xxxx;dbname=xxxx;", "xxxx", "xxxx"); //database connection
for ($i = 0; $i < count($practicename); $i++) {
if ($practicename[$i]) {
$practice_name_data = $practicename[$i];
$statement = $connection->prepare('INSERT INTO practices(prov_id,practice_name) VALUES (:prov_id,:practice_name)');
$statement->bindParam(':prov_id', $prov_id);
$statement->bindParam(':practice_name', $practice_name_data);
// etc.
$statement->execute();
}
}
Related
I Have Table Called XYZ
ID no no2 no3
1 465 Abc [BLOB - 15B]
2 465 Abc [BLOB - 18B]
3 465 Abc [BLOB - 80B]
4 456 Abc [BLOB - 50B]
i want to insert multiple images into "no3" using PDO
PHP
try {
$connection = new PDO($dsn, $username, $password, $options);
$sql = "INSERT INTO xyz (no, no2, no3) SELECT max(nos),nos2,:tmp from ASD;
for($i=0; $i<=count($data)-1; $i++)
{
$data = $_FILES['image']['tmp_name'];
$statement = $connection->prepare($sql);
$statement->bindParam(':tmp',addslashes(file_get_contents($data[$i])));
}
$statement->execute();
} catch(PDOException $error) {
echo $sql . "<br>" . $error->getMessage();
}
it insert only one image
As others have mentioned, you're missing a closing ", but it seems it is there in your actual code, because the behaviour you described doesn't match this syntax error.
So, looking further, you call execute only after the loop.
You will have to execute the statement over and over inside the for-loop as well.
Now you just overwrite the image in the bind parameter, call execute once, and thus only insert the last image in the database.
You should actually be able to prepare the statement once, and in the loop, for every iteration, bind a new value and execute the statement. So the code for the loop becomes this: (Assuming the details of loading the file, etc, is okay. I haven't investigated that).
$statement = $connection->prepare($sql);
for($i=0; $i<=count($data)-1; $i++)
{
$data = $_FILES['image']['tmp_name'];
$statement->bindParam(':tmp',addslashes(file_get_contents($data[$i])));
$statement->execute();
}
Correct PHP Code
$statement = $connection->prepare($sql);
$data = $_FILES['image']['tmp_name'];
for($i=0; $i<=count($data)-1; $i++)
{
$tmp = addslashes(file_get_contents($data[$i]));
$statement->bindParam(':tmp',$tmp);
$statement->execute();
}
Mate you forgot to add ending " at the of the query:
try {
$connection = new PDO($dsn, $username, $password, $options);
$sql = "INSERT INTO xyz (no, no2, no3) SELECT max(nos),nos2,:tmp from ASD";
for($i=0; $i<=count($data)-1; $i++)
{
$data = $_FILES['image']['tmp_name'];
$statement = $connection->prepare($sql);
$statement->bindParam(':tmp',addslashes(file_get_contents($data[$i])));
}
$statement->execute();
} catch(PDOException $error) {
echo $sql . "<br>" . $error->getMessage();
}
Ok, so I am having a lot of trouble with Prepared statements. I've done hours of research and still can't seem to fully understand everything...
I really feel like I need to understand Prepared statements because I was just about to release a few new free APIs on my website (which require API Key to execute API) but I recently realized how insecure everything is.... I can simply use SQL injection to bypass API Key check, e.g. 'OR'1'='1
Here is how I validate API Key:
$apikey = $_GET['key'];
$sql = "SELECT * FROM `table` WHERE `key` = '$apikey'";
$query = mysqli_query($con, $sql);
if($query)
{
$fetchrow = mysqli_fetch_row($query);
if(isset($fetchrow[0]))
{
echo "API Key is valid!";
}
else
{
echo "API KEY is invalid";
}
}
And like mentioned above this can easily be bypassed by executing my API like this
http://website.com/api.php?key='OR'1'='1
This really scared me at first, but then I did some research and learned a good way to prevent any form of SQL injection is to use prepared statement, so I did a lot of research and it just seems quite complicated to me :/
So I guess my question is, how can I take my above code, and make it function the same way using prepared statements?
Probably everything you need:
class Database {
private static $mysqli;
Connect to the DB:
public static function connect(){
if (isset(self::$mysqli)){
return self::$mysqli;
}
self::$mysqli = new mysqli("DB_HOST", "DB_USER", "DB_PASS", "DB_NAME");
if (mysqli_connect_errno()) {
/*Log error here, return 500 code (db connection error) or something... Details in $mysqli->error*/
}
self::$mysqli->query("SET NAMES utf8");
return self::$mysqli;
}
Execute statement and get results:
public static function execute($stmt){
$stmt->execute();
if ($mysqli->error) {
/*Log it or throw 500 code (sql error)*/
}
return self::getResults($stmt);
}
Bind results to the pure array:
private static function getResults($stmt){
$stmt->store_result();
$meta = $stmt->result_metadata();
if (is_object($meta)){
$variables = array();
$data = array();
while($field = $meta->fetch_field()) {
$variables[] = &$data[$field->name];
}
call_user_func_array(array($stmt, "bind_result"), $variables);
$i = 0;
while($stmt->fetch()) {
$array[$i] = array();
foreach($data as $k=>$v)
$array[$i][$k] = $v;
$i++;
}
$stmt->close();
return $array;
} else {
return $meta;
}
}
Class end :)
}
Example of usage:
public function getSomething($something, $somethingOther){
$mysqli = Database::connect();
$stmt = $mysqli->prepare("SELECT * FROM table WHERE something = ? AND somethingOther = ?");
$stmt->bind_param("si", $something, $somethingOther); // s means string, i means number
$resultsArray = Database::execute($stmt);
$someData = $resultsArray[0]["someColumn"];
}
Resolving your problem:
public function isKeyValid($key){
$mysqli = Database::connect();
$stmt = $mysqli->prepare("SELECT * FROM table WHERE key = ? LIMIT 1");
$stmt->bind_param("s", $key);
$results = Database::execute($stmt);
return count($results > 0);
}
PHP automatically closes DB connection so no worries about it.
$sql = "SELECT * FROM `table` WHERE `key` = ?";
if(stmt = $mysqli->prepare($sql)) {
$stmt->bind_param("i", $apikey);
$stmt->execute();
$stmt->bind_result($res);
$stmt->fetch();
$stmt->close();
}
See more - http://php.net/manual/en/mysqli.prepare.php
i am trying to concatenate sql queries and run later after loop. how is that possible? this is my vision:
for($i=1;$i<=10;$i++){
$item_.$i = "value_".$i;
sql = sql . " insert into table (`item`) values ('$item_'.$i.'')";
// this should be but an array
}
and save into db:
for($j=0;$j<sqlarray.length;$j++){
$sql_done = mysql_query($sqlarray[$j]);
}
i didnot try anything yet, because the database is big and i am afraid of destroying something important with my code..
thanks a lot
Use mysqli and bindings
see http://www.php.net/manual/en/mysqli.prepare.php
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
// define your query
$query = "INSERT INTO tablename (column1,column2) VALUES (:col1,:col2)";
if ($stmt = $mysqli->prepare($query)) {
// loop of insert
for($i=0;$i<10;$i++){
$stmt->bind_param("col1", $i);
$stmt->bind_param("col2", 'test'.$i);
$stmt->execute();
}
$stmt->close();
}else{
throw new Exception("unable to prepare query");
}
$mysqli->close();
Binding will avoid a lot of security issue, no one should use something else then binding ever.
Even better put everything in a transaction and in case of error your database remains unchanged.
see: http://www.php.net/manual/en/mysqli.commit.php for more info
and here is a proposal with commit or rollback
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
if (mysqli_connect_errno()) {
throw new Exception("Unable to connect");
}else{
try{
$mysqli->autocommit(FALSE);
// define your query
$query = "INSERT INTO tablename (column1,column2) VALUES (:col1,:col2)";
if ($stmt = $mysqli->prepare($query)) {
// loop of insert
for($i=0;$i<10;$i++){
$stmt->bind_param("col1", $i);
$stmt->bind_param("col2", 'test'.$i);
$stmt->execute();
}
$stmt->close();
}else{
throw new Exception("unable to prepare query");
}
$mysqli->commit();
}catch(Exception $e){
$mysqli->rollback();
}
$mysqli->close();
}
I did not try it but we should be near a good (best practice?) solution.
I hope this could help you.
For insert query you can write code like below:
$sql .= " insert into table (`item`) values ";
for($i=1;$i<=10;$i++){
$item_.$i = "value_".$i;
$sql = $sql . " ('$item_'.$i.''),";
}
mysqli_query( substr($sql ,0,-1) );
The above will concatenate all the insert data in a single string and execute at once.
I hope you were looking for this
$query = "insert into table_name values";
for($i=0;$i<4;$i++) {
$data1 = "test_".$i;
$data2 = "new_".$i;
$query .= "('','$data1','$data2'),";
}
$query = substr($query,0,-1);
echo $query;
Let me know
try below code
$sql="":
for($i=1;$i<=10;$i++)
{
$item_.$i = "value_".$i;
$sql.=" insert into table (`item`) values ('$item_'.$i.'')";
// this should be but an array
}
mysql_query($sql);
I am trying to set up my first transaction in MySQL using PHP/PDO...
I just have a quick question, what is the best way to determine if the previous query was successful or not? Here is what I have right now, but I would rather find a way to test the query with an if statement.
This is pretty much mock up code to try to get a working model.. I know $results isn't effectively testing if anything was good or bad.. i have it there more as a place holder for the real deal when the time comes..
if ($_POST['groupID'] && is_numeric($_POST['groupID'])) {
$sql = "SET AUTOCOMMIT=0";
$dbs = $dbo->prepare($sql);
$dbs->execute();
$sql = "START TRANSACTION";
$dbs = $dbo->prepare($sql);
$dbs->execute();
$sql = "DELETE FROM users_priveleges WHERE GroupID=:groupID";
$dbs = $dbo->prepare($sql);
$dbs->bindParam(":groupID", $_POST['groupID'], PDO::PARAM_INT);
$dbs->execute();
try {
$sql = "DELETE FROM groups WHERE GroupID=:groupID LIMIT 1";
$dbs = $dbo->prepare($sql);
$dbs->bindParam(":groupID", $_POST['groupID'], PDO::PARAM_INT);
$dbs->execute();
$results["error"] = null;
$results["success"] = true;
try {
$sql = "DELETE FROM users WHERE Group=:groupID";
$dbs = $dbo->prepare($sql);
$dbs->bindParam(":groupID", $_POST['groupID'], PDO::PARAM_INT);
$dbs->execute();
$results["error"] = null;
$results["success"] = true;
$sql = "COMMIT";
$dbs = $dbo->prepare($sql);
$dbs->execute();
}
catch (PDOException $e) {
$sql = "ROLLBACK";
$dbs = $dbo->prepare($sql);
$dbs->execute();
$results["error"] = "Could not delete associated users! $e";
$results["success"] = false;
}
}
catch (PDOException $e)
{
$sql = "ROLLBACK";
$dbs = $dbo->prepare($sql);
$dbs->execute();
$results["error"] = "COULD NOT REMOVE GROUP! $e";
$results["success"] = false;
}
}
Some general notes:
Don't use bindParam() unless you use a procedure that modifies the parameter's value
Therefore, use bindValue(). bindParam() accepts argument value as a referenced variable. That means you can't do $stmt->bindParam(':num', 1, PDO::PARAM_INT); - it raises an error.
Also, PDO has its own functions for controlling transactions, you don't need to execute queries manually.
I rewrote your code slightly to shed some light on how PDO can be used:
if($_POST['groupID'] && is_numeric($_POST['groupID']))
{
// List the SQL strings that you want to use
$sql['privileges'] = "DELETE FROM users_priveleges WHERE GroupID=:groupID";
$sql['groups'] = "DELETE FROM groups WHERE GroupID=:groupID"; // You don't need LIMIT 1, GroupID should be unique (primary) so it's controlled by the DB
$sql['users'] = "DELETE FROM users WHERE Group=:groupID";
// Start the transaction. PDO turns autocommit mode off depending on the driver, you don't need to implicitly say you want it off
$pdo->beginTransaction();
try
{
// Prepare the statements
foreach($sql as $stmt_name => &$sql_command)
{
$stmt[$stmt_name] = $pdo->prepare($sql_command);
}
// Delete the privileges
$stmt['privileges']->bindValue(':groupID', $_POST['groupID'], PDO::PARAM_INT);
$stmt['privileges']->execute();
// Delete the group
$stmt['groups']->bindValue(":groupID", $_POST['groupID'], PDO::PARAM_INT);
$stmt['groups']->execute();
// Delete the user
$stmt['users']->bindParam(":groupID", $_POST['groupID'], PDO::PARAM_INT);
$stmt['users']->execute();
$pdo->commit();
}
catch(PDOException $e)
{
$pdo->rollBack();
// Report errors
}
}
I wouldn't prepare & execute the transaction statements. I'd use PDO::beginTransaction() , PDO::commit(), and PDO::rollback().
PDO::prepare() and PDO::execute() return FALSE if there's an error, or else they throw PDOException if you setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION).
In your exception handler, you should check PDO::errorInfo() and report the nature of the error. Best practice is to log the raw error info, but give the user a more friendly message.
Don't echo the literal error message in the UI -- this can give the user inappropriate knowledge about your SQL query and schema.
PDO Statement's execute() returns TRUE on success and FALSE on failure, so you can test the return value of the previous execute() in your if statement.
$pdo_result = $dbs->execute();
if ($pdo_result) {
// handle success
} else {
// handle failure
// you can get error info with $dbs->errorInfo();
}
That said, as #Bill Kerwin correctly points out (in his answer that I'm totally upvoting because it's exactly correct), it would be preferable to use PDO::beginTransaction(), PDO::commit(), and PDO::rollback().
Trying to get a grasp of using PDO, and I'm using some pre-made functions to make things simpler for when I want to do a query. First one connects, second runs the query.
Unfortunately it won't let me INSERT rows using dbquery(). SELECT works fine, just can't seem to get anything else to work.
Here's the code:
function dbConnect()
{
global $dbh;
$dbInfo['database_target'] = "localhost";
$dbInfo['database_name'] = "mysqltester";
$dbInfo['username'] = "root";
$dbInfo['password'] = "password";
$dbConnString = "mysql:host=" . $dbInfo['database_target'] . "; dbname=" . $dbInfo['database_name'];
$dbh = new PDO($dbConnString, $dbInfo['username'], $dbInfo['password']);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$error = $dbh->errorInfo();
if($error[0] != "")
{
print "<p>DATABASE CONNECTION ERROR:</p>";
print_r($error);
}
}
function dbQuery($queryString)
{
global $dbh;
$query = $dbh->query($queryString);
$i = 0;
foreach ($query as $query2)
{
$queryReturn[$i] = $query2;
$i++;
}
if($i > 1)
{
return $queryReturn;
}
else
{
return $queryReturn[0];
}
}
PDO::query Only works with queries that return a result set (e.g. SELECT)
For INSERT/UPDATE/DELETE see PDO::exec
If you are going to be inserting user provided data into your DBMS I strongly suggest using the prepared statement functionality of PDO to provide automatic escaping to prevent SQL injection.
e.g.
<?php
$stmt = $dbh->prepare("INSERT INTO tester1 (name, age) VALUES (?, ?)");
$stmt->execute(array('James',25));
See PDO::prepare and PDOStatement::execute