Skip to content

Fix: ensure db state is reset on exception by adding try-finally blocks to core methods - #1058

Open
ZerfyT wants to merge 1 commit into
ThingEngineer:masterfrom
ZerfyT:master
Open

Fix: ensure db state is reset on exception by adding try-finally blocks to core methods#1058
ZerfyT wants to merge 1 commit into
ThingEngineer:masterfrom
ZerfyT:master

Conversation

@ZerfyT

@ZerfyT ZerfyT commented Sep 10, 2026

Copy link
Copy Markdown

DB state isn't reset when catching mysqli_sql_exception (PHP 8.1+)

Since PHP 8.1 now throws mysqli_sql_exception by default for db errors, if a native mysqli method throws an error, the exception bubbles up and completely skips the $this->reset() cleanup call inside MysqliDb.
Because of this, if we wrap a query in a try-catch block to handle an error in our app, the DB instance gets stuck in a "dirty" state. Internal variables like _bindParams or _queryOptions from the failed query are never cleared out.

Then, when we try to run our next (perfectly valid) query on the same instance, it accidentally reuses those leftover variables. This leads to confusing fatal errors like ArgumentCountError because the placeholders and parameters don't match up anymore.

Steps to reproduce

Just make sure you're on PHP 8.1+ where mysqli exceptions are on by default.

require_once('MysqliDb.php');
$db = new MysqliDb('localhost', 'root', 'root', 'my_database');

try {
    // 1. Run a query that we know will fail (like passing a bad column name or bad data type)
    $db->insert('test_table', [
        'invalid_column' => 'test' 
    ]);
} catch (Exception $e) {
    // 2. Catch it so our app doesn't crash
    echo "Caught exception: " . $e->getMessage() . "\n";
}

// 3. Try to run a completely normal query right after
// This crashes with a fatal ArgumentCountError because the old `_bindParams` are still hanging around.
$results = $db->get('test_table');

What actually happens

The second query dies with a fatal error:

Fatal error: Uncaught ArgumentCountError: The number of variables must match the number of parameters in the prepared statement...

Solution

A simple fix is to wrap the main execution blocks (in methods like get, update, delete, rawQuery, _buildInsert, lock, unlock) inside a try-finally. If an exception hits, we just force a $this->reset() before throwing it back up.

Something like this:

try {
    // ... normal execution code ...
    $res = $this->_dynamicBindResults($stmt);
    return $res;
} finally {
    // Clean up the dirty state before bubbling the exception up
    $this->reset();
    throw $e;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant