20

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();
?>
JochenJung
  • 7,183
  • 12
  • 64
  • 113
  • You can suppress warnings but the session won't start is the headers are already sent. You need to start the session before any output to the browser, no other way around. – Mihai Iorga Sep 07 '12 at 09:22
  • 1
    No, the session _will_ start again. It works. This is because the first session_start() already sent the cookies correctly – JochenJung Sep 07 '12 at 09:23
  • Structure your application flow better so you do all the stuff you need to do in the session first. Outputting stuff to the browser should be the very last thing you do; while you're assembling your HTML all your business logic should already be done and there should be no need to write to the session after output has already started. – deceze Sep 07 '12 at 09:28
  • 2
    `I do this, because as long as the session is open...` - only if you use the default files based handler on a machine with heavy handed file locking (i.e. MSWindows). Why not just use a better session handler? – symcbean Sep 07 '12 at 15:39
  • Didn't know it will be different with a custom session handler. Would be a good thing to try as well. Thanks! – JochenJung Sep 07 '12 at 22:46

2 Answers2

25
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
Nullcaller
  • 163
  • 9
VolkerK
  • 95,432
  • 20
  • 163
  • 226
  • 1
    Interesting idea. Tentative +1, I shall test it. – DaveRandom Sep 07 '12 at 09:38
  • 1
    `ini_set('session.cache_limiter', null);` added - still untested, but that would be a session related http header, too. – VolkerK Sep 07 '12 at 09:46
  • Yep, that 4th option fixes it. This is a working solution. http://codepad.viper-7.com/4gz38n (look at all three items in the history) – DaveRandom Sep 07 '12 at 09:46
  • Thanks! Seams the best idea. Restructuring might be the better way to go, though I'm not sure, whether it will work in all cases. Since I close the session quite early I come across places where I like to write to the session later in the code many times. – JochenJung Sep 07 '12 at 11:01
  • 3
    I found it was necessary to save the session id, like `$session_id = session_id()` (before `session_write_close()`), and use it like `session_start($session_id)` later. Otherwise my reopened session was empty. Also, I am using this technique to periodically reopen the session to check if the user is still logged in, in a script to implement server-sent events. – Steve Kehlet Jul 09 '15 at 18:22
  • 1
    This doesn't work in php 7.2 anymore :( "session_start(): Cannot start session when headers already sent" – Tobias Buschor Dec 08 '17 at 08:27
  • More recently, overriding the `use_cookies` setting will result in an error like `session_start(): Headers already sent. You cannot change the session module's ini settings at this time`, essentially swapping one error for another. Same with the `cache_limiter` – Umbrella Aug 01 '19 at 15:54
  • Thanks for this solution, but unfortunately it doesn't work since PHP 7.2. Any idea how to solve this in newer versions of PHP? – Werner Apr 20 '20 at 19:36
6

EDIT See @VolkerK's solution, it is better than this one.

Just buffer the output of your script while it executes to prevent the headers from being sent, and output it at the very end:

<?php

  session_start();
  // setting all the session vars I like to set
  session_write_close();

  // Start output buffer
  ob_start();

  // Code that takes some time to execute

  // Do session stuff at the end of the script
  session_start();
  $_SESSION['key'] = 'new value I like to store';
  session_write_close();

  // Send output to client
  ob_end_flush();

Ref: Output control functions

DaveRandom
  • 87,921
  • 11
  • 154
  • 174
  • 2
    That would work. But I think I would prefer just suppressing the warning, than using output buffering, which will slow down the content getting to the client. – JochenJung Sep 07 '12 at 09:27
  • @JochenJung Well realistically in that case you will have to suppress the warning. You can't have it both ways - because `session_start()` attempts to set a header, you cannot call it after the headers have been sent without raising a warning. Now I do to a large extent agree with you that it is silly that this is not possible, what is really required is `session_lock()`/`session_unlock()` that will release the session file so concurrent requests can use it, without having to send the header again. Unfortunately that's what your stuck with. – DaveRandom Sep 07 '12 at 09:32
  • @JochenJung I suppose another option would be to [`set_error_handler()`](http://php.net/manual/en/function.set-error-handler.php) to "handle" the inevitable error, but at the end of the day that's just a more complicated `@` operator when used like that. I understand your desire to avoid `@` at all costs (it is commendable) but in this specific situation I regard it as acceptable. Just make sure you comment it properly in your code. – DaveRandom Sep 07 '12 at 09:35
  • @JochenJung Also consider if `$_SESSION` is the best place for storing whatever this information is - could it be logged to a database? What exactly is the data you are storing, and why can't it be generated before you produce output? – DaveRandom Sep 07 '12 at 09:37