1

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?

9
  • With session_set_save_handler() you make certain methods from your session class callable, but when they are called they are not called from within the session object. Have a look at the User Contributed Notes. Commented Feb 27 at 19:33
  • With a quick test I can't get any errors to be produced: 3v4l.org/WfN3S
    – Chris Haas
    Commented Feb 27 at 19:41
  • @KIKOSoftware can you be more specific about which comments in the user-contributed notes that you're referring to? my guess is that the class is being destroyed before _close() is even getting called, but when I test, that isn't actually what happens.
    – tvanc
    Commented Feb 27 at 19:45
  • @ChrisHaas neither can I but I'm getting these errors in my error log.
    – tvanc
    Commented Feb 27 at 19:46
  • Although not really helpful in any way, looking at Symfony's code they are accessing private variables in the close method of their PDO handler. To me your code appears correct.
    – Chris Haas
    Commented Feb 27 at 19:51

1 Answer 1

1

As far as your question, "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:

enter image description here

Note:

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);
}

As far as "why is this happening?"

I don't think it should be. It seems like this is a good candidate for a PHP bug report.

0

Not the answer you're looking for? Browse other questions tagged or ask your own question.