- Headers
- What are they?
- What does PHP have to do with headers?
- Why can they only be sent before any output?
- Common causes
- Calling a function that sends header information to the browser after one has sent output via print(), echo(), etc.
- Calling a function that sends header information to the browser within an HTML file
- Uncommon / Hard-to-locate (the most annoying) causes
- Whitespace before opening the PHP parser (<?php)
- BOM
- Solutions
- Moving header-sending functions to before any output
- Output buffering
PHP - Common Newbie Pitfails
This article is the first installment in a series of (hopefully) many.
Written by the PHP experts at bytes.com, these articles hope to cover those common bear traps often encountered — and subsequently asked on the bytes.com forums — by PHP newcomers in a clear and concise manner.
1. Headers Already Sent
Warning: Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\site\php\index.php:1) in C:\xampp\htdocs\site\php\index.php on line 14
- The common error message.Headers
What are they?
When you write a letter to a friend, before giving that letter to the post office, you provide a set of details on the front of the envelope: name, address, etc. The post office is then able to use this address to give the letter to the correct location. Were you not to give this information, the post office would be quite unsure of how to handle the letter (and it would most likely end up in the bin, but that's irrelevant here).
Headers, in this sense, are the address of whomever you are intending to write a letter: a set of information / instructions for the post office (web browser) with which it can act accordingly.
So, to move away from the analogy: headers are a set of information / instructions given to the web-browser so that it can act upon them.
A common header that you are most likely aware of is the 'mime-type' header which looks like: Content-type: text/html. This header instructs the browser that whatever follows (after the headers) is to be rendered as HTML text. Were you to send the header Content-type: text/xml, then the browser would know to render the text as XML (in Firefox, the XML will be displayed in a neat format, for example).
What does PHP have to do with them?
PHP, by the very virtue of its name — PHP: Hypertext Preprocessor — has a lot to do with headers. If you have ever worked with the C programming language you will surely have heard of the C Preprocessor. This tool parses your .c file and makes any changes to it according to your rules. If you #define IMG_X 100, the preprocessor will change all occurrences of IMG_X with 100. This is effectively what PHP does, too. What does this have to do with headers? Well, it's what happens after the preprocessing that includes headers (or can do). PHP pushes whatever data resides in its buffer to the web-browser. Therefore, PHP has the ability to send headers to the browser.
Why can headers only be sent before output / why can they not be modified after output?
The answer to this is quite simple: once the browser has received the headers, it will not recognise any subsequent headers, thus making any attempts to do so pointless.
You may also ask: well, that doesn't sound so dangerous - why does PHP make a fuss about it? The answer again is simple: some functions in the PHP core rely on the need to send header information to the browser. We'll have a look at this in more detail when we come to those functions.
Causes
For every problem that arises there are always at least a couple of causes, and there'll always be those that make themselves that bit harder to find.
The common(er) causes.
- session_start()
The single most common cause of the title error, through my experience, is calling the session_start() function after sending output to the browser. The offending code often looks like this:
Expand|Select|Wrap|Line Numbers- <html>
- <head></head>
- <body>
- <h1>Starting session...</h1>
- <?php session_start(); ?>
- </body>
- </html>
To implement the session, PHP needs to create a cookie on the client's browser that will store the session ID. However, as we discovered before, if output has already been sent to the browser, PHP cannot then send the cookie header to the browser, thus the call to session_start() fails.
However, session_start() is not the only function to have this behaviour. Other functions include (incomplete list):
* Another common error is calling the header() function to redirect the user after output has been sent. The following is another frequent offender:
Expand|Select|Wrap|Line Numbers- <html>
- <head></head>
- <body>
- <h1>Redirecting in 5 seconds...</h1>
- <?php
- header("refresh: 5; url=/page.html");
- // Or
- header("location: /page.html");
- ?>
- </body>
- </html>
- Another common cause, which is really just a reiteration of the previously mentioned one, is calling a function like echo()* or print()* and then calling a function that is header-dependant. Just as with the session_start() and header() examples, this will cause an error/warning for the exact same reasons. Once you call echo() or print() the headers will be sent (the first time you call the function, that is), and, therefore, you cannot send them again with a call to session_start() or header(), etc.
Expand|Select|Wrap|Line Numbers- <?php
- print 'This call to print() implicitly flushes the headers to the browser.';
- session_start(); // :(
- ?>
The Uncommon causes.
Debugging is, in general, a lengthy and tiresome task. The debugging skill of the programmer is probably the biggest factor in the ability to debug a problem [...]
- Wikipedia: debugging.
- BOM (Byte-order Mark)
The Byte-order Mark is a sequence of characters that denotes how bytes are ordered. To explain it in detail would be to go out of the scope of this article (and also to digress).
All you need to know is that various — typically Windows-based — programs add this sequence of characters to the very beginning of a file. When PHP opens this file, it sees the characters and thinks to itself OK, we have some output to send to the browser! and then the headers are implicitly flushed and then the content is sent.
Therefore, if you have any calls in that file to a function such as header(), you will encounter that familiar error. - Whitespace before the opening PHP tag
This is one more common than the BOM problem and easier to spot but still just as frustrating, although the errors arise for exactly the same reason: anything before the opening PHP tag (<?php) will be thought of as output and sent to the browser, thereby flushing the headers also.
Solutions
The solutions are in fact very simple and are generally the same.
As we saw from all of the causes, the error arises when content is knowingly or unknowingly sent to the browser. From this we can deduce that the solution would be to not send any output before sending the headers. Consider the following example:
- session_start()
We saw before that starting the session after outputting some HTML would cause problems:
Expand|Select|Wrap|Line Numbers- <html>
- <head></head>
- <body>
- <h1>Starting session...</h1>
- <?php session_start(); ?>
- </body>
- </html>
Expand|Select|Wrap|Line Numbers- <?php session_start(); ?>
- <html>
- <head></head>
- <body>
- <h1>Starting session...</h1>
- </body>
- </html>
- Whitespace before the opening PHP tag
A very easy fix for this one - remove the whitespace! Sometimes it's not so obvious that it's there, so be sure to look carefully!
Expand|Select|Wrap|Line Numbers- <?php // notice there _is_ a space before the opening PHP tag.
- session_start();
- ?>
- BOM
With this cause the solution isn't as simple as simply removing any whitespace, or moving function calls to the beginning of a file because the text editor that places these characters into the file also makes them invisible! Also, the solution isn't the same across all text editors. For this reason, I will provide you with the search keywords for google: remove BOM <insert text editor name here>
Output Buffering
Sometimes you may find that on a large code-base, restructuring the code to alleviate these errors may be too time-consuming. Therefore, you'll need a quick-fix. Output Buffering may be just what you need.
Output buffering does what it says on the tin: buffers (stores) your output until you're ready to flush (send) it. Using this technique, you can buffer a block of code like so:
Expand|Select|Wrap|Line Numbers
- <?php
- /** Kick start output buffering */
- ob_start();
- /** Send some output */
- print 'Sending output!';
- /** Call a header-dependant function */
- session_start();
- /** flush the buffer contents */
- ob_end_flush();
Conclusion
So, there you go. I understand this article got a little more comprehensive than I intended, but never mind.
May the source be with you,
Mark Skilbeck.
P.S. Please mention any spelling/grammar errors and any information that is incorrect.