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.
Paul M. Jones commented on Thu Dec 29 15:48:54 2005:
Regarding PHP-based templates, you may wish to examine Savant.
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.
Julian Melville commented on Tue Jan 24 21:21:30 2006:
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).
Paul commented on Tue Jan 24 22:06:27 2006:
I had never noticed that option, but as you suggest it seems worthwhile for the improvements in readability and runtime containment. Thanks!
Jessica commented on Fri Sep 8 16:30:37 2006:
“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?
Paul commented on Fri Sep 8 17:16:31 2006:
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?
Jessica commented on Tue Sep 12 09:18:14 2006:
Thanks Paul, that makes a lot more sense now. The rest are all clear. Thanks for the article.