Page 1 of 1

How-To Write Debuggable Code Rate Topic: -----

#1 fury  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 2
  • Joined: 30-December 04

Posted 31 December 2004 - 12:19 AM

For those of you who take on massive coding tasks such as creating an entire site from scratch, you'll notice that it tends to become tedious to debug your code unless you've taken a few precautionary measures. This tutorial will start you off in the right direction. It won't make your code debuggable for you, but you'll have an idea on how to do it. Most code can be modified to fit the schema suited for debugging fairly easily, but this guide is written with a clean slate in mind; it will describe the process of preparing your code to be easily debuggable from scratch.

Use the include functionality to your advantage

If one of your files reuses a lot of functions or those functions are reused in many other files, put those functions in a separate file and include it on those files. Make each include file have a descriptive name. If parse errors occur and the related code is all in one or two files each 2,000 lines long, it could get frustrating and tedious to find and fix the errors; with each distinct type of function isolated into categorical include files you'll have an easier time finding and fixing parse errors.

What NOT to do: (this is a very simplified list only used as an example, not all functions that you'd generally need for a forum are listed)
  • /index.php with 5 functions: get_online_list(), get_personal_message_list(), get_forum_list(), get_template(), execute_display()
  • /topiclisting.php with 7 functions: get_online_list(), get_personal_message_list(), get_topic_list(), get_moderator_list(), make_page_navigation(), get_template(), execute_display()
What might happen if an error occurs:
  • Parse error occurred on topiclisting.php line 627, unexpected ] (which lands on the function get_template())
  • Or you might mess up a calculating the online list in index.php and there are 10 people online in the last 12,000 days
What to do instead:
  • /include/general.php with 3 functions: get_template(), make_page_navigation(), execute_display()
  • /include/users.php with 2 functions: get_online_list(), get_moderator_list()
  • /include/posts.php with 3 functions: get_forum_list(), get_topic_list(), get_personal_message_list()
  • /init.php with code to include the above files when necessary
  • /index.php with code to include init.php
  • /topiclisting.php with code to include init.php
How this improves your ability to fix the errors above:
  • The parse error occurs on line 87 of /include/general.php so you can open the text editor and see the error right then and there
  • The calculation for online listing is called from a common file, so if there is an error, it occurs everywhere it is used and thus is easier to find
Spread it out

Code that is laid out well is far easier to read and debug than code that is scrunched together. The PHP interpreter won't care how much white space you put in the file. Spread out your code by spacing it out with tabs and separating each distinct expression on its own line to make it more readable and the easier debugging will come naturally. Use parentheses sensibly to distinguish expressions from equations and operations and such.

What NOT to do:
if ((($val == (pow(13,$val*$val2))) OR (($val > 16 AND $val < 17) AND $val != 16.9) OR ($val2 === false))) { perform_function(); } else if ($val == $val2 AND $val2 != $val3 AND $val * 60 > $val2 / 60) { do_something_else(); }


* note that throughout this tutorial, the code boxes like the above one wrap text around to prevent the page from expanding. To get an accurate view of the example I'm giving, copy the contents of the code box and paste it into any text editor that doesn't have word wrap enabled.

What might happen if an error occurs:
  • The error might be at ANY position on that line so you are left fishing through the code trying to look for what happened
  • You can't hardly read it because of the mess of unnecessary parentheses and lack of necessary parentheses so you are left wondering what the hell you were thinking when you coded it
What to do instead:
if ($val == pow(13, $val * $val2)
        OR (
                $val > 16
                AND $val < 17
                AND $val != 16.9)
        OR $val2 === false)
{
        perform_function();
}
else if ($val == $val2
        AND $val2 != $val3
        AND ($val * 60) > ($val2 / 60))
{
        do_something_else();
}



How this improves your ability to fix the above errors:
  • The line number will show you which distinct expression caused the error and you will be able to fix it much faster
  • The code all fits in the editor window without scrolling horizontally, so you can view what the code is supposed to do at a glance
Use TextPad

Call me biased, but TextPad has every feature a debugger could ever ask for. At least, until you ask for automated debugging. TextPad has bracket matching, automatic tab insertion, block selection, regular expression search (works with replacing as well), syntax-sensitive color coding, and much more. TextPad will automatically tell you when a file you are working with has been modified and will ask you to reload it or continue working on it as is. This helps to ensure you don't overlap your files. I have found on more than one occasion that I opened one file to work on it, then opened several more files, and forgot I had opened the first one, then opened another instance and started working on it, only to find that by saving it I had lost the work I had done in the initial instance. Since I got TextPad, it alerts me when a file I'm working on has been modified since it was opened.

Link to TextPad: http://www.textpad.com/

How to use features in TextPad:
  • Bracket Matching: Place the cursor behind a bracket, parenthesis, or curly brace and press Ctrl+M. Textpad will attempt to find the matching (opposite) bracket/parenthesis/brace and will move the cursor to that position and highlight it. Great for sifting through complex nests of if/else or variables that are getting assigned a value requiring a complex formula.
  • Syntax-sensitive color coding: First, go to Configure, then click Preferences. Go to Document Classes and select HTML. By default, *.PHP is listed as a member of the HTML class and this is not an ideal class to highlight PHP's syntax. Click on it and then click the X. Click OK. Go to Configure then click New Document Class. Name it PHP, and for class members, type *.php, *.php3, *.php4, and any other files you work with that are coded in PHP. Enable syntax highlighting and select "php.syn" from the dropdown list.
  • Line numbers: Configure, Preferences, View - this is a necessary item that is not turned on by default. You can toggle it on a per-session basis by pressing Ctrl+Q and then pressing L if you don't wish to have line numbers on by default
  • Automatic tabbing: Configure, Preferences, Document Classes, PHP - select "Automatically indent blocks" - will automatically insert tabs when you press enter after typing a {, and will automatically retract a tab when you type a } on its own on the line. You can also tab/untab an entire chunk of lines by selecting those lines and pressing tab to increase the tab space or shift+tab to decrease.
  • Block selection: To toggle block select mode for a single selection, hold in Alt while clicking and dragging the mouse. To toggle block select mode as default for this session, press Ctrl+Q and then press B.
  • Regular expression searching: Searching is done by pressing F5 to initiate a new search and Ctrl+F to repeat the previous search. Replacing is done by pressing F8 to initiate a new search/replace and Ctrl+F8 to repeat the previous search/replace. There is a checkbox to enable Regular Expression in each dialog box and it follows UNIX extended regular expression syntax. The syntax can be changed to POSIX by going to Configure, Preferences, Editor and checking the bottom box.
Create wrappers for your database functionality

Creating a class to wrap your MySQL, PGSQL, etc functions in helps you to maintain a clean code base where the beef of your code is, and also makes it easy to add debugging features wherever you perform queries without having to modify every line of your code that has a query.

Example: Create a mysql class file to use instead of mysql_* functions:
class db
{
        var $link;
        function db()
        { // this function is called when the class is instantiated. it's best to do things like connecting here
                $this->link = mysql_connect(/*insert mysql parameters here*/) OR die(mysql_error());
        }
        function q($sql)
        { // this function is used to perform a mysql_query()
                $result = mysql_query($sql) OR die(mysql_error());
        }
}


A very simple example above, but as you can guess, by using this method instead of mysql_connect and mysql_query() throughout your code, you can insert debugging code (such as the or die() statement I snuck in there) without sifting through every part of your code and updating each query line to include the debugging code. If you want to get real creative, you can name each query. Add a string argument to the q() method so you can refer to it when you encounter an error and you can quickly single out the query that's causing the trouble

Monitor all of your functions with a debugging class

A step up from the previous suggestion is to wrap all of your functions up in a debugging class. If you create a central debugging class with which you store the switch to turn debugging on or off, the functions to locate the line of code causing the problem (debug_backtrace() php function comes in handy here), functions to time chunks of code by making use of microtime(), etc., you can make your debugging job a lot easier and it also makes for a good query/code performance monitor if you time certain chunks of code.

Example class including code timing and memory usage monitoring:
class debug
{
        var $enabled = true;
        var $debuginfo = '';
        var $start = 0;
        var $timers = array();
        function debug()
        {
                if ($this->enabled)
                {
                        $this->start = $this->get_mtime();
                        $memusage = memory_get_usage();
                        $this->debuginfo = "Debug info start timestamp: {$this->start} - $memusage bytes memory usage\n";
                }
        }
        function get_mtime()
        {
                if ($this->enabled)
                {
                        $time = explode(' ', microtime());
                        return $time[0] + $time[1];
                }
        }
        function starttimer($chunkname)
        {
                if ($this->enabled)
                {
                        $this->timers[$chunkname] = array('start' => $this->get_mtime(), 'mem_at_start' => memory_get_usage(), 'end' => 0.0, 'mem_at_end' => 0);
                }
        }
        function endtimer($chunkname)
        {
                if ($this->enabled)
                {
                        $this->timers[$chunkname]['end'] = $this->get_mtime();
                        $this->timers[$chunkname]['mem_at_end'] = memory_get_usage();
                        $total = $this->timers[$chunkname]['end'] - $this->timers[$chunkname]['start'];
                        $mem_start = $this->timers[$chunkname]['mem_at_start'];
                        $mem_end = $this->timers[$chunkname]['mem_at_end'];
                        $this->debuginfo .= "Chunk $chunkname took $total secs, script took $mem_start bytes memory before and $mem_end bytes after function executed\n---\n";
                }
        }
        function endscript()
        {
                if ($this->enabled)
                {
                        $total = $this->get_mtime() - $this->start;
                        $this->debuginfo .= "\nEnd of script debugging, total secs: $total";
                }
        }
}



Obviously this tutorial does not give you complete examples, but it gives you an idea of where to start if you want to spend a little extra time toward writing your code so that you can debug it more easily. Less time spent debugging your code means more time spent towards developing real features. Turning out a finished product faster, especially in a contracting situation, is more ideal than cutting corners to save your fingers in the short run.

This post has been edited by fury: 31 December 2004 - 12:45 PM


Is This A Good Question/Topic? 0
  • +

Replies To: How-To Write Debuggable Code

#2 knownasilya  Icon User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 148
  • Joined: 11-January 06

Posted 03 February 2006 - 06:37 PM

Excellent, Yeh, Includes and requires are nice. They make everything easier to maintain and look at. I use Notepad++, works like textPad. And wrapping your database connection info is deffinetly a must. Overal this was a good article. I recommend it to anyone that does php, and other coding in general.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1