Zend Database Profiler and Firebug/FirePHP

For some days, I’m looking around for logging and debugging options I can use for some projects of mine and projects in the work. Yesterday I had stumbled upon the Zend_Log and FirePHP duo and they worked perfectly well. The best of course was the log level option disabling logs which were not that important.


Today I looked for some database profiling things after seeing lots of posts together about Zend_Log_Writer_Firebug and Zend_Db_Profiler_Firebug. So I started to use Zend_Db_Profiler. And yes without Zend_Db! Why I’m not using Zend_Db is that, I do not want to get too complicated with the queries. I just want to execute a query. I don’t want a statement than, a result set, than a table adapter, than a row adapter than a cell adapter than an array than a character and looping of this event for all the rows. You may say that then return to the assembly but that’s not the thing. i just like the simple way I handle my results. So let’s move on.

So how shall we handle the profiling than. The bad news is if you have not wrapped your mysql_query (pgsql_query or whatever) to some other function you have already dead in the water! But I don’t think much people have left who does this for now. So Let’s talk about how we will use the Zend_Db_Profiler class and it’s functionality in my function:

//Define your profiler
$profiler = new Zend_Db_Profiler(true); //true means enable profiling

//This is the function which is called all around your project for sql queries
function query($str, $type = Zend_Db_Profiler::SELECT) {
	if ($dbProfileEnabled) { //Check if the profiling enabled or not...
	    $queryId = $profiler->queryStart($str, $type);
	    $qp = $profiler->getQueryProfile($queryId);
	    if ($qp->hasEnded()) {
		$queryId = $profiler->queryClone($qp);
		$qp = $profiler->getQueryProfile($queryId);
	    }
	    $qp->start($queryId);
	    $q = mysql_query($str);
	    $profiler->queryEnd($queryId);
	} else {
	    $q = mysql_query($str);
	}
	//Do some error handling or fetch the results from now on as you feel comfortable...
}

//Some database code are coming here

//Finally show the profiler results
//Returns an array containing Zend_Db_Profiler_Query instances
//You can use some additional functions like getElapsedSecs() or so
var_dump($profiler->getQueryProfiles());

That’s a simple snippet you will encounter for using Zend_Db_Profiler. I have copied and pasted this part from Zend_Db_Statement‘s execute(). It’s simple and it explains itself. First a simple check if profiling is activated or not. If it isn’t than you shouldn’t call a lot of meaningless things and just execute your query.

Once you enable profiling you encounter a simple queryStart() which basically sets the type of the query (SELECT, INSERT, DELETE, STATEMENT,… Check Zend_Db_Profiler constants for the complete list). This one creates an instance of Zend_Db_Profiler_Query which is a simple data holder. It containes what is the query, when is it started, when it ended or so. getQueryProfile() returns the instance on the given query id.

The part you will get confused is the hasEnded() part. This is because of object references. If the query has ended, it contains some information and it will break the previous informations. So we create a clone of the current instance and then we save the data to that.

start() begins the microtime counting and queryEnd() records the ending microtime with some additional exception checks. And that’s all folks! Your profiler works perfectly. You can reach this profile information by var_dump() or some additional handling which you should prefer.

So what about Firebug and FirePHP. The good thing, there is such a profiler class for Firebug too which handles the queries with a good way and shows it in the Firebug console. And it’s very easy to do so!

//Let's change how we defined our profiler
//$profiler = new Zend_Db_Profiler(true);//true means enable profiler
$profiler = new Zend_Db_Profiler_Firebug('My Database Query Logs'); //The name of your queries which you will see on top of a table
$profiler->setEnabled(true); //Enable the profiling

I have just changed the definition of the profile and all else works as it’s on the Firebug console. It’s created in a table and shows as a good thing. What you will remark in the FirePHP table is that there is Parameters part in it. So what is this?

As you may guess, Zend_Db_Profiler is called in a Zend_Db_Statement and a statement is a query which does not have it’s parameters and parameters are bound later during the runtime. As these parameters are kept as an array, you just bind these parameters to the profiler’s internal $_boundParams array. And later on these parameters can be seen on the logs. If you modify your code just like this:

//Let's change how we defined our profiler
//$profiler = new Zend_Db_Profiler(true);//true means enable profiler
$profiler = new Zend_Db_Profiler_Firebug('My Database Query Logs'); //The name of your queries which you will see on top of a table
$profiler->setEnabled(true); //Enable the profiling

//This is the function which is called all around your project for sql queries
function query($str, $params, $type = Zend_Db_Profiler::SELECT) {
        foreach($params as $key => $val) { //Do some logic for replacing the real values instead of placeholders
            $str = str_replace('@' . $key, $val, $str); //Do not forget about sql injection...
        }
	if ($dbProfileEnabled) { //Check if the profiling enabled or not...
	    $queryId = $profiler->queryStart($str, $type);
	    $qp = $profiler->getQueryProfile($queryId);
	    if ($qp->hasEnded()) {
		$queryId = $profiler->queryClone($qp);
		$qp = $profiler->getQueryProfile($queryId);
	    }
            $qp->bindParams($params);
	    $qp->start($queryId);
	    $q = mysql_query($str);
	    $profiler->queryEnd($queryId);
	} else {
	    $q = mysql_query($str);
	}
	//Do some error handling or fetch the results from now on as you feel comfortable...
}

//Application things are around

//Finally show the profiler results
//Returns an array containing Zend_Db_Profiler_Query instances
//You can use some additional functions like getElapsedSecs() or so
//var_dump($profiler->getQueryProfiles()); //You don't need this as we are using Firebug now

So now we have changed our sql statements a little bit, we have started to use “statements” instead of queries and we have parameters that we see on the Firebug console.

Well, well and we reach to the last part: Filtering. It’s not easy to find the query you search between hundreds maybe thousands of queries! You should be able to filter these queries from different levels. Like you may want to see only the update queries or perhaps only the insert queries. How can you filter it?

That’s easy too!

//Just set the query type you want to see
var_dump($profiler->getQueryProfiles(Zend_Db_Profiler::UPDATE));

You should remember that you need to send right query types to your execute() as these types are defined there. But this trick won’t do if you are using Zend_Db_Profiler_Firebug. As it just sends all the logs he finds so you should do this filtering before you create the profiling information:

//Let's change how we defined our profiler
//$profiler = new Zend_Db_Profiler(true);//true means enable profiler
$profiler = new Zend_Db_Profiler_Firebug('My Database Query Logs'); //The name of your queries which you will see on top of a table
$profiler->setEnabled(true); //Enable the profiling
//Look for this part where you set the filtering
$profiler->setFilterQueryType(Zend_Db_Profiler::UPDATE);

function query($str, $params, $type = Zend_Db_Profiler::SELECT) {
        foreach($params as $key => $val) { //Do some logic for replacing the real values instead of placeholders
            $str = str_replace('@' . $key, $val, $str); //Do not forget about sql injection...
        }
	if ($dbProfileEnabled) { //Check if the profiling enabled or not...
	    $queryId = $profiler->queryStart($str, $type);
	    $qp = $profiler->getQueryProfile($queryId);
	    if ($qp->hasEnded()) {
		$queryId = $profiler->queryClone($qp);
		$qp = $profiler->getQueryProfile($queryId);
	    }
            $qp->bindParams($params);
	    $qp->start($queryId);
	    $q = mysql_query($str);
	    $profiler->queryEnd($queryId);
	} else {
	    $q = mysql_query($str);
	}
	//Do some error handling or fetch the results from now on as you feel comfortable...
}

So that’s it. It was a good time I had and I was again impressed with the simplicity of how the job is done. And the best part is you are not dependent to Zend’s controller objects to send the data to Firebug. This time Wildfire really works as a channel and I really appreciate that!. It still depend on it. I thought that it wasn’t because I was already sending the headers via my Logger class and as there is singleton on Wildfire instance, by my Logger class that I was able to see the database logs. That’s a bit dissapointing but it works great! Check here for how to send your profiling data to the Firebug console via Wildfire channel or you can also check my previous post too :)