views:

257

answers:

4

I created a simple MySQLi class for a project I'm working on to make it easier and more streamlined to pull from and push to the DB. (also to get more familiar with OOP in PHP)

A problem I keep running into is in an effort to make it as efficient as possible. I try to close / free every query / statement / result set. In doing so I get the following error a lot:

Warning: mysqli_result::close(): Couldn't fetch mysqli_result

I think I get the above specifically because it's trying to close the query twice in a row.

Currently my class can make prepared and unprepared statements. So I try to close queries / statements in 2 places. I check when preparing a statement if I have already prepared a statement, if so I need to close the old one before I make a new one and lastly in the destructor of the class.

I realize I could just close the query / statement after I've pulled and stored the result but this seems to be messy for a few reasons. For one it removes the possibility of reusing the prepared statement. As well as disallowing me to pull some information about the query after it's been run such as affected rows and the like.

I know I could just store this info for every query and what not but it just seems like the proper way to go about this problem would be to close / free the query / statement if I need to make another one, and again at the end of the script.

I have tried looking around and reading up on how I should properly handle this task but I have been unable to come up with anything.

So my question is, is there anyway to test if a query or statement needs to be closed or freed? Or is there a better way I should be attempting to tackle this issue?

Thanks for any help or insight anyone can offer.

+1  A: 

I think that if there are no rows left in the result set, it should be closed automatically. So, if you iterate through the whole result set, there's no need to call mysqli_result. However, you should close it if you fetch, let's say, only the first row from a result set containing about 100 rows.

Ignas R
Hrm this seems to be somewhat true. I was messing around with some test code doing a `var_dump` after execute and the another after fetching. They print the same thing. When doing a `var_dump` after a close though you get `Property access is not allowed yet` so it's obviously doing something different. Though if it's any different efficiency wise is the question?Also when I limit a query to 1 row (e.g. `LIMIT 0, 1`) calling $stmt->fetch() on it's own doesn't close the statement. Though if I do a `while` loop and `echo` to confirm there's only one row it does close it.Any ideas?
anomareh
A: 

You could set it to null after you close it, and check for null in the destructor before closing it there.

Obviously, you'd only want to null a prepared statement when you're sure you're done using it.

mmsmatt
Is that the same as closing a statement? I really have no problems leaving things be so to say. I'm not hell bent on making sure it's closed for no reason. I was just under the impression that closing a statement and freeing result sets was the proper way to handle things efficiency wise. I know for small end things it's probably not the biggest deal though it matters if your project sees a lot of traffic and expands. If you were working under such conditions were every bit of efficiency mattered what would be the proper thing to do?
anomareh
A: 

You could use RAII or a factory class+reference counting to manage the statements. That way you don't need to explicitly check when it's time to close anything. Without seeing the details of how you're using your wrapper, it's hard to go into detail. Objective-C's accessor pattern may also be of use, which would look something like:

private function unsetStatement() {
    if (isset($this->_stmt)) {
        $this->_stmt->close();
        unset($this->_stmt);
    }
}
function getStatement() {
    return $this->_stmt;
}
function setStatement($stmt) {
    $this->unsetStatement();
    $this->_stmt = $stmt;
}

You'd only ever access the statement member field through the above functions, even within the class. Actually, it looks more like:

-(Statement *)statement {
    return stmt_;
}
-(void)setStatement:(Statement *)stmt {
    [stmt_ release];
    stmt = [stmt retain];
}
outis
A: 

This is what I use. If there are any results in the statement, I know I need to free the result:

if($stmt->num_rows() != 0)
{
    $stmt->free_result();

    return FALSE;
}
Justin