Tuesday, July 14, 2009

Advanced PHP for Web Professionals By Christopher Cosentino Chapter 8

Overview
Bugs are an everyday part of a programmer's life. We all forget a semicolon or an end bracket at one time or another. Sometimes, we spell a variable name wrong and wonder why the value that is supposed to be showing up on the screen keeps coming up blank. Other times you wonder what gremlins are lurking on the database server because the data just isn't going in (or coming out) the way you intended it. How many times have you looked at a piece of code over and over and wondered why in the heck your program isn't working?

Some bugs—the kind bugs, the ones who don't mind being squashed out—will immediately tell you that they are there, by way of the PHP parser. For example, bugs like the ever-present "Parse error"—or even the occasional "Warning: Division by zero"—let you know on which line they occurred. It's easy enough for you to fix them: You just open your script, add a semicolon here, change the spelling of a variable there, and you are on your way.

Users, unfortunately, don't have this luxury. Have you ever spent an hour or so on some online shopping site, carefully picking out some nice gear for yourself, and going through a long and arduous multipage form to provide your address and credit information before finally clicking that glossy submit button, only to have the page display in plain type "Script Error—Database Connection Failed On Line 193" and nothing else? You just gave these people your credit card number and when their script dies all you see is "Script Error…."

Better sites, smarter sites—that is, YOUR site—should not do that. If there is an error, it would be nice to at least inform users that something went a little awry and the administrators have been notified. Maybe even a note that says the order went through (or didn't). Do you want to subject users of your site to ugly error messages that provide them with no information? Of course not.

In addition, errors can display information that could be useful to malicious users who are trying to crack into your site. Errors typically display file paths, database server information, and other information that could compromise the security of your site.

This chapter explains PHP's error and logging functions.

Error Reporting
Types of Errors in PHP
Before we can get into remedying ugly error messages on your site, you need to understand the different types of errors. PHP4 can produce 11 different errors, as defined in Table 8-1 below (note that fatal errors stop the script from executing):

Table 8-1. PHP Error Types NAME
VALUE
DESCRIPTION

E_ERROR
1
Fatal error that occurs at script runtime.

E_WARNING
2
Nonfatal error that occurs at runtime (for example, if the script is unable to connect to MySQL).

E_PARSE
4
Error that occurs at compile time due to invalid syntax.

E_NOTICE
8
Nonfatal "notice." Not exactly an error, but a hint that you may be doing something you don't want to, such as dividing a number by zero.

E_CORE_ERROR
16
Fatal error that occurs when the PHP engine starts. You cannot run any PHP scripts if this error occurs.

E_CORE_WARNING
32
Nonfatal error that occurs when the PHP engine starts. You can still run PHP scripts, but you may have one or more problems depending on the error (for example, the GD library cannot be found).

E_COMPILE_ERROR
64
Fatal error that occurs when the script is compiled.

E_COMPILE_WARNING
128
Nonfatal error that occurs when the script is compiled.

E_USER_ERROR
256
User-generated fatal error. Same as E_ERROR, but never thrown by PHP. You can throw this error with the trigger_error() function. If you are using PHP's default error handler, then using this error causes script execution to stop.

E_USER_WARNING
512
User-generated nonfatal error. Same as E_WARNING, but never thrown by PHP. You can throw this error with the trigger_error() function.

E_USER_NOTICE
1024
User-generated notice. Same as E_NOTICE, but never thrown by PHP. You can throw this error with the trigger_error() function.

E_ALL
2047
Not really a type of error. Instead, it is all the errors rolled into one. This makes it easy to say that you want to report all of the errors when using the error_reporting() function.

Of all of these errors, the ones you should mainly concern yourself when it comes to reporting and logging are the following:

E_WARNING

E_NOTICE

E_USER_ERROR

E_USER_WARNING

E_USER_NOTICE

Other errors that occur usually result in the script not executing. Your reporting and logging function won't even be run, since there is no script execution.

Error Reporting Settings in php.ini
Side Note
This information applies to PHP Version 4 and later. These settings do not work with PHP Version 3.

The reason you see those "ugly" errors when you are coding is that PHP by default is set to show all errors (E_ALL), with the exception of PHP-generated notices (E_NOTICE). Open your php.ini file and scroll down to the section entitled "Error handling and logging." At the end of all those comments, you should see a line like the following:

error_reporting = E_ALL & ~E_NOTICE

This is the default error reporting setting. It's written using bitwise operators and is translated simply to "For Error Reporting Use Everything And Not E_NOTICE." You could also write it as follows (note that we leave out E_NOTICE):

[View full width]
error_reporting = 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

However, since you are a PHP developer, you really should set this line to read as follows when you are developing an application:

error_reporting = E_ALL

You want to see all the errors that your script is producing. You'd be surprised at how many uninitialized variables you might be using.

error_reporting()
The error_reporting() function allows you to override the default error reporting setting used in php.ini:

error_reporting(settings);

The error_reporting function takes one argument, which can either be a string that lists the names of the error reporting settings that you want to use (separated by bitwise operators), or an integer that is the sum of the values that you want to use. For example:

error_reporting(10);

is equivalent to:

error_reporting("E_WARNING | E_NOTICE");

The first version is easier to type, but the second version is more verbose. Additionally, you can subtract values from the E_ALL value (2047) to use "all but" reporting. The example below allows you to display all errors (E_ALL) except for E_NOTICE (2047 - 8):

error_reporting(2039);

If you do not want to report any errors, then you could simply use:

error_reporting(0);

This stops PHP from displaying any error messages on the screen that are caused by E_WARNING, E_NOTICE or any of the user-thrown warnings. Remember, any of the other warnings are thrown automatically by PHP because there is something either really wrong with the script (parse errors) or really wrong with the server itself.

You still may run into a problem if there is an error that causes the script to fail to compile (such as with parse errors). In that case, no portions of your script are even executed, and the error_reporting() variable is not run. When this happens, PHP uses its php.ini setting for error reporting.

php.ini Setting: display_errors
Additionally, there is another setting that stops error messages from being displayed on the user's browser. This setting, display_errors, can be found in php.ini. In the event that you want to be sure that errors are never displayed on the screen, you can change this setting to "0" in your php.ini file. You should leave this setting to "1" or "On" while you are developing your application.

You can also change the display_errors setting on a per file basis using the ini_set() function. The ini_set() function allows you to change php.ini file settings for the current script. For example, if you had turned off display_errors in your php.ini file, then you could turn it on to debug your script by using the following code, which is the php.ini equivalent of display_errors = On:

ini_set("display_errors",1);

Remember, if your script encounters an error that does not allow it to execute, then PHP uses the settings it finds in php.ini, regardless of what you have specified in your script.

php.ini Settings for Development and Testing
While developing and testing your scripts, you should use the following settings in your php.ini file:

error_reporting = E_ALL
display_errors = 1

This allows you to display all errors and get all the information that PHP provides about your script. For production sites, you may want to set your php.ini file to completely turn off any error display:

display_errors = 0

This next script demonstrates how warnings and notices are reported with different error reporting settings.

Script 8-1 error_reporting.php
1. <?
2. define("EVERYTHING",E_ALL);
3. define("NO_NOTICE",(E_ALL &~E_NOTICE));
4. define("NO_NOTICE_OR_WARN",(E_ALL &~(E_NOTICE | E_WARNING)));
5. $error_level = array(EVERYTHING,NO_NOTICE,NO_NOTICE_OR_WARN);
6. for($i = 0; $i < sizeof($error_level); $i++) {
7. error_reporting($error_level[$i]);
8. echo "<h3>Error Reporting Set To " . $error_level[$i] . "</h3>";
9. echo $uninitialized;
10. $connect = mysql_connect();
11. echo "<hr>";
12. }
13. ?>
14. <?
15. define("EVERYTHING",E_ALL);

Script 8-1. error_reporting.php Line-by-Line Explanation LINE
DESCRIPTION

2–4
Define some named constants to use in the script. The reason we define these constants is because we want to use some custom error reporting options, as seen in lines 3 and 4.

5
Create an array of the error reporting values defined in the previous lines.

6–12
Create a for loop to loop through the values in the array.

7
Change the error reporting value to the current value in the array. The error_reporting function cannot take a bitwise operation statement as an argument, so we use the values created on lines 3 and 4.

8
Display a short notice describing which error reporting value is being used. The constants we defined in lines 2–4 are actually redefinitions of existing constants, which are in turn defined by PHP as the corresponding values of the error reporting levels. Therefore, the value displayed on the screen is numerical.

9
Print the value of an uninitialized variable. This should display a warning (E_USER_NOTICE) if the error reporting is set to display E_USER_NOTICE.

10
Attempt to connect to a MySQL database. This should generate a warning message if the error reporting message is set to display E_USER_WARNING.

12
Close the for loop started on line 6.

Logging Errors
As was stated earlier, you do not want to reveal error information to end users. At the same time, as the administrator of a site, you still need to know if something has gone amiss with the script. PHP provides the error_log() function for this purpose. You can turn disable_errors off in your PHP site and still monitor any problems that occur using error_log().

The error_log() function can take a variable amount of arguments depending on which of four types of logging that you choose to use.

error_log($message, $message_type, $destination, $extra_headers);

Table 8-2 below explains the four types of error logging available in PHP.

Table 8-2. Types Of Error Logging In PHP MESSAGE TYPE
DESCRIPTION

0
Depending on the "error_log" setting in php.ini, the $message is sent to a system-specific logging mechanism or to a file.

By default, error_log is not set in php.ini. If it is not set, and you use this type of error logging, then the error message usually appears in Apache's error log file.

Example:

error_log("Error!", 0);

1
$message is sent to the email address specified in the $destination argument of this function. This is the only type that uses the $extra_headers argument.

Example:

[View full width]
error_log("Error!",1,"email@example.com","FROM:
Website Error Logging <web@example.com>")

2
$message is sent to the PHP debugging connection. The $destination argument must be used to specify the host name or IP address of the debugging server. Commercial PHP IDEs (Integrated Development Environments), such as PHPIDE from Nusphere and the ZEND IDE, are able to accept messages in this fashion.

Example:

error_log("Error!",2,"192.168.0.1:2020");

3
$message is written to the end of the file specified by $destination. If the $destination file does not exist, PHP attempts to create it. You must have your permissions set correctly so that PHP can write to the directory specified in $destination. You do not have to specify the fill path for $destination.

Example:

[View full width]
error_log("Error!",3,"c:/PHP_ERRORS/script_errors.
txt");

This next script provides some working examples of error logging.

Script 8-2 error_logging.php
[View full width]
1. <?
2. echo "<p>Sending Error To Apache Error Log.";
3. error_log("Test Error Message!",0);
4. echo "<p>Sending Error To Email.";
5. error_log("Test Error Message!",1,"admin@example.com","FROM: WEB ERROR LOG
<error@example.com>");
6. echo "<p>Sending Error to File.";
7. error_log("Test Error Message!\r\n",3,"errors.txt");
8. ?>

Script 8-2. error_logging.php Line-by-Line Explanation LINE
DESCRIPTION

2
Display a message on the screen stating that an error is being sent to the Apache error log.

3
Send an error to the Apache error log.

4
Display a message on the screen stating that an error is being sent to an email address.

5
Send an error to an email address. Replace "admin@example.com" with your email address.

6
Display a message on the screen stating that an error is being sent to a file.

7
Send an error to the file. If the file does not exist, then PHP attempts to create it in the current directory.

Custom Error Handlers
PHP's default error handlers are useful, but they don't do anything more than just tell you that there is an error. Luckily, PHP allows you to use your own custom error handlers so that you can take a specific action, depending on which error has occurred. The function that allows you to use your own error handler is called set_error_handler():

set_error_handler($error_handler);

set_error_handler() takes one argument, $error_handler, which is the name of the error handling function that you have created.

If you call set_error_handler() with 0, then the error handler is set back to the default PHP error handler.

set_error_handler(0);
//error handler is PHP's default error handler

set_error_handler() returns the value of the old error handler when it is called. This allows you to easily revert to the old error handler. For example:

set_error_handler("foo");
$old_error_handler = set_error_handler("bar");
//$old_error_handler = "foo";
set_error_handler($old_error_handler);
//error handler is now "foo"

You can also easily revert to the prior error handler by calling the restore_error_handler() function, which takes no arguments:

set_error_handler("foo");
set_error_handler("bar");
restor_error_handler();
//error_handler = "foo"

When an error is triggered, PHP calls the error function specified in set_error_handler() and sends it the following arguments (in the following order):

The error number— This is the value of the error from Table 8-1.

The text of the error.

The name and path of the file in which the error occurred.

The line on which the error occurred.

The context at the time of the error— Basically, a large array containing the states of all the variables available to the script when the error occurred.

You can use as many or as few of these arguments as you want, but the arguments must appear in the order above.

Side Note
If PHP encounters errors in your error_handling function, it uses the default or previous error handling method to deal with the error.

This next script provides an example of a custom error handler.

Script 8-3 error_handler.php
[View full width]
1. <?
2. error_reporting(E_ALL);
3. function error_handler($number, $string, $file, $line) {
4. error_log("Error ($number) on line $line in file $file. The error was \"$string\"\n",
3, "errors.txt");
5. }
6. ini_set('display_errors',0);
7. set_error_handler("error_handler");
8. $row = mysql_fetch_array($result);
9. ?>

Script 8-3. error_handler.php Line-by-Line Explanation LINE
DESCRIPTION

2
Set error reporting to report all errors.

3–5
Define a function to handle errors. Accept the error number, error string, file in which the error occurred, and line number of the error as arguments.

4
Log the error to a text file.

5
Close the error_handler() function.

6
Use the ini_set() function to disable the displaying of errors on the screen.

7
Set the error handler to the custom handler defined on line 3.

8
Generate an error by calling a function that we know will not work.

Script 8-4 Output of error_handler.php
[View full width]
1. Error (8) on line 8 in file c:\apache\htdocs\chapter08\error_handler.php. The error was
"Undefined variable: result"
2. Error (2) on line 8 in file c:\apache\htdocs\chapter08\error_handler.php. The error was
"Supplied argument is not a valid MySQL result resource"

Script 8-4. Output of error_handler.php Line-by-Line Explanation LINE
DESCRIPTION

1
The log shows that this is a E_WARNING error, because the error type is (8). The rest of the line contains the information sent to the log from the custom error handler.

2
The log shows that this is an E_NOTICE error, because the error type is (2).

Triggering Errors
At times, it is very useful to trigger your own errors, depending on what has occurred in the script. For example, you may want to log failed login attempts to one of your password-protected sites. You may also want to take a different action depending on what error occurred. For example, if a database connection is not working, you may want to have a message sent to an email address, rather than just log the error into a text file.

The trigger_error() function is called as follows:

trigger_error($error_message, $error_type);

The trigger_error() function takes two arguments:

$error_message— A string describing the error.

$error_type— The type of error that you wish to trigger, which can be one of three values:

E_USER_ERROR

E_USER_WARNING

E_USER_NOTICE

Calling trigger_error() with the error type E_USER_ERROR will cause the script to start running at that point.

Side Note
There is another function called user_error(), which is an alias for trigger_error(). Both functions take the same arguments and do the exact thing.

This next function uses trigger_error() and a custom error handler to provide different actions according to the error.

Script 8-5 trigger_error.php
[View full width]
1. <?
2. function error_handler($number, $string, $file, $line) {
3. $timestamp = date("m-d-Y H:i:s (T)");
4. switch($string) {
5. case("INVALID LOGIN"):
6. global $username;
7. error_log("Failed login attempt by $username on $timestamp\n",3,"failed_logins.
txt");
8. break;
9. case("DATABASE CONNECT ERROR"):
10. global $mysqlerr;
11. $message = "Database Error!\n";
12. $message .= "Unable to connect to database at $timestamp\n\n";
13. $message .= "ERROR INFORMATION:\n";
14. $message .= "FILE:\t$file\n";
15. $message .= "LINE:\t$line\n";
16. $message .="MYSQL:\t$mysqlerr\n";
17. error_log($message,1,"me@example.com", "FROM: PHP MONITOR <noreply@example.com>");
18. failure();
19. break;
20. default:
21. if($number != 8) {
22. error_log("Error ($number) on line $line in file $file. The error was \"" .
trim($string) . "\"\n", 3, "error_log.txt");
23. }
24. break;
25. }
26. }
27. function failure() {
28. ?>
29. <h3>Sorry,</h3>
30. <p>A problem has occured. The site administators have been notified and the problem
should be resolved shortly.
31. <p>Please try again later.
32. <?
33. }
34. set_error_handler("error_handler");
35. if(!mysql_connect()) {
36. $mysqlerr = mysql_error();
37. trigger_error("DATABASE CONNECT ERROR",E_USER_NOTICE);
38. }
39. $username = "bob";
40. trigger_error("INVALID LOGIN",E_USER_NOTICE);
41. $msg = "Something Went Wrong!";
42. trigger_error($msg,E_USER_ERROR);
43. ?>

Script 8-5. trigger_error.php Line-by-Line Explanation LINE
DESCRIPTION

2–26
Define a custom error-handling function called error_handler(). This function takes as arguments:

$number— The numerical value of the error.

$string— The error message.

$file— The path and name of the file in which the error occurred.

$line— The line on which the error occurred.

3
Create the $timestamp variable and assign it the current date and time.

4–25
Create a switch statement to check the $string variable that is sent by PHP when an error is encountered.

5–8
If $switch is equal to "INVALID LOGIN", then log the error to a file and break out of the switch statement. You must make the $username a global variable in line 6 so that it can be used in this function.

9–19
If $switch = "DATABASE CONNECT ERROR", then execute lines 10–19.

10
Make $mysqlerr a global variable, so that its value can be used in this function.

11–16
Create text for an email message and store it in the $message variable. The text contains the relevant information pertaining to this error.

17
Use the error_log() function to send $message to an email address.

18
Execute the failure() function.

19
Break out of the switch statement.

20–24
If none of the above cases are met, then execute lines 21–24.

21–23
If the error value does not equal "8" (an E_NOTICE error), log the error to a text file.

24
Break out of the switch statement.

25
Close the switch statement.

26
End the function declaration.

27–33
Define a function, failure(), to be used when an error occurs that does not allow the script to run as intended.

28—32
Display a message to the user stating that the site is currently down.

33
End the function declaration.

34
Execute the set_error_handler() function, specifying the custom error handler "error_handler" as the argument. This disables PHP's built-in error-handling mechanism and uses error_handler() in its place.

35
Attempt to connect to a MySQL database. We assume this will fail, since we do not provide any connection information (host, username, password). If the connection attempt results in an error (it will), execute lines 36–38.

36
Assign the MySQL error message to the $mysqlerr variable. This variable is used in the error_handler() function.

37
Trigger an error using a custom error string, "DATABASE CONNECT ERROR". This string tells the custom error_handler() function to send an email to the administrator reporting that the database connection is not working.

39
Create the variable $username to hold the string "bob". This will be used as a test case to see if the error_handler() function is correctly logging user login errors.

40
Trigger another error with the string "INVALID LOGIN". This error causes the custom error handler to log the invalid login to a file. The log file stores the username of the person who attempted to log in when the login failed.

41
Create a variable to hold an arbitrary message to send to the trigger_error() message in the next line.

42
Trigger another error, using the $msg variable as the string. This error is logged in the error_log.txt file, since the error string ($msg) does not match any of the switch cases in the error_handler() function.

Tracking User Logins
This next script builds on the previous examples to create a small application that tracks the following:

Valid Logins— The script logs the date, time, username, IP address, and browser for each successful login.

Invalid Logins— The script logs the username and the date and time the user tried (unsuccessfully) to access the site.

Failed Database Connection— The script sends an email to the webmaster's email address in the event that the database connection fails.

Side Note
This can create an incredible amount of spam if it is placed on a busy site. Every time a database connection attempt fails, an email is sent to the email address specified in the script. If you get 1000 hits a day and the database connection is down, then expect to find at least 1000 emails in your inbox (depending on how many connection attempts occur per page).

Script 8-6 login_track.php
[View full width]
1. <?
2. /* SQL REQUIRED FOR THIS SCRIPT *****
3. create table users (
4. id INT NOT NULL,
5. username VARCHAR(16),
6. password VARCHAR(8),
7. primary key(id));
8. *****/
9.
10. function connect() {
11. global $dbuser, $dbpw, $dbhost;
12. if(!$db = @mysql_pconnect($dbhost,$dbuser,$dbpw)) {
13. global $mysqlerr;
14. $mysqlerr = mysql_error();
15. trigger_error("DATABASE CONNECT ERROR",E_USER_NOTICE);
16. failure();
17. } else {
18. mysql_select_db("php", $db);
19. return 1;
20. }
21. } //end connect()
22.
23. function check_user($user, $password) {
24. if(connect()) {
25. $password = substr($password, 0, 8);
26. $sql = "select * from users where username = '$user' and password = '$password'";
27. if(!$result = mysql_query($sql)) {
28. failure();
29. }
30. if (mysql_num_rows($result) == 1) {
31. setcookie("user",$user);
32. setcookie("password",$password);
33. trigger_error("VALID LOGIN",E_USER_NOTICE);
34. return 1;
35. } else {
36. trigger_error("INVALID LOGIN",E_USER_NOTICE);
37. return 0;
38. }
39. }
40. } //end check_user
41.
42. function error_handler($number, $string, $file, $line) {
43. global $admin_email;
44. $timestamp = date("m-d-Y H:i:s (T)");
45. switch($string) {
46. case("INVALID LOGIN"):
47. global $user;
48. error_log("Failed login attempt by $user on $timestamp\n",3,"failed_logins.txt");
49. echo "<h3>Login Failed. Try Again!</h3>";
50. break;
51. case("VALID LOGIN");
52. global $user, $REMOTE_ADDR, $HTTP_USER_AGENT;
53. error_log("LOGIN: $user on $timestamp from $REMOTE_ADDR using $HTTP_USER_AGENT\n",
3,"valid_logins.txt");
54. break;
55. case("DATABASE CONNECT ERROR"):
56. global $mysqlerr;
57. $message = "Database Error!\n";
58. $message .= "Unable to connect to database at $timestamp\n\n";
59. $message .= "ERROR INFORMATION:\n";
60. $message .= "FILE:\t$file\n";
61. $message .= "LINE:\t$line\n";
62. $message .="MYSQL:\t$mysqlerr\n";
63. error_log($message,1,$admin_email, "FROM: PHP MONITOR <noreply@example.com>");
64. failure();
65. break;
66. default:
67. if($number != 0) {
68. error_log("Error ($number) on line $line in file $file. The error was \"" . trim(
$string) . "\"\n", 3, "error_log.txt");
69. }
70. break;
71. }
72. } //end error_handler
73.
74. function failure() {
75. ?>
76. <h3>Sorry,</h3>
77. <p>A problem has occured. The site administators have been notified and the problem
should be resolved shortly.
78. <p>Please try again later.
79. <?
80. die();
81. } // end failure
82.
83.
84. function head() {
85. ?>
86. <html>
87. <head>
88. <title>Login Tracker</title>
89. </head>
90. <body bgcolor="#FFFFFF">
91. <h1>Login Tracker</h1>
92. <?
93. } //end head
94.
95. /***** MAIN *****/
96. $admin_email = "email@example.com";
97. $dbuser = "mysqluser";
98. $dbpw = "password";
99. $dbhost = "192.168.0.5";
100. $page = "login_track.php";
101.
102. error_reporting(0);
103. set_error_handler("error_handler");
104.
105. if(!isset($user) or !check_user($user, $password)) {
106. head();
107. ?>
108. <h1>You must log in to view this page</h1>
109. <form action = "<?=$page?>" method="post">
110. <P>Username: <input type="text" name="user"><br>
111. Password: <input type="password" name="password" maxlength="8" size="8"><br>
112. <input type="submit" name="submit" value="Submit">
113. </form>
114. <?
115. } else {
116. head();
117. ?>
118. <h1>Authorized!</h1>
119. <P>Your content here...
120. <?
121. }
122. ?>
123. </body>
124. </html>

Script 8-6. login_track.php Line-by-Line Explanation LINE
DESCRIPTION

2–8
The SQL required for this script.

10–21
Define a function, connect(), that is used to connect to the database.

11
Allow these variables to be used in the function.

12
Attempt to connect to the database (in this case, a MySQL database). If an error occurs, execute lines 13–16. If no error occurs, execute lines 17–20.

13
Assign the variable $mysqlerr to the global scope of the script.

14
Assign the error information to the $mysqlerr variable that is provided from the mysql_error() function.

15
Trigger a user-defined error that gets sent to the error_handler() function.

16
Execute the failure() function.

17–20
Since no error occurred, select the proper database on the MySQL server and return true to the calling function.

21
End the function declaration.

23–40
Define a function, check_user(), that is used to check the user-entered username/password combination against the values stored in the database.

24
Attempt to connect to the database. If the connection fails, the connect function automatically logs the error and kills the script.

25
Truncate the user-entered password so that it is only eight characters long.

26
Define an SQL statement to select all the information from the users' table that has a username and password that matches the user-entered values.

27–29
Send the query to the server and check to make sure the query actually went through. If it did not, then call the failure() function to display the failure notice to the user.

30
If there is one row in the result set, we know that the user entered the correct values. If no rows are returned from the database, then we know that the user's values did not match those in the database. (There shouldn't be more than one result row, because your application that enters the information into the database should be using unique email addresses.)

31–32
Set cookies in users' browsers so that they do not have to enter their username and password anymore, since we know the values are correct.

33
Trigger an error so that the successful login attempt is stored in the log file.

34
Return true to the calling function so that it knows the attempt was successful.

35–38
If the user-entered values do not match those in the database, then log the attempt and return false to the calling function.

39
Close out the if statement started on line 24.

40
End the function declaration.

42
Define the custom error-handling function, including the error number ($number), error string ($string), file in which the error occurred ($file), and line on which the error occurred ($line) as arguments.

43
Assign $admin_email as a global so that it can be used by this function.

44
Create a variable to hold the current date and time.

45–71
Create a switch statement that reads through the possible error strings.

46–50
If the error string is "INVALID LOGIN", grab the value of $user from the global variable space, log the error to the "failed_logins.txt" file, and print an error message to the user, then break out of the switch.

51–54
If the error string is "VALID LOGIN", grab the value of $user, $REMOTE_ADDR (IP address of the user), and $HTTP_USER_AGENT (user's Web browser) from the global variable space, and log the information to the "valid_logins.txt" file, then break out of the switch.

55–65
If the error string is "DATABASE CONNECT ERROR", then execute lines 56–65.

56
Grab the value of $mysqlerr from the global variable space. This variable was assigned a value when the error first occurred.

57–62
Create a lengthy message containing all of the available error information.

63
Send the message to the value of the $admin_email variable.

64
Execute the failure() function, which prints an error message to the screen and kills the script.

65
Break out of the switch statement.

66–70
If the error string is anything other than the above (and the error number was not "0", which is merely a warning), then log the error with the available information to the "error_log.txt" file. Finally, break out of the switch.

71
Close out the switch statement.

72
End the function declaration.

74–81
Define a function, failure(), that, when called, prints an error message to the user and kills the script.

75–79
Print the error message to the user.

80
Kill the script with the die() function.

81
End the function declaration.

84–93
Define a function, head(), that prints out the beginning HTML for the page as well as a heading.

95
Begin the main execution phase of the script.

96–100
Define some variables that are used by the script.

102
Turn off error reporting using the error_reporting() function.

103
Set the error handler to the custom error-handling function defined above.

105
If the $user variable has not been set or the check_user() function failed, execute lines 106–115.

106
Print out the beginning HTML for the page.

107–114
Print out the form so that users can enter their username and password to view the content of the page.

115–121
If the $user variable is set and the check_user() function returned true, then the user is valid and may view the protected content.

122–124
Print out the closing HTML for the page.

0 comments: