Common Style Mistakes, Part 2
by John Coggeshall07/10/2003
In the previous article I started this series by discussing a few of the more common and serious stylistic mistakes in PHP scripts. In this article I'll continue introduce a few more good ideas which make your code more professional and save you a considerable amount of headache and development time. Let's get started by discussing a very common fault in many PHP scripts: reinventing the wheel.
More common stylistic errors
Tip 4: Don't reinvent the wheel
One of the biggest non-critical errors that PHP developers make when writing scripts is attempting to reinvent the proverbial wheel. For example, here's a function which wraps a large string to a specific column width for display to the browser or other medium:
<?php
function textwrap($text, $wrap=80, $break='<br>') {
$len = strlen($text);
if ($len > $wrap) {
$h = '';
$lastWhite = 0;
$lastChar = 0;
$lastBreak = 0;
while ($lastChar < $len) {
$char = substr($text, $lastChar, 1);
if (($lastChar - $lastBreak > $wrap)
&& ($lastWhite > $lastBreak)) {
$h .= substr($text, $lastBreak,
( $lastWhite - $lastBreak)) . $break;
$lastChar = $lastWhite + 1;
$lastBreak = $lastChar;
}
/* You may wish to include other characters
as valid whitespace... */
if ($char == ' ' || $char == chr(13) || $char == chr(10)) {
$lastWhite = $lastChar;
}
$lastChar = $lastChar + 1;
}
$h .= substr($text, $lastBreak);
}
else{
$h = $text;
}
return $h;
}?>
Is this a useful function to have available for your scripts? Of course
it is. However, the work put into developing this function has been
completely wasted because PHP already has an internal function
wordwrap() which accomplishes this exact same goal. Nothing
is more frustrating, or a bigger waste of time, than to spend hours
writing code to accomplish a task only to find out later that the function
you are trying to develop already exists.
It is always a better idea to use these PHP's built-in functions whenever possible. Not only are they less prone to having bugs than your own code, but they are always going to make your scripts execute faster. As a rule of thumb, I've found many of the functions that I've thought would be really nice to have in PHP actually do exist, so don't waste your time writing custom code without checking the PHP manual first.
Tip 5: Take advantage of the heredoc syntax
One of the most convenient features of PHP is that it is an embedded language that allows you to place HTML code directly into your script's logic. The purpose of a feature such as this is not only to produce dynamic content more easily, but also to produce cleaner code. Under certain circumstances this ability can be misused or overused, resulting in some pretty ugly scripts. Here's an example of how using embedded HTML can become more trouble than it's worth (assume all variables are defined appropriately):
<a href="<? echo $url; ?>">
<font face="arial" size=<? if($bighead > 0) echo $size+1; else echo $size; ?>>
<? if($bighead > 0) echo "<b>$headline</b>"; else echo $headline; ?></font>
</a>
Again, although this works, it is extremely difficult to read and
determine exactly what this code segment is trying to do. A better
alternative to this problem is to avoid embedded HTML altogether, instead
using an echo statement to display this hyperlink:
<?php
echo "<a href=\"$url\">" .
"<font face=\"arial\" size=" .
(($bighead > 0) ? $size +1 : $size) . ">" .
(($bighead > 0) ? "<b>$headline</b>" : $headline) .
"</font></a>";
?>
Although a bit easier to read, it still doesn't seem very clean. For
situations such as this, PHP offers a third alternative which can combine
the benefits of both the echo statement and embedded HTML,
namely, the heredoc syntax. heredoc strings are defined as follows:
$variable = <<< IDENTIFIER
[Content of the string]
IDENTIFIER;
IDENTIFIER is a string conforming to the same rules as any
other PHP variable. Also note that the closing IDENTIFIER
(following the content of the string) must meet very strict guidelines in
order to function properly:
- the closing
IDENTIFIERmust appear on its own line - the
IDENTIFIERstring must not be preceded or followed by any white space such as spaces, tabs, et cetera, except the new line (\n) character
An elusive bug often occurs when using the heredoc syntax in a
Windows-based text editor. The problem lies in Windows using a
two-character combination (\r\n) to indicate a new
line. Since UNIX systems use only the new line character (\n)
to indicate a new line, the carriage return character (\r) is
considered white space by PHP and often causes problems with heredocs. Any
Windows compatible text editor with UNIX support provides means to save
files using only the new line character, but being unaware of the problem
makes it almost impossible to figure out.
When using the heredoc syntax, the string stored in
$variable will behave in the exact same manner as a
double-quoted string. It will be parsed and any variable references will
be replaced. However, there is no need to escape characters, such as the
double-quote character, as would be necessary when working with a standard
string. To see how the heredoc syntax is used, let's rewrite the example
shown above:
if($bighead > 0) {
$link_size = $size +1;
$link_html = "<b>$headline</b>";
} else {
$link_size = $size;
$link_html = $headline;
}
echo <<< HTML_LINK
<a href="$url"><font face="arial" size=$link_size>$link_html</font></a>
HTML_LINK;
As is the case with embedding HTML in your scripts, heredocs can also be used inappropriately. However, in this case, using the heredoc has made our code much more readable and was a good choice. Using good judgment in choosing what method works best for your situation is an important aspect of writing code that's professional and easy to understand.
Tip 6: Label functions and variables appropriately
All of the discussion so far has been aimed at writing code that is easy to understand. Perhaps the most important technique is to label your functions and variables appropriately. For instance, consider the following call to a user-defined function:
<?php
$c = snews('O', 80, 7, 4, 2, 1);
echo $c;
?>
Do you have any idea what this function does? It looks as if it has something to do with news of some sort. Perhaps it saves news. What do the parameters mean? What does the return value contain? Unfortunately, there is no way to tell from this code snippet.
In reality, this function was used to display a formatted HTML table representing the latest news on this particular web site. Unfortunately, the script this line of code belongs to has been very poorly designed. As your scripts become more and more complex, it becomes essential to describe both variables you are using and the functions they are used with clearly. There are many different ways to do this.
To start, it is always good to give functions and variables descriptive
names. The name should give a third party who knows nothing of your
scripts a fairly good idea of what that function or variable does in case
documentation is not available (although you always should document
important code). The only time this rule does not necessarily apply is
when using temporary variables for use within a for loop or other similar
situation. Even in these cases, single-letter variable names should be
avoided. If our example function had been named shownews(),
or, even better, create_news_table() even someone who knows
nothing of the script could make a reasonable assumption to the function's
purpose. The same concepts apply to describing your variable names. If
the $c variable had been named $content, or even
$news_table_content, you would know exactly what that
variable should contain after the function executes.
In the case of passing or using constants within your PHP scripts, it
is simply a bad idea to use them directly. Again, what exactly do the
values of 'O', 80, 7,
4, 2, and 1 mean in that function
call? Unfortunately, there is absolutely no way to tell. A much better
solution is to use the PHP define() function to define
labeled constants and then use those constants as necessary:
<?php
define('NEWS_GRP_OTHER', 'O');
define('NEWS_MAX_COLUMNS', 80);
define('NEWS_MAX_ROWS', 7);
define('NEWS_MAX_ARTICLES', 2);
define('NEWS_FONT_SIZE', 1);
snews(NEWS_GRP_OTHER,
NEWS_MAX_COLUMNS,
NEWS_MAX_ROWS,
NEWS_MAX_ARTICLES,
NEWS_FONT_SIZE);
?>
The function call is already much easier to understand. It's clear now
what each parameter being passed to it is for. It can be confidently
modified. Furthermore, if you want to change the maximum number of rows
per article there is no need to change a value throughout the entire
script; rather, just change the value provided in the
define() function. To clean things up even further, all of
the define statements for this script can also be stored in a separate
included file. Obviously, this method is more professional and is strongly
recommended any time constants are used.
Let's pull it all together now. You be the judge in determining if a third-party could understand what the code snippet does (assuming the appropriate constants have been defined in the included file):
<?php
include_once('constants.php');
$news_table_html = create_news_table(NEWS_GRP_OTHER,
NEWS_MAX_COLUMNS,
NEWS_MAX_ROWS,
NEWS_MAX_ARTICLES,
NEWS_FONT_SIZE);
echo $news_table_html;
?>
Conclusion
That is the end of part two of my PHP Paranoia series. I hope you understand the importance of developing good practices and habits when writing your scripts. It not an understatement to claim that adopting these practices will significantly reduce the number of problems and bugs which occur in your scripts. You'll also find them significantly easier to maintain.
John Coggeshall is a a PHP consultant and author who started losing sleep over PHP around five years ago.
Read more PHP Foundations columns.
Return to the PHP DevCenter.
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 4 of 4.
-
$news_table_html
2003-07-14 01:10:54 anonymous2 [Reply | View]
Pardon me nitpicking, the news_table_html variable is useless here. Using this variable gives no extra information (which it could have) than that its content is html (who would have guessed it, the instant it is echoed?) - refactor it out and save a possibly distracting line of code. Otherwise, good point. ;) -
$news_table_html
2003-07-14 09:56:52 John Coggeshall |
[Reply | View]
In simplistic examples sometimes it's hard to illistrate a point which only really applies in much more complex examples. :)
Regards,
John
-
heredoc and parameters
2003-07-15 10:29:56 shmert [Reply | View]
Thanks for clearing up the heredoc syntax, I've seen it here and there and never understood what it was doing. I think I'll definitely use that when generating dynamic HTML, since escaping quotes is always a big mess.
As for the snews() function, you could also take a more Objective-C style approach and just include some variable names when calling the method:
<?php
generateNewsTable($maxrows=1, $offset=42, $background="#336699");
?>
This is good for functions which are used in many different places, with different parameters, in which case you probably wouldn't want to define() a parameter for each instance.
-
Re: Tip 4: Don't reinvent the wheel
2004-02-26 07:05:15 kickthedonkey [Reply | View]
This is a very good point. PHP is so large, that if you find yourself trying to write a function for something pretty basic, its a good idea to spend a few minutes searching the most excellent PHP documentation (http://us4.php.net/manual/en/).


