462 Chapter 23 Debugging cannot automatically tell you that a particular line will generate an error.Your testing needs to provide one of the situations that create the error. Handling runtime errors requires a certain amount of forethought; to check for dif- ferent types of failure that might occur, and then take appropriate action. It also takes careful testing to simulate each class of runtime error that might occur. This does not mean that you need to attempt to simulate every different error that might occur. MySQL for example can provide one of around 200 different error num- bers and messages.You do need to simulate an error in each function call that is likely to result in an error, and an error of each type that is handled by a different block of code. Failure to Check Input Data Often we make assumptions about the input data that will be entered by users. If this data does not fit our expectations, it might cause an error, either a runtime error or a logic error (detailed in the following section). A classic example of a runtime error occurs when we are dealing with user input data and we forget to AddSlashes() to it.This means if we have a user with a name such as O’Grady that contains an apostrophe, we will get an error from the database function. We will talk more about errors because of assumptions about input data in the next sec- tion. Logic Errors Logic errors can be the hardest type of error to find and eliminate.This type of error is where perfectly valid code does exactly what it is instructed to do, but that was not what the writer intended. Logic errors can be caused by a simple typing error, such as: for ( $i = 0; $i < 10; $i++ ); { echo 'doing something<br />'; } This snippet of code is perfectly valid. It follows valid PHP syntax. It does not rely on any external services, so it is unlikely to fail at runtime. Unless you looked at it very carefully, it probably will not do what you think it will or what the programmer intend- ed it to do. At a glance, it looks as if it will iterate through the for loop ten times, echoing “doing something" each time. The addition of an extraneous semicolon at the end of the first line means that the loop has no effect on the following lines.The for loop will iterate ten times with no result, and then the echo statement will be executed once. Because this code is a perfectly valid, but inefficient, way to write code to achieve this result, the parser will not complain. Computers are very good at some things, but they do not have any common sense or intelligence. A computer will do exactly as it is told. You need to make sure that what you tell it is exactly what you want. 29 525x ch23 1/24/03 2:56 PM Page 462 463 Variable Debugging Aid Logic errors are not caused by any sort of failure of the code, but merely a failure of the programmer to write code that instructs the computer to do exactly what he want- ed. As a result, errors cannot be detected automatically.You will not be told that an error has occurred, and you will not be given a line number to look for the problem at. Logic errors will be detected only by proper testing. A logic error such as the previous trivial example is fairly easy to make, but also easy to correct as the first time your code runs you will see output other than what you expected. Most logic errors are a little more insidious. Troublesome logic errors usually result from developers’ assumptions being wrong. Chapter 22,“Using PHP and MySQL for Large Projects,” recommended using other developers to review code to suggest additional test cases, and using people from the tar- get audience rather than developers for testing. Assuming that people will enter only cer- tain types of data is very easy to do and very easy to leave undetected if you do your own testing. Let’s say that you have an Order Quantity text box on a commerce site. Have you assumed that people will only enter positive numbers? If a visitor enters negative ten, will your software refund his credit card with ten times the price of the item? Suppose that you have a box to enter a dollar amount. Do you allow people to enter the amount with or without a dollar sign? Do you allow people to enter numbers with thousands separated by commas? Some of these things can be checked at client-side (using, for example, JavaScript) to take a little load off your server. If you are passing information to another page, has it occurred to you that there might be characters that have special significance in a URL such as spaces in the string you are passing? An infinite number of logic errors is possible.There is no automated way to check for them.The only solution is, first, to try to eliminate assumptions that you have implicitly coded into the script and, second, test thoroughly with every type of valid and invalid input possible, ensuring that you get the anticipated result for all. Va riable Debugging Aid As projects get more complex, it can be useful to have some utility code to help you identify the cause of errors. A piece of code that you might find useful is contained in Listing 23.1.This code will echo the contents of variables passed to your page. Listing 23.1 dump_variables.php—This Code Can Be Included in Pages to Dump the Contents of Variables for Debugging <?php // these lines format the output as HTML comments // and call dump_array repeatedly echo '\n<! BEGIN VARIABLE DUMP >\n\n'; 29 525x ch23 1/24/03 2:56 PM Page 463 464 Chapter 23 Debugging echo '<! BEGIN GET VARS >\n'; echo '<! '.dump_array($HTTP_GET_VARS).' >\n'; echo '<! BEGIN POST VARS >\n'; echo '<! '.dump_array($HTTP_POST_VARS).' >\n'; echo '<! BEGIN SESSION VARS >\n'; echo '<! '.dump_array($HTTP_SESSION_VARS).' >\n'; echo '<! BEGIN COOKIE VARS >\n'; echo '<! '.dump_array($HTTP_COOKIE_VARS).' >\n'; echo '\n<! END VARIABLE DUMP >\n'; // dump_array() takes one array as a parameter // It iterates through that array, creating a string // to represent the array as a set function dump_array($array) { if(is_array($array)) { $size = count($array); $string = ''; if($size) { $count = 0; $string .= '{ '; // add each element's key and value to the string foreach($array as $var => $value) { $string .= “$var = $value"; if($count++ < ($size-1)) { $string .= ', '; } } $string .= ' }'; } return $string; } else { // if it is not an array, just return it Listing 23.1 Continued 29 525x ch23 1/24/03 2:56 PM Page 464 465 Error Reporting Levels return $array; } } ?> This code will iterate through four arrays of variables that a page receives. If a page was called with GET variables, POST variables, cookies, or has session variables, these will be output. A line of HTML will be generated for each type of variable. The lines will resemble this: <! { var1 = 'value1', var2 = 'value2', var3 = 'value3' } > We have put the output within an HTML comment so that it is viewable, but will not interfere with the way that the browser renders visible page elements.This is a good way to generate debugging information. The exact output will depend on the variables passed to the page, but when added to Listing 20.4, one of the authentication examples from Chapter 20,“Using Session Control in PHP,” it adds the following lines to the HTML generated by the script: <! BEGIN VARIABLE DUMP > <! BEGIN GET VARS > <! > <! BEGIN POST VARS > <! { userid = testuser, password = test123 } > <! BEGIN SESSION VARS > <! { valid_user = testuser } > <! BEGIN COOKIE VARS > <! { PHPSESSID = 2552dc82bb465af56d65e9300f75fd68 } > <! END VARIABLE DUMP > You can see that it is displaying the POST variables sent from the login form on the previous page—userid and password. It is also showing the session variable that we are using to keep the user’s name in—valid_user. As discussed in Chapter 20, PHP uses a cookie to link session variables to particular users. Our script is echoing the pseudo-ran- dom number, PHPSESSID, which is stored in that cookie to identify a particular user. Error Reporting Levels PHP allows you to set how fussy it should be with errors.You can modify what types of events will generate messages. By default, PHP will report all errors other than notices. The error reporting level is set using a set of predefined constants, shown in Table 23.1. Listing 23.1 Continued 29 525x ch23 1/24/03 2:56 PM Page 465 466 Chapter 23 Debugging Table 23.1 Error Reporting Constants Value Name Meaning 1 E_ERROR Report fatal errors at runtime 2 E_WARNING Report nonfatal errors at runtime 4 E_PARSE Report parse errors 8 E_NOTICE Report notices, notifications that something you have done might be an error 16 E_CORE_ERROR Report failures in the startup of the PHP engine 32 E_CORE_WARNING Report nonfatal failures during the startup of the PHP engine 64 E_COMPILE_ERROR Report errors in compilation 128 E_COMPILE_WARNING Report nonfatal errors in compilation 256 E_USER_ERROR Report user triggered errors 512 E_USER_WARNING Report user triggered warnings 1024 E_USER_NOTICE Report user triggered notices 2048 E_ALL Report all errors and warnings Each constant represents a type of error that can be reported or ignored. If for instance, you specify the error level as E_ERROR, only fatal errors will be reported.These constants can be combined using binary arithmetic, to produce different error levels. The default error level, report all errors other than notices, is specified as E_ALL & ~E_NOTICE This expression consists of two of the predefined constants combined using bitwise arithmetic operators.The ampersand (&) is the bitwise AND operator and the tilde (~) is the bitwise NOT operator.This expression can be read as E_ALL AND NOT E_NOTICE. E_ALL itself is effectively a combination of all the other error types. It could be replaced by the other levels ORed together using the bitwise OR operator (|). E_ERROR | E_WARNING | E_PARSE | E_NOTICE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR |E_COMPILE_WARNING | E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE Similarly, the default error reporting level could be specified by all error levels except notice ORed together. E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE 29 525x ch23 1/24/03 2:56 PM Page 466 . wrong. Chapter 22,“Using PHP and MySQL for Large Projects,” recommended using other developers to review code to suggest additional test cases, and using people from the tar- get audience rather. occur. MySQL for example can provide one of around 200 different error num- bers and messages.You do need to simulate an error in each function call that is likely to result in an error, and an. in—valid_user. As discussed in Chapter 20, PHP uses a cookie to link session variables to particular users. Our script is echoing the pseudo-ran- dom number, PHPSESSID, which is stored in that cookie