tags:

views:

78

answers:

2

Please save me. I know that this question has been asked many times, however, I can't seem to find solutions that are relevant to my situation.

The problem: Warning: Cannot modify header information - headers already sent by (output started at /.../Sites/st.ambulance/resources/views/login.view.php:32) in /.../Sites/st.ambulance/resources/controllers/tables_.php on line 47

This is the code block with line 32:

      <label>Month</label>
        <select name="dob_month">
            <?php for($i=0,$j=1;$i<sizeof($month);$i++,$j++){ ?>
                <option value="<?php h($j) ?>"><?php h($month[$i]) ?></option>  //line 32
            <?php } ?>
        </select>

The definition of the h function:

function h($s){
    echo(htmlspecialchars($s,ENT_QUOTES));
}

This is tables_.php:

<?php
 $error = "";
 if(isset($_POST['guest_tables'])){
 if(isset($_SESSION['logged_in']) && $_SESSION['logged_in']){
    $guest = array();
    $volunteer = array();

    $guest = isset($_POST['guest']) ? $_POST['guest'] : null;
    $volunteer = isset($_POST['volunteer']) ? $_POST['volunteer'] : null;

    $seat_array = array($volunteer['seat_no'].$volunteer['table']);
    $seat_count = 0;
    $table_seat_error = "";

    if($form->is_seatOccupied($volunteer['table'],$volunteer['seat_no']) != "")
            $table_seat_error .= "Seat (".$volunteer['seat_no'].")
                at table (".$volunteer['table'].") is currently occupied";

    if($_SESSION['no_guests'] >= 1){
        if($guest && $volunteer){
            foreach($guest as $gue){

                $seat_table = $gue['seat_no'].$gue['table'];
                for($h=0;$h<sizeof($seat_array);$h++){
                    if($seat_table == $seat_array[$h] )
                        $seat_count = $seat_count + 1;
                }
                if($form->is_seatOccupied($gue['table'], $gue['seat_no']) != "")
                            $table_seat_error .= "Seat (".$gue['seat_no'].")
                                at table (".$gue['table'].") is currently occupied";

                $seat_array[] = $seat_table;
            }
            if($seat_count == 0){
                if($table_seat_error == ""){
                    for($d=0;$d<$_SESSION['no_guests'];$d++){
                        $_SESSION['guests'][$d]['table'] = $guest[$d]['table'];
                        $_SESSION['guests'][$d]['seat'] = $guest[$d]['seat_no'];
                    }

                    $_SESSION['volunteer']['table'] = $volunteer['table'];
                    $_SESSION['volunteer']['seat'] = $volunteer['seat_no'];

                    $form->set_guests($_SESSION['guests']);
                    $form->set_volunteer($_SESSION['volunteer']);

                    header('location: /branch/menus.php'); //line 47
                    exit();
                }
                else{
                    $error = $table_seat_error;
                }
            }
            else{
                $error = "You have selected the same seat for two or more
                    people: one person, per seat, per table. Only.";
            }
        }
    }
    else{

        $_SESSION['volunteer']['table'] = $volunteer['table'];
        $_SESSION['volunteer']['seat'] = $volunteer['seat_no'];

        if(!$form->is_seatOccupied($_SESSION['volunteer']['table'],
                $_SESSION['volunteer']['seat']) != ""){
            $form->set_volunteer($_SESSION['volunteer']);

            header('location: /branch/menus.php');
            exit();

            }
      }
    }
  }
 ?>

EDIT: would it help to know that I'm trying to handle multiple forms on a single page?

+3  A: 

Line 47 modifies the response headers:

header('location: /branch/menus.php'); //line 47

Line 47 cannot be executed because the headers have already been sent, which happened to occur on line 32. All that happened on line 32 was that PHP decided it had enough content in the response to start sending it back to the browser.

Generally, if you want to modify the headers (whatever you're doing on line 47), you need to do it at the very beginning of the file.

This is a good time to learn about the MVC design pattern - it would eliminate the possibility of this issue in the future. In MVC, the Controller executes first, and prepares everything for the View. So, you can modify your headers all you want in the Controller, and they won't be sent until during or after the View is processed.

EDIT: It looks like you are using MVC, but somehow your view is executing before the controller has started (or perhaps finished)... that shouldn't happen! Unfortunately, the code you've posted doesn't illustrate how either the controller or view are being accessed... but they're being accessed out of order.

As TRiG point out in the comments below, you probably want to insert an exit() statement after line 47 as follows:

header('location: /branch/menus.php'); //line 47
exit();

This will cause the server to immediately send the Location redirect to the browser, and the request/response cycle is complete. All your header('Location: ...'); calls should be immediately followed by exit();. This doesn't fix the issue at hand, but it is very important.

Dolph
Also, (a) if you output a `Location` header you should immediately follow it with `exit;`, as any code after that almost certainly doesn't need to run; and (b) to properly conform to the HTTP specs, `Location` headers should have a full, not a relative, URL. Web-Sniffer and Google both have trouble with relative URLs in `Location` headers. (In Google Webmaster Tools you see complaints if pages on your site redirect with relative URLs.)
TRiG
@TRiG ++; damned good points
Dolph
Would you be willing to look through the whole code?
Tunji Gbadamosi
A: 

switch on output buffering, locate php.ini and look for / uncomment / change / add the following line:

output_buffering = On

edit: and include a line saying ob_start(); in some initial include, like some common init.php or database.php
edit^2: the ob_start is not needed (anymore?); this works, save as test.php and request. Seeing is believing.

Output has started
<?php
    header("Content-Type: text/plain");
?>

The point is that in HTTP the response of the server consists of 2 parts: the headers and the body.

The headers contain information about the content, the connection etc, like what type of content, e.g.

Content-Type: text/html
or
Content-Type: image/jpeg

indicating it's html or jpg. Without it, the browser wouldn't be sure what to do with the content. Of course once PHP has to start outputting the body part of the response, there's no way to change the header. So you can either do anything header-related first before emitting any byte of the body, or you could instruct php to hold it's breath and emit the body only when the script is done, or when you say so. And that's what they call output buffering.

mvds
That's not going to fix anything unless output buffering is actually being *used*.
Dolph
Well, putting output_buffering = On in php.ini turns it on for any page rendered. quoting from my (debian lenny stock install) php.ini: `You can enable output buffering during runtime by calling the output buffering functions. You can also enable output buffering for all files by setting this directive to On.`
mvds
None of the code the OP posted gives any clue he's trying to use OB functions, so turning OB on in `php.ini` will have no effect - other than enabling the OP to go back and add OB to everything. Even then, that's just a hack, and doesn't fix the design issue.
Dolph
wow, you're right, didn't realize but you actually have to manualy ob_start() somewhere.
mvds
I stand corrected: setting the `output_buffering = On` **does** enable output buffering from the start, without `ob_start()`!
mvds