More Books
PHP 5 Unleashed
PHP 5 Unleashed
Table of Contents
Copyright
Lead Author
Contributing Authors
Acknowledgments
We Want to Hear from You!
Reader Services
Introduction
Organization of the Book
Part I. Working with PHP for General Web Development
Chapter 1. Basic PHP Development
How PHP Scripts Work
Basic PHP Syntax
Basic PHP Data Types
Variable Manipulation
Control Structures
User-Defined Functions
Dynamic Variables and Functions
Multiple File PHP Scripts
References
Strings in PHP
Comparing Strings
Advanced String Comparison
Search and Replacement
Formatting Strings
Strings and Locales
Formatting Date and Time Values
Summary
Chapter 2. Arrays
Basic Arrays
Implementing Arrays
More Array Materials
Chapter 3. Regular Expressions
The Basics of Regular Expressions
Limitations of the Basic Syntax
POSIX Regular Expressions
Perl-Compatible Regular Expressions (PCRE)
PCRE Modifiers
A Few Final Words
Chapter 4. Working with Forms in PHP
HTML Forms 101
Working with Form Submissions in PHP
Summary
Chapter 5. Advanced Form Techniques
Data Manipulation and Conversion
Form Data Integrity
Form Processing
Summary
Chapter 6. Persistent Data Using Sessions and Cookies
HTTP Cookies
PHP Sessions
Advanced Sessions
Summary
Chapter 7. Using Templates
The What and Why of Templates
The Smarty Template Engine
Summary
Part II. Advanced Web Development
Chapter 8. PEAR
What Is PEAR?
Getting and Installing PEAR
Using the PEAR Package Manager
Using the PEAR Website
Using PEAR Packages in Applications
Summary
Reference
Chapter 9. XSLT and Other XML Concerns
Relating XML to HTML
Using XSLT to Describe HTML Output Using XML Input
PHP4 and XSLT Using the DOM XML Module
PHP4 and XSLT Using the XSLT Module
PHP5 and XSLT
Accessing XML Data Using SimpleXML
Generating XML Documents Using PHP
Summary
References
Chapter 10. Debugging and Optimizations
Debugging Your PHP Scripts
Optimizing Your PHP Scripts
Summary
Chapter 11. User Authentication
Authenticating Users in PHP
Securing PHP Code
Summary
Chapter 12. Data Encryption
Shared Secret Versus Public Key
Shared Secret Algorithms
Public Key Cryptography
Using Public Keys in PHP
Summary
Chapter 13. Object-Oriented Programming in PHP
Why Objects?
Creating Basic Classes
Advanced Classes
Special Methods
Class Autoloading
Object Serialization
Exceptions
Iterators
Summary
Chapter 14. Error Handling
The PHP Error-Handling Model
What to Do About Errors
The Default Error Handler
Error Suppression
Custom Error Handlers
Causing Errors
Putting It All Together
Summary
Chapter 15. Working with HTML/XHTML Using Tidy
Introduction
Basic Tidy Usage
Tidy Configuration Options
Using the Tidy Parser
Applications of Tidy
Summary
Chapter 16. Writing Email in PHP
The MIME Protocol
Implementing MIME Email in PHP
Summary
Part III. Building Applications in PHP
Chapter 17. Using PHP for Console Scripting
Core CLI Differences
Working with PHP CLI
CLI Tools and Extensions
Summary
Chapter 18. SOAP and PHP
What Are Web Services?
Installation
Creating Web Services
Consuming Web Services
Looking for Web Services
Summary
Chapter 19. Building WAP-Enabled Websites
What Is WAP?
System Requirements
Introduction to WML
Serving WAP Content
Sample Applications
Summary
Part IV. I/O, System Calls, and PHP
Chapter 20. Working with the File System
Working with Files in PHP
File Permissions
File Access Support Functions
Summary
Chapter 21. Network I/O
DNS/Reverse DNS Lookups
Socket Programming
Network Helper Functions
Summary
Chapter 22. Accessing the Underlying OS from PHP
Introduction
Unix-Specific OS Functionality
Platform-Independent System Functions
A Brief Note About Security
Summary
Part V. Working with Data in PHP
Chapter 23. Introduction to Databases
Using the MySQL Client
Basic MySQL Usage
Summary
Chapter 24. Using MySQL with PHP
Performing Queries from PHP
A MySQLi Session Handler
What Is a Custom Session Handler?
Summary
Chapter 25. Using SQLite with PHP
What Makes SQLite Unique?
Basic SQLite Functionality
Working with PHP UDFs in SQLite
Odds and Ends
Summary
Chapter 26. PHP's dba Functions
Preparations and Settings
Creating a File-Based Database
Writing Data
Reading Data
Sample Application
Conclusion
Part VI. Graphical Output with PHP
Chapter 27. Working with Images
Basic Image Creation Using GD
Using the PHP/GD Drawing Functions
Working with Colors and Brushes
Using Fonts and Printing Strings
General Image Manipulation
Other Graphics Functions
Summary
Chapter 28. Printable Document Generation
A Note Regarding the Examples in This Chapter
Generating Dynamic RTF Documents
Generating Dynamic PDF Documents
Related Resources
Part VII. Appendixes
Appendix A. Installing PHP5 and MySQL
Installing PHP5
Installing MySQL and PHP Modules
Installing PEAR
Appendix B. HTTP Reference
What Is HTTP?
PHP Programming Libraries for HTTP Work
Understanding an HTTP Transaction
HTTP Client Methods
What Comes Back: Server Response Codes
HTTP Headers
Encoding
Identifying Clients and Servers
The "Referer"
Fetching Content from an HTTP Source
Media Types
Cookies: Preserving State and a Tasty Treat
Security and Authorization
Client-Side Caching of HTTP Content
Appendix C. Migrating Applications from PHP4 to PHP5
Configuration
Object-Oriented Programming (OOP)
New Behavior of Functions
Further Reading
Appendix D. Good Programming Techniques and Performance Issues
Common Style Mistakes
Common Security Concerns
Style and SecurityLogging
Summary
Appendix E. Resources and Mailing Lists
Relevant Websites
Mailing Lists and Newsgroups
Index
SYMBOL
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z

Debugging Your PHP Scripts

Tracking down and removing bugs in applications is a fact of life when writing computer programs, regardless of language. Unlike many development platforms, debugging Web applications offers a unique challenge that at times can be difficult. As you will see, however, many different tools (both commercial and open source) and a number of techniques have been developed that will be infinitely useful to you.

When you are writing programs, regardless of language, bugs can be classified into two different categories: syntax and logical bugs. Syntax bugs are very easy to identify because they always relate to errors you have made in the actual writing of the program itself. These errors can be forgetting a semicolon or brace, simple typos, or other syntax-related errors. Because most syntactic bugs will prevent your application from running at all, they are usually obvious to spot. On the other hand, the second classification of bugs, logical bugs, can be much more elusive. Logical bugs are not always immediately obvious, because your application may function as intended 90 percent of the time, breaking only under unique circumstances. As you will see, most of my discussion of debugging will be focused on techniques and tools for finding and fixing logical bugs.

Syntax-Related Bugs

The easiest bugs to find in your application are syntax-related bugs; they will always cause some sort of error and more than likely will halt your script entirely. Most syntax-related bugs materialize with an error message resembling the following:

PHP Parse error: parse error, unexpected ??? in <filename> on line <line_number>

Where ??? can be a number of different values (see the note that follows), <filename> is the name of the file where the error occurred, and <line_number> is the line number where the error was detected.

NOTE

In parse errors, the ??? represents a scanner token. If you are curious as to what types of values may occur, see the PHP documentation for the tokenizer extension, which provides a complete list of all valid PHP tokens.


It is important to note that when you are dealing with errors such as this, PHP can tell you only where the error was detected, not necessarily where it actually occurred. Thus, when dealing with syntax errors, it is important to realize the line number that PHP reports as causing the error may be inaccurate. A common example of this is if a control block is started using the { character, but is not closed. In a situation such as this, PHP will report a Parse error occurring on a line number that does not exist (one past the end of the file). Thus when attempting to track down syntax-related errors, always check up your code starting from the line where PHP first detected the error.

Logical Bugs

Logical bugs represent what most programmers classify as "bugs" that occur when your application runs, but doesn't run correctly. Unfortunately, there is no way to teach a single technique that will ensure that your code will be free from these bugs. Rather, a combination of good practices, useful techniques, knowledge of the code in question, and experience are needed to properly address logical bugs. Because this chapter (or this book for that matter) can't provide you with knowledge of an arbitrary piece of code or experience, we'll have to settle with a discussion of the best practices and useful debugging techniques.

Preventing Bugs

As is the case anytime something is being created, the best way to encourage a positive outcome is to appropriately plan that which you are creating. Thus, the first step in creating any application is to have a reasonable idea of how exactly you plan to create it. This planning can be accomplished in any way you see fitfrom using UML, to creating a detailed design document, to just having a decent idea in your head. The important piece here is not how the plan is fashioned, but that the plan has indeed been fashioned to some degree. What is the goal of the script you are writing? Do you know what exactly is involved in creating this script? Do you have at the very least a rough idea of how you will implement this script? These are all questions that any good developer answers before writing a single line of code. It may seem like a bit of a waste of time, but the time spent early on can save huge headaches and time debugging later.

After you have a plan, another solid debugging technique again has nothing to do with the code itself. Rather, it involves establishing a programming style that will be used when the code is written. How will you name your functions and variables? How many spaces will you use for your indentation for each layer of code? Although these questions are trivial and unnecessary for smaller scripts, large scripts quickly become unmanageable and therefore bug-prone without a coding standard.

Last but not least, especially for large projects, is documentation. As scripts become larger and larger, it is easy to forget exactly what a particular function is used for or when it is called. Don't be afraid to comment your code! On the other side of the coin, however, don't comment too much, either. Although everyone has an individual commenting style (as well as some formal styles used with documentation-generation systems such as PHPDoc), comments shouldn't be more than perhaps a function description and brief in-line comments when necessary. By getting into the habit of commenting your code when the application logic is not immediately obvious, you accomplish two goalsnot only do you make your application easier to understand, but it also forces you to think about your code, allowing you to sometimes catch oversights before they become a real issue.

Simple Script Tracing

Unfortunately, no matter how well thought out a particular script or application is, no matter how talented or experienced the programmer, there will always be bugs in any significant amount of code. Tracking down these logic-related bugs, as I've already mentioned, can be an extremely difficult and time-consuming task. Although experience is by far the best tool to finding these bugs, PHP provides a few things that can make your life as a debugger easier.

One of the most important things to remember when debugging your scripts is this: To fix a bug, first you must understand the bug. Without properly understanding exactly why a particular piece of code is not functioning properly, any attempts made to fix it could very well result in the debugging version of wack-a-mole, where fixing one bug causes another to pop up. To understand why a particular bug is in your code, first you'll need to see exactly what your application is doing. This can become a problem for a language such as PHP, because your script is often running on a server that could be halfway around the world, thus making the standard debugging tools useless.

Although many common debugging tools are not useful when you're working with PHP, some functions and techniques can be used to ease your debugging life. The first of these is any of the standard output functions such as echo or printf. The technique is simpleif you are curious about the flow of your application logic or the value of a particular variable, display it using a statement such as echo (see Listing 10.1):

Listing 10.1. The Poor Man's Application Trace
<?php

     $foo = rand(1,10);
     echo "The value of \$foo is: $foo<BR />";
     if($foo > 5) {
          echo "Hello, World!<BR />";
     }
?>

Listing 10.1 provides a simple example of what is affectionately called the "poor man's application trace." Obviously, using a debugging method such as this has some significant drawbacks that must be addressed. For starters, it can quickly become incredibly annoying to write echo statement after echo statement (and then remove each later) to follow the flow of your script. One solution that solves at least half of this problem is to wrap every debugging message in a conditional operator and turn the messages on and off based on a constant as shown in Listing 10.2:

Listing 10.2. A Slightly Improved Poor Man's Application Trace
<?php
     define('DEBUG', true);
     $foo = rand(1,10);

     debug("The value of \$foo is: $foo<BR />");
     if($foo > 5) {
          echo "Hello, World!<BR />";
     }

     function debug($dbgmsg) {
          if(DEBUG) {
               echo $dbgmsg;
          }
     }
?>

In Listing 10.2 we have improved slightly on the concept of the poor man's application trace by creating a debug() function that handles the actual output of any debugging messages by first checking to see if the DEBUG constant is true. Using this method, we have a fairly reasonable method of following your script logic without having it become a maintenance nightmare. We could improve on this technique even further if we wanted by logging debugging messages to a file, displaying them in a pop-up window using some JavaScript magic, or both, as shown in Listing 10.3:

Listing 10.3. An Even Better Poor Man's Application Trace
<?php
     define('DEBUG', true);
     $foo = rand(1,10);

     debug("The value of \$foo is: $foo<BR />");
     if($foo > 5) {
          echo "Hello, World!<BR />";
     }

     function debug($dbgmsg) {
          if(!DEBUG) return;

          error_log($dbgmsg);

          $dbgmsg = addslashes(htmlentities($dbgmsg));
          $dbgmsg = nl2br($dbgmsg);
          $dbgmsg = str_replace("\n", "", $dbgmsg);
          $dbgmsg = str_replace("\r", "", $dbgmsg);

     ?>
     <SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript"     >
          <!--
          debug_console("<PRE><?php echo $dbgmsg; ?></PRE>");
          //-->
     </SCRIPT>
     <?php
     }
?>

NOTE

Although it's not immediately obvious, the reason I am doing so many manipulations to the debugging message in this example is to avoid problems with JavaScript when dealing with multiple-line strings. In JavaScript, all strings must be represented on a single line, and therefore all line breaks must be removed.


In Listing 10.3, we have yet again improved on our tried and true debugging technique by logging debugging information using PHP's error_log() function and displaying it within its own pop-up window using some JavaScript code. For Listing 10.3 to work, however, you must have the debug_console() JavaScript function defined somewhere in your HTML output. Following is the debug_console() function code snippet I used for this example:

<SCRIPT LANGUAGE="JavaScript">
<!--
function debug_console(content) {
            top.consoleRef=window.open('','myconsole',
                                       'width=640,height=350'
                                       +',menubar=0'
                                       +',toolbar=0'
                                       +',status=0'
                                       +',scrollbars=1'
                                       +',resizable=1')
            top.consoleRef.document.writeln(
                      '<html><head><title>My Debugging Console</title></head>'
                      +'<body bgcolor=white onLoad="self.focus()">'
                      +content
                      +'</body></html>'
        )
//-->
</SCRIPT>

Using Assertions in PHP

Another potentially useful technique for debugging PHP applications, assertions, is also a common technique found in other languages. Assertions are statements that you define in your scripts that are assumed to always be either true or false. Although they are not designed to be used in the course of your normal script logic, assertions can be quite useful for performing sanity checks to ensure variables within your application are at least realistic values.

The reason assertions are particularly useful for this task in a development scenario is their ease of configuration for different situations. For instance, assertions can be turned on and off using a single function call, among other useful capabilities.

In practice, assertions are used in PHP through two separate functions, assert() and assert_options(), which define assertions and the behavior of those assertions, respectively. The syntax for the assert() function is as follows:

assert($assertion);

$assertion is the assertion to evaluate. This value can either be a string that will be evaluated as PHP code or a Boolean expression. In either case, the result should be written in such a way that the result evaluates to a Boolean false.

NOTE

In general, the $assertion parameter should be represented as a string. Beyond being more efficient (because it will not be evaluated unless assertions are enabled), it also provides more information when the assertion fails, as you will soon see.


Because the best way to understand assertions is to see them in action, let's take a look at a small PHP script example shown in Listing 10.4:

Listing 10.4. Using the assert() Function
<?php

    function add_odd_numbers($x, $y) {
        assert('!(($x % 2) && ($y % 2))');
        return ($x + $y);
    }

    $answer_one = add_odd_numbers(3, 5);
    $answer_two = add_odd_numbers(2, 4);

    echo "3 + 5 = $answer_one\n";
    echo "2 + 4 = $answer_two\n";

?>

In this trivial example a function add_odd_numbers() has been defined, which accepts two parameters, $x and $y, representing the numbers to add. Within this function I have placed an assertion and provided it the string '!(($x % 2) && ($y % 2))' as a parameter. This function is then used in the remainder of the script in both an appropriate and inappropriate way.

When this script is executed, it will produce the following output:

Warning: assert(): Assertion "!(($x % 2) && ($y % 2))" failed in assert.php on line 4
3 + 5 = 8
2 + 4 = 6

As you can see from this output, by using assert() it is clear that something that was assumed to be true (the parameters being passed are odd numbers) turned out for some reason to be not true. In this case, as is the default behavior, the result is a runtime warning:

Warning: assert(): Assertion "!(($x % 2) && ($y % 2))" failed in assert.php on line 4

Immediately, this warning identifies not only the location within the source tree of the failed assertion, but also provides the assertion statement that failed.

Beyond this default behavior, assertions in PHP can also trigger a number of other events. All these additional behaviors are controlled through the use of the assert_options() function, whose syntax is as follows:

assert_options($option [, $value]);

$option is one of the constants found in Table 10.1, and the optional parameter $value is the value to set that option to. When executed, the assert_options() function will return the current value for the provided option and, if provided, set that option to the new value. The possible options to configure when working with assertions are as follows:

Table 10.1. Assertion Options

Constant Name

Default

Description

ASSERT_ACTIVE

true

Are assertions enabled?

ASSERT_WARNING

true

Should assertions cause standard PHP warnings?

ASSERT_BAIL

false

Should failed assertions cause the script to halt?

ASSERT_QUIET_EVAL

false

If an error occurs valuating an assertion when passed a string, should it report an error?

ASSERT_CALLBACK

NULL

The name of the function to call if an assertion fails.


As you can see, assertion in PHP are a fairly flexible construct and have many uses in the development and debugging of PHP applications. One of the more interesting uses of assertions is when a callback function is provided. By registering a callback function, you then have the capability to process failed assertions on your own, making things such as automated test suites much easier. When using an assertion callback function, assert() will call it and pass it three parameters: the filename where the assertion failed, the line number of the failed assertion, and if available, the assertion code that failed. An example of using a callback function with assertions can be found in Listing 10.5:

Listing 10.5. Using Assertion Callbacks
<?php
    assert_options(ASSERT_CALLBACK, "assert_failure");
    assert_options(ASSERT_WARNING, false);

    function assert_failure($filename, $line_num, $asserted_code) {

        $code = (empty($asserted_code)) ? "Unknown code" : $asserted_code;
        echo "The assertion '$asserted_code' failed in '$filename' " .
             "(line: $line_num)\n";

    }

    function integer_divide($x, $y) {

        assert('!(is_long($x) && is_long($y))');
        return (int)($x / $y);
    }

    $answer = integer_divide(10, 6);
    echo "10 / 6 = $answer\n";
?>