I'm working on learning to use prepared statements with mysqli in PHP and usually, if I'm having a problem with a query I just echo it to the screen to see what it looks like as a first step.
How can I do that with a prepared statement?
I'd like to see the SQL statement after the variables are substituted.
Using prepared statements:
When you prepare the statement, it is sent to the MySQL server
When you bind the variables + execute the statement, only the variables are sent to the MySQL server
And the statement + bound variables are executed on the MySQL server -- without it re-doing the "preparation" each time the statement is executed (which is why prepared statements can be good for performance when the same statement is executed several times)
There is no "building" of an SQL query on the PHP side, so, there is no way to actually get that query.
Which means that if you want to see an SQL query, you have to use, well, SQL queries, and not prepared statements.
You can use PDOStatement->debugDumpParams to get some informations about the prepared statement (in case you're using pdo).
Prepared statements are logged in MySQL's general log:
For prepared statements that are executed with the mysql_stmt_prepare() and mysql_stmt_execute() C API functions, the server writes Prepare and Execute lines to the general query log so that you can tell when statements are prepared and executed.
[...] the server writes the following lines to the general query log:
Prepare [1] SELECT ?
Execute [1] SELECT 3
So for debugging purposes active the general log and keep an eye on that file.
edit: oh, the question has a [mysqli] tag... completely overlooked that.
If the statement isn't executed at all have you (double/tripple) checked that no error occurred along the way?
echo "<pre>Debug: start</pre>\n";
$mysqli = new mysqli('localhost', 'localonly', 'localonly', 'test');
if ($mysqli->connect_error) {
die('Connect Error (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error);
}
$result = $mysqli->query('CREATE TEMPORARY TABLE foo (id int auto_increment, x int, primary key(id))');
if ( false=== $result) {
die('error : '. $mysqli->error);
}
$stmt = $mysqli->prepare('INSERT INTO foo (x) VALUES (?)');
if ( false===$stmt ) {
die ('prepare() failed: ' . $mysqli->error);
}
$result = $stmt->bind_param('i', $x);
if ( false===$result ) {
die('bind_param() failed');
}
$x = 1;
$result = $stmt->execute();
if ( false===$result ) {
die('execute() failed: '.$stmt->error);
}
echo "<pre>Debug: end</pre>\n";
I usually do this when I need to debug a prepared sql with parameters.
Example of prepare and execute:
$sql = "SELECT VAL1, VAL2 FROM TABLE(?, '?', '?', '?', '?', ?, '?', '?', '?')";
$prep = ibase_prepare( $sql ) or die("Error");
$query = ibase_execute($prep, $param1, $param2, .....) or $err = true;
^^^^^^^^^^^^^^^^^^^^^^^
The easy way to debug the resulting SQL of the sentence it's:
printf( str_replace('?', '%s', $sql), $param1, $param2, ....);
^^^^^^^^^^^^^^^^^^^^^^
You only need to do one printf, replacing the ? on the prepared SQL string by one %s. printf will interpret all as one string, taking each parameter as placing it on each replaced %s.
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).
I've created a set of classes that extend the default mysqli and mysqli_stmt classes to allow you to view a rendition of the potential query string, which should provide what you're looking for:
https://github.com/noahheck/E_mysqli
This is a (close to) drop-in replacement for you normal mysqli object that returns a custom mysqli_stmt object when you prepare() the query string. After binding your parameters, E_mysqli will allow you to view the resultant query string as a new property of the stmt object:
$mysqli = new E_mysqli($dbHost, $dbUser, $dbPass, $dbName);
$query = "INSERT INTO registration SET name = ?, email = ?";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("ss", $_POST['name'], $_POST['email']);
$stmt->execute();
echo $stmt->fullQuery;
would result in:
INSERT INTO registration SET name = 'John Doe', email = 'john.doe#example.com'
There are some caveats with using this extension (explained in the README at the github project), but for troubleshooting troublesome areas of your application, or transitioning to an object oriented style from procedural, this should provide a level of help for most users.
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.
Agreeing with Pascal MARTIN (+1) so I suggest another technique for debugging: var_dump() or log every variable you're inserting into the statement, that way you should be able to figure out if it is wrong data or logically wrong SQL.
You can use tool like Lottip. The idea is act like MySQL proxy. It parses MySQL packets, extracts query and it's params so you can see prepared statements with it's content.
Related
I am trying out prepared statements, but the below code is not working. I am getting the error:
Fatal error: Call to a member function execute() on a non-object in
/var/www/prepared.php on line 12
<?php
$mysqli = new mysqli("localhost", "root", "root", "test");
if ($mysqli->connect_errno) {
echo "Failed to connect to MySQL: " . $mysqli->connect_error;
}
$stmt = $mysqli->prepare("INSERT INTO users (name, age) VALUES (?,?)");
// insert one row
$stmt->execute(array('one',1));
// insert another row with different values
$stmt->execute(array('two',1));
?>
Also, do I need to use mysqli for prepared statements? Can anyone point me to a complete example on prepared statements from connection to insertion to selection with error handling?
From the mysqli::prepare docs:
The parameter markers must be bound to application variables using mysqli_stmt_bind_param() and/or mysqli_stmt_bind_result() before executing the statement or fetching rows.
bind_param docs.
i.e.:
$name = 'one';
$age = 1;
$stmt = $mysqli->prepare("INSERT INTO users (name, age) VALUES (?,?)");
// bind parameters. I'm guessing 'string' & 'integer', but read documentation.
$stmt->bind_param('si', $name, $age);
// *now* we can execute
$stmt->execute();
Also do I need to use mysqli for prepared statement. Can anyone point me to a complete example on prepared statement from connection to insertion to selection with error handling
You can also use PDO which I much prefer. In fact, it looks like you're confusing PDO and Mysqli in your code example.
$db = new PDO($dsn, $user, $pass);
$stmt = $db->prepare("INSERT INTO users (name, age) VALUES (?,?)");
$stmt->execute(array($name1, $age1));
$stmt->execute(array($name2, $age2));
Unlike with mysqli you don't have to call a separate binding function, although that feature is available if you prefer/want/need to use it.
Another fun thing about PDO is named placeholders which can be much less confusing in complex queries:
$db = new PDO($dsn, $user, $pass);
$stmt = $db->prepare("INSERT INTO users (name, age) VALUES (:name,:age)");
$stmt->execute(array(':name' => $name1, ':age' => $age1));
$stmt->execute(array(':name' => $name2, ':age' => $age2));
Connection
The importance of mysqli connection is often overlooked, being diminished to a single line. Whereas a correct connection code can solve a multitude of problems, from security to usability.
Given your code is the usual procedural PHP, here is a simple mysqli connection code to be included in your scripts:
$host = '127.0.0.1';
$db = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
$mysqli = new mysqli($host, $user, $pass, $db);
$mysqli->set_charset($charset);
} catch (\mysqli_sql_exception $e) {
throw new \mysqli_sql_exception($e->getMessage(), $e->getCode());
}
unset($host, $db, $user, $pass, $charset); // we don't need them anymore
The full explanation can be found in my article How to connect properly using mysqli (as well as many useful hints), but just a small citation to highlight most important parts:
setting the proper character set for the connection will eliminate the whole class of errors, such as weird characters/question marks instead of your data, empty json_encode() output, problems with storing emojis, etc.
setting the proper error reporting mode will eliminate the cryptic error messages like mysqli_fetch_assoc() expects parameter... / Call to a member function bind_param()..., giving you the actual error message from MySQL instead.
security is not a laughing matter, there should be not a chance to leak your database details to the outside
Insertion
Insert query is relatively simple, and it is already covered in the other answer.
All you need is to replace all variables (along with surrounding quotes!) in the query with question marks, then prepare the query, then shove all variables with their types into bind_param() and finally execute the query.
Only a quick tip: MySQL will gladly accept all variables as strings, so don't go nuts finding the correct type for a certain variable, simply using "s" for any.
So basically inserting would be like this
$sql = "INSERT INTO users (name, email, password) VALUES (?,?,?)";
$stmt= $conn->prepare($sql);
$stmt->bind_param("sss", $name, $email, $password_hash);
$stmt->execute();
The same principle should be used for all other query types, such as UPDATE or DELETE.
Selection
Running a select query is almost the same, but with one small trick. For some unknown reason you cannot use familiar fetch functions right off the prepared statement. So you need to get the mysqli_result first, and then you'll be able to use fetch_assoc(), fetch_obj() etc:
$sql = "SELECT * FROM users WHERE id=?"; // SQL with parameters
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $id);
$stmt->execute();
$result = $stmt->get_result(); // get the mysqli result
$user = $result->fetch_assoc(); // fetch data
Tip: there is absolutely no need for the familiar mysqli_num_rows() function. If you think of it, you can always use the data itself, to see whether your query returned any rows:
$user = $result->fetch_assoc();
if ($user) {
// found!
}
the same goes for the multiple rows, thanks to
another tip: there is a handy function fetch_all() that can get you an array of all selected rows in one go. For example, if a query returns multiple rows, you can get them into array by changing the last line to
$users = $result->fetch_all(MYSQLI_ASSOC); // fetch data
Error handling
Error handling is the most important yet somewhat surprising part. Despite what numerous articles and examples say, as a rule, you shouldn't write any error handling code at all. It sounds absolutely crazy but that's exactly how things must be done. Most of time all you need to do is just report the error. And mysqli/PHP already can do it for you, no help required. Therefore, you shouldn't write any code that verifies the query execution result - in case of error mysqli will report it automatically, thanks to the mysqli_report() function call mentioned in the #Connection part. Again, the full explanation of this principle can be found in another article, dedicated to general PHP error reporting.
On a rare occasion when you really need to handle the error, that is to perform some action in case of error instead of just reporting it, then wrap your query(es) in a try..catch.
I am trying out prepared statements, but the below code is not working. I am getting the error:
Fatal error: Call to a member function execute() on a non-object in
/var/www/prepared.php on line 12
<?php
$mysqli = new mysqli("localhost", "root", "root", "test");
if ($mysqli->connect_errno) {
echo "Failed to connect to MySQL: " . $mysqli->connect_error;
}
$stmt = $mysqli->prepare("INSERT INTO users (name, age) VALUES (?,?)");
// insert one row
$stmt->execute(array('one',1));
// insert another row with different values
$stmt->execute(array('two',1));
?>
Also, do I need to use mysqli for prepared statements? Can anyone point me to a complete example on prepared statements from connection to insertion to selection with error handling?
From the mysqli::prepare docs:
The parameter markers must be bound to application variables using mysqli_stmt_bind_param() and/or mysqli_stmt_bind_result() before executing the statement or fetching rows.
bind_param docs.
i.e.:
$name = 'one';
$age = 1;
$stmt = $mysqli->prepare("INSERT INTO users (name, age) VALUES (?,?)");
// bind parameters. I'm guessing 'string' & 'integer', but read documentation.
$stmt->bind_param('si', $name, $age);
// *now* we can execute
$stmt->execute();
Also do I need to use mysqli for prepared statement. Can anyone point me to a complete example on prepared statement from connection to insertion to selection with error handling
You can also use PDO which I much prefer. In fact, it looks like you're confusing PDO and Mysqli in your code example.
$db = new PDO($dsn, $user, $pass);
$stmt = $db->prepare("INSERT INTO users (name, age) VALUES (?,?)");
$stmt->execute(array($name1, $age1));
$stmt->execute(array($name2, $age2));
Unlike with mysqli you don't have to call a separate binding function, although that feature is available if you prefer/want/need to use it.
Another fun thing about PDO is named placeholders which can be much less confusing in complex queries:
$db = new PDO($dsn, $user, $pass);
$stmt = $db->prepare("INSERT INTO users (name, age) VALUES (:name,:age)");
$stmt->execute(array(':name' => $name1, ':age' => $age1));
$stmt->execute(array(':name' => $name2, ':age' => $age2));
Connection
The importance of mysqli connection is often overlooked, being diminished to a single line. Whereas a correct connection code can solve a multitude of problems, from security to usability.
Given your code is the usual procedural PHP, here is a simple mysqli connection code to be included in your scripts:
$host = '127.0.0.1';
$db = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
$mysqli = new mysqli($host, $user, $pass, $db);
$mysqli->set_charset($charset);
} catch (\mysqli_sql_exception $e) {
throw new \mysqli_sql_exception($e->getMessage(), $e->getCode());
}
unset($host, $db, $user, $pass, $charset); // we don't need them anymore
The full explanation can be found in my article How to connect properly using mysqli (as well as many useful hints), but just a small citation to highlight most important parts:
setting the proper character set for the connection will eliminate the whole class of errors, such as weird characters/question marks instead of your data, empty json_encode() output, problems with storing emojis, etc.
setting the proper error reporting mode will eliminate the cryptic error messages like mysqli_fetch_assoc() expects parameter... / Call to a member function bind_param()..., giving you the actual error message from MySQL instead.
security is not a laughing matter, there should be not a chance to leak your database details to the outside
Insertion
Insert query is relatively simple, and it is already covered in the other answer.
All you need is to replace all variables (along with surrounding quotes!) in the query with question marks, then prepare the query, then shove all variables with their types into bind_param() and finally execute the query.
Only a quick tip: MySQL will gladly accept all variables as strings, so don't go nuts finding the correct type for a certain variable, simply using "s" for any.
So basically inserting would be like this
$sql = "INSERT INTO users (name, email, password) VALUES (?,?,?)";
$stmt= $conn->prepare($sql);
$stmt->bind_param("sss", $name, $email, $password_hash);
$stmt->execute();
The same principle should be used for all other query types, such as UPDATE or DELETE.
Selection
Running a select query is almost the same, but with one small trick. For some unknown reason you cannot use familiar fetch functions right off the prepared statement. So you need to get the mysqli_result first, and then you'll be able to use fetch_assoc(), fetch_obj() etc:
$sql = "SELECT * FROM users WHERE id=?"; // SQL with parameters
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $id);
$stmt->execute();
$result = $stmt->get_result(); // get the mysqli result
$user = $result->fetch_assoc(); // fetch data
Tip: there is absolutely no need for the familiar mysqli_num_rows() function. If you think of it, you can always use the data itself, to see whether your query returned any rows:
$user = $result->fetch_assoc();
if ($user) {
// found!
}
the same goes for the multiple rows, thanks to
another tip: there is a handy function fetch_all() that can get you an array of all selected rows in one go. For example, if a query returns multiple rows, you can get them into array by changing the last line to
$users = $result->fetch_all(MYSQLI_ASSOC); // fetch data
Error handling
Error handling is the most important yet somewhat surprising part. Despite what numerous articles and examples say, as a rule, you shouldn't write any error handling code at all. It sounds absolutely crazy but that's exactly how things must be done. Most of time all you need to do is just report the error. And mysqli/PHP already can do it for you, no help required. Therefore, you shouldn't write any code that verifies the query execution result - in case of error mysqli will report it automatically, thanks to the mysqli_report() function call mentioned in the #Connection part. Again, the full explanation of this principle can be found in another article, dedicated to general PHP error reporting.
On a rare occasion when you really need to handle the error, that is to perform some action in case of error instead of just reporting it, then wrap your query(es) in a try..catch.
I am trying out prepared statements, but the below code is not working. I am getting the error:
Fatal error: Call to a member function execute() on a non-object in
/var/www/prepared.php on line 12
<?php
$mysqli = new mysqli("localhost", "root", "root", "test");
if ($mysqli->connect_errno) {
echo "Failed to connect to MySQL: " . $mysqli->connect_error;
}
$stmt = $mysqli->prepare("INSERT INTO users (name, age) VALUES (?,?)");
// insert one row
$stmt->execute(array('one',1));
// insert another row with different values
$stmt->execute(array('two',1));
?>
Also, do I need to use mysqli for prepared statements? Can anyone point me to a complete example on prepared statements from connection to insertion to selection with error handling?
From the mysqli::prepare docs:
The parameter markers must be bound to application variables using mysqli_stmt_bind_param() and/or mysqli_stmt_bind_result() before executing the statement or fetching rows.
bind_param docs.
i.e.:
$name = 'one';
$age = 1;
$stmt = $mysqli->prepare("INSERT INTO users (name, age) VALUES (?,?)");
// bind parameters. I'm guessing 'string' & 'integer', but read documentation.
$stmt->bind_param('si', $name, $age);
// *now* we can execute
$stmt->execute();
Also do I need to use mysqli for prepared statement. Can anyone point me to a complete example on prepared statement from connection to insertion to selection with error handling
You can also use PDO which I much prefer. In fact, it looks like you're confusing PDO and Mysqli in your code example.
$db = new PDO($dsn, $user, $pass);
$stmt = $db->prepare("INSERT INTO users (name, age) VALUES (?,?)");
$stmt->execute(array($name1, $age1));
$stmt->execute(array($name2, $age2));
Unlike with mysqli you don't have to call a separate binding function, although that feature is available if you prefer/want/need to use it.
Another fun thing about PDO is named placeholders which can be much less confusing in complex queries:
$db = new PDO($dsn, $user, $pass);
$stmt = $db->prepare("INSERT INTO users (name, age) VALUES (:name,:age)");
$stmt->execute(array(':name' => $name1, ':age' => $age1));
$stmt->execute(array(':name' => $name2, ':age' => $age2));
Connection
The importance of mysqli connection is often overlooked, being diminished to a single line. Whereas a correct connection code can solve a multitude of problems, from security to usability.
Given your code is the usual procedural PHP, here is a simple mysqli connection code to be included in your scripts:
$host = '127.0.0.1';
$db = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
$mysqli = new mysqli($host, $user, $pass, $db);
$mysqli->set_charset($charset);
} catch (\mysqli_sql_exception $e) {
throw new \mysqli_sql_exception($e->getMessage(), $e->getCode());
}
unset($host, $db, $user, $pass, $charset); // we don't need them anymore
The full explanation can be found in my article How to connect properly using mysqli (as well as many useful hints), but just a small citation to highlight most important parts:
setting the proper character set for the connection will eliminate the whole class of errors, such as weird characters/question marks instead of your data, empty json_encode() output, problems with storing emojis, etc.
setting the proper error reporting mode will eliminate the cryptic error messages like mysqli_fetch_assoc() expects parameter... / Call to a member function bind_param()..., giving you the actual error message from MySQL instead.
security is not a laughing matter, there should be not a chance to leak your database details to the outside
Insertion
Insert query is relatively simple, and it is already covered in the other answer.
All you need is to replace all variables (along with surrounding quotes!) in the query with question marks, then prepare the query, then shove all variables with their types into bind_param() and finally execute the query.
Only a quick tip: MySQL will gladly accept all variables as strings, so don't go nuts finding the correct type for a certain variable, simply using "s" for any.
So basically inserting would be like this
$sql = "INSERT INTO users (name, email, password) VALUES (?,?,?)";
$stmt= $conn->prepare($sql);
$stmt->bind_param("sss", $name, $email, $password_hash);
$stmt->execute();
The same principle should be used for all other query types, such as UPDATE or DELETE.
Selection
Running a select query is almost the same, but with one small trick. For some unknown reason you cannot use familiar fetch functions right off the prepared statement. So you need to get the mysqli_result first, and then you'll be able to use fetch_assoc(), fetch_obj() etc:
$sql = "SELECT * FROM users WHERE id=?"; // SQL with parameters
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $id);
$stmt->execute();
$result = $stmt->get_result(); // get the mysqli result
$user = $result->fetch_assoc(); // fetch data
Tip: there is absolutely no need for the familiar mysqli_num_rows() function. If you think of it, you can always use the data itself, to see whether your query returned any rows:
$user = $result->fetch_assoc();
if ($user) {
// found!
}
the same goes for the multiple rows, thanks to
another tip: there is a handy function fetch_all() that can get you an array of all selected rows in one go. For example, if a query returns multiple rows, you can get them into array by changing the last line to
$users = $result->fetch_all(MYSQLI_ASSOC); // fetch data
Error handling
Error handling is the most important yet somewhat surprising part. Despite what numerous articles and examples say, as a rule, you shouldn't write any error handling code at all. It sounds absolutely crazy but that's exactly how things must be done. Most of time all you need to do is just report the error. And mysqli/PHP already can do it for you, no help required. Therefore, you shouldn't write any code that verifies the query execution result - in case of error mysqli will report it automatically, thanks to the mysqli_report() function call mentioned in the #Connection part. Again, the full explanation of this principle can be found in another article, dedicated to general PHP error reporting.
On a rare occasion when you really need to handle the error, that is to perform some action in case of error instead of just reporting it, then wrap your query(es) in a try..catch.
I am trying out prepared statements, but the below code is not working. I am getting the error:
Fatal error: Call to a member function execute() on a non-object in
/var/www/prepared.php on line 12
<?php
$mysqli = new mysqli("localhost", "root", "root", "test");
if ($mysqli->connect_errno) {
echo "Failed to connect to MySQL: " . $mysqli->connect_error;
}
$stmt = $mysqli->prepare("INSERT INTO users (name, age) VALUES (?,?)");
// insert one row
$stmt->execute(array('one',1));
// insert another row with different values
$stmt->execute(array('two',1));
?>
Also, do I need to use mysqli for prepared statements? Can anyone point me to a complete example on prepared statements from connection to insertion to selection with error handling?
From the mysqli::prepare docs:
The parameter markers must be bound to application variables using mysqli_stmt_bind_param() and/or mysqli_stmt_bind_result() before executing the statement or fetching rows.
bind_param docs.
i.e.:
$name = 'one';
$age = 1;
$stmt = $mysqli->prepare("INSERT INTO users (name, age) VALUES (?,?)");
// bind parameters. I'm guessing 'string' & 'integer', but read documentation.
$stmt->bind_param('si', $name, $age);
// *now* we can execute
$stmt->execute();
Also do I need to use mysqli for prepared statement. Can anyone point me to a complete example on prepared statement from connection to insertion to selection with error handling
You can also use PDO which I much prefer. In fact, it looks like you're confusing PDO and Mysqli in your code example.
$db = new PDO($dsn, $user, $pass);
$stmt = $db->prepare("INSERT INTO users (name, age) VALUES (?,?)");
$stmt->execute(array($name1, $age1));
$stmt->execute(array($name2, $age2));
Unlike with mysqli you don't have to call a separate binding function, although that feature is available if you prefer/want/need to use it.
Another fun thing about PDO is named placeholders which can be much less confusing in complex queries:
$db = new PDO($dsn, $user, $pass);
$stmt = $db->prepare("INSERT INTO users (name, age) VALUES (:name,:age)");
$stmt->execute(array(':name' => $name1, ':age' => $age1));
$stmt->execute(array(':name' => $name2, ':age' => $age2));
Connection
The importance of mysqli connection is often overlooked, being diminished to a single line. Whereas a correct connection code can solve a multitude of problems, from security to usability.
Given your code is the usual procedural PHP, here is a simple mysqli connection code to be included in your scripts:
$host = '127.0.0.1';
$db = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
$mysqli = new mysqli($host, $user, $pass, $db);
$mysqli->set_charset($charset);
} catch (\mysqli_sql_exception $e) {
throw new \mysqli_sql_exception($e->getMessage(), $e->getCode());
}
unset($host, $db, $user, $pass, $charset); // we don't need them anymore
The full explanation can be found in my article How to connect properly using mysqli (as well as many useful hints), but just a small citation to highlight most important parts:
setting the proper character set for the connection will eliminate the whole class of errors, such as weird characters/question marks instead of your data, empty json_encode() output, problems with storing emojis, etc.
setting the proper error reporting mode will eliminate the cryptic error messages like mysqli_fetch_assoc() expects parameter... / Call to a member function bind_param()..., giving you the actual error message from MySQL instead.
security is not a laughing matter, there should be not a chance to leak your database details to the outside
Insertion
Insert query is relatively simple, and it is already covered in the other answer.
All you need is to replace all variables (along with surrounding quotes!) in the query with question marks, then prepare the query, then shove all variables with their types into bind_param() and finally execute the query.
Only a quick tip: MySQL will gladly accept all variables as strings, so don't go nuts finding the correct type for a certain variable, simply using "s" for any.
So basically inserting would be like this
$sql = "INSERT INTO users (name, email, password) VALUES (?,?,?)";
$stmt= $conn->prepare($sql);
$stmt->bind_param("sss", $name, $email, $password_hash);
$stmt->execute();
The same principle should be used for all other query types, such as UPDATE or DELETE.
Selection
Running a select query is almost the same, but with one small trick. For some unknown reason you cannot use familiar fetch functions right off the prepared statement. So you need to get the mysqli_result first, and then you'll be able to use fetch_assoc(), fetch_obj() etc:
$sql = "SELECT * FROM users WHERE id=?"; // SQL with parameters
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $id);
$stmt->execute();
$result = $stmt->get_result(); // get the mysqli result
$user = $result->fetch_assoc(); // fetch data
Tip: there is absolutely no need for the familiar mysqli_num_rows() function. If you think of it, you can always use the data itself, to see whether your query returned any rows:
$user = $result->fetch_assoc();
if ($user) {
// found!
}
the same goes for the multiple rows, thanks to
another tip: there is a handy function fetch_all() that can get you an array of all selected rows in one go. For example, if a query returns multiple rows, you can get them into array by changing the last line to
$users = $result->fetch_all(MYSQLI_ASSOC); // fetch data
Error handling
Error handling is the most important yet somewhat surprising part. Despite what numerous articles and examples say, as a rule, you shouldn't write any error handling code at all. It sounds absolutely crazy but that's exactly how things must be done. Most of time all you need to do is just report the error. And mysqli/PHP already can do it for you, no help required. Therefore, you shouldn't write any code that verifies the query execution result - in case of error mysqli will report it automatically, thanks to the mysqli_report() function call mentioned in the #Connection part. Again, the full explanation of this principle can be found in another article, dedicated to general PHP error reporting.
On a rare occasion when you really need to handle the error, that is to perform some action in case of error instead of just reporting it, then wrap your query(es) in a try..catch.
I am trying out prepared statements, but the below code is not working. I am getting the error:
Fatal error: Call to a member function execute() on a non-object in
/var/www/prepared.php on line 12
<?php
$mysqli = new mysqli("localhost", "root", "root", "test");
if ($mysqli->connect_errno) {
echo "Failed to connect to MySQL: " . $mysqli->connect_error;
}
$stmt = $mysqli->prepare("INSERT INTO users (name, age) VALUES (?,?)");
// insert one row
$stmt->execute(array('one',1));
// insert another row with different values
$stmt->execute(array('two',1));
?>
Also, do I need to use mysqli for prepared statements? Can anyone point me to a complete example on prepared statements from connection to insertion to selection with error handling?
From the mysqli::prepare docs:
The parameter markers must be bound to application variables using mysqli_stmt_bind_param() and/or mysqli_stmt_bind_result() before executing the statement or fetching rows.
bind_param docs.
i.e.:
$name = 'one';
$age = 1;
$stmt = $mysqli->prepare("INSERT INTO users (name, age) VALUES (?,?)");
// bind parameters. I'm guessing 'string' & 'integer', but read documentation.
$stmt->bind_param('si', $name, $age);
// *now* we can execute
$stmt->execute();
Also do I need to use mysqli for prepared statement. Can anyone point me to a complete example on prepared statement from connection to insertion to selection with error handling
You can also use PDO which I much prefer. In fact, it looks like you're confusing PDO and Mysqli in your code example.
$db = new PDO($dsn, $user, $pass);
$stmt = $db->prepare("INSERT INTO users (name, age) VALUES (?,?)");
$stmt->execute(array($name1, $age1));
$stmt->execute(array($name2, $age2));
Unlike with mysqli you don't have to call a separate binding function, although that feature is available if you prefer/want/need to use it.
Another fun thing about PDO is named placeholders which can be much less confusing in complex queries:
$db = new PDO($dsn, $user, $pass);
$stmt = $db->prepare("INSERT INTO users (name, age) VALUES (:name,:age)");
$stmt->execute(array(':name' => $name1, ':age' => $age1));
$stmt->execute(array(':name' => $name2, ':age' => $age2));
Connection
The importance of mysqli connection is often overlooked, being diminished to a single line. Whereas a correct connection code can solve a multitude of problems, from security to usability.
Given your code is the usual procedural PHP, here is a simple mysqli connection code to be included in your scripts:
$host = '127.0.0.1';
$db = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
$mysqli = new mysqli($host, $user, $pass, $db);
$mysqli->set_charset($charset);
} catch (\mysqli_sql_exception $e) {
throw new \mysqli_sql_exception($e->getMessage(), $e->getCode());
}
unset($host, $db, $user, $pass, $charset); // we don't need them anymore
The full explanation can be found in my article How to connect properly using mysqli (as well as many useful hints), but just a small citation to highlight most important parts:
setting the proper character set for the connection will eliminate the whole class of errors, such as weird characters/question marks instead of your data, empty json_encode() output, problems with storing emojis, etc.
setting the proper error reporting mode will eliminate the cryptic error messages like mysqli_fetch_assoc() expects parameter... / Call to a member function bind_param()..., giving you the actual error message from MySQL instead.
security is not a laughing matter, there should be not a chance to leak your database details to the outside
Insertion
Insert query is relatively simple, and it is already covered in the other answer.
All you need is to replace all variables (along with surrounding quotes!) in the query with question marks, then prepare the query, then shove all variables with their types into bind_param() and finally execute the query.
Only a quick tip: MySQL will gladly accept all variables as strings, so don't go nuts finding the correct type for a certain variable, simply using "s" for any.
So basically inserting would be like this
$sql = "INSERT INTO users (name, email, password) VALUES (?,?,?)";
$stmt= $conn->prepare($sql);
$stmt->bind_param("sss", $name, $email, $password_hash);
$stmt->execute();
The same principle should be used for all other query types, such as UPDATE or DELETE.
Selection
Running a select query is almost the same, but with one small trick. For some unknown reason you cannot use familiar fetch functions right off the prepared statement. So you need to get the mysqli_result first, and then you'll be able to use fetch_assoc(), fetch_obj() etc:
$sql = "SELECT * FROM users WHERE id=?"; // SQL with parameters
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $id);
$stmt->execute();
$result = $stmt->get_result(); // get the mysqli result
$user = $result->fetch_assoc(); // fetch data
Tip: there is absolutely no need for the familiar mysqli_num_rows() function. If you think of it, you can always use the data itself, to see whether your query returned any rows:
$user = $result->fetch_assoc();
if ($user) {
// found!
}
the same goes for the multiple rows, thanks to
another tip: there is a handy function fetch_all() that can get you an array of all selected rows in one go. For example, if a query returns multiple rows, you can get them into array by changing the last line to
$users = $result->fetch_all(MYSQLI_ASSOC); // fetch data
Error handling
Error handling is the most important yet somewhat surprising part. Despite what numerous articles and examples say, as a rule, you shouldn't write any error handling code at all. It sounds absolutely crazy but that's exactly how things must be done. Most of time all you need to do is just report the error. And mysqli/PHP already can do it for you, no help required. Therefore, you shouldn't write any code that verifies the query execution result - in case of error mysqli will report it automatically, thanks to the mysqli_report() function call mentioned in the #Connection part. Again, the full explanation of this principle can be found in another article, dedicated to general PHP error reporting.
On a rare occasion when you really need to handle the error, that is to perform some action in case of error instead of just reporting it, then wrap your query(es) in a try..catch.