View RSS Feed

traq

Every PHP Tutorial Is Wrong

Rating: 30 votes, 4.87 average.
Many PHP tutorials begin with an introduction to the "basics" of the language: the <?php ?> tags, and usually the echo construct. For example, something like this:

PHP Code:
<html>
<head>
    <title>My First PHP Page</title>
</head>
<body>
    <?php
        
echo "Hello World!";
    
?>
</body>
</html>
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.

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:

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>
...and everything works great.

The next fancy thing you might do is try to remember the visitor's name during their entire visit:

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>
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):
Quote Originally Posted by PHP
Warning: session_start() [function.session-start]: "Cannot send session cookie - headers already sent by omg What does this even mean My SCRIPT IS BROKEN whatamigoingtodo omgomgomg type in www.dynamicdrive.com NEED HLP PLZ PHP BROKE URGENT!!!?!!!!!!
You didn't even finish reading that, did you? (You should, and for so many reasons.)

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:
  1. PHP script executes, on your server
  2. 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 written echo "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:

  1. All of your PHP code, programming logic, functions, variables, whatever, need to come first.
  2. All of your output needs to go last.


There are several (good) ways to rewrite those earlier examples:

PHP Code:
<?php // PHP first!
$message "Hello World";
?>
<html>
<head>
    <title>My First PHP Page</title>
</head>
<body>
    <?= $message ?>
</body>
</html>
This treats the HTML part of the page as a "template." The only processing we're doing is creating a message. The <?= 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:
  1. really easy now => more confusion later
  2. fairly easy now => less confusion later

?

But wait; here's an even better way:

PHP 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;
?>
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 between <?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

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;
}
Questions? Ask. Late.

- Adrian

Submit "Every PHP Tutorial Is Wrong" to del.icio.us Submit "Every PHP Tutorial Is Wrong" to StumbleUpon Submit "Every PHP Tutorial Is Wrong" to Google Submit "Every PHP Tutorial Is Wrong" to Digg

Updated 11-03-2012 at 01:27 AM by traq

Categories
Web Design issues , PHP coding

Comments

  1. keyboard's Avatar
    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;
    ?>
    How's there any advantage for doing that instead putting the stuff into a variable then just adding in an echo in the middle of your html?

    I know on several occasions you've mentioned that you should do all your php before you output anything. Is there any advantage to doing this?
    The very first bit of code you wrote works... why is there anything wrong with it? (I'm not saying we should all not use doctypes and not use semi-colons in Javascript, but this is different...)
  2. traq's Avatar
    Yes, it "works" just fine.

    (Btw, the doctype is only missing because I copied the original example directly from a tutorial, which I later decided to leave unnamed (both because it wasn't my intent to bad-mouth anyone, and because it's not really that bad a tutorial, as tutorials go). I'm not trying to write my own tutorial, here, either; just want to talk about some of the concepts behind how to code.)

    The advantage is twofold:

    1) it's easier to follow what's going on (I'm going to talk about controllers next). All the PHP happens in one place. It also reinforces the fact that PHP is processed first, and is completely isolated from its output: HTML, the DOM, and JavaScript don't even know that PHP exists.

    2) putting PHP first prevents all kinds of output-related problems. These examples are very simple, so no, it's not essential; but when you start working with sessions, cookies, http headers, or error handling for more complex HTML generation, it becomes very important to have control over how and when you send output to the browser.

    These benefits are a little difficult to realize with simple examples, but I believe it's good to make good habits early on. The fact that no one teaches these things holds a lot of people back - I can't count how many times I've explained (for example) why PHP can't do anything with a variable that JavaScript just collected from a form input, even though the PHP was "after" the form on the page.
    Updated 11-04-2012 at 04:00 PM by traq
  3. keyboard's Avatar
    I get what you're saying in point 2, and it's a good point!
    It helps to answer some of the basic questions that a lot of first timers have. (in a lot of tuts, it never really explains that php is run on a server (server-side) etc.).

    However, if you're worried about conflicting with headers you (should) be able to just start up the output buffer at the top of the page and dump it at the end.
    But it is still useful to know how it all works!

    I'd like to mention then, that these don't really apply to experienced php users... more to beginners. Would you agree with me?


    p.s. the doctype and the javascript without semi-colons was just to point out that there are a lot of things that you don't "have" to do, but you're supposed to
  4. traq's Avatar
    Personally, I believe that output buffering is (almost always) a workaround for something that could be solved by planning the program's flow more carefully. I'm not saying OB is a bad thing; but it is expensive in processing terms, and at least as hard(harder, IMO) for a beginner to understand than "PHP first."

    About beginners vs. experienced PHP developers: yes, the more experienced developer might know enough, and understand what's going on well enough, that they can write really good programs without putting PHP first, but the concept is still beneficial. I went through a lot of effort to build PHP-first habits over the past 1-1/2 years (or so), and it's really helped me with my programming. Granted, I'm doing things that are far more complicated than most PHPcoders would ever even consider, but I honestly think that I would have been better off if I could have learned this way from the beginning.
    Updated 11-04-2012 at 10:32 PM by traq (typos)
  5. bernie1227's Avatar
    Sorry to interject guys, however, I just wanted to ask traq something. If this tutorial is for people who are only just starting php, as this tutorial, you implied, is meant to be a replacement of the tried and tested <?php echo "Hello World." ?> Then shouldn't you be spending more time explaining the syntax and how the code works rather than chucking code at them and saying Stick this in your code and see what happens.? See the fact that you're doing that can have the effect on people, that rather than being able to see what the code does, and more importantly, how it works and how to write their own, the user may just turn into a copy-paste coder.

    I guess what I'm saying is; This could be a much better beginning php tutorial if you were to attempt to let the user know how it works. A good example of this in your tutorial are these parts:
    Firstly this one;
    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>
    The idea behind this method of teaching is good, however, you're not doing the key part a tutorial should do, which is explain how the code works. Basically all you say in explanation of this is; ".....you might do is try to remember the visitor's name during their entire visit:.....". So all we get from it is that it remembers the visitors name. But then we ask ourselves how, but you don't tell us. A much better way, would be to analyse how the code works rather than throwing at them and saying, "here, put this on a server". For example, we could do something like:

    in this code, the first piece of php you see is session start how this works is blablablablabla
    The next thing the program does is check whether the user has submitted the form properly. This is through the use of $_GET which is a super global variable and using the empty function to see if it's empty. If it's not empty (not is signified by !), blablablablabla
    then it sets the name of the user in the submitted form to a variable (variables are signified by $), blablablablablablablablabla
    you may also wonder what the htmlspecialchars function does (functions are signified by () at the end, and we will create our own later), what it does is blablablablablablablablablablablabla
    Then it uses another super global variable, $_SESSION, blablablablablablablablablablablablablablablablablabla
    etc.


    Anyway, you get the idea. Another place in the tutorial you could do better to explain, would be here:

    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>SimpleDynamic 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
    }
    There is so much more you could explain and ways you could explain it than just throwing in a few comments. "// define a function that gets the visitor's name
    // (function definitions are "okay" after output):
    ", does squat to tell us how this code works.

    Anyway, sorry for butting in again guys, it's just something for you to think about when doing future tutorials. You have done a very good job explaining separation of concerns and such, but not so much on the actual code.
    Thanks,
    Bernie
  6. traq's Avatar
    You're not butting in!

    You've got a good point. To answer your question in a very general way, I think the reason I didn't spend more time explaining syntax and code examples is that I didn't really intend to... What I wanted to hit was what I thought was wrong with so many tutorials, not to define a "new standard" for tutorials. Just the idea. Likewise, while I'm talking about beginners, and what I think would be good things to teach them first, beginners weren't really my sole target audience. I had something I'd been thinking about for a long time, and I wanted to share it with people with a wide range of experiences.

    I'm not trying to sidestep what you're saying. I completely agree that this is [would be] a lousy tutorial. I'm wanting to do a tutorial in the future, but it would need to have a lot more effort put into it than this blog post...

    My second blog, on retrospect, looks even more like it's "supposed to be" a tutorial. Maybe I'm going in the wrong direction - maybe I should be doing "Tutorial-First." Thoughts on this? Ideas for a specific topic?
  7. bernie1227's Avatar
    I don't really mind whether you do tutorials aimed towards people with more experience, however you may wish to actually make it more obvious in the blog what the purpose of the blog is and who it is aimed at, as this tutorial definitely seems to be geared more towards beginners, otherwise you wouldn't be explaining how to use php.