PHP DevCenter
oreilly.comSafari Books Online.Conferences.

advertisement


PHP Foundations Common Style Mistakes, Part 2

by John Coggeshall
07/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 IDENTIFIER must appear on its own line
  • the IDENTIFIER string 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.


Valuable Online Certification Training

Online Certification for Your Career
Earn a Certificate for Professional Development from the University of Illinois Office of Continuing Education upon completion of each online certificate program.

PHP/SQL Programming Certificate — The PHP/SQL Programming Certificate series is comprised of four courses covering beginning to advanced PHP programming, beginning to advanced database programming using the SQL language, database theory, and integrated Web 2.0 programming using PHP and SQL on the Unix/Linux mySQL platform.

Enroll today!


Sponsored by: