You've done your best to make your code watertight, and yes, some errors still manage to seep through. How do you go about removing the little blighters? That process is called troubleshooting. Troubleshooting gradually eliminates potential causes of problems until you've found the right one and then change the code to eliminate it. Troubleshooting works for both syntax and logic errors. Of course, each time you change something, you must test again, to see if the correction worked, if the change you made provided more clues as to the nature of the problem, or if it may have caused a new problem elsewhere.
Understanding PHP Error Messages
Whenever PHP encounters an error condition that affects its capability to complete data processing, it generates an error message. Serious errors cause PHP to simply stop whatever it's doing and quit processing, displaying a "Fatal Error" error message on the screen (if you've enabled the display of error messages in your configuration file). Errors that are not quite as serious may interrupt only a small part of the overall processing PHP is attempting to do, and in that case perhaps only a "Warning" error message is displayed. Errors that don't affect processing may still occur, and those you have to figure out for yourself.
Configuring PHP for Error Handling
You can set a number of options in the php.ini file to help you control PHP's behavior when errors are encountered. Some of the more important of these configuration settings are:
- display_errors: Sets the display of error messages to the screen on or off. Note that if you set error messages off, a serious error may leave the user with a blank screen.
- error_reporting: Can be set using a value representing the level of error reporting to perform. For example, setting this option to the value 3 means the first two error types of error messages listed in the documentation will be generated.
- log_errors: Tells PHP whether to log errors to the error log. As with any other logging system, you may need to decide how many error messages you want to accumulate in the log file.
- track_errors: Enable this option and the most recent error message, whatever it be, is always available in the built-in $php_errmsg variable. This variable is not a superglobal variable; it's available only within the scope in which the error occurred.
PHP Error Types
In the broad scheme of things there are basically only two types of errors: errors that break your program through bad syntax or a runtime problem, and errors that simply produce the wrong answer. PHP makes several error levels available within that first category:
- Fatal Error: Completely shuts down processing. There are fatal runtime errors, compile-time errors, and parse errors.
- Warning: Indicates that an error condition may have occurred, but the script will go on processing beyond the point at which the error occurred.
- Notice: Indicates that something of interest may have happened. These errors include runtime notices and user-generated notices.
If you've enabled the display_errors option in php.ini, error messages will be displayed to the screen, and that's often the fastest way to find and debug errors while you're building an application. For production applications, you may want to disable the display_errors option, and have errors logged instead.
Syntax Errors
Syntax errors are simply errors in writing your code, such as typos, missing symbols or marks, and incorrect use of operators. They can't be parsed properly, and generate error messages. There are several common causes of syntax errors that you can learn to avoid:
- Simple typos: It's easy to type a semicolon or a curly brace instead of a colon or square bracket. Learn to check your spelling and syntax as you type.
- Not properly closing statements: Put all the braces in your statements even when they aren't technically required for the code to run. It's a little more work, but once you get used to it, you'll be more apt to code them correctly, without missing braces. Look out for improperly closed code like this:
for ($myloop01 = 0; $myloop01<10; $myloop01++) { for ($myloop02 = 2; $myloop02<20; $myloop02++) { for ($myloop03 = 3; $myloop03<30; $myloop03++) { for ($myloop04 = 4; $myloop04<40; $myloop04++) { ... More code goes here... } } - Code not indented properly: It's not always obvious if the loops are closed 10 pages of code later, but indenting the code certainly helps, as well as make it easier to understand afterward. Try formatting your loops this way:
for ($myloop01 = 0; $myloop01<10; $myloop01++) { for ($myloop02 = 2; $myloop02<20;$myloop02++) { for ($myloop03 = 3; $myloop03<30; $myloop03++) { for ($myloop04 = 4; $myloop04<40; $myloop04++) { ... More code goes here... } } }From the indentation, it's pretty easy to see the missing brace.
- Forgetting to end a statement with a semicolon: It's true that there are a few instances in which it is acceptable to leave the semicolon off at the end of a line, but not having a semicolon when you should is a big problem. Always insert the semicolon, even when you don't have to, to enforce the habit.
- Misspelling the name of a function: Even a small typo in the name of a function will cause an error. Transposing, omitting, or adding letters to a function name are all errors to avoid. Be careful with your typing, and check your spelling often (don't count on any spell-checking program to catch function name errors.
- Forgetting the closing quotes for a string, or incorrectly mixing single and double quotes: It's easy to forget to close quotes on a string, or how many single quotes you've used, especially when you're constructing long strings with lots of variables interspersed, like this:
echo "This is the beginning of a string with " . $number " quotes, both
single, like this \' and double, like this \" and quotes in HTML, like this
<img src="image.gif">;
The first two quotes (the single and the double) are properly escaped, but the last two double quotes in the HTML are not. Additionally, the finishing double quote is left off. PHP would generate parse errors (incorrect syntax) on this statement.
As mentioned, you'll see syntax errors frequently the first time you test your code. Although PHP provides some very good error messages, one difficulty you might encounter when debugging is finding out exactly where the error is occurring. For example, suppose you have the following code and try to run it:
<?php
//generate a parse error
$line of code = "error: no semicolon"
?>
This code produces a syntax or parse error. The PHP processing engine can't completely parse the code because of the error (missing semicolon), so the code doesn't run. Instead it generates the following error message:
Parse error: parse error in /home/servata/www/error_test_file.php on line 3
The error message contains a description of the error and the line number that generated it. Sometimes, though, the line number can be misleading, such as when you forget a curly brace, as shown in this example:
<?php //generate a parse error if ($my_test == 0) { //do something } else { ?> <html> <head> <title></title> </head> <body> lots of html here<br> lots of html here<br> lots of html here<br> lots of html here<br> lots of html here<br> lots of html here<br> lots of html here<br> lots of html here<br> lots of html here<br> lots of html here<br> lots of html here<br> lots of html here<br> lots of html here<br> lots of html here<br> lots of html here<br> </body> </html>
And the error message:
Parse error: parse error in /home/servata/www/error_test_file02.php on line 31
As good as this information is, in this particular case, the line number is misleading because there are only 29 lines in the code, and even if you look at the bottom of the file it may not be apparent that the problem is a missing curly brace, because you've closed off your PHP properly before beginning your HTML.
Most PHP errors are on or very close to the line number noted in the error message—but not always. It really pays to be attentive to writing good code in the first place, and with some practice you can avoid most syntax errors altogether. To eliminate them, just rewrite your code and test again.
Logic Errors
Logic errors occur when your code runs but doesn't produce the right answer. For example, if you were supposed to add two numbers but have subtracted them instead, the code runs just fine, but hopefully you notice right away that the answer is wrong.
Logic errors can be difficult to detect, particularly when an incorrect answer only occurs under a special set of circumstances. If getting the right answer is critical (such as when performing bank transactions), it pays to have a more elaborate and comprehensive testing program specifically designed to test all possible inputs. And when reporting errors, the reports must specify in detail the circumstances under which each error occurred.
You'll have logical errors in odd ways. For example, you run the program and it either starts returning data that can't possibly be correct, or it doesn't return any data at all. In some cases it may not even return any output to the screen. At times your program works fine, but then you give it to the user and it suddenly breaks. All of these are usually the result of mistakes in the logic of program.
There is further distinction to make here between the types of logical error. Some logical errors occur at runtime, and only under certain conditions. For example, the PHP code seems fine, and is even accepted and executed by the PHP engine, but errors occur when you run the program, stopping your program from completing. These are known as runtime errors. Runtime errors that stop your program from completing execution are termed fatal. Syntax errors are invariably fatal, but runtime errors that stop your program's execution only under certain circumstances, aren't always fatal.
The second type of logical error doesn't actually cause your program to break but runs to completion and then returns incorrect information or no information at all. These are, for want of a better description, unexpected output errors. Let's take a deeper look at runtime errors first.
Runtime Errors
Runtime errors differ from parse errors in that they occur after the PHP engine has successfully parsed and is executing your code. Runtime errors are often caused by a bad connection to an outside resource such as a file or a database, but sometimes the errors are more mundane (although still hard to find). For example, PHP will throw an error if you try to divide by zero. In the next few sections you'll explore these error types, but the file and database connections are covered in later chapters.
Dividing by Zero
Mathematically, there's no acceptable way to divide by zero (no correct answer can be represented) so inadvertently dividing by zero causes PHP to emit an error. No one would deliberately create an application that tries to divide by zero (except perhaps as an example of a program that won't work), so this type of error is usually caused unintentionally. Because variables are used to contain and process values and often depend on user input, and because users sometimes enter erroneous values, you can easily see how division by zero might occur in practice.
For example, suppose you have a form in which you ask a user to enter several values (quantities for an order, or the number of children he has, or the like) and then use these numbers to calculate a discount, total price, rooms required, or that kind of thing.
If the user happens to make a mistake or simply forgets to enter a value, or just doesn't properly understand what he's supposed to do, you could easily end up with a situation in which the program tries to divide by the non-existent number. Depending on the calculations, then, an attempt to divide by zero could be made, and an error will be thrown.
Here's a very simple form that demonstrates what happens when a divide-by-zero error is encountered. Fire up your trusty HTML editor and enter the following code, naming it divide_by_zero.php:
<html> <head><title></title></head> <body> <b>Divide Any Number (except zero)</b> <br> <br> <?php if (isset($_POST['posted'])) { $first_number = $_POST [' first_number' ]; $second_number = $_POST [' second_number' ]; $answer = $first_number / $second_number; echo "Your answer is " . $answer . "<br>"; } ?> <form method="POST" action="divide_by_zero.php"> <input type="hidden" name="posted" value="true"> Please enter the first number: <br> <br> <input name="first_number" type="text" value="0"><br> Please enter the second number: <br> <br> <input name="second_number" type="text" value="0"><br> Divide when ready! <br> <br> <input type="submit" value="Divide"> </form> </body> </html>
This is a very simple example, but notice that it doesn't show any kind of a parse error if the user enters appropriate values. As you develop, remember that most of the time you'll be inclined to use appropriate values, and it's easy to miss this type of error as you test and check your application.
In this example notice that if you happen to enter (or leave) the first number as 0, no error is generated (the answer is zero; try it to see for yourself!). And even if the second number is erased from the screen entirely, PHP still interprets the empty string as 0, and produces the error message. One thing you can do to at least reduce the possibility of error is to set the default values of the first_number and second_number form fields to 1 instead of 0 in your HTML code. You also could validate incoming values to make sure they are not equal to zero. (Validation of form submission values is covered later in this chapter.)
So in your applications, watch out for the possibility of division by zero, as well as other mathematical functions that may cause problems, such as calculating the square root of a negative number.
Infinite (Never-Ending) Loops
Looping structures are some of the most basic programming constructs in any language, and they are also the foundation for one of the most common errors in programming, the infinite or never-ending loop. Their power inherently makes them susceptible to this error because they are designed to keep running until a particular condition is met. If that condition is never met, they never end.
In PHP, one of the problems with using looping structures such as while and do while is that if the condition that you set is never matched, PHP executes the loop a potentially infinite number of times within the limits of the max_execution_time setting (in the php.ini file). If the max_execution_time setting is exceeded, PHP emits a warning error and ends processing. Like any error message, you can display it and see what happened, but you cannot recover from this particular error condition. Consider the following, but do not attempt to run it:
$cntr=l;
$test = True;
while ($test)
{
$cntr++;
}
While the counter variable ($cntr) is incremented each time through the loop, $test has no possibility of becoming False, so the loop never ends. If you happen to be running this on a Windows server, you could well crash the server (Linux machines running Apache seem to behave in a much better fashion, but you'll still get the warning when you exceed max_execution_time).
To fix this type or error (or make sure it never happens in the first place) make certain that whatever condition is used to run the loop has some way to force it to become False (or whatever value it needs to be to end the loop).
Logical Output Errors
If your application has no parse errors, and never emits a warning for a runtime error, it can still give you a logical output error. Basically, this means that your program has been executed correctly, but you just didn't set up your processing properly, or you didn't anticipate that a particular value might be used during processing. You get output correctly, just not the value you expected.
A very good example of this is when you use percentages. For example, suppose you are expecting the user to enter a percentage number of 0.0775 (the sales tax rate in California), but the user instead enters 7.75. Imagine what that would do to the total sales tax charged on his order! But nothing in this scenario would automatically cause a warning or error; PHP would simply perform the calculations and give the answer. It's really up to you to anticipate and avoid these errors, and only plenty of thorough testing will give you confidence in your application.
Functions Without a Return Value
When you create your own functions (covered in Chapter 6), you may include the return statement, instructing the function what value to return when the function is called. Not all functions need a return statement, so it's relatively easy to forget to include one. And if you leave the return statement out when it's required, your function will return nothing.
If you use that function and expect a result from it that you use elsewhere, it might be a while before you realize that your function is returning nothing. Uncovering this mistake can be very frustrating, so always give your functions at least one test run to make sure they are returning a result if you expect them to.
Function Arguments Order Incorrect
Functions frequently need to be sent arguments to perform their processing. Functions expect arguments in a particular order, and if you happen to mix that order up, the function has no way to know that. So if your function happens to require two numerical values which it then performs a calculation on, and you mix up the order of these arguments, watch out! You'll get a value back, alright, but it won't be correct.
Setting Values vs. Comparing Values
One common mistake in PHP is to use the equals sign (=) to try to compare values, when in fact it sets a value instead. No warning is given; if you screw this one up, PHP simply sets the value and proceeds on that basis. It's very common to find this mistake in if...then...else structures, like this:
If ($my_value = $your_value) {
//Do this
} else {
//Do that
}
Unfortunately, it doesn't look obviously wrong, and because it sounds right in your head (after all, you're probably reading either usage of = or == in your head as "equals"), especially if you're familiar with another language that allows the single = sign for comparison, it's not always easy to avoid. There's no magic bullet for this one; just be careful: use = to set values, and use == to compare values.
