DebuggingForCookbookAuthors

Summary: Share tips and tricks with other authors about PmWiki specific debugging
Version: n/a
Prerequisites: Be interested in authoring a cookbook recipe
Status: perpetual
Maintainer: XES
License: CC0
Users: (View / Edit)
Categories: PmWiki developer

Questions answered by this recipe

This section is optional; use it to indicate the types of questions (if any) this recipe is intended to answer.

Description

Code snippets to help authors debug PmWiki cookbook writing problems.

Notes

Please feel free to borrow code snippets and to share code snippets with descriptions.

Code Snippets

Revision tracking

PM says: ...it would be good for recipe authors to add lines like

$RecipeInfo['RecipeName]['Version'] = '20061025';

to recipes, for use with Analyze Results and Recipe Check.

You can also return the recipe version as a custom page variable

$FmtPV['$RecipeNameVersion'] = "'RecipeName version {$RecipeInfo['RecipeName']['Version']}'"; // return version as a custom page variable

and declare a global variable to use as (:if enabled RecipeName:) for a recipe installation check

global $RecipeName; $RecipeName= 1; # if RecipeName enabled

Debugging messages: outputting messages, variables, and arrays to a wikipage

This mini-recipe utilises the PmWiki (:messages:) markup.

I called this function "sms" after the text messaging that people use on phones. Feel free to rename it, but I wanted an easy-to-type short function name. The great thing about the function is that I no longer need to do a global on $MessagesFmt in every function I want to output a message from. It also automatically adds a linebreak or <pre> for array variable dumps so that messages are neat,and helps when it comes time to clean out all my debug lines from a finished script (delete the sms function and all lines containing "sms"). All arrays are dumped in pre tags to preserve line spacing, and you can print out strings in pre tags by setting the optional second parameter to true or 1.

// Debug message function
function sms($text,$switch=0){
	global $MessagesFmt;
	if ($switch == true || is_array($text)) {
		$MessagesFmt[] = "<pre>" . print_r($text,true) . "</pre>\n";
	} else {
		$MessagesFmt[] = $text . "<br />\n";
	}
}

Debug message function usage

Add (:messages:) markup to the test page you're using while debugging the program. This line tells PmWiki to output the $MessagesFmt array here. This line is required on a wikipage to display the message output. (note, if needed you can add it to a GroupHeader/GroupFooter or to the site template while debugging).

The following code example adds output to the wikipage that says "checkpoint 1"

sms("checkpoint 1");

The following example outputs the contents of an array within <pre> tags, so that it doesn't output directly to the browser breaking the headers of the wiki (in this case the second parameter is optional. If you want to output a variable in the print_r with <pre> pass 1 or true as the second parameter to sms.

sms($_POST, true);

Another debugging function

Another way that is in my personal use for quite a while now is to output debugging messages directly in a file in the cookbook dir (not pmwiki dir for security). It works by calling a function debugMsg($tag, $text), and is configurable to output only text "posted" with certain tags. The advantage is that you can leave it in the code (even in a production environment) and switch it on/off as you need. It cannot handle arrays at the moment, but can surpress repeated posts. Find the code on my profile page (might become a recipe or just get here once). ThomasP April 12, 2007, at 05:15 AM

Updating PmWiki pages from cookbook modules [requires PmWiki 2.2.0beta12 or higher]

See Using PmWiki built-in security & permissions below!!

UpdatePage is a built-in PmWiki function that allows you to update pages in the wiki with full and proper updating of the wiki -- RecentChanges, Diffs, or auto-adding Categories (see other changes in the Release Notes) for example.

Usage:
UpdatePage($pagename, $oldpage, $newpage);

In this case $oldpage is the array format pulled from

ReadPage($pagename);

$newpage should be a similar array. Note that $newpage['text'] would be the new text to be the full body of the page. A diff will be calculated between $oldpage['text'] and $newpage['text']

Example use:

$pagename = "Group.PageName";
$text ="I want to completely change the page body and replace it with this text.";
XESReplacePage($pagename,$text);

function XESReplacePage($pagename, $text){
	$oldpage = ReadPage($pagename);
	$newpage = $oldpage;
	$newpage['text'] = $text;
	UpdatePage($pagename, $oldpage, $newpage);
}

Changing programming attributes into page variables

If you want to program a calculation in a script and offer it as a new page variable, you would have to save it with the page first, then make it available to authors with a command such as the following example (which creates the page variable {$Revision} from the attribute "rev" read from ReadPage($pagename);).

$FmtPV['$Revision'] = '$page["rev"]';

Using PmWiki built-in security & permissions

Using UpdatePage does not check whether or not someone has permissions to edit a page before writing to the page. If you need to check security settings before writing to a page:

    # change 'false' to 'true' if you want a password prompt
    $page = RetrieveAuthPage($pagename, 'edit', false);
    if (!$page) { Abort("You don't have permission to edit $pagename"); }

    # create the new page to be saved
    $new = $page
    $new['text'] = 'new text';

    # Update the page
    UpdatePage($pagename, $page, $new); 

Another procedure to check a user's authentication level, without reading page data is to use CondAuth(), which is only a simple permission check:

    if (!CondAuth($pagename, 'read')) {
       ##  browser doesn't have read permissions
       ##  ....
    } 

Debug by levels (#dbg_by_level)

I use an altered version of XES's debug function above. I call it dbg() and it is defined in the toolbox recipe.

It is used as follows:

dbg(0,"This message is basically turned off, but left in for possible future use");
dbg(1,"This message is unnecessary in normal debugging because it is too detailed");
dbg(2,"This is a pretty detailed message, showing the value of important variables at key moments");
dbg(3,"This message will show most flow-control and the value of very important variables");
dbg(4,"This message will show only entry/exit with parameters/return values on key functions");
dbg(5,"This message will always be displayed and is for short-term use while debugging a specific section of code");

Then depending on the value of the global $DebugLevel this message will be printed or not printed in the (:messages:) section. I use 5 levels (6 if you include 0) as delineated above.

I leave my debug statements in all the time and just control whether they are executed or not based on the setting of the debuglevel. Obviously you have to carefully plan/design/place your debug statements with a clear plan of when you want them displayed (is this important to a high-level summary? then give it a 4. Is this only of interest when I'm needing the most detailed information? Then give it a 1.) But if you develop consistently with this then it can be a huge help in debugging and testing.

Peter Bowers May 18, 2008, at 02:07 PM

Debugging pmwiki rules (#ruledebug)

This alteration of MarkupToHTML() in pmwiki.php is something I comment in/out when I need to look at how/why a given rule is interacting with other rules. It takes a "snapshot" of the markup (full page!) after each rule and places it in the markup.txt file. As you can imagine, this file gets long very quickly and it *really* slows down execution. Also note the markup.txt file is always accessed in "append" mode -- thus I must manually delete the file before I load my page to see what's going on (otherwise is just continues to append to the existing file). This little trick has really saved my neck on many occasions when a bug I was dealing with was caused by the interaction of various other rules...

Do note that making changes to pmwiki.php or other core files is NOT recommended under normal circumstances. This is a change that should be made only temporarily and only on a development platform.

Seriously, don't do this on a live machine. It will cause all kinds of problems! The idea is to insert this change, load the page which has the problem, REMOVE THESE LINES (or comment them out) and then analyze the markup.txt output.

The actual change occurs in the commented out section below - I've left a line or 2 around it to show you where it gets placed within MarkupToHTML()

      if ($RedoMarkupLine) { $lines=array_merge((array)$x,$lines); continue 2; }
/*====place a comment at the end of this line to enable rule-based debugging
$f = fopen("markup.txt", "a");
fwrite($f, "\n-----====----====----====----====-----\n");
fwrite($f, "++++MARKUP RULE: " . $p . " => " . $r . "++++\n");
fwrite($f, "-----====----====----====----====-----\n");
fwrite($f, $x);
fclose($f);
====place a comment at the beginning of this line to enable rule-based debugging */
    }
    if ($x>'') $out .= "$x\n";

Thus when it is active (only temporarily) it looks like this (note the addition of the end-comment on the 2nd line and the begin-comment on the 3rd-to-last line):

      if ($RedoMarkupLine) { $lines=array_merge((array)$x,$lines); continue 2; }
/*====place a comment at the end of this line to enable rule-based debugging*/
$f = fopen("markup.txt", "a");
fwrite($f, "\n-----====----====----====----====-----\n");
fwrite($f, "++++MARKUP RULE: " . $p . " => " . $r . "++++\n");
fwrite($f, "-----====----====----====----====-----\n");
fwrite($f, $x);
fclose($f);
/*====place a comment at the beginning of this line to enable rule-based debugging */
    }
    if ($x>'') $out .= "$x\n";
Peter Bowers May 18, 2008, at 02:07 PM

See Also

Contributors

  • XES October 09, 2006, at 02:25 AM
  • Peter Bowers November 18, 2011, at 10:28 AM

Comments

See discussion at DebuggingForCookbookAuthors-Talk

User notes : If you use, used or reviewed this recipe, you can add your name. These statistics appear in the Cookbook listings and will help newcomers browsing through the wiki.