PHP 8 Correct use of ob_start(); and ob_end_flush();

Hi

I have a login script that on successful login performs -

header('Location:home.php');

This is not executing. I have investigated and come to the conclusion that it is because the headers have already been sent, probably because I have introduced echoes to output messages to help with debugging. I have added ob_start(); at the very beginning of the script and ob_end_flush(); immediately after header('Location:home.php');

This fix works, but am I doing it properly. Is this the best place for ob_start();, first line of the script or should it maybe come after session_start();, or somewhere else. And is it ok to ob_end_flush(); immediately after the header('Location:home.php');

The only reason I am doing this is to get PHP to redirect to the home page. I want to be sure I am using the function correctly, efficiently and not building in other problems for myself. I am completely unfamiliar with buffers and flushing them. It works but am I doing the right thing in the best way.

First of all, I did research this and checked out https://www.sitepoint.com/community/t/when-to-use-ob-start-and-ob-end-flush/5462

This was interesting, but mainly because it ended in a fist fight :grin: However I did grasp that it is best to separate output from other processing, which seems like good advice. But when you want to include echoes for help with debugging it makes that difficult. Also the post says start as near the top as possible (although obviously at this point tension was building) -

I appreciate that salathe was not recommending this approach but are there any exceptions to this like after starting a session etc.

Thanks

The redirect upon successful completion in your post method form processing code should be to the exact same URL of the current page, to cause a get request for that page. This is a design pattern called PRG (Post, Redirect, Get.) It provides a better User eXperience (UX) since it prevents the browser from re-submitting the post method form data should you reload or navigate away from and back to that URL. If you want the user to be able to go to a different page, supply navigation links to do so. Note: you would typically put the logic for a site-wide operation like the login on any page that needs it, so, there’s actually no need to be going to another page after successfully logging in.

Using output buffering would hide (discard) the output from any debugging statements or php none-fatal errors, so, it’s not a ‘solution’ for the case of debugging problems. If you are debugging a problem in your script, you don’t care if the header() redirect works. You would actually comment the redirect out until you find and fix any problem prior to the redirect.

Only use output buffering if you WANT to buffer output, such as if you want to capture the output into a variable from a statement/section of code that doesn’t support returning the output. Using it to make your script ‘work’ is a sign that your code isn’t well organized and you should instead spend time fixing the organization of your code, not trying to figure out where to put the buffering statements.

3 Likes

Yep,

example of me being lazy, just thought I’d leave all the echoes in until Id got it all working but ended up trying to code out a problem I coded in. Learned something there!

I did not realise this, I have seen it done in sample scripts but always found the header option easier to code. But again I now understand more and will adopt this practice.

Well I don’t really know what it does and really have no use for it to be honest except it solved this problem. Yep it was not a proper fix I now realise, so I won’t waste my time and I’ll look into output buffering when I actually have a genuine need for it.

As always thanks, your advice is invaluable to me.

Hi
Okay I have taken your advice and now have this script that works and I am hoping it reflects the better coding practices you mentioned, including PDO.

I hope to build on this basis but your suggestions are new coding disciplines to me, so I would very much appreciate it if you could warn me if I am still attacking it wrongly.

One concern I have is am I declaring the common Doctype and header tag info in the right place, immediately after session start or would it be best elsewhere?

Another concern is I have a different body class for submitted and not submitted. I am not sure how to handle that if I am combining both displays on one page to avoid using the PHP header function. I’m also worried that I’m specifying one HTML layout before submitting and then another after, I’m concerned they may conflict. I’m still a bit unsure on this aspect.

I hope I’ve made a fairly valiant attempt to implement your suggestions but Im a bit stumped by the added intricasies.

Cheers for the help

<?php
session_start();
// Define common HTML for both form not submitted and form submitted
?>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Login</title>
		<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css">
		<link href="style.css" rel="stylesheet" type="text/css">
	</head>
<?php
// Check if the login form was submitted, isset() will still be set if fields 
// are empty however. Needs to be trimmed and addressed later 
if ( !isset($_POST['username'], $_POST['password']) ) {
	// Not set so display login form	
?>

	<body>
		<div class="login">
			<h1>Login</h1>
			<form action="index.php" method="post">
				<label for="username">
					<i class="fas fa-user"></i>
				</label>
				<input type="text" name="username" placeholder="Username" id="username" required>
				<label for="password">
					<i class="fas fa-lock"></i>
				</label>
				<input type="password" name="password" placeholder="Password" id="password" required>
				<input type="submit" value="Login">
			</form>
		</div>
	</body>
</html>
<?php
}
else {
require_once('let_me_in.php'); // Connection credentials

// PDO Connection script
$sql = "SELECT id, password, username from accounts WHERE username = ?";

$stmt = $pdo->prepare($sql);
$stmt->execute([
	$_POST['username']
	]);
if(!$row = $stmt->fetch())
{
	// username was not found
	$errors['login'] = "Invalid Username/Password.";
} else {
	// username found, verify the password hash
	if(!password_verify($_POST['password'],$row['password']))
	{
		// password doesn't match
		$errors['login'] = "Invalid Username/Password.";
	} else {
		// password matches
		
		session_regenerate_id();
		$_SESSION['id'] = $row['id'];					
		$_SESSION['loggedin'] = TRUE;
		$_SESSION['name'] = $row['username'];
		
	}
}
?>
			
<body class="loggedin">
<nav class="navtop">
	<div>
		<h1>Website Title</h1>
		<a href="profile.php"><i class="fas fa-user-circle"></i>Profile</a>
		<a href="logout.php"><i class="fas fa-sign-out-alt"></i>Logout</a>
	</div>
</nav>
<div class="content">
	<h2>Home Page</h2>
	<p>Welcome back, <?=$_SESSION['name']?>!</p>
</div>
</body>
</html>

<?php
}
?>
1 Like

You just need a little housekeeping.

<?php
session_start();

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    require_once 'let_me_in.php'; // Connection credentials

// PDO Connection script
    $sql = "SELECT id, password, username from accounts WHERE username = ?";

    $stmt = $pdo->prepare($sql);
    $stmt->execute([
        $_POST['username']
    ]);
    if (!$row = $stmt->fetch()) {
        // username was not found
        $errors['login'] = "Invalid Username/Password.";
    } else {
        // username found, verify the password hash
        if (!password_verify($_POST['password'], $row['password'])) {
            // password doesn't match
            $errors['login'] = "Invalid Username/Password.";
        } else {
            // password matches

            session_regenerate_id();
            $_SESSION['id'] = $row['id'];
            $_SESSION['loggedin'] = true;
            $_SESSION['name'] = $row['username'];
        }
    }
}
?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Login</title>
        <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css">
        <link href="style.css" rel="stylesheet" type="text/css">
    </head>

<?php if(isset($_SESSION['loggedin']) && $_SESSION['loggedin'] == true): ?>
<body class="loggedin">
<nav class="navtop">
    <div>
        <h1>Website Title</h1>
        <a href="profile.php"><i class="fas fa-user-circle"></i>Profile</a>
        <a href="logout.php"><i class="fas fa-sign-out-alt"></i>Logout</a>
    </div>
</nav>
<div class="content">
    <h2>Home Page</h2>
    <p>Welcome back, <?=$_SESSION['name'];?>!</p>
</div>
</body>
</html>

<?php else: ?>
    <body>
        <div class="login">
            <h1>Login</h1>
            <form method="post">
                <label for="username">
                    <i class="fas fa-user"></i>
                </label>
                <input type="text" name="username" placeholder="Username" id="username" required>
                <label for="password">
                    <i class="fas fa-lock"></i>
                </label>
                <input type="password" name="password" placeholder="Password" id="password" required>
                <input type="submit" value="Login">
            </form>
        </div>
    </body>
</html>
<?php endif; ?>