Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Debugging PHP code

Debugging PHP

Debugging PHP code

by  sleipnir214  Posted    (Edited  )
Code Debugging -- General musings in no particular order

1. Specific problems

1.1. Form Data Missing (the dreaded, ubiquitous register_globals problem)

A lot of users report that data from submitted HTML forms does not appear in their scripts. In fact, this is probably the problem most commonly reported in the forum.

For example, after submitting the following HTML form:

Code:
      <html>
         <body>
            <form method="post" action="http://somedomain.com/somescript.php">
               <input type="text" name="thedata">
               <input type="submit">
            </form>
         </body>
      </html>

when the script somecript.php runs, the variable $thedata has no value.

This is because the runtime configuration directive "register_globals" has been set to "off". This is the default setting for PHP, and has been the default setting since PHP version 4.2.0. As this page describes, http://www.php.net/manual/en/security.registerglobals.php, it is considered best to leave it set to off.

Whether or not "register_globals" set to off, the data from the form will be available in PHP's superglobal variables (http://www.php.net/manual/en/reserved.variables.php) -- in the case of the above HTML, as $_POST['thedata'].

Another example is when passing the values into PHP through a GET-method form (see the example form above, but replace 'method="post"' with 'method="get"') or by passing the variables in the URL (A URL like http://thedomain.com/thescript.php?somevariable=somevalue, which performs a GET-method input without the form). In this case, you would still use a superglobal array, but you must reference the elements of $_GET, not $_POST.

Keep in mind that cookie values and session variable values are also accessible through superglobal arrays. To access cookie values, reference the elements of $_COOKIE. For session variables, use $_SESSION.


I strongly recommend using the superglobals regardless of your "register_globals" setting for following reasons:

1. The superglobals are available everywhere. Should you write code that uses user-defined functions (a good idea), you don't have to use the "global" directive (http://www.php.net/manual/en/language.variables.scope.php) to access the values.

2. Since values from cookies, sessions, get-method input, and post-method input are all turned into global variables, you can have a collision of values should, for example, a cookie variable and a post-method variable have the same name.

3. Your code will be easier to maintain. You may not remember six months from now how the value got into "$thedata", but you will have a pretty good idea how the value got into "$_POST['thedata']".

4. It is more secure. See http://www.php.net/manual/en/security.registerglobals.php for more information.


1.2. Permission problems

Your code produces an error like: "Warning: fopen(<some filename>) [function.fopen]: failed to create stream: Permission denied in <your script> on line <line number>"

In general, the user who is running this script does not have permission to the file. A web server will run as a specific user, and so any scripts that run through a web server will also run as if invoked by the server's user.

You can fix this by using the permission modification functions of your operating system to give the appropriate user permission to the file.


1.3. Parse errors

Your code is producing an error like: "Parse error: parse error, unexpected <thing> in <your script> on line <line number>".

"Parse error" is PHP's way of telling you that it got confused while trying to understand your code. The line number provided tells you where PHP realized it was confused -- but that line may not be where the actual error exists. The actual code bug that caused the parse error can occur hundreds of lines before the line number provided by PHP.

Begin at the line number provided by PHP and begin looking backward through your code. Most parse errors exist because of typographical errors, so look for missing semicolons at the ends of lines; unbalanced quotes, double-quotes, brackets, braces, and parentheses; missing dollar-signs at the beginnings of variables; and unescaped quote symbols in strings.

If you are having a hard time nailing down the line where the error has occurred, comment out the entire script up to the line number provided by PHP, the re-run the script. If the error goes away, move the beginning of the comment block down in your script and try again. Keep track of the logical blocking of your code -- if you block off all but the closing brace of the statement block of a while loop, you will introduce new parse errors. When you have made the error go away and reappear by moving the comment block, the offending line will be between the last two places the comment-block began.


1.4. PHP and HTTP Header Manipulation

Your code is producing the error: "Warning: Cannot modify header information - headers already sent by...".

An HTTP transmission from the server must be of the form: headers, blank line, content, in that order. If at any time you output something that is not a header, PHP will assume that you are done outputting headers and are now outputting content, so it will send the blank line. After that, any invocation of the header-manipulation functions will generate an error. Header-manipulation functions include header(), setcookie(), and session_start().

All header-manipulation function invocations must appear before any output is generated by your script. "Output" includes but is not limited to anything (even blank lines) appearing outside the "<?php..?>" tags, errors and warnings reported by the script engine, and your own print or echo statements.

To take care of the error, reorganize your code so that any header-manipulating functions appear at a point in the logic of your code that no output has been produced.

ob_start() (http://www.php.net/manual/en/function.ob-start.php) can be used to buffer all the output and send it to the browser all at once at the end of your script, allowing you to use the header-manipulating functions anywhere in your code. But only if ob_start() is invoked before any output is generated -- any output that happens before the ob_start() call will be sent to the browser immediately, thus telling PHP that you're done outputting headers, and are now outputting content. Then any header-manipulating function calls will cause PHP to generate an error.

Also, use of output control functions will cause your script to consume more resources on the web server. I strongly recommend reorganizing your code before using the output control functions.


1.5. Database access debugging:

Another commonly-reported problem is errors accessing databases. Most of these issues, I've found, stem from the fact that the programmer has not written into his script sufficient error-handling to provide enough information to trouble-shoot the problem.

Catch errors at every step of the connection, database selection, and query steps. Output any errors by using mysql_error(). The simplest way to catch errors is to perform your query-invoking code with a die() statement. Something like:
Code:
      $result = mysql_query ($query) or die (mysql_error());
If your code has an error, the script will stop and display the error. If you want your script to handle the error a little more gracefully, test the return from mysql_query() against the value FALSE using a following if-statement:
Code:
      $result = mysql_query($some_select_query);

      if ($result === FALSE)
      {
         print "I could not perform the query.  Continuing";
      }
      else
      {
         while ($mydataarray = mysql_fetch_array ($result))
         {
            .
            .
            .
         }
      }

Many more errors access MySQL come from incorrect assumptions about queries produced by code. If the query is well-formed it will produce no errors. But this does not mean it will necessarily will do what you expect. Pick a preferred MySQL admin tool and get familiar with it. As you are creating your queries, use that admin tool in parallel to your program script development. For example, if you are producing a query by concatenating strings, you can debug your query by outputting it to the browser or screen and copy-and-pasting that query string to your admin tool for testing. You can verify that the query does what you expect.



2. General Debugging.

2.1. Do leave PHP's error reporting turned on, and don't prepend all your function calls with the @-sign to stop warnings.

2.2. Don't write 10,000 lines of code then try to debug them all at once. Incremental coding is a handy thing. Use function stubs (blocks of code that are placeholders but don't do anything) to keep the structure of your code right until you get everything written.

2.3. Do put error-handling code in your scripts. Write the error-handlers first, while your screen isn't cluttered up with the "meat" of your script. It makes it easier to see the program flow to catch errors. Your early scripts may not need this, but as your scripts grow more complex, your later scripts will. Getting into the habit at the start can save you a lot of grief down the road.

2.4. Do indent your code. There have been myriad code snippets posted in this forum where the logical error stemmed from the programmer's being unable to trace his own code logic.

2.5. Do footprint your code when troubleshooting. Insert print statements that show variable states and logical positions in your code. A lot of errors I've seen in the forum were because of a programmer's assuming a variable had a value it did not.

2.6. Verifying output from a web server

Sometimes it's hard to tell from watching the output of a web browser whether your script is working correctly. This is usually true when your script outputs binary data such as an image or a PDF. If your script is producing an error message or warning, all you will get is a web page with a missing image.

The best way I've found is to connect to the web server using telnet on port 80 and sending by hand the information necessary to get my script to run.

If your script expects no input or is expecting GET-method input, after connecting I type in:
Code:
         GET /mypath/myscriptname.php?fubar=1 HTTP/1.1[enter]
         Host: mywebserverhostname.com[enter]
         [enter]

(If your script is expecting no input, you would exclude the part "?fubar=1".)


If your script expects POST-method input, something like this will work:
Code:
         POST /mypath/myscriptname.php HTTP/1.1[enter]
         Host: mywebserverhostname.com[enter]
         Content-Type: application/x-www-form-urlencoded[enter]
         Content-Length: 11[enter]
         [enter]
         foo=3&bar=4[enter]

(The gotcha is that the "Content-Length:" header must reflect the number of characters you're sending in. Some servers are more picky about this than others.)


You can combine POST- and GET-method input:
Code:
         POST /showpost.php?fubar=3 HTTP/1.1[enter]
         Host: imod-demo.imodinc.com[enter]
         Content-Type: application/x-www-form-urlencoded[enter]
         Content-Length: 11[enter]
         [enter]
         foo=3&bar=4[enter]


The web server will then reply with HTTP headers and the raw data your script is sending out. Something like:
Code:
         HTTP/1.1 200 OK
         Date: Thu, 25 Sep 2003 20:11:43 GMT
         Server: Apache
         X-Powered-By: PHP/4.3.3
         Content-length: 6432
         Content-Description: PHP Generated Data
         Content-Type: image/jpeg
      
         <image data>


This will give you the actual raw output. If your script is outputting errors or warnings, you will be able to see them, too.

I find it easier to set up the input in a text editor and copy-and-paste the text into my telnet client.



3. Getting the most out of Tek-Tips.

Read Eric S. Raymond's advice on getting the most out of a technical forum. It's available here: http://www.catb.org/~esr/faqs/smart-questions.html

Things I think are useful:

3.1. Pick a useful title for your post. The presence of the word "Help" in your subject is pleonastic (http://www.yourdictionary.com/ahd/p/p0372000.html) and must never be used. Works that describe you rather than the problem ("Newbie needs...", "I'm lost...", etc.) are a waste of screen space and should be avoided. Greetings, such as "hello!", are meaningless within the context of asking a question -- just assume that by logging into Tek-Tips, you've already said hello to all of the site's more than half-million registered users.

3.2. Post the minimum amount of code necessary to demonstrate your problem. Don't throw 1,000 lines of code at the forum users and expect them to know that to do with it.

3.3. Indentation is a good thing -- the more easily Tek-Tips users can read your code and follow your program logic, the more likely they will answer your question.

3.4. Place your code inside a pair of [ignore]
Code:
..
[/ignore] tags so that your use of the variables as array indeces doesn't send TGML into spasms. This isn't important for PHP, since PHP prepends all variables with the dollar sign. But it's a good habit to get into.

3.5. Post verbatim the exact error message produced by PHP, not your paraphrase of it, and highlight the offending line in some way.

3.6. Describe what you have done to work around the problem so far.



version: 2.2.1
Released: 2003-10-23 11:22 U.S. CDT





Register to rate this FAQ  : BAD 1 2 3 4 5 6 7 8 9 10 GOOD
Please Note: 1 is Bad, 10 is Good :-)

Part and Inventory Search

Back
Top