Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Dreaded "headers already sent" error

LEGEND ,
Sep 15, 2008 Sep 15, 2008
I'm getting a "headers already sent" error and I can't figure out why.
I have checked for and eliminated all whitespace in my php include files
and still I'm getting the error:

> Warning: Cannot modify header information - headers already sent by
> (output started at
> /Users/brett/vhosts/modules/admin/Products/add_div.inc.php:51)
> in /Users/brett/vhosts/modules/admin/Products/update_grp.inc.php on
> line 44


After updating a table record, I am trying to redirect to a page
containing accordion panels populated with table LIST pages. Here is my
redirect:

>
> if ($Result1) {
> header('Location: http://' . $_SERVER['HTTP_HOST'] .
> dirname($_SERVER['PHP_SELF']) .
> '/admin.php?content=Products/seeSelections');
> exit;
> }


I have looked at the include file listed in the error message and can
find no problem. Here is the code:

> <?php
> if (!function_exists("GetSQLValueString")) {
> function GetSQLValueString($theValue, $theType, $theDefinedValue = "",
> $theNotDefinedValue = "")
> {
> $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) :
> $theValue;
>
> $theValue = function_exists("mysql_real_escape_string") ?
> mysql_real_escape_string($theValue) : mysql_escape_string($theValue);
>
> switch ($theType) {
> case "text":
> $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
> break;
> case "long":
> case "int":
> $theValue = ($theValue != "") ? intval($theValue) : "NULL";
> break;
> case "double":
> $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'"
> : "NULL";
> break;
> case "date":
> $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
> break;
> case "defined":
> $theValue = ($theValue != "") ? $theDefinedValue :
> $theNotDefinedValue;
> break;
> }
> return $theValue;
> }
> }
>
> $editFormAction = $_SERVER['PHP_SELF'];
> if (isset($_SERVER['QUERY_STRING'])) {
> $editFormAction .= "?" . htmlentities($_SERVER['QUERY_STRING']);
> }
>
> if ((isset($_POST["MM_insert"])) && ($_POST["MM_insert"] == "addDiv")) {
> $insertSQL = sprintf("INSERT INTO divisions (div_name) VALUES (%s)",
> GetSQLValueString($_POST['div_name'], "text"));
>
> mysql_select_db($database_testadmin, $testadmin);
> $Result1 = mysql_query($insertSQL, $testadmin) or die(mysql_error());
> }
> ?>
> <form id="addDiv" name="addDiv" method="POST" action="<?php echo
> $editFormAction; ?>">
> <label for="div_name">Name:</label>
> <br />
> <input type="text" name="div_name" id="div_name" />
> <br /><br />
> <input type="submit" name="submit" id="submit" value="Add entry" />
> <input name="MM_insert" type="hidden" id="MM_insert" value="addDiv" />
> </form>

Line 51 in "add_div.inc.php" is the last line shown above - </form> -
and there is no whitespace after the closing >.
Line 44 in "update_grp.inc.php" is the header redirect shown above.
What could be the problem?

TIA

Brett

TOPICS
Server side applications
968
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Sep 15, 2008 Sep 15, 2008
Brett wrote:
> I'm getting a "headers already sent" error and I can't figure out why.
> I have checked for and eliminated all whitespace in my php include files
> and still I'm getting the error:
>
>> Warning: Cannot modify header information - headers already sent by
>> (output started at
>> /Users/brett/vhosts/modules/admin/Products/add_div.inc.php:51)
>> in /Users/brett/vhosts/modules/admin/Products/update_grp.inc.php on
>> line 44

The error message says that output was started on line 44 of
update_grp.inc.php. That's where you should look for the problem.

--
David Powers, Adobe Community Expert
Author, "The Essential Guide to Dreamweaver CS3" (friends of ED)
Author, "PHP Solutions" (friends of ED)
http://foundationphp.com/
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Sep 15, 2008 Sep 15, 2008
Thanks David, I looked at line 44:

> if ($Result1) {
> header('Location: http://' . $_SERVER['HTTP_HOST'] .
> dirname($_SERVER['PHP_SELF']) .
> '/admin.php?content=Products/seeSelections');
> exit;
> }

but I don't see where the problem is. Line 44 starts with:
header('Location: ...etc. as shown above. I have used this code on many
other pages and it works fine, why not here?

Best,

Brett
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Sep 15, 2008 Sep 15, 2008
Brett wrote:
> but I don't see where the problem is. Line 44 starts with:
> header('Location: ...etc. as shown above. I have used this code on many
> other pages and it works fine, why not here?

As always with PHP error messages, you need to start working backwards
from that point. One thing I would suggest looking for is the use of the
Byte Order Mark. In each page, select Modify > Page Properties. In the
Title/Encoding section, make sure that Include Unicode Signature (BOM)
is deselected.

--
David Powers, Adobe Community Expert
Author, "The Essential Guide to Dreamweaver CS3" (friends of ED)
Author, "PHP Solutions" (friends of ED)
http://foundationphp.com/
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Sep 15, 2008 Sep 15, 2008
David,

I'm completely stumped. I've looked at the code for
"update_grp.inc.php" and I can see no place that the header has been
sent before. Here is the complete code for that page:

> <?php
> if (!function_exists("GetSQLValueString")) {
> function GetSQLValueString($theValue, $theType, $theDefinedValue = "",
> $theNotDefinedValue = "")
> {
> $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) :
> $theValue;
>
> $theValue = function_exists("mysql_real_escape_string") ?
> mysql_real_escape_string($theValue) : mysql_escape_string($theValue);
>
> switch ($theType) {
> case "text":
> $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
> break;
> case "long":
> case "int":
> $theValue = ($theValue != "") ? intval($theValue) : "NULL";
> break;
> case "double":
> $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'"
> : "NULL";
> break;
> case "date":
> $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
> break;
> case "defined":
> $theValue = ($theValue != "") ? $theDefinedValue :
> $theNotDefinedValue;
> break;
> }
> return $theValue;
> }
> }
>
> $editFormAction = $_SERVER['PHP_SELF'];
> if (isset($_SERVER['QUERY_STRING'])) {
> $editFormAction .= "?" . htmlentities($_SERVER['QUERY_STRING']);
> }
> if ((isset($_POST["MM_update"])) && ($_POST["MM_update"] == "editGrp")) {
> $updateSQL = sprintf("UPDATE grp SET grp_name=%s WHERE grp_id=%s",
> GetSQLValueString($_POST['grp_name'], "text"),
> GetSQLValueString($_POST['grp_id'], "int"));
>
> mysql_select_db($database_testadmin, $testadmin);
> $Result1 = mysql_query($updateSQL, $testadmin) or die(mysql_error());
>
> if ($Result1) {
> header('Location: http://' . $_SERVER['HTTP_HOST'] .
> dirname($_SERVER['PHP_SELF']) .
> '/admin.php?content=Products/seeSelections');
> exit;
> }
> }
>
> $colname_getGrp = "-1";
> if (isset($_GET['grp_id'])) {
> $colname_getGrp = $_GET['grp_id'];
> }
> mysql_select_db($database_testadmin, $testadmin);
> $query_getGrp = sprintf("SELECT * FROM grp WHERE grp_id = %s",
> GetSQLValueString($colname_getGrp, "int"));
> $getGrp = mysql_query($query_getGrp, $testadmin) or die(mysql_error());
> $row_getGrp = mysql_fetch_assoc($getGrp);
> $totalRows_getGrp = mysql_num_rows($getGrp);
>
> ?>
> <form action="<?php echo $editFormAction; ?>" id="editGrp"
> name="editGrp" method="POST">
> <label for="grp_name">Name:</label>
> <br />
> <input name="grp_name" type="text" id="grp_name" value="<?php echo
> htmlentities($row_getGrp['grp_name'], ENT_COMPAT, 'UTF-8'); ?>" />
> <br /><br />
> <input type="submit" name="submit" id="submit" value="Update entry" />
> <input name="grp_id" type="hidden" id="grp_id" value="<?php echo
> $row_getGrp['grp_id']; ?>" />
> <input type="hidden" name="MM_update" value="editGrp" />
> </form>
>
> <?php
> mysql_free_result($getGrp);
> ?>
As I mentioned before, line 44 starts with "header(Location: ...etc.)
Can you spot the problem? I checked the Page properties on all pages
which are included and none have the BOM selected. Any other thoughts?

Many thanks for your help.

Brett
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Sep 15, 2008 Sep 15, 2008
Brett wrote:
> I'm completely stumped.

The problem is here:

>> ?>
>> <form action="<?php echo $editFormAction; ?>" id="editGrp"
>> name="editGrp" method="POST">
>> <label for="grp_name">Name:</label>
>> <br />
>> <input name="grp_name" type="text" id="grp_name" value="<?php echo
>> htmlentities($row_getGrp['grp_name'], ENT_COMPAT, 'UTF-8'); ?>" />
>> <br /><br />
>> <input type="submit" name="submit" id="submit" value="Update entry" />
>> <input name="grp_id" type="hidden" id="grp_id" value="<?php echo
>> $row_getGrp['grp_id']; ?>" />
>> <input type="hidden" name="MM_update" value="editGrp" />
>> </form>

The form is HTML output. It doesn't matter that the header() function is
on line 44 ahead of this output. The reason it triggers "headers already
sent" is because the form is part of the include file. There must be no
output to the browser in an include file if you're using header() or
sessions.

--
David Powers, Adobe Community Expert
Author, "The Essential Guide to Dreamweaver CS3" (friends of ED)
Author, "PHP Solutions" (friends of ED)
http://foundationphp.com/
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Sep 15, 2008 Sep 15, 2008
David,

I found the solution to my problem. I needed to use output buffering to
prevent headers being sent.
What I did was place ob_start() at the beginning of the page with all of
the accordion elements, and ob_end_flush() at the very end. Now when I
edit a record in one accordion panel and click update, I'm redirected to
the same page with the default settings for all panels and the record
shows the update.

Whew! That's a relief!

As always, thanks for taking the time to help.

Best,

Brett
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Sep 15, 2008 Sep 15, 2008
.oO(Brett)

>I found the solution to my problem. I needed to use output buffering to
>prevent headers being sent.

No. It's an ugly workaround, but doesn't fix the real problem. You
should re-order your code so that all tests and calculations happen
before the first line of output.

Micha
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Sep 15, 2008 Sep 15, 2008
> The reason it triggers "headers already sent" is because the form is
> part of the include file.

I began to realize that, but I didn't know what to do about it.
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Sep 15, 2008 Sep 15, 2008
>
> No. It's an ugly workaround, but doesn't fix the real problem. You
> should re-order your code so that all tests and calculations happen
> before the first line of output.
I thought that was the purpose of output buffering. What is the real
problem then? I thought the problem was that I couldn't redirect to my
page because I was getting the headers error.

What makes this an ugly workaround? I have 20 includes in this page,
each of which has some html, either a table or form. Are you saying
that my includes should only contain the php tests and validations, and
that all forms and tables should be in the accordion panels directly in
this page? So the dynamic data in my forms and tables need to be placed
within this page? Doesn't this defeat the purpose of using includes to
make a site more modular?

Micha, I'm not challenging your knowledge, you've probably forgotten
more than I'll ever know, but I'm trying to understand these concepts
better.

Best,

Brett


Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Sep 16, 2008 Sep 16, 2008
David,

I'm beginning to think I don't understand about using the header()
function. I have read in your books and others that "Using header() or
starting a PHP session must be done before any output is sent to the
browser." Does that mean in the "sending" page not the "receiving"
page? I thought it meant the receiving page, the page to which you are
redirected. Now I'm thinking I have misunderstood this entirely.

So my question is, as I posed to Micha in this thread, how does one
construct a page like I have with 20 include files, each of which
contains html. As you may know from my post on your FOE forum "backend
blight", the page I'm trying to build has 5 Spry accordions each of
which contains 4 panels for LIST, ADD, EDIT, DELETE. My intention was
to create the individual pages for each panel and include() them in the
appropriate panel, forms, tables and all. I believed this "modular"
approach was the best and cleanest way to accomplish this. Now I'm not
sure how to go about it given my desire to use header() to redirect on
update or delete.

Also, Micha says that my solution - using ob_start() and ob_end_flush()
is an ugly workaround. I'm trying to understand why that is if it
accomplishes what is needed and functions as expected. Is this a case
of coder aesthetics and my solution is just not elegant, or is it truly
flawed?

Thanks to you and Micha for responding.

Best,

Brett
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Sep 16, 2008 Sep 16, 2008
Brett wrote:
> So my question is, as I posed to Micha in this thread, how does one
> construct a page like I have with 20 include files, each of which
> contains html.

The simple answer is: "You don't". Twenty include files sounds like
overkill. The other problem is that you're mixing the business logic of
your page with the presentation. In an ideal situation, you should keep
separate the code that queries the database and makes other decisions.
Dreamweaver does that by putting all the decision-making code above the
DOCTYPE declaration. It's a technique that works fine for small
applications, but when you start building something more ambitious, you
need to think about adopting the Model-View-Controller (MVC) design pattern.

MVC is a much more complex way of building a site, and it's an approach
that I understand in theory, but have never created in practice. If
you're interested in learning more, you might want to take a look at
"Practical Web 2.0 Applications with PHP" by Quentin Zervaas. I managed
to get only halfway through the book before being sidetracked onto
another project, but it's excellent. Be warned, though, it's pretty
advanced.

> Also, Micha says that my solution - using ob_start() and ob_end_flush()
> is an ugly workaround. I'm trying to understand why that is if it
> accomplishes what is needed and functions as expected. Is this a case
> of coder aesthetics and my solution is just not elegant, or is it truly
> flawed?

It's ugly because you're relying on a hack to solve your problem. If
your code is well designed, you don't need the output buffer.

--
David Powers, Adobe Community Expert
Author, "The Essential Guide to Dreamweaver CS3" (friends of ED)
Author, "PHP Solutions" (friends of ED)
http://foundationphp.com/
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Sep 16, 2008 Sep 16, 2008
.oO(Brett)

>> No. It's an ugly workaround, but doesn't fix the real problem. You
>> should re-order your code so that all tests and calculations happen
>> before the first line of output.
>
>I thought that was the purpose of output buffering. What is the real
>problem then?

It's just a workaround, not a solution. The purpose for output buffering
is not to overcome a "suboptimal" code structure, but for example to
apply some kind of filters to the final page before it gets delivered to
the client (gzip compression for example).

Turning it on just in order to prevent the headers problem is ugly IMHO,
because it's not really what OB is meant for and it requires more CPU
time and memory on the server. It's also quite ugly if your script
already generates a lot of output, but then has to discard it because of
a redirect. This would mean a lot of HTML generated for nothing.

Although this shouldn't become a real problem on a modern server, it
could be completely avoided quite easily with just a little better code
structure.

>I thought the problem was that I couldn't redirect to my
>page because I was getting the headers error.

Yep.

>What makes this an ugly workaround? I have 20 includes in this page,
>each of which has some html, either a table or form. Are you saying
>that my includes should only contain the php tests and validations, and
>that all forms and tables should be in the accordion panels directly in
>this page? So the dynamic data in my forms and tables need to be placed
>within this page?

Nope. Includes are still perfectly fine and important for this.

>Doesn't this defeat the purpose of using includes to
>make a site more modular?

Usually the creation of the HTML output should be the last step in the
whole process, when all other tests, actions etc. are done and their
results are known.

In case of a form for example you could easily split the processing into
multiple pieces. One function does the validation, one processes the
data and another one just prints the entire form. Three completely
independent things: validate(), process() and render() for example.

If the validation fails, process() will not be called (or at least
doesn't do anything with the data), instead render() should just show
the form again with error messages:

validate() --> FALSE
process() not called
render()

But if the validation succeeds, then process() will be called and might
even do a redirect to a "thank you" page. Until that point there was no
output, hence there's no headers problem at all:

validate() --> TRUE
process() --> redirect, script stops
render() not called

>Micha, I'm not challenging your knowledge, you've probably forgotten
>more than I'll ever know

;)

>but I'm trying to understand these concepts
>better.

No problem.

Micha
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Sep 16, 2008 Sep 16, 2008
Micha,

Thanks for your reply. I completely reworked the page based on your
recommendations, and now it works quite well with no header() problems.
Basically I took my include files, separated the validation and
processing and put that above the doctype, then put the forms, etc. in
their respective Spry accordion panels in the body. I see now what you
mean about it having better structure.

As always, thanks for your help and advice.

Kind regards,

Brett
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Sep 16, 2008 Sep 16, 2008
David,

Thank you very much for your input. I feel quite confident that the MVC
approach would be well over my head at this point. So I needed to find
another way. After reading your post and Micha's, I realized that this
required a major redo. Actually, it turned out to be a less daunting
task than I originally feared, taking only a couple of hours. That
short time was helped by the fact that I already had the code in my
include files, so it only meant cutting and pasting.

Anyway, it's working now and all processing and validation is above the
doctype, and all html below. No more header() problems.

Thanks very much for your continued help.

Best,

Brett

PS. I ordered "PHP Object-Oriented Solutions" last week and am
expecting it to arrive any day now.*
*
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Sep 17, 2008 Sep 17, 2008
LATEST
.oO(Brett)

>Micha,
>
>Thanks for your reply. I completely reworked the page based on your
>recommendations, and now it works quite well with no header() problems.
>Basically I took my include files, separated the validation and
>processing and put that above the doctype, then put the forms, etc. in
>their respective Spry accordion panels in the body. I see now what you
>mean about it having better structure.
>
>As always, thanks for your help and advice.
>
>Kind regards,

You're welcome.

Micha
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines