PHP 5.6: headers_sent intermittently returns true, empty file name, and line 0
Asked Answered
O

3

8

I'm intermittently getting this issue in my PHP scripts (PHP 5.6, Apache 2.2):

Warning: Cannot modify header information - headers already sent in /path/to/index.php on line 55

This warning doesn't have the "sent by" portion that I've seen in other questions about this, so I added this code just before the offending header() and setcookie() calls:

if (headers_sent($filename, $linenum)){
    echo("Output buffer: #" . ob_get_contents() . "#");
    echo "Headers already sent in $filename on line $linenum: ";
    print_r(headers_list());
}

Here's the output I got when the issue occurred:

Output buffer: ##
Headers already sent in on line 0:
Array (
  [0] => X-Powered-By: PHP/5.6.23
  [1] => Content-type: text/html; charset=UTF-8
)

(Side note: I have output_buffering set to 4096 bytes in php.ini, so shouldn't the 63 characters in these two headers be buffered up and waiting for more, rather than being sent prematurely?)

This issue arises the first time I spin up a Docker container containing the webserver. After that, it only (but not always) occurs when I access my site for the first time in a while (maybe one or two hours), when I call header() and setcookie() to log a user in or to redirect to the login page.

I've read and reread this answer to the general "Headers already sent" error, and, to the best of my ability, I've ruled out these possible causes :

  1. HTML blocks or calls to print, echo, etc. before my calls to setcookie() or header()
  2. whitespace outside my PHP tags
  3. BOMs
  4. auto_prepend_file php.ini setting
  5. gzip stream encoding - zlib is installed, but zlib.output_compression is Off
  6. Duplicate extension= php.ini settings

That answer mentions that

It's typically a PHP extension or php.ini setting if no error source is concretized.

So, I'm now looking through my extensions... get_loaded_extensions gives me a 51-length Array with these entries:

Core, date, ereg, libxml, openssl,
pcre, zlib, filter, hash, Reflection,
SPL, session, standard, apache2handler, bz2,
calendar, ctype, curl, dom, exif,
fileinfo, ftp, gd, gettext, iconv,
mysqlnd, PDO, Phar, posix, shmop,
SimpleXML, snmp, soap, sockets, sqlite3,
sysvmsg, sysvsem, sysvshm, tokenizer, xml,
xmlwriter, xsl, mysql, mysqli, pdo_mysql,
pdo_sqlite, wddx, xmlreader, json, zip, mhash

I'm not using all of these, so I plan to go through and remove the unused ones, and hope one of those was causing the problem.

Worst-case scenario, I'll try bumping my output_buffering value or use ob_start() and ob_end_flush() to the starts and ends of my files. I don't know why this would fix it when my current output_buffering value of 4096 doesn't, and I understand that this workaround comes with its own issues.

What am I missing here—are there other possible causes I need to check for? Should I try a different PHP version, or perhaps run a subset of my code on a clean PHP install with no extensions?

EDIT: Added the ob_get_contents() call and output, and information about being able to consistently reproduce this by spinning up new Docker containers. Removed info about my error_reporting value; changing this only uncovered an always_populate_raw_post_data deprecation notice, fixing which had no effect on the issue described here.

Orator answered 16/3, 2017 at 7:17 Comment(6)
Please provide the full PHP file code that you set and check the headers inside. I suppose it's an index.php? If it's included, then please provide the whole include chain sources including the index file.Gifferd
Are you calling ob_start() at the very beginning of your entry point script? That's not clear from your question but without that ob_get_contents() won't work. Also, try to use var_dump(ob_get_contents()). I think it's better than simply echoing the values because it'll say the length of the string.Whoreson
How did you determine that fixing the always_populate_raw_post_data thing didn't solve the issue? That's certainly a cause for it. On another note - looking at successfully loaded extensions won't help; chances are that error would be triggered for the opposite - a failure to load an extension. If it says line 0, you can be certain that it has nothing to do with the code, but something triggered while PHP itself is initialized.Finny
@GustavoStraube Ah, I assumed that ob_get_contents() would work with my output_buffering setting in php.ini. Maybe I'm fundamentally misunderstanding output buffering.Orator
@ChristosLytras The files are closed source, but I'll see about posting them, if it's necessary.Orator
@Finny I have this line in my php.ini: always_populate_raw_post_data = -1. Thanks for the tip about extensions; I'll look for ways to identify any extensions that failed to load (but I'd expect to see logs to that effect).Orator
O
1

After taking out some unused legacy code—including require_once calls to a file that was all legacy code—this problem is no longer occurring. It may recur under the "intermittent" conditions I initially observed, but my method for reproducing it no longer exhibits this problem.

I don't know why this seems to have helped - the removed lines were completely within <php? ?> tags, and I checked the removed file for whitespace outside tags, BOMs, and CRLFs.

I also don't know why this issue was intermittent; if it had something to do with the removed code or files, it should have happened every time.

Thanks to all for the comments and answers!

Orator answered 24/3, 2017 at 1:47 Comment(0)
S
1

I have seen the problem arise due to line ending format in the past. Are you moving files from one operating system environment to another?

Sometimes a hidden carriage return (/r) or an other special white-space character can cause this but not be visible in the files.

Steelhead answered 23/3, 2017 at 19:29 Comment(1)
Thanks for the tip; I am moving files from Windows to PHP and back. However, I failed to find any CRLFs using a couple methods described here: https://mcmap.net/q/142953/-how-do-you-search-for-files-containing-dos-line-endings-crlf-with-grep-on-linux/877682Orator
A
1

It can be UTF8 BOM character, or wrong newline, non printable characters, wrong php close tag (i don`t use them, to prevent endline problems). Even if there was no such problem, it can ( and will ) occur later even if you do everything to prevent it. Problems can be with FTP servers, text editors, wrong PHP configurations, encodings, wrong cgi configurations. To prevent all of them i use empty output buffer, just start buffreing in first php file

index.php:

if(version_compare(PHP_VERSION, '7.0.0') >= 0 || ob_get_level() < 1)
    ob_start();

HtmlResponce.php:

ob_end_clean();
echo $this->getRenderedContent();

FileResponce.php:

ob_end_clean();
readfile($this->content);

in php5 and php7 output buffering has few differences, also output buffering can be configured in php.ini and has differences in cgi and apache mod configurations. You shuld not relay on "default" configuration. To handle warning messages, use set_error_handler() you will strip them too with ob_end_clean()

if you start new project look at symfony components, i use a lot of them in my CMS. That is your chance to prevent sleepless nights with strange PHP bugs.

Agone answered 24/3, 2017 at 1:10 Comment(3)
As mentioned in my answer, I ruled out the common problems you described (as best I could), and I wanted to avoid using ob_start if at all possible, for performance reasons. Thanks for the advice; I'll definitely check out symfony components if/when I start a new PHP project.Orator
there is no performance loose in this case. buffer closed before output. No double buffering - no increase in memory usage.Sophi
After doing some research, it looks like my belief that output buffering is associated with performance benefits was mistaken. It's a bad idea for enormous output (say over 200 KB), but that doesn't apply to my case. Thanks for setting me straight!Orator
O
1

After taking out some unused legacy code—including require_once calls to a file that was all legacy code—this problem is no longer occurring. It may recur under the "intermittent" conditions I initially observed, but my method for reproducing it no longer exhibits this problem.

I don't know why this seems to have helped - the removed lines were completely within <php? ?> tags, and I checked the removed file for whitespace outside tags, BOMs, and CRLFs.

I also don't know why this issue was intermittent; if it had something to do with the removed code or files, it should have happened every time.

Thanks to all for the comments and answers!

Orator answered 24/3, 2017 at 1:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.