Every PHP Tutorial Is Wrong
by
, 11-02-2012 at 11:24 PM (92024 Views)
Many PHP tutorials begin with an introduction to the "basics" of the language: the<?php ?>
tags, and usually theecho
construct. For example, something like this:
Before I blast this, I want to make clear that it's definitely not one of the worst examples out there. It actually parses! Additionally, the reason this is "Wrong" has nothing to do with the example itself, but with how it is presented.PHP Code:
<html>
<head>
<title>My First PHP Page</title>
</head>
<body>
<?php
echo "Hello World!";
?>
</body>
</html>
Hello, World!
PHP is very welcoming to new coders because of the flexibility it allows in writing code, and, more importantly, in integrating that code with your HTML. Just open PHP (<?php
), write your code, and close PHP (?>
) to get back to your HTML page. A nice, simple example of a dynamic page:
...and everything works great.PHP Code:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Simple, Dynamic PHP</title>
</head>
<body>
<form>
<p>
<label>What's your name?
<input name="visitor_name">
</label>
<input type="submit" value="Tell Us!">
</p>
</form>
<?php
if( !empty( $_GET['visitor_name'] ) ){
$visitor_name = htmlspecialchars( $_GET['visitor_name'] );
}else{
$visitor_name = 'Guest';
}
echo "
<p>Welcome to my Simple, Dynamic Website, $visitor_name!</p>";
?>
</body>
</html>
The next fancy thing you might do is try to remember the visitor's name during their entire visit:
Looks great, right? You didn't change anything, just added a few lines to save the visitor's name to the PHP session. However, when you run this new script, you get a broken page (maybe with a warning):PHP Code:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Simple, Dynamic PHP</title>
</head>
<body>
<form>
<p>
<label>What's your name?
<input name="visitor_name">
</label>
<input type="submit" value="Tell Us!">
</p>
</form>
<?php
session_start();
if( !empty( $_GET['visitor_name'] ) ){
$visitor_name = htmlspecialchars( $_GET['visitor_name'] );
$_SESSION['visitor_name'] = $visitor_name;
}elseif( !empty( $_SESSION['visitor_name'] ) ){
$visitor_name = $_SESSION['visitor_name'];
}else{
$visitor_name = 'Guest';
}
echo "
<p>Welcome to my Simple, Dynamic Website, $visitor_name!</p>";
?>
</body>
</html>
You didn't even finish reading that, did you? (You should, and for so many reasons.)Originally Posted by PHP
So, what happened?
You wrote this in a straightforward, logical order:
- DOCTYPE and HTML <head>
- The <form> to get the visitor's name
- The PHP stuff to write the welcome message
- The closing HTML tags
Yes, that's the order that the HTML page goes in, but it's not the order that things actually happen in:
- PHP script executes, on your server
- PHP sends the script output to the visitor's browser
This hints at THE FUNDAMENTAL CHANGE you made to your webpage, probably without realizing it:
.........This Is No Longer HTML. None Of It.
.........You are writing PHP.
.........PHP is writing HTML.
It's not a webpage any longer; it's a program. It makes a webpage. Even if you simply change the extension on your webpage to.php
, without adding one opening<?php
tag anywhere, it's now a program, not a webpage. Things now happen in PHP order, not HTML order. Even when you "close" PHP by using the?>
tag, PHP doesn't really "close": it just takes everything you wrote, up to the next<?php
tag, and treats it as if you'd writtenecho "all that stuff";
.
What's the solution? A lot of new coders struggle with this (or similar) problems, sorting things out by trial and error. Even many coders who make their living as Web Designers, and have a few years' experience with PHP, never really figured out what the solution was: they just built habits that happened to avoid most problems. When the problem surfaces again, they'll still be stumped by it.
Separation of Concerns
The solution is, for lack of a simpler term, to do things "in PHP order." If you can untangle your PHP from your HTML, your program will be easier to understand and less error-prone. The big, bad programming world calls this "Separation of Concerns" - meaning, simply, that your code shouldn't be tangled up. There's a lot more to it than this, but to start with, just know:
- All of your PHP code, programming logic, functions, variables, whatever, need to come first.
- All of your output needs to go last.
There are several (good) ways to rewrite those earlier examples:
This treats the HTML part of the page as a "template." The only processing we're doing is creating a message. ThePHP Code:
<?php // PHP first!
$message = "Hello World";
?>
<html>
<head>
<title>My First PHP Page</title>
</head>
<body>
<?= $message ?>
</body>
</html><?=
tag (available always since version 5.4; only if `short_tags` are turned on in earlier versions) is specifically for templating: it means<?php echo $variable; ?>
.
It may seem like unnecessary extra work, but we've now separated our program from its output - this is only a bit more complicated to start, but prevents a lot of problems from ever showing at all up later on. So, do you prefer:
- really easy now => more confusion later
- fairly easy now => less confusion later
?
But wait; here's an even better way:
Now, there's only one point where there's any output, and it's at the very end. There's no wishy-washy-switching-back-and-forth betweenPHP Code:
<?php // PHP first!
$message = "Hello World";
$HTML = "
<html>
<head>
<title>My First PHP Page</title>
</head>
<body>
$message
</body>
</html>";
// all done
echo $HTML;
?><?php
and<html>
at all. More importantly, you understand what you're doing just a little bit better.
If your program:
- has
<?php
at the very top,- has only one part that generates output,
- that part is at the very end, and
- you can write
exit;
afterwards without affecting anything
...you've got the makings of a pretty good program. (Assuming, of course, it also throws no fatal errors.) As a bonus, it won't cause as many problems when you start to make your programs more complex.
Speaking Of Which
Questions? Ask. Late.PHP Code:
<?php
// it works up here
session_start();
// let's get the visitor name from a function (see bottom of script)
$visitor_name = get_visitor_name();
// build our HTML
// this type of string definition is called a "heredoc" -
// it's very useful for writing large chunks of HTML because you can use quotes freely.
// a heredoc starts with three less-than (<) brackets, a space, and an opening token:
$HTML = <<< HTML
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Simple, Dynamic PHP</title>
</head>
<body>
<form>
<p>
<label>What's your name?
<input name="visitor_name">
</label>
<input type="submit" value="Tell Us!">
</p>
</form>
<p>Welcome to my Simple, Dynamic Website, $visitor_name!</p>
</body>
</html>
HTML
; // a heredoc ends with a matching token, followed by a semicolon.
// all done
echo $HTML;
exit; // works!
// define a function that gets the visitor's name
// (function definitions are "okay" after output):
function get_visitor_name(){
if( !empty( $_GET['visitor_name'] ) ){
$visitor_name = htmlspecialchars( $_GET['visitor_name'] );
$_SESSION['visitor_name'] = $visitor_name;
}elseif( !empty( $_SESSION['visitor_name'] ) ){
$visitor_name = $_SESSION['visitor_name'];
}else{
$visitor_name = 'Guest';
}
return $visitor_name;
}
- Adrian