Generally speaking, your PHP code can be sorted into two categories:
- code which does work (processing input, controller logic, database access, error handling, etc.), and
- code which produces output (
echo,<?= $var ?>, plain<html>, etc.).
work goes FIRST. output goes LAST.
a bad example
<?php
$page["id"] = 1;
$page["title"] = "Hello, World!";
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title><?= $page["title"] ?></title>
</head>
<body>
<?php
session_start();
if (isset($_SESSION["user"])) {
echo '<p class="welcome">Welcome, '.$_SESSION["user"].'!';
} else {
echo '<p class="welcome">Welcome, Guest!</p>';
echo '<a href="/login.php">Click Here to log in</a>';
}
?>
<h1><?= $page["title"] ?></h1>
<?php
$DB = new PDO( … );
$stmt = $DB->prepare("SELECT heading,body FROM content WHERE page=:page");
$stmt->execute(["page" => $page["id"]]);
while ($row = $stmt->fetch()) {
echo "<h2>".htmlspecialchars($row["heading"], ENT_QUOTES, "UTF-8")."</h2>";
echo "<p>".htmlspecialchars($row["body"], ENT_QUOTES, "UTF-8");
}
?>
</body>
</html>As you might have guessed, this won't even work. For example, you'd say, "everyone knows session_start() has to be at the top of your script!!" (ahh, but why?)
But there are probably more problems than you think. What happens if there's an error connecting to the database?
"Welcome, Guest! Fatal Error: uncaught PDOException with message 'here's my DB password!' ..."?
You can't even show a nice error page, because you've already started sending this page to your user.
a better example
<?php
session_start();
$page["id"] = 1;
$page["title"] = "Hello, World!";
$user = $_SESSION["user"] ?? "Guest";
$DB = new PDO( … );
$rows = $DB->prepare("SELECT heading, body FROM content WHERE page=:page");
$rows->execute(["page" => $page["id"]]);
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title><?= htmlspecialchars($page["title"], ENT_QUOTES, "UTF-8") ?></title>
</head>
<body>
<p class="welcome">Welcome, <?= htmlspecialchars($user, ENT_QUOTES, "UTF-8") ?>!
<?php if ($user === "Guest") : ?>
<p><a href="/login.php">Click Here to log in</a>
<?php endif; ?>
<h1><?= htmlspecialchars($page["title"], ENT_QUOTES, "UTF-8") ?></h1>
<?php foreach ($rows as $row) : ?>
<h2><?= htmlspecialchars($row["heading"], ENT_QUOTES, "UTF-8") ?></h2>
<p><?= htmlspecialchars($row["body"], ENT_QUOTES, "UTF-8") ?>
<?php endforeach; ?>
</body>
</html>Just a little bit of reorganization, and all the problems are gone. See how there's a clear, dividing line that separates your "behind-the-scenes" work on the server from the body of your response?
Writing your code this way will also help you understand how php actually runs, too.
Looking at a php script, it's easy to assume that it's like an html page, and that everything happens top-to-bottom. But this does not hold true in php - php runs on your webserver, and html is rendered on the user's web browser. The work and the output happen at completely different times and places: php code, even if it's the last thing on your webpage, runs before all the html is rendered.
If you're not thinking about this, it can make some behaviors seem unexpected or out of order. But intentionally putting all the "work" at the top makes it more natural to read and understand the order of operations.
"php FIRST" is really the least you should be doing; but it is a big, positive step. If you do this and nothing more, my rage will be quieted. However, it's also a solid foundation for learning about:
- templating
- separation of concerns
have fun!