457 Programming Errors This script will generate the following error message: Parse error: parse error in /home/book/public_html/chapter23/error.php on line 4 Obviously, as our script only has three lines, our error is not really on line four. Errors in which you open something, but fail to close it will often show up like this.You can run into this problem with single and double quotes and also with the various forms of parentheses. The following script will generate a similar syntax error: <?php if (true) { echo 'error here'; ?> These errors can be hard to find if they result from a combination of multiple files.They can also be difficult to find if they occur in a large file. Seeing “parse error on line 1001" of a 1000 line file can be enough to spoil your day and act as a subtle hint that you should try to write more modular code. In general though, syntax errors are the easiest type of error to find. If you make a syntax error, PHP will give you a message telling you where to find your mistake. Runtime Errors Runtime errors can be harder to detect and fix. A script either contains a syntax error or it does not. If the script contains a syntax error, the parser will detect it. Runtime errors are not caused solely by the contents of your script.They can rely on interactions between your scripts and other events or conditions. The following statement: include ('filename.php'); is a perfectly valid PHP statement. It contains no syntax errors. This statement might, however, generate a runtime error. If you execute this state- ment and filename.php does not exist or the user who the script runs as is denied read permission, you will get an error resembling this one: Fatal error: Failed opening required 'filename.php' (include_path='.:/usr/local/lib/php') in /home/book/public_html/chapter23/error.php on line 1 Although nothing was wrong with our code, because it relies on a file that might or might not exist at different times when the code is run, it can generate a runtime error. The following three statements are all valid PHP. Unfortunately, in combination, they are attempting to do the impossible—divide by zero. 29 525x ch23 1/24/03 2:56 PM Page 457 458 Chapter 23 Debugging $i = 10; $j = 0; $k = $i/$k; This code snippet will generate the following warning: Warning: Division by zero in /home/book/public_html/chapter23/div0.php on line 3 This will make it very easy to correct. Few people would try to write code that attempt- ed to divide by zero on purpose, but neglecting to check user input often results in this type of error. This is one of many different runtime errors that you might see while testing your code. Common causes of runtime errors include the following: n calls to functions that do not exist n reading or writing files n interaction with MySQL or other databases n connections to network services n failure to check input data We will briefly discuss each. Calls to Functions That Do Not Exist It is easy to accidentally call functions that do not exist.The built-in functions are often inconsistently named.Why does strip_tags() have an underscore, whereas stripslashes() does not? It is also easy to call one of your own functions that does not exist in the current script, but might exist elsewhere. If your code contains a call to a nonexistent function, such as nonexistent_function(); you will see an error message similar to this: Fatal error: Call to undefined function: nonexistent_function() in /home/book/public_html/chapter23/error.php on line 1 Similarly, if you call a function that exists, but call it with an incorrect number of param- eters, you will receive a warning. The function strstr() requires two strings: a haystack to search and a needle to find. If instead we call it like this: strstr(); 29 525x ch23 1/24/03 2:56 PM Page 458 459 Programming Errors We will get the following warning: Warning: Wrong parameter count for strstr() in /home/book/public_html/chapter23/error.php on line 1 As PHP does not allow function overloading, this line will always be wrong, but we might not necessarily always see this warning. That same statement within the following script is equally wrong: <?php if($var == 4) { strstr(); } ?> but except in the, possibly rare, case in which the variable $var has the value 4, the call to strstr() will not occur, and no warning will be issued. Calling functions incorrectly is easy to do, but as the resulting error messages identify the exact line and function call that are causing the problem, they are equally easy to fix. They are only difficult to find if your testing process is poor and does not test all condi- tionally executed code.When you test, one of the goals is to execute every line of code exactly once. Another goal is to test all the boundary conditions and classes of input. Reading or Writing Files Although anything can go wrong at some point during your program’s useful life, some things are more likely than others. Errors accessing files are likely enough to occur that you need to handle them gracefully. Hard drives fail or fill up, and human error results in directory permissions changing. Functions such as fopen() that are likely to fail occasionally generally have a return value to signal that an error occurred. For fopen(),a return value of false indicates failure. For functions that provide failure notification, you need to carefully check the return value of every call and act on failures. Interaction with MySQL or Other Databases Connecting to and using MySQL can generate many errors.The function mysql_ connect() alone can generate at least the following errors: n MySQL Connection Failed: Can't connect to MySQL server on 'hostname' (111) n MySQL Connection Failed: Can't connect to local MySQL server through socket '/tmp/mysql.sock' (111) 29 525x ch23 1/24/03 2:56 PM Page 459 460 Chapter 23 Debugging n MySQL Connection Failed: Unknown MySQL Server Host 'hostname' (2) n MySQL Connection Failed: Access denied for user: 'username@local- host' (Using password: YES) As you would probably expect, mysql_connect() provides a return value of false when an error occurs.This means that you can easily trap and handle these types of common errors. If you do not stop the regular execution of your script and handle these errors, your script will attempt to continue interacting with the database.Trying to run queries and get results without a valid MySQL connection will result in your visitors seeing an unprofessional-looking screen full of error messages. Many other commonly used MySQL related PHP functions such as mysql_ pconnect(), mysql_select_db(),and mysql_query() also return false to indicate that an error occurred. If an error occurs, you can access the text of the error message using the function mysql_error(),or an error code using the function mysql_errno(). If the last MySQL function did not generate an error, mysql_error() returns an empty string and mysql_errno() returns 0. For example, assuming that we have connected to the server and selected a database for use, the following code snippet: $result = mysql_query( 'select * from does_not_exist' ); echo mysql_errno(); echo '<br />'; echo mysql_error(); might output: 1146 Table 'dbname.does_not_exist' doesn't exist Note that the output of these functions refers to the last MySQL function executed (other than mysql_error() or mysql_errno()). If you want to know the result of a command, make sure to check it before running others. Like file interaction failures, database interaction failures will occur. Even after com- pleting development and testing of a service, you will occasionally find that the MySQL daemon ( mysqld) has crashed or run out of available connections. If your database runs on another physical machine, you are relying on another set of hardware and software components that could fail—another network connection, network card, routers, and so on between your Web server and the database machine. You need to remember to check if your database requests succeed before attempting to use the result.There is no point in attempting to run a query after failing to connect to the database and no point in trying to extract and process the results after a running a query that failed. 29 525x ch23 1/24/03 2:56 PM Page 460 461 Programming Errors It is important to note at this point that there is a difference between a query failing and a query that merely fails to return any data or affect any rows. A SQL query that contains SQL syntax errors or refers to databases, tables, or columns that do exist might fail.The following query for example: select * from does_not_exist; will fail because the table name does not exist, and it generates an error number and message retrievable with mysql_errno() and mysql_error(). A SQL query that is syntactically valid, and refers only to databases, tables, and columns that exist will not generally fail.The query might however return no results if it is querying an empty table or searching for data that does not exist. Assuming that you have connected to a database successfully, and have a table called exists and a column called column name, the following query, for example: select * from exists where column_name = 'not in database'; will succeed but not return any results. Before you use the result of the query, you will need to check for both failure and no results. Connections to Network Services Although devices and other programs on your system will occasionally fail, they should fail rarely unless they are of poor quality.When using a network to connect to other machines and the software on those machines, you will need to accept that some part of the system will fail often.To connect from one machine to another, you are relying on numerous devices and services that are not under your control. At the risk of being repetitive, you really need to carefully check the return value of functions that attempt to interact with a network service. A function call such as $sp = fsockopen ( 'localhost', 5000 ); will not provide an error message if it fails in its attempt to connect to port 5000 on the machine localhost. Rewriting the call as $sp = fsockopen ( 'localhost', 5000, &$errorno, &$errorstr ); if(!$sp) echo “ERROR: $errorno: $errorstr"; will check the return value to see if an error occurred, and display an error message that might help you solve the problem. In this case, it would produce the output: ERROR: 111: Connection refused Runtime errors are harder to eliminate than syntax errors because the parser cannot sig- nal the error the first time the code is executed. Because runtime errors occur in response to a combination of events, they can be hard to detect and solve.The parser 29 525x ch23 1/24/03 2:56 PM Page 461 . seeing an unprofessional-looking screen full of error messages. Many other commonly used MySQL related PHP functions such as mysql_ pconnect(), mysql_ select_db() ,and mysql_ query() also return. not exist, and it generates an error number and message retrievable with mysql_ errno() and mysql_ error(). A SQL query that is syntactically valid, and refers only to databases, tables, and columns. using the function mysql_ error(),or an error code using the function mysql_ errno(). If the last MySQL function did not generate an error, mysql_ error() returns an empty string and mysql_ errno() returns