The solution to this problem is to save the value of the property $p_stmt->insert_id
in a variable and bind that variable instead. You have to do the same for trim()
and date()
anyway, albeit for different reasons.
$insert_id = $p_stmt->insert_id;
$page_content = trim($_POST["page_content"]);
$version_notes = trim($_POST["version_notes"]);
$date = date("Y-m-d H:i:s");
$pv_stmt->bind_param('issss', $insert_id, $page_title, $page_content, $version_notes, $date);
As of PHP 8.1, you can also bind by value when you pass all values as an array to execute()
. No temporary variables are needed.
$pv_stmt = $mysqli->prepare("INSERT INTO ww_page_versions (page_id, page_title, page_content, version_notes, version_timestamp) VALUES (?, ?, ?, ?, ?)");
$pv_stmt->execute([$p_stmt->insert_id, $page_title, trim($_POST["page_content"]), trim($_POST["version_notes"]), date("Y-m-d H:i:s")]);
Explanation of why binding insert_id
doesn't work
The reason why binding mysqli_stmt::insert_id
results in a value NULL
becomes a little bit more clear on PHP 8.1. Since this version, PHP has added property types to most of built-in classes, even for properties that aren't true properties but are __get()
calls instead. With PHP 8.1 you get the following error:
Fatal error: Uncaught Error: Cannot access uninitialized non-nullable property mysqli_stmt::$insert_id by reference
PHP claims that the property is uninitialized and you can't have a reference to an uninitialized property. You can mimick the same behaviour with this code:
class A {
public int $insert_id;
}
$a = new A();
$stmt = $mysqli->prepare('SELECT ?');
$stmt->bind_param('s', $a->insert_id);
$stmt->execute();
For the same reason, assignment by reference to a variable would not work:
$foo =& $p_stmt->insert_id;
But you might ask, how can the property be uninitialized when you can read its value without any issue. The answer is because internally these properties are implemented using function calls in PHP, similar to __get()
magic method. The value is read from the internal memory of mysqlnd, not from the property itself. Properties of mysqli
are just a facade to the underlying client library mysqlnd.