Stanislav Stanislav - 3 months ago 20
PHP Question

Why i cant set session var in __destruct() method?

I try set session var in __destruct() method.
Method __destruct() is running, but session var not set.
While, session in __contruct() or other methods (e.g. test()) working as expected.

public function test()
{
$_SESSION['MyVarTest'] = rand(200,300); ← working correctly
}

public function __destruct()
{
echo 'called';
$_SESSION['MyVar'] = rand(1,100); ← not working
}


Updated version. Now i try native PHP Session and Symfony component, and both not working in __destruct() method.

<?php

namespace Project\Modules\Cart\Storage;

use Illuminate\Support\Collection;

class Session
{

/**
* @var \Symfony\Component\HttpFoundation\Session\Session
*/
protected $session;

protected $cart;

public function __construct()
{
$this->cart = new Collection();

$this->session = app('session');
print_r($_SESSION);

}

public function test()
{
$this->session->set('json', rand(1,100)); ← working correctly
$_SESSION['json'] = rand(1,100); ← working correctly
return $this->cart->toJson();
}

public function __destruct()
{
echo 'called';
$_SESSION['MyVar'] = rand(1,100); ← not working

$this->session->set('cart', serialize($this->cart->toArray())); ← not working
}

}

Answer

Symfony sessions are using custom session handlers. (via session_set_save_handler)

Custom session handlers cause PHP to register a shutdown function (i.e. register_shutdown_function), invoking session_register_shutdown, which adds another shutdown handler (so it will be executed last), calling session_write_close, effectively closing your session then.

After that function has been called, no further writes will be stored. [as the session handler won't be called anymore afterwards.]

And as destructors (of objects not cleaned up yet) run only after the shutdown functions were invoked, this session write will fail.

The only solution to avoid this is either not using a custom session handler (not an option for you I guess), restarting the session manually (as long as order of destruction does not destroy the class first), or explicitly destroying all the references to this object in shutdown handlers or before.