E-Scribe : a programmer’s blog

About Me

PBX I'm Paul Bissex. I build web applications using open source software, especially Django. Started my career doing graphic design for newspapers and magazines in the '90s. Then wrote tech commentary and reviews for Wired, Salon, Chicago Tribune, and others you never heard of. Then I built operations software at a photography school. Then I helped big media serve 40 million pages a day. Then I worked on a translation services API doing millions of dollars of business. Now I'm building the core platform of a global startup accelerator. Feel free to email me.

Book

I co-wrote "Python Web Development with Django". It was the first book to cover the long-awaited Django 1.0. Published by Addison-Wesley and still in print!

Colophon

Built using Django, served with gunicorn and nginx. The database is SQLite. Hosted on a FreeBSD VPS at Johncompanies.com. Comment-spam protection by Akismet.

Pile o'Tags

Stuff I Use

bitbucket, Django, Emacs, FreeBSD, Git, jQuery, LaunchBar, Markdown, Mercurial, OS X, Python, Review Board, S3, SQLite, Sublime Text, Ubuntu Linux

Spam Report

At least 236509 pieces of comment spam killed since 2008, mostly via Akismet.

Quick, but not dirty, PHP

Though I'm doing more and more work in Python, I still write a lot of PHP code, especially for quick one-off web automation tasks.

There is plenty of activity on the other end of the scale in the PHP world now: frameworks like Cake, WASP, Solar, TaniPHP, the forthcoming Zend Framework. All this action is very cool, but doesn't address the one-page script -- and the one-page script is still worth doing right.

Many people come into PHP helter-skelter, not realizing that "Wow, it works!" is not the highest level of achievement possible. I don't offer myself as a PHP guru, but below are some of the conventions (I dare not call them "patterns") I use that I think are worth passing along.

The tone here is prescriptive ("do this"). This was a stylistic choice; naturally you are free to do whatever you want. Disagreement, corrections, and additional tips are welcome in the comments!

Use "require" instead of "include"

There's almost never a reason to use include. It fails silently. How often do you include something in your page that you consider optional? Use require or require_once.

Use "foreach()"

I almost never find C-style for loops necessary in PHP. The foreach construct is concise and elegant. If you have a list of links and a list of labels, don't do this:

$labels = array(
    'Home', 
    'Contact', 
    'About Us',
    );
$links = array(
    'home.html', 
    'contact.html', 
    'about.html',
    );
for ($i=0; $i++; $i<3)
    {
    $html .= "<li><a href='{$links[$i]}'>{$labels[$i]}</a></li>"; 
    }

Do this:

$links = array(
    'Home' => 'home.html', 
    'Contact' => 'contact.html', 
    'About Us' => 'about.html',
    );
foreach ($links as $label => $link)
    {
    $html .= "<li><a href='$link'>$label</a></li>";
    }

The loop syntax, and the HTML with interpolated variables, is much easier to read.

For loops where you're stepping through individual items, use plural/singular variable name pairs:

foreach ($stories as $story)

Use trailing commas in array definitions

Notice the commas after the final items in the arrays above. Those aren't typos. They make each line interchangeable, so that if you reorder your array you don't have to waste time adding and removing commas. This trick is commonly used in Python code as well.

Make interpolation clean

Notice the statements in the above examples that build up the HTML. The HTML attributes inside are single-quoted, which is perfectly legal, and avoids a lot of ugly blackslash-doublequote escape sequences. (Just be careful if you're inserting values that may contain single quotes themselves.)

Don't use "array_push()"

It's not necessary. The empty-bracket syntax is equivalent, and more concise and readable:

$stories[] = cleanup($story);

Use extract() inside functions

The extract() function is handy, but it can make a mess of your global namespace. Use it inside functions, which have their own self-contained namespaces. This can simplify variable interpolation greatly.

function comment_html($comment_data)
    {
    // We presume this data is sanitized
    // Keys: name, email, url, comment
    extract ($comment_data); 
    $html = "<p>Comment from <a href='$url'>$name</a></b></p>";
    $html .= "<p>$comment</p>";
    return $html;
    }

Return, don't print

As in the previous code snippet, functions should build up strings and return them, not print directly.

Use pure PHP templates

I'm a big fan of Smarty, but as I've scaled down my PHP work I don't need it much. Still, I always use templating. PHP is a template language, after all. My script files typically set up an array called $page that contains all the content passed to the template (having it all in one array makes it easy to var_dump it during debugging, and often simplifies database fetching). The end of the controller looks like this:

...
$page['title'] = "My Little Page";
$page['body'] = $html_fetched_from_database;
require "templates/page.tpl.php";

And templates/page.tpl.php looks something like this:

<?php extract($page); ?>
<html>
<head><title><?= $title ?></title>
</head>
<body>
<h1><?= $title ?></h1>
<?= $body ?>
...

When I have to embed larger chunks of HTML in a PHP file I use heredoc-style quoting, with handy folding and colorizing in TextMate.

Read good code

Finally, keep looking for and reading good code written by other people. Dive into the PEAR library, for example. Even if the sample is more complex than your current project, you'll see functions you never knew about and constructs you hadn't thought to use.

For bonus points, learn a little Python or Ruby. Even a smidgen of another language will help you understand PHP's strengths and weaknesses.

Tuesday, November 29th, 2005
+ +
6 comments

Comment from Paul M. Jones , 4 weeks later

Regarding PHP-based templates, you may wish to examine Savant.

http://phpsavant.com/

Savant uses PHP as the template markup language, but adds path management, helper plugins, and output filters. This lets you have slightly better controller/view separation and greater convenience.

Comment from Julian Melville , 8 weeks later

When using extract(), as well as using it within a function to limit namespace pollution, I use it with the EXTRACT_IF_EXISTS option like this:

$name = NULL;
$email = NULL;
extract($array, EXTRACT_IF_EXISTS);

The modifier means that only the name and email keys will be extracted from the array, and also makes the code a lot more explicit about where all these variables suddenly came from (which is nice for subsequent readers).

Comment from Paul , 8 weeks later

I had never noticed that option, but as you suggest it seems worthwhile for the improvements in readability and runtime containment. Thanks!

Comment from Jessica , 9 months later

"Return, don’t print

As in the previous code snippet, functions should build up strings and return them, not print directly. "

Why? Can you explain some of these with reasons, not just saying they're the way to do it?

Comment from Paul , 9 months later

Returning values, rather than printing, increases modularity. For example, having functions that print values directly locks you into calling those functions in a certain order. If they return values, you can rearrange those pieces (or even transform their contents if needed) in the code that calls them without diving into the base functions at all.

For another example, you may want to use the text output of a function in a non-printing context -- for instance, you might want to send it to a logfile or use it as the content of an email notification.

Returning values instead of printing makes it much easier to write functions that can be reused flexibly throughout your code.

Thanks for the comment. Are there other instances here where I fail to sufficiently explain why something seems (to me) like a good idea?

Comment from Jessica , 9 months later

Thanks Paul, that makes a lot more sense now. The rest are all clear. Thanks for the article.

Comments are closed for this post. But I welcome questions/comments via email or Twitter.