How to use mysqli prepared statements?

17,040

Solution 1

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();

Solution 2

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));

Solution 3

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.

Share:
17,040
aWebDeveloper
Author by

aWebDeveloper

A passionate Web Developer

Updated on June 30, 2022

Comments

  • aWebDeveloper
    aWebDeveloper almost 2 years

    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?