Jump to content
iwato

The header( ) Function

Recommended Posts

DISCLAIMER: After seeing what has been written on the internet on this subject I am loathe to start here, but have I any other choice?

QUESTION:  The PHP header(location: './sample.html'); is telling me that my headers have already been sent and is referring me back to the beginning of my PHP script.  Can I buffer my way out of this mess?  If so,

  • Where to I start and stop the buffer?
  • Where do I place the header( ) function?

BACKGROUND:  I have just spent several weeks setting up an email verification system and am putting on the decorative touches.  Truly, I thought that I had almost reached the end; truly I fear now that I am far from finished.  I hope that I am wrong.

 

Share this post


Link to post
Share on other sites

Headers can only be sent before anything is printed. If you're not printing anything and it's pointing to the first line of your document there are two possible reasons:

  1. There's a space or line break before the opening <?php tag
  2. The document is saved as UTF-8 with a byte-order mark (BOM).

Eliminate all whitespace before the <?php tag and make sure to save your documents as UTF-8 without a BOM.

Share this post


Link to post
Share on other sites

As I feared, this will likely not be easy.  Here is all that comes before the <?php tag.  I have scrutinized the invisibles of all lines before the tag.  All lines end with line breaks and begin with either tabs or nothing.  Of course, there are blank spaces within the lines of code, else the code would be unintelligible.

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="utf-8" />
	<title>Gate Seven - An Invitation and Gift</title>
	<meta name="generator" content="BBEdit 11.6" />
	<link rel="stylesheet" href="../../_utilities/css/gc_splash.css">
	<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
	<script>
		$( document ).ready(function() {
			console.log( "ready!" );
			var tongue = 'other_tongue';
			$('#other').hide();
			$('#tongue').change(function() {
				if ($('#tongue').val() == tongue) {
					$('#other').show();
				}
				if ($('#tongue').val() != tongue) {
					$('#other').hide();
				}
			});
		});
	</script>
</head>
<body>
	<?php

 

Edited by iwato

Share this post


Link to post
Share on other sites

If the problem is within the PHP code, then would this not be a likely source?  Specifically, the first line. For, the file confirmation_mail.php contains headers.

	$html_message = file_get_contents('../../confirmation_mail.php');
	$html_message = str_replace('%username%', $name, $html_message);
	$html_message = str_replace('%email%', $email, $html_message);
	$html_message = str_replace('%hash%', $hash, $html_message);



Could I find a better place for it?  I think not.
Could I buffer it?  Surely, but I am not sure how.

Surely the email that PHP sends on the sending page must be sent before the header( ) function is invoked else the PHP on the sending page would fail, would it not?

Surely, the document confirmation_mail.php must be included in the document before it is sent, else it would be omitted from the mail, would it not?

I do not see how to get around this problem, if it is, indeed, it is its proper source.

Roddy

Edited by iwato

Share this post


Link to post
Share on other sites

This is how the email is sent.

      if (!$mail->send()) {
            echo "Mailer Error: " . $mail->ErrorInfo;
      } else {
//			echo "Message sent!";
			header('Location: ../../gate_confirmation.html');
	  }

 

Share this post


Link to post
Share on other sites

it would be helpful to share the relevant code in one block, instead of three code blocks in as many posts, since it makes it harder to understand the context of your issue.  And in particular with headers, seeing the whole file is important.

Share this post


Link to post
Share on other sites

You should have the PHP run first, then the HTML.

Here's the general structure of my PHP applications:

<?php
// Form handling logic
if(isset($_POST['submit')) {
  // If submission was successful
  // Do something
  header('Location: http://www.example.com/target.php');
  exit;
}

// Set variables for use in templates
$template = 5;

// Include a template file
include 'template.php';
?>

 

  • Like 1

Share this post


Link to post
Share on other sites

You should get in the habit of structuring your code like Ingolme mentions.  Put your PHP code first that will decide what the page is even going to do, and output HTML and whatever later on.  If you're just going to redirect there's no reason to output a bunch of HTML anyway, right?  So figure out first if you're going to redirect or show HTML or whatever else, and then do that.

  • Like 1

Share this post


Link to post
Share on other sites

This is how the email is sent.

I have eliminated all comments and non-essential code.  Sensitive information has been replaced with placeholders.

In addition, I have added row spaces to departmentalize the code into specific tasks for easier digestion.

No rearrangement of code that I have made achieves the desired affect -- namely, the loading of the confirmation page (gate_confirmation.html).   The sending of the verification email (confirmation_mail.php) works fine!

The desired affect, by the way, can be viewed by going to the last (5th) frame of the last gate (7th) in the second splash panel at www.grammarcaptive.com/overview.  The quickest way to get there is by using the back arrows built-into the splash panels.  If you know the way, you can arrive in seconds.

Please enter a phony email address, if you do not wish to be entered into my database.  The only check for validity is format. 

Also, if you wish to repeat the procedure, you can get the splash panel to reopen by clicking on my stylized image on the main page..

		require_once '../../_utilities/php/php_mailer/PHPMailerAutoload.php';
		$name = $email = $newsletter = $webinar = $language = $error_msg = '' ;

		if ($_SERVER["REQUEST_METHOD"] == "POST") {


			if (empty($_POST['language'])) {
				$error_msg = "Please select your first language.";
			} else if ($_POST['language'] == 'other_tongue') {
				if (empty($_POST['other'])) {
					$error_msg = "Please enter your first language.";
				} else {
					$language = filter_var($_POST["other"], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
				}
			} else {
				$language = filter_var($_POST["language"], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
			}


			if (!($_POST["newsletter"]) && !($_POST["webinar"])) {
				$error_msg = "Have you forgotten to select a gift?";
			} else {
				$newsletter = filter_var($_POST["newsletter"], FILTER_SANITIZE_NUMBER_INT, FILTER_VALIDATE_INT);
				$newsletter = !empty($newsletter) ? "$newsletter" : 0;
				$webinar = filter_var($_POST["webinar"], FILTER_SANITIZE_NUMBER_INT, FILTER_VALIDATE_INT);
				$webinar = !empty($webinar) ? "$webinar" : 0;
			}


			if (empty($_POST["email"])) {
				$error_msg = "Please enter your email address.";
			} else if (!filter_var($_POST["email"], FILTER_VALIDATE_EMAIL, FILTER_SANITIZE_EMAIL)) {
				$error_msg = "Invalid email format. Please try again!";
			} else {
				$email = filter_var($_POST["email"], FILTER_VALIDATE_EMAIL, FILTER_SANITIZE_EMAIL);
			}


			if (empty($_POST['name'])) {
				$error_msg = "Please enter a name with which you would like to be greeted.";
			} else {
				$name = filter_var($_POST["name"], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
			}


			if ((!empty($name) && !empty($language) && !empty($email) && (isset($_POST["newsletter"]) || isset($_POST["webinar"])))) {
				$hostname = "localhost";
				$user = "...";
				$pwd = "...";
				$db = "...";
				$mysqli_obj = mysqli_connect($hostname, $user, $pwd, $db);
				$mysqli_obj->set_charset("utf8");


				function random_password() {
					$alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!$%&!$%&!$%&!$%&!$%&!$%&';
					$pass = array();
					$alphaLength = strlen($alphabet) - 1;
					for ($i = 0; $i < 8; $i++) {
						$n = rand(0, $alphaLength);
						$pass[] = $alphabet[$n];
					}
					return implode($pass);
				}
				$password = random_password();

                                        
                $hash = password_hash($password, PASSWORD_DEFAULT);
				$active = 0;

                                        
                $tbl_name = '---.---';
				$sql_1 = "INSERT INTO " . $tbl_name . " (user_name, language, email_address, hash, active, newsletter, webinar) VALUES ('" . $name . "','" . $language . "','" . $email . "','" . $hash . "','" . $hash . "','" . $newsletter . "','" . $webinar . "')"; 
				$mysqli_obj->query($sql_1);

                      
				$mail = new PHPMailer;
				$mail->CharSet = 'UTF-8';
				$mail->isSMTP();
				$mail->SMTPDebug = 0;
				$mail->Debugoutput = 'html';
				$mail->Host = "---.---.com";
				$mail->Port = ...;
				$mail->SMTPAuth = true;
				$mail->Username = "---@---.---";
				$mail->Password = "...";
				$mail->setFrom('---@---.com', 'Grammar Captive');
				$mail->addReplyTo('---@---.com', 'Grammar Captive Administration');
				$mail->addAddress($email, $name);
				$mail->Subject = 'Account Verification';

                      
                $html_message = file_get_contents('../../confirmation_mail.php');
				$html_message = str_replace('%username%', $name, $html_message);
				$html_message = str_replace('%email%', $email, $html_message);
				$html_message = str_replace('%hash%', $hash, $html_message);

                      
                $mail->msgHTML($html_message);
				$alt_message = 'Congratulations, ' . $name . '! You have successfully created a Grammar Captive account./n/r Please click on the link below to verify that you are the owner of the account and to receive the first edition of Seven Gates, the Grammar Captive weekly newsletter./n/r http://www.grammarcaptive.com/email_verify.php?name=' . $name . '&email=' . $email . '&hash=' . $hash;
				$mail->AltBody = $alt_message;

                      
                if (!$mail->send()) {
					echo "Mailer Error: " . $mail->ErrorInfo;
				} else {
					header('Location: ../../gate_confirmation.html');
				}

                      
            }
		}
 

I suppose that the best way for you to help me at this time is to regroup the above code in the matter that you believe will work.   Then, I can test and compare it with Ingolme's explanation and grasp the entire concept in a flash.

Roddy

Edited by iwato

Share this post


Link to post
Share on other sites

The concept is simple: All processing first, HTML last. Move all output to the bottom of your code. The first thing in your document should be an opening <?php tag.

The code in your post does not have the HTML in it, which is crucial for understanding how to fix your problem. No HTML can be printed before a header() function call, or session_start() or setcookie() either.

Share this post


Link to post
Share on other sites
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="utf-8" />
	<title>Gate Seven - An Invitation and Gift</title>
	<meta name="generator" content="BBEdit 11.6" />
	<link rel="stylesheet" href="../../_utilities/css/gc_splash.css">
	<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
	<script>
		$( document ).ready(function() {
			console.log( "ready!" );
			var tongue = 'other_tongue';
			$('#other').hide();
			$('#tongue').change(function() {
				if ($('#tongue').val() == tongue) {
					$('#other').show();
				}
				if ($('#tongue').val() != tongue) {
					$('#other').hide();
				}
			});
		});
	</script>
</head>
<body>

This is the HTML that appears before the <?php tag.  I see no effect on the headers.  All non-essential blank spaces were stripped away.  Actually, there were none.  Only tabs and line breaks remain.

Share this post


Link to post
Share on other sites

Headers have to be sent before any content. There should be nothing between the start of the document and the opening <?php tag.

HTTP messages are divided into two sections: The headers and the body. The headers contain information about the page, while the body contains the actual page itself. The headers must always come first. Any header sent after the body was sent will actually be considered part of the body, but rather than doing that PHP will give you a warning instead.

The HTML must go last, after all the PHP, not before it.

Share this post


Link to post
Share on other sites

Firstly, already long ago I tried moving the last, above, displayed section of PHP to the top of the page, but it had no effect.

Secondly, there is PHP contained in the body of the page.

<form id="subscribe" method="post" name="subscribe" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']);?>">
              
<input type="text" name="name" id="name" value='<?php echo $name;?>'>
<input type="checkbox" value="1" id="checkbox1" name="newsletter" <?php if(isset($_POST['newsletter'])) echo 'checked'; ?> />
  
<?php echo '<p><span style="font-size:0.7em; color:#f00;">' . $error_msg . '</span></p>';?>

etc.

I fear that I must do what I was loathe to do at the beginning and believed that this dialogue would inevitably lead me -- separate the email processing page from the form page.  Am I correct?

Edited by iwato

Share this post


Link to post
Share on other sites

It's not necessary to separate it into two files. It doesn't matter if there's PHP embedded in the HTML, you just have to put the form processing logic before the code that prints out the HTML.

Do that first, then if you're getting errors find out what's causing them,

Share this post


Link to post
Share on other sites

Please read carefully.  Long ago I tried putting the PHP first, but there was no change.  The header( ) function failed.

Maybe it is because I am using the PHPMailer Autoloader.  It spews out the entire correspondence with the SMTP server, as well as error messages that surely must surely be generated by PHP echo statements.  Then too, the statements are unformatted, so there is likely no HTML contained within.

Roddy

Edited by iwato

Share this post


Link to post
Share on other sites

Turn off debugoutput in your PHPMailer settings. That's what's causing the problem. 

You still should have HTML last. If another problem occurs then find out why and fix it.

Share this post


Link to post
Share on other sites

IF the error is one line then its your script showing the error, else if multiple lines it is usually phpmailer's STMPDebug, but your code shows debug as disabled? so you should not be getting any multiple line error messages, and since it is disabled, Debugoutput="html" won't take effect in making outputted error message break into multiple lines using <br>, instead of errors bundled together in a big block of text which is more difficult to read.

The idea is to process the email sent values IF SET, by validation/sanitization and if valid, send email and redirect, before it reaches '<!DOCTYPE html>' else return to form until valid. It does not matter about php within body, as  its the placement of header() function is the key factor here.

Edited by dsonesuk

Share this post


Link to post
Share on other sites

I would like to thank everyone for your patience, encouragement, and most of all your insight.

The failure of the confirmation page to appear has been resolved.  Apparently there were two complications that had been resolved separately, but not together:  one, placing the PHPMailer code ahead of all HTML code, and two, setting the STMPDebug property value to 0.  Doing both of these together appears to have resolved the problem.

Hooray, hooray, hooray!

Roddy

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×