Viewed   101 times

How do I reopen a session in PHP without getting header already sent warnings?

After setting all the session vars I like to set, I close the session with session_write_close(). I do this, because as long as the session is open, there may be only one active connection from the same client. But I like to have multiple parallel ones.

However if I like to set another session variable at a later point, I need to reopen the session with session_start() once again. This works, but as I already send code to the client it prints "headers already sent"-warnings. Why is it trying to set the cookie again? The cookie is already set. Only thing I need is to gain access to writing the session files on the server again.

Well, I can suppress them. But is there a way of reopening a session, that has been closed with session_write_close without re-sending the Cookie-header? The Cookie-header is already sent correctly by the first session_start(). So the second one just needs to give me back access to writing to the session files stored on the web server.

<?php
session_start();
// setting all the session vars I like to set
session_write_close(); // <-- // To allow parallel requests by the same user, while this script is still running

// Code that takes some time to execute
// It also prints output, so no more cookie headers after this point

@session_start(); // <-- works, but I like to use it without suppressing warnings
$_SESSION['key'] = 'new value I like to store';
session_write_close();
?>

 Answers

4
session_start();
...
session_write_close();
...

ini_set('session.use_only_cookies', false);
ini_set('session.use_cookies', false);
ini_set('session.use_trans_sid', false);
ini_set('session.cache_limiter', null);
session_start(); // second session_start

This will prevent php from calling php_session_send_cookie() a second time.
See it working.

Though restructuring the scripts still seems to be the better option...


For PHP 7.2+, you will basically need to re-implement session cookies to avoid errors and warnings:

ini_set('session.use_only_cookies', false);
ini_set('session.use_cookies', false);
ini_set('session.use_trans_sid', false);
ini_set('session.cache_limiter', null);

if(array_key_exists('PHPSESSID', $_COOKIE))
    session_id($_COOKIE['PHPSESSID']);
else {
    session_start();
    setcookie('PHPSESSID', session_id());
    session_write_close();
}

session_start();
...
session_write_close();
...

session_start(); // second session_start
Wednesday, October 26, 2022
 
nca
 
nca
5

As it turns out, @hakre was on the right path, but the change didn't solve the problem. The problem is that Plesk directly assigns *.php files to be processed through php_fpm. In the Virtual Host Apache configuration file we have...

<Files ~ (.php$)>
    SetHandler proxy:unix:///var/www/vhosts/system/wwphelps.com/php-fpm.sock|fcgi://127.0.0.1:9000
</Files>

This is how Plesk is specifically attaching one version of PHP over another by domain name. And anything that doesn't fall into this rule is interpreted by Apache's global rules, which for me are looking at a different install of PHP. (Why Plesk doesn't have a global override to point at their own PHP installs is a bit of a wonder, but I suspect this is a bug they've never encountered before.) So, logically, we'd need only add for each file we want to process without the .php suffix...

<Files action1>
    SetHandler proxy:unix:///var/www/vhosts/system/wwphelps.com/php-fpm.sock|fcgi://127.0.0.1:9000
</Files>

It is true that you need to do this, but for me it didn't work as advertised. My web page simply said "Access Denied" and my error files pointed me to FPM's security.limit_extensions paramter. In other words, despite specifically identifying a file I wanted to use without a suffix, FPM rejected it anyway. Here's where I got lazy. I reset the variable to nothing. In Plesk that's done by creating or modifying a php.ini file inside the domain's conf directory and adding (including the header if it's not already there)...

[php-fpm-pool-settings]
security.limit_extensions =

Restart Apache and Bob's your uncle.

According to a really rapid Google search, the ability to directly modify FPM parameters from inside Plesk is still up for debate.

Now, this comes with a price. From the perspective of the FPM socket, suddenly any file in you're web root "could" be executed as a PHP file, including images customers upload and you blindly put in your [ROOT]/images directory. You're partially saved by the fact that unless you've told Apache otherwise, just any old file won't be interpreted as a PHP file. However, you'd be better protected if all files you upload through your site are (a) thoroughly vetted to be sure they are what they claim to be and (b) are either saved outside the web root or in a DB so that nobody can "execute" them by referring to them directly.

Finally, there ought to be a way to override in a config file those files that I want to intentionally violate security.limit_extensions. Unfortunately, FPM and Apache don't appear to talk to each other, otherwise the use of the block would completely override security.limit_extensions. For all I know there's a way to do it. I'd be curious to know, but I have a working solution, so I'm back to work.

Monday, August 29, 2022
 
2

From docs:

In order to kill the session altogether, like to log the user out, the session id must also be unset. If a cookie is used to propagate the session id (default behavior), then the session cookie must be deleted. setcookie() may be used for that.

Example:

session_unset();
session_destroy();
session_write_close();
setcookie(session_name(),'',0,'/');
session_regenerate_id(true);
Tuesday, September 6, 2022
 
3

PHP's native $_SESSION sessions transparently serialize and unserialize objects that support PHP's serialization protocol or the Serializable interface. You do not need to explicitly serialize them.

PHP cannot serialize resources because these are handles to some stateful resource outside PHP's control. This is why you cannot serialize PDO or PDOStatement objects.

By default an object is serialized by saving all property names and values and unserialized by creating an object with the same class (without invoking the constructor) and directly setting the serialized properties. You can customize serialization behavior for your objects using the __sleep and __wakeup magic methods or by implementing the Serializable interface. But not both! If you use implements Serializable, __sleep and __wakeup are ignored.

One important note: when using object serialization, you must have the class definition loaded before you unserialize (or have an autoloader that can load it) and it must match the class definition of the object that was serialized. Class definitions are not stored in the serialized data.

For example suppose you have the following:

class Test {
    public $version = 1;
    protected $abc;
    public function setAbc($abc) {
        $this->abc = $abc;
    }
}

$t = new Test();
$t->setAbc(123);
$_SESSION['mytest'] = $t;

Now imagine you change Test one day to be like this instead:

class Test {
    public $version = 2;
    private $def;
    public function setDef ($def) {
        $this->def = $def;
    }
}

Now suppose you load into your new code an object serialized when Test was at version 1:

$t = $_SESSION['mytest']; // this was stored yesterday, when Test was version 1

var_dump($t)

You will get this:

object(Test)#1 (3) {
  ["version"]=>
  int(1)
  ["def":"Test":private]=>
  NULL
  ["abc":protected]=>
  int(123)
}

Furthermore, you can't use old methods:

if ($t->version == 1) { // Check for class version
    $t->setAbc(345); // "Fatal error: Call to undefined method Test::setAbc()"
}
Monday, December 12, 2022
3

Sometimes ini_set plays up and is unable to set the ini values correctly, might be down to permissions.

the below does not fully resolve the issue with ini_set, and if anyone knows the reason(s) why ini_set does not work on some type's of host, then please share!

Try the following:

<?
if(ini_set('session.name', 'DOMAIN1') === false || !session_name('DOMAIN1'))
{
    die('Unable to set sesssion scope');
}

phpinfo();
?>

alternatively you can just use session_name() to set it, and ill always advise you not to just run functions and hope the always check the in an if statement and prepare for the worst case scenario, thats when your application becomes reliable and less error_prone.

Sunday, August 21, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :