Debugging and Performance

Debugging is an important part of coding. One way to make the debugging process easier is to write quality code to begin with. This article, the first of two parts, will point out some of the most common coding errors, and help you identify problems in your code. It is excerpted from chapter 12 of Zend PHP Certification, written by George Schlossnagle et al (Sams; ISBN: 0672327090).

Making mistakes is human, and so is fixing them. In your day-to-day programming adventures, it’s inevitable to introduce bugs in your PHP code, especially when you’re writing very complex applications with tens of thousands of lines of code spread across tens of files.

When you’re prototyping an application, being able to avoid common programming mistakes is important to ensure that your code will be well-written from the very start. In this chapter, we’ll provide you with some guidelines on writing efficient code, debugging faulty scripts, and identifying bottlenecks when performance becomes an issue for both you and your clients.

Terms You’ll Need to Understand
  • Bug

  • Coding standard

  • Code readability

  • Comparison operators

  • Performance

  • Caching

  • Portability

Techniques You’ll Need to Master
  • Writing readable code

  • Proper commenting

  • Comparing heterogeneous data

  • Debugging

  • Identifying and preventing performance bottlenecks

  • Preventing performance issues

  • Improving database performance

  • Using content and bytecode caching

Coding Standards

Writing your code in a structured manner is, perhaps, the smartest decision you can make. Although there aren’t any predefined coding standards that everyone in the programming community recognizes as better than the rest, deciding from the very beginning on a set of conventions will go a long way toward helping you make fewer mistakes.

Documenting your code is particularly important. To make this job—probably at the top of the Ten Most Hated Tasks of programmers worldwide—a bit easier, you can even use one of the many automated tools available on the market, such as PHPDocumentor, which can extract documentation directly from your code if you structure your comments in a particular way.

Regardless of how you introduce them in your applications, good comments and documentation will make sharing your code with other members of your team easier, as well as make sure that you’ll remember what it does when you get back from that three-week vacation. Remember, preventing bugs is much better than hunting for them.

Extra whitespace and empty lines, although unimportant as far as the functionality of your code is concerned, can be an extremely valuable tool for writing better code:

if ($foo == 'bar') 
{
       $i = 0;

       /**
     * foreach loop, get the content out of it
        */

       foreach ( .... ) 
       {
       }
}

By separating your code into logical groups, your source will be cleaner and easier to read. Also, indenting each line according to the code block it belongs to helps you figure out immediately what the structure of your script is.

{mospagebreak title=Flattening if Statements}

Consider the following snippet of code:

if ($is_allocated) 
{
    if ($has_been_mangled) 
    {
        if ($foo == 5) 
        {
             print "foo is 5";
        } 
        else 
        {
             print "You entered the wrong data!";
        }
    } 
    else 
    {
        return false;
    }
}
else 
{
    return false;
}

As you can see, the many nested if statements here cause the code to look very busy and difficult to read. An easy way to improve the situation consists of "flattening" your if statements so that you can achieve the minimum level of indentation without compromising either the functionality of your code or its performance. The preceding script, for example, could be rewritten as follows:

if (!$is_allocated) 
{
    return false;
}

if (!$has_been_mangled) 
{
    return false;
}

if ($foo == 5) 
{
    print "foo is 5";
} 
else 
{
    print "You entered the wrong data!";
}

This approach gives you a better structure with fewer levels of nesting so that your code is easier to understand. Note that the type of operations performed is pretty much the same as before—and the elimination of two else statements will make the code easier to parse for the interpreter.

{mospagebreak title=Splitting Single Commands Across Multiple Lines}

One of the great things about PHP is that it doesn’t require you to write a single statement all on one line of code. In fact, any statement can be split across an arbitrary number of lines without any change in its functionality—provided, of course, that the split doesn’t take place in the middle of a text string. This is particularly useful when you have a complex line of code that spans a large number of characters:

$db->query("select foo,
            bar,
            mybar as foobar
            from tbl1
            where tbl1.mybar='foo'");

This database query is split over several lines. The main advantage here is that you can immediately see what the query does, which tables are involved, and which conditions you are placing in the where clause. If the same query had been placed all on the same line, understanding its purpose would have taken a lot more effort, and the risk of introducing new bugs by modifying it would have been greater.

Concatenation Versus Substitution

If you are inserting data into a long string—such as a database query—you can use the concatenation operator, but doing so often limits your ability to read the query properly:

$db->query
("insert into foo(id,bar) 
values('".addslashes($id).
"','".addslashes($bar)."')");

On the other hand, you could just use one of the printf() functions to do the job for you:

$db->query(sprintf("insert into foo(id,bar)
values('%s','%s')", addslashes($id), addslashes($bar) ));

As you can see, the entire statement is now a lot easier to read, although you will lose some performance by switching to sprintf() from the concatenation operator, which is native to the PHP interpreter and doesn’t require the execution of any external libraries. The literals in the string passed to sprintf() will be substituted with the values of the parameters passed afterwards in the order in which they appear in the call. Combined with the ability to split your commands over several lines, this approach can enhance readability to a large degree.

Choose Your Opening Tags Carefully

Mixing PHP and HTML code is one of the characteristics of PHP that make it both easy to use and powerful, although it’s easy to abuse this capability and come up with code that is difficult to read.

When writing code for an application that could run on heterogeneous systems, it’s always a good idea to be very careful about which opening tag styles you use. In Chapter 1, "The Basics of PHP," we mentioned that there are several of them, but only the canonical tags <?php ?> are fully portable. Short tags (which include the echo tag <?=) and ASP tags can all be turned off through PHP configuration directives.

Thus, the following

<?php print "Testing 1 2 3" ?>

is longer than

<?= "Testing 1 2 3" ?>

But not quite as portable. Note, also, that there is a subtle difference between print and echo. Although they are both language constructs, the former acts as a function—meaning that it actually returns a value (always a Boolean True)—whereas the latter does not. Thus, the following code is valid, although quite pointless:

<?php echo print (10) ?> 
{mospagebreak title=One Equal, Two Equals, Three Equals}

How often did you write the following code?

if ($a = 5) 
{
    print "a is 5";
}

If you’re like most programmers, the answer is an unfortunate "often." The problem here is caused by the fact that the if statement allows for any operations to take place inside its condition—including assignments. Thus, the preceding line is not technically incorrect, but it’s obviously not what the author intended to perform, as it will always be evaluated to true, making the if statement pointless and, what’s worse, changing the value of $a.

Clearly, the statement should have been written as follows:

if ($a == 5
{
    print "a is 5";
} 

In this case, the condition is a comparison operator, and it will be evaluated as true only if the value of $a is 5.

There is, luckily, a very easy way to avoid this mistake once and for all, without any possibility of ever slipping again: make sure that the condition is written in such a way that it cannot possibly be misinterpreted:

if (5 == $a) 
{
       print "a is 5";
}

With this approach, if you mistakenly only use one equal sign instead of two, as in 5 = $a, the interpreter will print out an error because you can’t assign anything to an immediate value. If you make a habit of writing all your conditions this way, you will never fall in the assignment trap again!

There’s Equal and Equal

As we mentioned in Chapter 1, PHP is a loosely typed language. This means that, under the right circumstances, it will automatically juggle data types to perform its operations according to how programmers are most likely to want it to.

There are scenarios, however, in which this is not a desirable approach, and you want, instead, PHP to be strict and literal in the way it compares data. Consider, for example, what would happen if you were dealing with information coming from a patient’s medical record. In this situation, you’ll want to make sure that nothing is left to chance and that PHP doesn’t attempt to interpret user input in too liberal a way.

Generally speaking, it’s always a good idea to use the identity operators (=== and !==) whenever you know that a value has to be of a certain type:

if ($a !== 0) {
    echo '$a is not an integer zero';
}
{mospagebreak title=Testing for Resource Allocation}

One of the most common mistakes that causes code to become unreliable consists of using external resources without ensuring that they are available. For example, look at the following code:

$res = mysql_query("select foo from bar");
while ($row = mysql_fetch_array($res)) 
{
    print $row['foo']."<br>";
}

See what’s wrong? The author doesn’t test for the query’s failure before moving on to perform other tasks that use the resource returned by mysql_query(). The query could fail for a number of reasons, even though it is syntactically correct—for example, the server might be unavailable, or there could be a network interruption. What’s worse in this particular case, the MySQL extension does not cause a fatal error if a query cannot be executed. Therefore, the script moves on, and a cascade of additional problems could be caused by this initial blunder.

If, on the other end, error conditions are properly tested for, this issue doesn’t even present itself:

if (!$res = mysql_query("select foo from bar")) 
{
    /**
    * no valid result, log/print error,
mysql_error() will tell you */ } else { while ($row = mysql_fetch_array($res)) { print $row['foo']."<br>"; } }

It’s undoubtedly hard to write an if statement every time you execute a query—but also necessary if you are serious about error management. To make things a bit easier on yourself (and your entire team), you could adopt one of the many abstraction layers available or write one yourself. This way, the actual error management can be performed in a centralized location (the abstraction layer), and you won’t have to write too much code.

It’s important to keep in mind that this process is required whenever you interact with an external resource, be it a database, a file, or a network connection.

Starting with PHP 5, you can use other error-control structures known as exceptions. However, remember that these are not available in PHP 4 and, therefore, cannot be used to solve a problem that appears in the exam.

Ternary Operators and if Statements

if statements are necessary control structures for all but the simplest of PHP scripts. As a result, sometimes they will tend to be very complex, even if you nest them on various levels.

Luckily, the ternary conditional operator that you saw in Chapter 1 can be used to simplify the use of if statements by embedding them directly in a larger expression. For example, consider the following snippet of code:

function is_my_country($country) 
{
    if (strlen($country) == 3) 
    {
        return 1;
    } 
    else 
    {
        return 0;
    }
}

It could also be written as

function is_my_country($country) {
   return (strlen($country)==3) ? 1 : 0;
}

As you can see, the function is much shorter than the if statement in the preceding example. This can be very valuable if you’re dealing with a complex piece of code such as the following:

$db->query(sprintf("insert into foo(f1,f2,f3)
values('%s','%s','%s')", (isset($_SESSION['foobar'])) ? 'yes' : 'no', (isset($_POST['myfoo']) && $_POST['myfoo']!='')
? $_POST['myfoo'] : 'no', 'foo'));

A call such as the preceding one would have been a lot more complex if it had been written using traditional if statements—not to mention that you would have needed either a number of new variables to hold the information, or a different set of function calls for each possible scenario.

Please check back next week for the conclusion of this article.

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri