mysqli_free_result () VS mysqli_stmt_free_result() differences and usage? - php

What are the differences and specific use cases of these two functions? Here's what I found:
mysqli_free_result — Frees the memory associated with a result
mysqli_stmt_free_result — Frees stored result memory for the given statement handle
An example will be nice.

These are two different functions, but you don't need to use any of them.
mysqli_free_result
mysqli_free_result() is the procedural equivalent of $result->free(). All it does is it unsets the results from the variable. The object still exists, but becomes unusable.
$id = 1;
$stmt = $mysqli->prepare('SELECT * FROM student_detail WHERE ID=?');
$stmt->bind_param('s', $id);
$stmt->execute();
$result = $stmt->get_result();
$result->free();
var_dump($result->fetch_assoc()); // error
It also has 2 aliases. These 4 are all the same thing:
$result->free();
$result->close();
$result->free_result();
mysqli_free_result($result);
mysqli_stmt_free_result
Does the same thing, but with the statement itself. It is the equivalent of $stmt->free_result()
$id = 1;
$stmt = $mysqli->prepare('SELECT * FROM student_detail WHERE ID=?');
$stmt->bind_param('s', $id);
$stmt->execute();
$result = $stmt->store_result();
$stmt->free_result();
var_dump($stmt->fetch()); // false
You can assume that if you ever find yourself needing to use any of them, then it means you have probably done something wrong.
For example, some people claim it saves memory for your server when you free the result once you are done with it. In reality you should encapsulate your database queries in such way that the mysqli_result lives only as long as you need it. Take a look at one method I have written a while ago:
public function safeQuery(string $sql, array $params = []): ?array {
$stmt = $this->prepare($sql);
if ($params) {
$stmt->bind_param(str_repeat("s", count($params)), ...$params);
}
$stmt->execute();
if ($result = $stmt->get_result()) {
return $result->fetch_all(MYSQLI_BOTH);
}
return null;
}
It executes prepared statement, gets the result, fetches the records and the mysqli_result is alive as long as it is needed and not a moment more.

They're equivalent, it depends on how you created the results in the first place.
If you use a prepared statement and then use $stmt->store_result(), you would free the results using $stmt->free_result().
If you perform an ordinary query like $result = $conn->query(...), you would free it using $result->free_result().

Related

Will overriding the variable holding a prepared statement close the statement?

Some people recommends calling close() on a prepared statement when I am done with the result of a query.
I often reuse the same variable name for the prepared statement, so I was wondering if overriding the variable automatically calls close() on the prepared statement ?
Example of how I currently do it (The SQL is made up for the examples):
// Fetch the requested user
$stmt = $mysqli->prepare("SELECT * FROM user WHERE id = ?");
$stmt->bind_param("i", $_GET['userid']);
$stmt->execute();
$user = $stmt->get_result()->fetch_assoc();
// Fetch all posts associated with the user
$stmt = $mysqli->prepare("SELECT * FROM posts WHERE user_id = ?");
$stmt->bind_param("i", $user['id']);
$stmt->execute();
$result = $stmt->get_result();
$posts = array();
while ($row = $result->fetch_assoc()) {
$posts[] = $row;
}
Should I call $stmt->close(); between fetching the user and fetching the posts or is it done when I override $stmt by calling $stmt = $mysqli->prepare(...); ?
Yes, most of the time, because PHP will try to clean up objects which have no reference as soon as possible. That means that once you overwrite it, there will be no more references to the old object and PHP will clean it up. Part of cleaning up mysqli_stmt object involves closing the statement.
But the reason why some people recommend calling $stmt->close() explicitely is to avoid errors such as this:
mysqli_sql_exception: Commands out of sync; you can't run this command now in ...
This error happens when you have not fetched all results from MySQL and you try to create a new statement by calling prepare or query. MySQL will not let you execute anything else until you fetch all remaining rows. This is usually achieved with get_result() or store_result(). If you always fetch the results in their entirety then you really don't need to worry much about when exactly the statement gets closed. Let PHP take care of it for you.
The best course of action is to avoid using mysqli functions directly. You should write some simple function which will encapsulate all the mysqli functionality so that you never have to worry about this low-level stuff. A sample function could look like this:
function safeQuery(\mysqli $db, string $sql, array $params = []): ?array {
$stmt = $db->prepare($sql);
if ($params) {
$stmt->bind_param(str_repeat("s", count($params)), ...$params);
}
$stmt->execute();
if ($result = $stmt->get_result()) {
return $result->fetch_all(MYSQLI_BOTH);
}
return null;
}
$result = safeQuery($mysqli, 'SELECT * FROM user WHERE id = ?', [$_GET['userid']]);
if ($result) {
$user = $result[0];
$posts = safeQuery($mysqli, 'SELECT * FROM posts WHERE user_id = ?', [$user['id']]);
foreach ($posts as $post) {
}
}

num_rows: get_result vs store_result

The following code returns 0, even though there are 5 entries in the table categories with cat = 1.
$sql = "SELECT name FROM categories WHERE cat = ?";
$stmt = $db->prepare($sql);
$cat = 1;
$stmt->bind_param("i", $cat);
$stmt->execute();
$stmt->get_result();
echo $stmt->num_rows;
However, when I change $stmt->get_result(); to $stmt->store_result(); the output is 5. Why does get_result() not work here?
I found for instance on this answer: https://stackoverflow.com/a/8722329/2311074 that get_result() should also work.
Looks like the other answer is wrong and get_result doesn't change the state of a statement (which is quite logical, as you are asking for mysqli_result and therefore supposedly going to work with it from now on).
You see, the way you are using get_result is quite pointless. To make any point of it, you have to assign the result to a variable, which will give you the desired outcome:
$res = $stmt->get_result();
echo $res->num_rows;
Note that the num_rows property is quite useless in general. If you want to know whether your query returned any data or not, just collect the rows into array and then you use this array for the purpose.
$data = $stmt->get_result()->fetch_all();
if ($data) {
// whatever
} else {
// oops!
}
And of course you shouldn't use such a query only to count the goods available in a category. For such a purpose a count(*) query have to be used.

PDO can I reuse the same Statement Handle for multiple queries?

I am using PDO to access my data base and am looping using two while loops with fetch at the same time, seen below:
$DBH = new PDO('mysql:host=localhost;dbname=database;charset=utf8',$dblogin,$dbpass);
$sql = 'SELECT * FROM table';
$STH = $DBH->prepare($sql);
$STH->execute();
while ($bm_table = $STH->fetch(PDO::FETCH_ASSOC))
{
// SQL Query
$sql1 = 'QUERY HERE';
$STH1 = $DBH->prepare($sql1);
$STH1->execute();
// Loops through using different handle, but what if I used STH again?
while ($row = $STH1->fetch(PDO::FETCH_ASSOC))
{
SomeFunction($bm_table,$row);
}
}
As you can see above I am using a different statement handle ($STH, $STH1 etc.) Is this necessary? Or can I use just one statement handle for everything. The reason I have used multiple handles is as the $bm_table value that uses $STH, will still be in use while I am fetching $row wouldn't that change the value of $bm_table or stop the fetch from working? How does the handles with PDO work? Especially when in this case I have two simultaneous fetch loops running at the same time using the same PDO connection.
So the main thing I am looking for here is if I have two statements that are running simultaneously is it important that I use different handles when I continue to use the same connection?
$STH and STH1 are not statement handles, they're just PHP variables. You can reassign a variable if you no longer need its old value. But in the case of this code, you still need the old value.
If you assign $STH inside the outer loop to the handle returned by the second prepare() call, then when it gets back to the top of the loop and re-executes the $STH->fetch() test, it will try to fetch from the second query, not the first one. This will immediately end the outer loop because all those rows have been read.
You can reuse a statement handle for repetitions of the same query. This is very useful when the query has parameters:
$stmt = $DBH->prepare("SELECT * FROM tablename WHERE id = :id");
$stmt->bindParam(':id', $id);
foreach ($id_array as $id) {
$stmt->execute();
$row = $stmt->fetch();
// do stuff with $row
}
If I understand you correctly what you want is dynamic query?... just put a parameter on your method then...
something like this. call it as much as you want with difference parameters though.
Class SampleClass{
public function GetAll($tablename)
{
$sth = $this->prepare("SELECT * FROM $tablename");
$sth->execute();
return $sth->fetchAll();
}
}

MySQLI Prepared Statement: num_rows & fetch_assoc

Below is some poorly written and heavily misunderstood PHP code with no error checking. To be honest, I'm struggling a little getting my head around the maze of PHP->MySQLi functions! Could someone please provide an example of how one would use prepared statements to collect results in an associative array whilst also getting a row count from $stmt? The code below is what I'm playing around with. I think the bit that's throwing me off is using $stmt values after store_result and then trying to collect an assoc array, and I'm not too sure why...
$mysqli = mysqli_connect($config['host'], $config['user'], $config['pass'], $config['db']);
$stmt = $mysqli->prepare("SELECT * FROM licences WHERE generated = ?");
$stmt->bind_param('i', $core['id']);
$result = $stmt->execute();
$stmt->store_result();
if ($stmt->num_rows >= "1") {
while($data = $result->fetch_assoc()){
//Loop through results here $data[]
}
}else{
echo "0 records found";
}
I feel a little cheeky just asking for code, but its a working demonstration of my circumstances that I feel I need to finally understand what's actually going on. Thanks a million!
I searched for a long time but never found documentation needed to respond correctly, but I did my research.
$stmt->get_result() replace $stmt->store_result() for this purpose.
So, If we see
$stmt_result = $stmt->get_result();
var_dump($stmt_result);
we get
object(mysqli_result)[3]
public 'current_field' => int 0
public 'field_count' => int 10
public 'lengths' => null
public 'num_rows' => int 8 #That we need!
public 'type' => int 0
Therefore I propose the following generic solution. (I include the bug report I use)
#Prepare stmt or reports errors
($stmt = $mysqli->prepare($query)) or trigger_error($mysqli->error, E_USER_ERROR);
#Execute stmt or reports errors
$stmt->execute() or trigger_error($stmt->error, E_USER_ERROR);
#Save data or reports errors
($stmt_result = $stmt->get_result()) or trigger_error($stmt->error, E_USER_ERROR);
#Check if are rows in query
if ($stmt_result->num_rows>0) {
# Save in $row_data[] all columns of query
while($row_data = $stmt_result->fetch_assoc()) {
# Action to do
echo $row_data['my_db_column_name_or_ALIAS'];
}
} else {
# No data actions
echo 'No data here :(';
}
$stmt->close();
$result = $stmt->execute(); /* function returns a bool value */
reference : http://php.net/manual/en/mysqli-stmt.execute.php
so its just sufficient to write $stmt->execute(); for the query execution.
The basic idea is to follow the following sequence :
1. make a connection. (now while using sqli or PDO method you make connection and connect with database in a single step)
2. prepare the query template
3. bind the the parameters with the variable
4. (set the values for the variable if not set or if you wish to change the values) and then Execute your query.
5. Now fetch your data and do your work.
6. Close the connection.
/*STEP 1*/
$mysqli = mysqli_connect($servername,$usrname,$pswd,$dbname);
/*STEP 2*/
$stmt = $mysqli->prepare("SELECT * FROM licences WHERE generated = ?");
/*Prepares the SQL query, and returns a statement handle to be used for further operations on the statement.*/
//mysqli_prepare() returns a statement object(of class mysqli_stmt) or FALSE if an error occurred.
/* STEP 3*/
$stmt->bind_param('i', $core['id']);//Binds variables to a prepared statement as parameters
/* STEP 4*/
$result = $stmt->execute();//Executes a prepared Query
/* IF you wish to count the no. of rows only then you will require the following 2 lines */
$stmt->store_result();//Transfers a result set from a prepared statement
$count=$stmt->num_rows;
/*STEP 5*/
//The best way is to bind result, its easy and sleek
while($data = $stmt->fetch()) //use fetch() fetch_assoc() is not a member of mysqli_stmt class
{ //DO what you wish
//$data is an array, one can access the contents like $data['attributeName']
}
One must call mysqli_stmt_store_result() for (SELECT, SHOW, DESCRIBE, EXPLAIN), if one wants to buffer the complete result set by the client, so that the subsequent mysqli_stmt_fetch() call returns buffered data.
It is unnecessary to call mysqli_stmt_store_result() for other queries, but if you do, it will not harm or cause any notable performance in all cases.
--reference: php.net/manual/en/mysqli-stmt.store-result.php
and http://www.w3schools.com/php/php_mysql_prepared_statements.asp
One must look up the above reference who are facing issue regarding this,
My answer may not be perfect, people are welcome to improve my answer...
If you would like to collect mysqli results into an associative array in PHP you can use fetch_all() method. Of course before you try to fetch the rows, you need to get the result with get_result(). execute() does not return any useful values.
For example:
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli($config['host'], $config['user'], $config['pass'], $config['db']);
$mysqli->set_charset('utf8mb4'); // Don't forget to set the charset!
$stmt = $mysqli->prepare("SELECT * FROM licences WHERE generated = ?");
$stmt->bind_param('i', $core['id']);
$stmt->execute(); // This doesn't return any useful value
$result = $stmt->get_result();
$data = $result->fetch_all(MYSQLI_ASSOC);
if ($data) {
foreach ($data as $row) {
//Loop through results here
}
} else {
echo "0 records found";
}
I am not sure why would you need num_rows, you can always use the array itself to check if there are any rows. An empty array is false-ish in PHP.
Your problem here is that to do a fetch->assoc(), you need to get first a result set from a prepared statement using:
http://php.net/manual/en/mysqli-stmt.get-result.php
And guess what: this function only works if you are using MySQL native driver, or "mysqlnd". If you are not using it, you'll get the "Fatal error" message.
You can try this using the mysqli_stmt function get_result() which you can use to fetch an associated array. Note get_result returns an object of type mysqli_result.
$stmt->execute();
$result = $stmt->get_result(); //$result is of type mysqli_result
$num_rows = $result->num_rows; //count number of rows in the result
// the '=' in the if statement is intentional, it will return true on success or false if it fails.
if ($result_array = $result->fetch_assoc(MYSQLI_ASSOC)) {
//loop through the result_array fetching rows.
// $ rows is an array populated with all the rows with an associative array with column names as the key
for($j=0;$j<$num_rows;$j++)
$rows[$j]=$result->fetch_row();
var_dump($rows);
}
else{
echo 'Failed to retrieve rows';
}

Is it possible to use mysqli_fetch_object with a prepared statement

All the examples I see using mysqli_fetch_object use mysql_query(), I cannot get it to work with prepared statements. Does anyone know what is wrong with this code snippet, as fetch_object returns null.
$sql = "select 1 from dual";
printf("preparing %s\n", $sql);
$stmt = $link->prepare($sql);
printf("prepare statement %s\n", is_null($stmt) ? "is null" : "created");
$rc = $stmt->execute();
printf("num rows is %d\n", $stmt->num_rows);
$result = $stmt->result_metadata();
printf("result_metadata %s\n", is_null($result) ? "is null" : "exists");
$rc = $result->fetch_object();
printf("fetch object returns %s\n", is_null($rc) ? "NULL" : $rc);
$stmt->close();
The output is:
preparing select 1 from dual
prepare statement created
num rows is 0
result_metadata exists
fetch object returns NULL
This is the code I use to create an object from a prepared statement.
It could perhaps be used in a subclass of mysqli?
$query = "SELECT * FROM category WHERE id = ?";
$stmt = $this->_db->prepare($query);
$value = 1;
$stmt->bind_param("i", $value);
$stmt->execute();
// bind results to named array
$meta = $stmt->result_metadata();
$fields = $meta->fetch_fields();
foreach($fields as $field) {
$result[$field->name] = "";
$resultArray[$field->name] = &$result[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $resultArray);
// create object of results and array of objects
while($stmt->fetch()) {
$resultObject = new stdClass();
foreach ($resultArray as $key => $value) {
$resultObject->$key = $value;
}
$rows[] = $resultObject;
}
$stmt->close();
MySql Native Driver extension (mysqlnd), has the get_result method:
$stmt->execute();
$obj = $stmt->get_result()->fetch_object();
I don't believe the interface works like that.
Going by the documentation and examples (http://www.php.net/manual/en/mysqli.prepare.php) it seems that $stmt->execute() does not return a resultset, but a boolean indicating success / failure (http://www.php.net/manual/en/mysqli-stmt.execute.php). To actually get the result, you need to bind variables to the resultset (aftere the execute call) using $stmt->bind_result (http://www.php.net/manual/en/mysqli-stmt.bind-result.php).
After you did all that, you can do repeated calls to $stmt->fetch() () to fill the bound variables with the column values from the current row. I don't see any mention of $stmt->fetch_object() nor do I see how that interface could work with a variable binding scheme like described.
So this is the story for "normal" result fetching from mysqli prepared statments.
In your code, there is something that I suspect is an error, or at least I am not sure you intended to do this.
You line:
$result = $stmt->result_metadata();
assignes the resultset metadata, which is itself represented as a resultset, to the $result variable. According to the doc (http://www.php.net/manual/en/mysqli-stmt.result-metadata.php) you can only use a subset of the methods on these 'special' kinds of resultsets, and fetch_object() is not one of them (at least it is not explicitly listed).
Perhaps it is a bug that fetch_object() is not implemented for these metadata resultsets, perhaps you should file a bug at bugs.mysql.com about that.

Categories