Sep 132011

While I am a firm believer in TDD [Test Driven Development], we all know that a lot of the time you dump an object or variable to chase a bug down the rabbit hole. And while both print_r and var_dump are usable for this, especially after Xdebug made var_dump a lot nicer. However, this was not enough for me, I wanted more power, at first for dealing with the depth and recursion, so I built my own debug tool. It has grown quite a bit and it is available on Github [1].
It supports these features:

  • Automatic block recursive references (only show an object once)
  • Easy on-the-fly change of depth* Blacklist classes, keys and properties
  • Meta info about objects like access level of properties
  • Includes knowledge of where it dies when you debug-die
  • Alternative adapters for outputting to file, json or firephp
  • Inspect global defines
  • Output a trace of “how you got here”
  • Dump an api (method list) of an object

The tool is basically just a Debug class with an adapter (typically Html for browser output). It also comes with a css for styling the output as well as a “bootstrap” file where you can set up default values, import them from your framework and define convenience method. The most used such functions are d() and dd(). The first will debug dump any amount of variables with the default values. The second will dump and die, with an ob_flush, allowing you to use the debug dumping anywhere in your project without worrying about started output or headers etc.
You can try it for yourself, but here is a little screencast that demonstrates the kinds of outputs it shows:


 Posted by at 8:39 am
Mar 302011

1. Installation
Create a file “package.json” with the following content (set uid of your package)
"uid": "(your package uid)"

Create a file called “package.json” with with content as example below (add IP restriction or/and keys), the example gives unlimited access.
"": {
"allow": {
"ips": [ "*" ],
"authkeys": [ "*" ]

Download the server library, the easiest is to use phar file, and set up your virtual host:
<VirtualHost *:80>
php_value auto_prepend_file phar:///var/www/firePHP_1.0/firephp.phar/FirePHP/Init.php
SetEnv INSIGHT_CONFIG_PATH /var/www/firePHP_1.0/package.json,/var/www/firePHP_1.0/credentials.json

Install client plugin (FirePHP Companion) in your web browser,

2. Page Context
$inspector = FirePHP::to("page");
$console = $inspector->console();
$console->log("Hello World");

3. Request Context
$inspector = FirePHP::to("request");
$console = $inspector->console();
$console->log("Hello World");

4. Controller Context
$inspector = FirePHP::to("request");
$console = $inspector->console();
$console->log("Hello World");
$controller = FirePHP::to("controller");

5. Package Context

$package = FirePHP::to("package");
$package->addQuickLink("Link 1", "");
$package->addQuickLink("Link 2", array(
"target" => "window", // tab (default), window or hidden
"url" => ""

$controller = FirePHP::to("controller");

6. Basic API
$inspector = FirePHP::to("request");
$console = $inspector->console();
$variable = "Hello World";


// Message Label
$console->label("My label")->log($variable);

// Table
$header = array("Column 1 Heading", "Column 2 Heading");
$table = array(
array("Row 1 Column 1 Value", "Row 1 Column 2 Value"),
array("Row 2 Column 1 Value", "Row 2 Column 2 Value"),
$console->table("Table", $table);
$console->table("Table with header", $table, $header);

// Stack Traces
$console->trace("My stack trace");

// Group
$group = $console->group("myGroup", "Group Title")->open();
$console->info("Hey!, I am in a group");
$console->error("I am in a group too!");

// Console controller
$controller = FirePHP::to("controller");

7. Conditional Logging

$inspector = FirePHP::to("request");
$console = $inspector->console();

$console->on("Show warnings")->warn("I am warning you!");

// Console controller
$controller = FirePHP::to("controller");

8. Engine API
$inspector = FirePHP::to("request");
$console = $inspector->console();
$engine = FirePHP::plugin("engine");
// Send all exceptions and errors to a specific console
// Console controller
$controller = FirePHP::to("controller");

// Handle manually
try {
throw new Exception("Hello, I am a manually handled exception");
} catch(Exception $e) {

throw new Exception("Test Exception");

9. FirePHP API
$inspector = FirePHP::to("request");

// Log p() messages to the provided console
$firephp = FirePHP::plugin("firephp");
$inspectorConsole = $inspector->console("InspectorConsole");
$firephp->declareP($inspectorConsole, true);

$variable = array("key"=>"value");
p($variable); // or
p($inspector, "Inspector");

// Log the current FirePHP version to a FirePHP console.
$versionConsole = $inspector->console("Version Console");

// Send information about the PHP environment to an Environment console.
$envConsole = $inspector->console("Environment");

// Console controller
$controller = FirePHP::to("controller");

//Send all errors and exceptions to a Problems console.
$problemConsole = $inspector->console("Problem Console");

throw new Exception("I am your problem");

Mar 302011

1. FirePHP 0.5 – Installation
1. Dwonload The Server Library and include it
2. require_once("./FirePHPCore-0.3.2/lib/FirePHPCore/FirePHP.class.php");
Or find a plugin for your framework.

2. Object Oriented API
$firephp = FirePHP::getInstance(true);
$firephp->log("Hello World");

3. Procedural API
fb("Hello World");
fb("Hello World", "Label");

4. Options

// Defaults:
$options = array("maxObjectDepth" => 5, // Maximum depth to traverse objects.
"maxArrayDepth" => 5, // Maximum depth to traverse arrays.
"maxDepth" => 10, // Maximum depth to traverse mixed arrays/objects.
"useNativeJsonEncode" => true, // Set to FALSE to use JSON encoder included with FirePHPCore instead of json_encode().
"includeLineNumbers" => false); // Include File and Line information in message

$firephp = FirePHP::getInstance(true);
$firephp->log("Hello World");

5. Log level

$firephp = FirePHP::getInstance(true);
$firephp->log("Logs a message to firebug console.");
$firephp->info("Logs a message to firebug console and displays an info icon before the message.");
$firephp->warn("Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise.");
$firephp->error("Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count.");
// or
$firephp->fb("Log with fb() method", FirePHP::LOG);
$firephp->fb("Info with fb() method", FirePHP::INFO);
$firephp->fb("Warning with fb() method", FirePHP::WARN);
$firephp->fb("Error with fb() method", FirePHP::ERROR);

6. Dump

$firephp = FirePHP::getInstance(true);

7. Trace

$firephp = FirePHP::getInstance(true);
$firephp->trace("Trace Label");

8. Groups

$firephp = FirePHP::getInstance(true);
$firephp->group("Test Group");
$firephp->log("Hello World");

$firephp->group("Collapsed and Colored Group",
array("Collapsed" => true,
"Color" => "#FF00FF"));
$firephp->log("Hello World");

9. Tables

$firephp = FirePHP::getInstance(true);

$table = array();
$table[] = array("Col 1 Heading","Col 2 Heading");
$table[] = array("Row 1 Col 1","Row 1 Col 2");
$table[] = array("Row 2 Col 1","Row 2 Col 2");
$table[] = array("Row 3 Col 1","Row 3 Col 2");

$firephp->table("Table Label", $table);

10. Exception Handling

$firephp = FirePHP::getInstance(true);
$firephp->registerErrorHandler( $throwErrorExceptions=false);
$firephp->registerAssertionHandler( $convertAssertionErrorsToExceptions=true, $throwAssertionExceptions=false);

throw new Exception("Test Exception");
// or manually
try {
throw new Exception("Test Exception");
} catch(Exception $e) {

11. Disabling firePHP

$firephp = FirePHP::getInstance(true);
$firephp->log("Hello again");

Oct 022009

During development of a new web application that required users to log in, we had problems with users being logged out too soon. The cookies were configured to end when the sessions did, but the max life time given to sessions turned out to be completely ignored.

The application had a standard LAMP environment, the key element as we will see, being the OS: the Debian based distro Ubuntu Hardy.

How PHPs garbage collection handles sessions

The main issue behind our problem turned out to be garbage collection. PHPs garbage collection also handles sessions. This is necessary because sessions are usually stored as flat files at a given location, and since each session has a unique id they will accumulate into quite a large amount over time.

During the start up over every session, there is a chance the session garbage collection will run as well. Session garbage collection deletes all session files if they are older than the set session max life time. The default probability for a new session also running session garbage collection is one in a hundred, this probability is configurable in php.ini, along with the location of the session files.

The problem

The Ubuntu server running our web application was also running an additional application which did not always have the same PHP configuration. Apache is fully capable of this when using virtual hosts – the point is, the max life time changes were not done in any of the php.ini files. Despite of this, that is exactly were there is a clue to what was happening. The PHP version we are using is an Ubuntu/Debian package, which has some changes to the original PHP default setup. In all of the php.ini files, in the SESSION section just above the garbage collection probability setting, we found that the session garbage collection by default  was actually turned off. The reason given is Debians strict permissions on the default session save path. Furthermore, the section informs that PHPs garbage collection has been replaced by a cron job.

A closer inspection of the cron job revealed this to be true, every half hour, it will look for a maxlifetime setting in all php.ini files it can find, see if it has found a value larger than its default of 24 minutes, and go on to delete all session files in the default session save path older than this value. In our case our maxlifetime settings were not in either of the ini files, which means the default value of 24 minutes was effective.

The solution

The cron job calls /usr/lib/php5/maxlifetime to determine how old sessions can be before they can be deleted, and in this file it is possible to set a new default max life time corresponding to the highest value set in either your web app or in an Apache virtual host configuration.

If you are uneasy with the OS entering territory on what arguably should be PHPs business, you can of course also remove or comment the cron job and activate PHPs session garbage collector instead.

May 072009

Last night I nailed an annoying bug in my bug tracker that has been tricky to locate, and was related to MySQL, transactions and apparently only certain MySQL versions which behaved correctly.

To ensure consistency in the bug tracker, I use a transaction to commit several changes. In this case, after an issue was created, it wouldn’t save stuff being inserted after the transaction->commit(). However – the id fields which were auto_inc – would all increment by one for each row that was never inserted. This would only happen on a select few MySQL versions, which lead me to believe it was actually a bug in those specific MySQL versions, and not in my code (it’s never me, of course :P ). This was also based on the assumption that doing a commit() would stop “transaction mode” and turn “autocommit” back on. One glance at the PHP manual proved me wrong.

It all did come down to the fact that in the class file that was creating an issue, the “autocommit” parameter for mysqli was never set back to “true” after committing a transaction. According to the manual, this should mean that all subsequent queries for that connection would “fail” unless a new “commit()” has been made. However, this obviously didn’t happen, as I have most of my bug tracker installations running properly, without this problem happening at all.

Under no circumstance was a commit() called after that point – which should mean that any queries being executed after that point were being rolled back. However, that only happed on a specific few MySQL versions, and none of the ones I tested on (confirmed at least 5.0.61 and 5.1.3). To make matters worse, the auto_inc id field did increment by one, even though the row was never saved!

The solution was simple – either do a second commit() at the end of the function to make sure all the queries after the initial transaction were commited, or make sure that the transaction turns on autocommit when it was done. In this case, I created a second function in the transaction class to commitAndEnd(), which turned autocommit back on after committing.

Lesson learned: Read manuals, don’t make assumptions. And don’t blindly trust MySQL.