0

Based on research here and elsewhere, I have the following code that sets a cookie on a site:

add_action( 'init', 'my_set_cookie',1 );

function my_set_cookie() {
    if (! $_COOKIE['mycookie']) {
       $guid = 'xxxx'; // normally a real guid value so it will be unique
       setcookie('mycookie', $guid, time()+(3600*24*30),'/');
    }
return;
}

But even though the init hook is used, and with a priority of '1', I still get 'headers already sent' error for the 'setcookie' statement. Using another priority (say '99') also gives the error.

Is there a different hook to use to set the cookie?

  • Are you absolutely sure there's no prior output? A stray space or tab in a plugin file? A PHP warning? The moment a single character of output is made, PHP has to send headers, after which it's too late. Once they're sent there's no way to go back and add/change things. It could be as simple as a plugin with a closing tag and a space character at the end of a file. Messing with the priority on the add_action call will have no effect, as it should have worked to begin with – Tom J Nowell Jan 6 '19 at 2:05
  • Good idea. The error message indicates that the theme's header.php has started the output. So does that mean that the theme is loaded before the init hook can fire? Is there a hook that can be used before the theme is loaded - perhaps that's the solution? – Rick Hellewell Jan 6 '19 at 3:57
  • Theme is loaded before init is fired... – Krzysiek Dróżdż Jan 6 '19 at 8:34
  • @RickHellewell where is the code snippet from your question? If header.php has already been sent then this is way too late for the init hook which will have been fired long before that. Which file is this in? And what's the full error message? Usually headers being sent is referring to the HTTP headers, nothing to do with WordPress' header.php, this is a general PHP thing, not something WordPress itself is complaining about – Tom J Nowell Jan 6 '19 at 13:42
  • This code is in my plugin that is activated. The error message indicates that output was already sent by the theme's 'header.php'. The error line number is the output of the '<!DOCTYPE html>' before the '<head>' statement. The plugin has to set a cookie, so the 'init' hook was used, as recommended in numerous places here and elsewhere. I tried using the 'muplugins_loaded' hook, which is first on the list from the link from the answer by Krzysiek Dróżdż. So, what is the recommended way for a plugin to set a cookie? rather use a cookie, not a session var, as the cookie needs to last a year. – Rick Hellewell Jan 6 '19 at 23:16
3

It’s a problem as old as WP, or even older...

You can’t set cookies, or send any other headers, if any of the content of the site is already sent.

You try to set cookies using init hook. And it is OK in most cases, since no output should be printed yet. But... Not all code is written correctly.

There are many other thing done before init. Take a look at actions run during a typical request.

As you can see... Themes get loaded before init hook. And so do plugins.

So if their code cause any warning/notice (and the debug is on) or they generate any other output, then you’ll get error while setting cookie.

Another popular cause for this problem is using closing PHP tags (?>) at the end of files. If there is any white characters after that closing tag, then it’s treated as output also.

-1

After trying various hooks to try to set the cookie before the theme loaded it's header.php file (none of the hooks worked, even those very early in the 'load cycle'), I figure that I might temporarily disable the error so that the cookie can be set. I based my answer on this question.

Now, I realize that this is not a standard way of doing it, and probably not recommended, but my justification is that if a theme is not doing things 'normally' or 'correct', and therefore causing the 'headers already sent' error message to display on the screen, then quickly bypassing that error display (and then restoring whatever error trapping settings are in place) is a solution that will work in my situation.

It appears from my testing that even if 'headers already sent' happens, the cookie is set properly. That doesn't really sound like it should work, but it appeared to work in my testing.

So, the technique I used is

  • get the current error settings to be used later

  • turn off display of errors

  • do things that cause the error (in my case, set the cookie)

  • restore the error settings to original valuess

Here is the code that I used. Again, probably not 'kosher', but it worked in my case.

// some themes cause a 'headers already sent' message, so we bypass it here
// by turning off that error message while we set the cookie.
// see https://stackoverflow.com/a/26154061/1466973
//  $random_guid is a guid set previously
$previousErrorLevel = error_reporting();
error_reporting(\E_ERROR);
setcookie('cookie_name', $random_guid,time()+(3600*24*30),'/');
error_reporting($previousErrorLevel);

Since it worked for my case, I am marking my answer as correct, although the other answer is valid. And I expect a couple of downvotes, as this is not kosher. But it worked for my problem and circumstances.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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