My name is Paul Bissex, and e-scribe.com is my consulting business. I build web applications using as much open source software as possible. From September to June I teach web design and other important non-photographic professional skills to photographers. In the '90s I wrote technology commentary and reviews for magazines, newspapers, and web publications, including Wired, Salon.com, FamilyPC, the late lamented Web Review, and the Chicago Tribune. Feel free to email me.
I'm co-authoring a book, "Python Web Development with Django", with Jeff Forcier and Wesley Chun. It will be published by Prentice Hall in July 2008, but is available for pre-ordering on Amazon now.
This site is built on a fresh trunk checkout of Django, running on Python 2.5.1, served by Apache and mod_python. The database is SQLite. The operating system is FreeBSD, on a VPS hosted at Johncompanies.com. Comment-spam protection by Akismet. Vintage topo imagery from the Maptech archive.
Akismet, del.icio.us, Django, dpaste.com, Emacs, FreeBSD, Freenode, jQuery, LaunchBar, MacPorts, Markdown, Mercurial, OS X, Postfix, Python, SQLite, Subversion, TextMate, Trac, Ubuntu Linux, wmii
Copyright 2008
by Paul Bissex
and E-Scribe New Media
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!
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.
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)
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.
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.)
It's not necessary. The empty-bracket syntax is equivalent, and more concise and readable:
$stories[] = cleanup($story);
The [extract()][3] 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;
}
As in the previous code snippet, functions should build up strings and return them, not print directly.
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.
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.
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).
I had never noticed that option, but as you suggest it seems worthwhile for the improvements in readability and runtime containment. Thanks!
"Return, dont 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?
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?
Thanks Paul, that makes a lot more sense now. The rest are all clear. Thanks for the article.
Comments use Markdown syntax. Your comment will not appear until approved, which may take a few hours or more. Spammers will be torpedoed.
The iPhone keyboard doesn't suck
Python one-liner of the day
7 comments
How not to advocate via Google Code
2 comments
99 problems
3 comments
bitmonk
Obscure "svn mv" problem solved
88 days ago
Charlie
Book news: Rough Cuts and Amazon
89 days ago
Simon Griffee
Django Mercurial mirror tweaks
106 days ago
Jason Calleiro
From PHP to Python
107 days ago
Yuli
dpaste.com
110 days ago
bruce
Neat Python hack: infix operators
114 days ago
David Reynolds
The original Lego Star Wars
122 days ago
At least 36614 pieces of comment spam killed since January 12th. Thanks are mostly due to Akismet.
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.