Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I have a mySQLi prepared statement and a function I want to pass it to to check to see if the input meets the right format before executing it. I have determined in my actual code that if I move the $stmt->execute() statement just outside of the function that it works, but inside the function it does not. I know that an old school concatenated SQL string can be passed like this, but what is the right way to handle this with prepared statements?
function validateForm($stmt, $inputType){
// A bunch of stuff here to validate for $inputType.
$stmt->execute();
}
$editSQL = "UPDATE mytable SET input1 = ?, input2 = ?, input3 = ? WHERE thisID = ?";
$stmt = $conn->prepare($editSQL);
$stmt->bind_param('sibi', $input1, $input2, $input3, $thisID);
$input1 = $vPara[1][5];
$input2 = $vPara[2][5];
$input3 = $vPara[3][5];
$thisID = $_SESSION['thisID'];
validateForm($stmt, $inputType);
The specific error I get is:
Fatal error: Call to a member function execute() on a non-object
I don't see why it could potentially not work.
You mentioned that if you use execute outside of this function it works. So leave it the way it works because That's recommended due to Single responsibility principle.
Your validator should not be responsible for executing statements but
only for what its name says = validation.
The only thing that validator should do is to return true or false
and upon that decision, you should make execution or not
so change your code to the following:
<?php
function isFormValid($inputType)
{
// A bunch of stuff here to validate for $inputType.
if (//test cases) {
$validationResult = true;
} else {
$validationResult = false;
}
return $validationResult;
}
$editSQL = "UPDATE mytable SET input1 = ?, input2 = ?, input3 = ? WHERE thisID = ?";
$stmt = $conn->prepare($editSQL);
$stmt->bind_param('sibi', $input1, $input2, $input3, $thisID);
$input1 = $vPara[1][5];
$input2 = $vPara[2][5];
$input3 = $vPara[3][5];
$thisID = $_SESSION['thisID'];
if(isFormValid($inputType)) {
$stmt->execute();
} else {
//do error processing echo, trow exception etc
}
?>
Note that I changed the validator name, so now it really expresses what it does, instead of being called validateForm but actually doing also a statement execution.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
if ($_SERVER['REQUEST_METHOD'] == 'POST'){
$follow = strip_tags($_POST["follow"]);
$follow = addslashes($follow);
$follow = mysqli_real_escape_string($conn, $follow);
$sesid = $_SESSION["id"];
$rowid = $row['id'];
$followers = $conn->query("INSERT INTO followers (forid, fromid) VALUES ('$rowid', '$sesid'");
echo "<h3><center>Sucessfully followed!</center></h3>";
}
It doesn't seem to work. It doesn't throw any errors. I'm a new(er) PHP developer. Thank you!
STOP this madness... Do not insert values into a query, that's how bad things happen.
Also, add some error checking here and there, it's impossible to know what's wrong with the piece of code (or is it?):
Try something more like this:
$conn = new mysqli('localhost', 'root', 'password', 'db_name');
if ($conn->connect_errno) {
throw new Exception('Connection Error' . $conn->connect_err);
}
Now let's deal with the post data:
if(isset($_POST)) {
$follow = $_POST["follow"];
$sesid = $_SESSION["id"];
$rowid = $row['id']; // don't know where this is coming from
if($stmt = $conn->prepare("INSERT INTO followers (forid, fromid) VALUES (?, ?)") {
$stmt->bind_param('si', $follow, $sesid);
if(!$stmt->execute()) {
throw new Exception('Error! Could not execute query.');
}
$stmt->close();
} else {
throw new Exception('Could not prepare query!');
}
} else {
// Add a error checking here
throw new Exception('No post data');
}
Prepared statements will help you avoid anyone trying to insert undesired content into your query (SQL injection).
The way it works,
Prepare the query ($conn->prepare())
Note here that there are simple ? where the values would otherwise be. That's for our next step.
Bind the parameters of your query with $stmt->bind_param(). This will tell PHP where each value should go, starting at the second parameter position.
The documentation for the bind_param function: bool mysqli_stmt::bind_param ( string $types , mixed &$var1 [, mixed &$... ] )
A little explanation for the first param:
s - stands for string (the $follow, I assume is a string)
i - stands for integer. The session ID
Then, finally, execute the query ($stmt->execute()). That will do the hard work of adding the values to your database.
Explicitly close the connection to your database ($conn->close());
Read more on PHP's official documentation.
This question already has answers here:
Can I parameterize the table name in a prepared statement? [duplicate]
(2 answers)
Closed 5 years ago.
I'm trying to execute delete SQL statement upon button press, which was working if I binded 1 parameter, but I want to make the delete.php generic, as not to have multiple of them just for referencing different tables:
<?php
include "header.php";
include "db.php";
$_POST['table'] = "customer";
$_POST['column'] = "cID";
$_POST['del_id'] = 26;
if(isset($_POST['del_id']))
{
if ($stmt = $conn->prepare("DELETE FROM customer WHERE ? = ?"))
{
$stmt->bind_param('si', $_POST['column'], $_POST['del_id']);
$stmt->execute();
}
else echo("Oops");
}
This binding executes but doesn't do anything to the table, only binding the final value 'del_id', executes correctly, and binding 3 arguments including the table name, just causes prepare() to fail.
I am setting the _POST vars in other places from AJAX POSTs, above is just for testing that this bit works or not. I also haven't gotten round to doing validation yet before that comes up.
Very PHP nooby, likely a simple mistake, or just something I'm not aware of, in which case I'd be rather curious as to why the table/column names can't be parameterised, as it's been eluding me for some time. As a workaround, would some form of concatenation work instead, to be able to drop dynamic names into this query from multiple different places?
You should validate both the table name and column name before running the delete.
Since you can't prepare either the table or column names, just put them in the sql statement before the prepare.
<?php
include "header.php";
include "db.php";
$_POST['table'] = "customer";
$_POST['column'] = "cID";
$_POST['del_id'] = 26;
// Add code to prevent SQL injection
$table = $_POST['table'] == 'customer' ? $_POST['table'] : '';
$column = $_POST['column'] == 'customer' ? $_POST['column'] : '';
if(isset($_POST['del_id']) && $table != '' && $column != '') {
if ($stmt = $conn->prepare("DELETE FROM `".$table."` WHERE `".$column."` = ?"))
{
$stmt->bind_param('i', $_POST['del_id']);
$stmt->execute();
}
else echo("Oops");
}
Yes, to achieve this you will need to have a combination of concatenation and parameters for the Prepared Statement:
if ($stmt = $conn->prepare("DELETE FROM " . $_POST['table'] . " WHERE " . $_POST['column'] . " = ?"))
{
$stmt->bind_param('si', $_POST['del_id']);
$stmt->execute();
}
Make sure you pay attention on additional validations you will need on the table and column names. This should be validated against your data model and not just making sure its a valid identifier. Further, take a look at some ORMs/Query Builders to learn how to elaborate upon this idea. It's a good learning exercise.
I'm having a slight issue with prepared statements for PHP and MySQLi. My code takes POST data, prepares a statement and then binds the POST data to the query. After this it executes, seemingly without error, but the statement clearly doesn't ever get executed correctly since no database changes occur.
A quick overview of my problem:
I'm using MySQLi and prepared statements.
I've updated some previous PHP/MySQLi code that was not using prepared statements.
Since updating, a problem has occured with $query->execute().
Code:
if($query = $this->mysqli->prepare("UPDATE article SET copy = ?, title = ?, alias = ?, description = ?, category = ?, category_id = ?, author = ?, permission = ?, mode = ?, comments = ?, revised = ?, frequency = ?, priority = ? WHERE id = ?"))
{
if
(
$query->bind_param
(
"sssssdsssssssd",
$_POST['edit_article'],
$_POST['edit_title'],
$_POST['edit_alias'],
$_POST['edit_description'],
$_POST['edit_category'],
$result['id'],
$_POST['edit_author'],
$_POST['edit_permission'],
$_POST['edit_mode'],
$_POST['edit_comments'],
$date_time,
$_POST['edit_frequency'],
$_POST['edit_priority'],
$id
)
)
{
if($query->execute())
{
echo $this->mysqli->error; // does nothing.
// These statements print to the screen successfully with the correct values.
echo $_POST['edit_frequency'] . "<br />";
echo $_POST['edit_priority'] . "<br />";
//$_SESSION['article_edited'] = true;
// die;
//exit(header("Location: " . __MANAGER__ . $_POST['edit_alias']));
}
else
{
$_SESSION['article_edited'] = false;
exit(header("Location: " . __MANAGER__ . $_POST['edit_alias']));
}
}
}
else
{
$_SESSION['article_edited'] = false;
exit(header("Location: " . __MANAGER__ . $_POST['edit_alias']));
}
Could anybody perhaps explain a better way to debug this code? It makes no sense to me since I check whether the execute() function works with if($query->execute()) and it's successful. Assuming of course the problem does not lie with the execute function, but elsewhere. The problem can't be the POST data since it validates fine with:
if
(
$_POST['foo'] && isset($_POST['foo']) &&
$_POST['bar'] && isset($_POST['bar'])
)
{
// Statements.
}
I am clueless.
EDIT
I would like to note I echo the values of 2 particluar POST vars since those were the ones I added in when updating the code. It has since stopped working. Also note I have used print_r($_POST) and all POST vars are set and contain values.
Your update will return success IF there is no error at SQL level. Note that NOT finding candidate rows to updates is NOT a SQL error. That is a logical error/expected condition. If failure to find a row to update is an error condition for your code, then you will need to modify your error check to include a check for that.
if($query->execute())
{
if($mysqli->affected_rows == 0)
// some logical error
Problem has been solved.
The error was, as expected, not caused by a malfunction or faulty logic, rather an unchanged variable $id, that should have been converted to $_POST['id']. For those that read this in the future, it may be worthwhile noting that Amit Agrawal's reply is actually an important one.
Please bear in mind that the SQL statement was not in error, it just so happened that no rows were affected since the variable $id was not set or initialised. Therefore, the appropriate course of action would be something along the lines of:
if($query->execute())
{
if($this->mysqli->affected_rows == 0)
{
// Appropriate action (such as raising an error), e.g.,
die("No rows were affected");
}
else
{
// Appropriate action.
}
}
else
{
die("Query could not be executed.");
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
Function
public function isUsernameAvailable($username) {
$sql = "SELECT COUNT(*) FROM users WHERE username = '$username'";
$sql = $this->db->quote($sql);
if ($sth = $this->db->query($sql)) {
if ($sth->fetchColumn() > 0) {
return false;
} else {
return true;
}
}
}
if (!$user->isUsernameAvailable($_POST['username'])) {
echo 'The username is already taken!';
$error = 1;
}
I get the output "The username is already taken!" even though It isn't.
select count(*)... will ALWAYS return a record, even nothing is matched
so, you can try this :
select 1 from ...;
between, a proper method to check any record return should be using rowCount
as indicate by the rest :-
you should make use of bind rather than using quote
function should always return a value
Your isUsernameAvailable function will return NULL (equals to FALSE) when the query fails. This will trigger the The username is already taken! message.
Looks like your SQL query always fails, which I think is no wonder because you blindly apply quotes onto it.
After running a SQL query, you must more properly deal with the case that the query failed, e.g. display that an internal SQL error occured and log it, so you can look into the log and locate the cause of the error.
Additionally, you should use the PDO more properly, specifically when you want to prevent SQL injection (which you must care about). See Best way to stop SQL Injection in PHP.
Use rowCount() instead of fetchColumn()
if ($sth->rowCount() > 0) {
fetchColumn returns false if there is no column and also see ajreal answer.
if ($sth = $this->db->query($sql)) {
if ($sth->fetchColumn() == false) {
return true;
} else {
return false;
}
}
Can you try using the following code?
$st->rowCount() > 0
$sql = "SELECT COUNT(*) FROM users WHERE username = '$username'";
$sql = $this->db->quote($sql);
You are quoting the whole query, this does not make any sense and is outright dangerous from a security standpoint – and probably results in an invalid query anyway.
Here's a more proper code:
// return TRUE if user is available
public function isUsernameAvailable($username) {
$sql = "SELECT id FROM users WHERE username = :username";
$stmt = $this->db->prepare($sql);
$stmt->execute(array(':username' => $username));
return $stmt->fetch() === FALSE;
}
if (!$user->isUsernameAvailable($_POST['username'])) {
echo 'The username is already taken!';
$error = 1;
}
I'm playing around with MySQLi at the moment, trying to figure out how it all works. In my current projects I always like to echo out a query string while coding, just to make sure that everything is correct, and to quickly debug my code. But... how can I do this with a prepared MySQLi statement?
Example:
$id = 1;
$baz = 'something';
if ($stmt = $mysqli->prepare("SELECT foo FROM bar WHERE id=? AND baz=?")) {
$stmt->bind_param('is',$id,$baz);
// how to preview this prepared query before acutally executing it?
// $stmt->execute();
}
I've been going through this list (http://www.php.net/mysqli) but without any luck.
EDIT
Well, if it's not possible from within MySQLi, maybe I'll stick with something like this:
function preparedQuery($sql,$params) {
for ($i=0; $i<count($params); $i++) {
$sql = preg_replace('/\?/',$params[$i],$sql,1);
}
return $sql;
}
$id = 1;
$baz = 'something';
$sql = "SELECT foo FROM bar WHERE id=? AND baz=?";
echo preparedQuery($sql,array($id,$baz));
// outputs: SELECT foo FROM bar WHERE id=1 AND baz=something
Far from perfect obviously, since it's still pretty redundant — something I wanted to prevent — and it also doesn't give me an idea as to what's being done with the data by MySQLi. But I guess this way I can quickly see if all the data is present and in the right place, and it'll save me some time compared to fitting in the variables manually into the query — that can be a pain with many vars.
I don't think you can - at least not in the way that you were hoping for. You would either have to build the query string yourself and execute it (ie without using a statement), or seek out or create a wrapper that supports that functionality. The one I use is Zend_Db, and this is how I would do it:
$id = 5;
$baz = 'shazam';
$select = $db->select()->from('bar','foo')
->where('id = ?', $id)
->where('baz = ?', $baz); // Zend_Db_Select will properly quote stuff for you
print_r($select->__toString()); // prints SELECT `bar`.`foo` FROM `bar` WHERE (id = 5) AND (baz = 'shazam')
I have struggled with this one in the past. So to get round it I wrote a little function to build the SQL for me based on the SQL, flags and variables.
//////////// Test Data //////////////
$_GET['filmID'] = 232;
$_GET['filmName'] = "Titanic";
$_GET['filmPrice'] = 10.99;
//////////// Helper Function //////////////
function debug_bind_param(){
$numargs = func_num_args();
$numVars = $numargs - 2;
$arg2 = func_get_arg(1);
$flagsAr = str_split($arg2);
$showAr = array();
for($i=0;$i<$numargs;$i++){
switch($flagsAr[$i]){
case 's' : $showAr[] = "'".func_get_arg($i+2)."'";
break;
case 'i' : $showAr[] = func_get_arg($i+2);
break;
case 'd' : $showAr[] = func_get_arg($i+2);
break;
case 'b' : $showAr[] = "'".func_get_arg($i+2)."'";
break;
}
}
$query = func_get_arg(0);
$querysAr = str_split($query);
$lengthQuery = count($querysAr);
$j = 0;
$display = "";
for($i=0;$i<$lengthQuery;$i++){
if($querysAr[$i] === '?'){
$display .= $showAr[$j];
$j++;
}else{
$display .= $querysAr[$i];
}
}
if($j != $numVars){
$display = "Mismatch on Variables to Placeholders (?)";
}
return $display;
}
//////////// Test and echo return //////////////
echo debug_bind_param("SELECT filmName FROM movies WHERE filmID = ? AND filmName = ? AND price = ?", "isd", $_GET['filmID'], $_GET['filmName'], $_GET['filmPrice']);
I have also build a little online tool to help.
Mysqli Prepare Statement Checker
I recently updated this project to include composer integration, unit testing and to better handle accepting arguments by reference (this requires updating to php 5.6).
In response to a request I received on a project I created to address this same issue using PDO, I created an extension to mysqli on github that seems like it addresses your issue:
https://github.com/noahheck/E_mysqli
This is a set of classes that extend the native mysqli and mysqli_stmt classes to allow you to view an example of the query to be executed on the db server by interpolating the bound parameters into the prepared query then giving you access to resultant query string as a new property on the stmt object:
$mysqli = new E_mysqli($dbHost, $dbUser, $dbPass, $dbName);
$query = "UPDATE registration SET name = ?, email = ? WHERE entryId = ?";
$stmt = $mysqli->prepare($query);
$stmt->bindParam("ssi", $_POST['name'], $_POST['email'], $_POST['entryId']);
$stmt->execute();
echo $stmt->fullQuery;
Will result in:
UPDATE registration SET name = 'Sue O\'reilly', email = 'sue.o#example.com' WHERE entryId = 5569
Note that the values in the fullQuery are escaped appropriately taking into account the character set on the db server, which should make this functionality suitable for e.g. log files, backups, etc.
There are a few caveats to using this, outlined in the ReadMe on the github project, but, especially for development, learning and testing, this should provide some helpful functionality.
As I've outlined in the github project, I don't have any practical experience using the mysqli extension, and this project was created at the request of users of it's sister project, so any feedback that can be provided from devs using this in production would be greatly appreciated.
Disclaimer - As I said, I made this extension.
Just set it to die and output the last executed query. The Error handling should give you meaningful information which you can use to fix up your query.
You can turn on log queries on mysql server.
Just execute command:
sql> SHOW VARIABLES LIKE "general_log%";
sql> SET GLOBAL general_log = 'ON';
And watch queries in the log file.
After testing turn log off:
sql> SET GLOBAL general_log = 'OFF';
I was able to use var_dump() to at least get a little more info on the mysqli_stmt:
$postmeta_sql = "INSERT INTO $db_new.wp_postmeta (post_id, meta_key, meta_value) VALUES (?, ?, ?)";
$stmt = $new_conn->prepare($postmeta_sql);
$stmt->bind_param("sss", $post_id, $meta_key, $meta_value);
echo var_dump($stmt);
$stmt->execute();
$stmt->close();