I have a class like this:
class Session {
private Session_Database $db;
public function __construct(){
// Instantiate new Database object
$this->db = new Session_Database();
// Set handler to overide SESSION
session_set_save_handler(
[$this, '_open'],
[$this, '_close'],
[$this, '_read'],
[$this, '_write'],
[$this, '_destroy'],
[$this, '_gc']
);
// Start the session
session_start();
}
/* ... */
public function _close(): bool {
// Close the database connection
$this->db->close();
return true;
}
/* ... */
}
I'm seeing this error in the logs:
PHP Fatal error: Uncaught Error: Cannot access private property
Session::$db
in [line number of$this->db->close()
]Stack trace:
#0 [internal function]:Session->_close()
#1 {main}\n thrown in ...
However when I test the code by stepping through in xdebug it works as expected.
What is going on?
This appears to happen if memory is exhausted during the course of a PDO query. I believe it has something to do with the memory used by PDO Buffered queries and the way the PHP internal code frees up memory after memory is exhausted.
I can reproduce the condition you have experienced with the following code, (note: this is using register_shutdown_function
. Both the session_close code and the register_shutdown_function
will run after a memory exhausted error):
<?php
class TestingClass
{
private int $something_private;
public function getThisDone(): void
{
$this->something_private = 0;
}
}
// get a reference to the testing class
$obj = new TestingClass();
register_shutdown_function(fn() => $obj->getThisDone());
// get a pdo connection and run a query that returns a lot of data
$pdoConnection = get_db_read_connection();
$pdoStatement = $pdoConnection->prepare('SELECT * FROM your_table_with_a_lot_of_records LIMIT 100000');
$results = $pdoStatement->fetchAll(PDO::FETCH_CLASS, '\stdClass');
And the resulting fatal error:
I cannot reproduce the error if I simply trigger an out of memory error with string concatenation, like below.
// use code above but replace pdo connection and query with this
while(true)
{
$data .= str_repeat('#', PHP_INT_MAX);
}
I don't think it should be. It seems like this is a good candidate for a PHP bug report.