PHP Profile Class

September 24, 2008

Profiling is a important part of software development, allowing you to find bottlenecks in your application and discover areas that would benefit from optimisation. In PHP a common technique is to calculate the total time to render a page. This can be achieved using the microtime function:

<?php
$startTime = microtime(true);

// normal page code here

$time = microtime(true) - $startTime;
echo "<!-- Page generation time: {$time} seconds -->";
?>

This simple approach is a a great way to find out how long certain pages are taking to load. Sometimes, however, you want more granular profiling information. That’s why I created a Profile class, which records the execution time of a given method. Here’s how to use it:

$p = new Profile();
$time = $p->profile("myClassName", "methodName", array("arg1", "arg2"));

You can also pass in an optional fourth argument, which specifies how many times to run the given method. After running it multiple times you can call the Profile::printDetails() method to view the total time, average invocation time, and the maximum single invocation time. Below is a simple example:

<?php
include("Profile.php");

/**
 * Test class just for the purposes of demonstration
 */
class Test {
	public function add($a, $b) {
		// add the numbers together 100 times
		for($i = 0; $i < 100; $i++) {
			$c = $a + $b;
		}
	}
}

$p = new Profile();
$p->profile("Test", "add", array(1, 2), 1000);
$p->printDetails();
?>

If you run the above code you’ll see the following output:

Test::add(1, 2) was invoked 1000 times
Total duration: 0.0447s
Average duration: 0s
Worst duration: 0.002s

The code listing for the Profile class is below, with some of the comments removed for the sake of brevity. A fully commented version is available for download.

<?php
/**
 * Class to time the execution of method calls.
 *
 * @author Ben Dowling - www.coderholic.com
 */
class Profile {
	/**
	 * Stores details about the last profiled method
	 */
	private $details;

    public function __construct() {}

	/**
	 * @param classname string
	 * @param methodname string
	 * @param methodargs array
	 * @param invocations int The number of times to call the method
	 * @return float average invocation duration in seconds
	 */
	public function profile($classname, $methodname, $methodargs, $invocations = 1) {
		if(class_exists($classname) != TRUE) {
			throw new Exception("{$classname} doesn't exist");
		}

		$method = new ReflectionMethod($classname, $methodname);

		$instance = NULL;
		if(!$method->isStatic()) 		{
			$class = new ReflectionClass($classname);
			$instance = $class->newInstance();
		}

		$durations = array();
		for($i = 0; $i < $invocations; $i++) {
			$start = microtime(true);
			$method->invokeArgs($instance, $methodargs);
			$durations[] = microtime(true) - $start;
		}

		$duration["total"] = round(array_sum($durations), 4);
		$duration["average"] = round($duration["total"] / count($durations), 4);
		$duration["worst"] = round(max($durations), 4);	

		$this->details = array(	"class" => $classname,
							   	"method" => $methodname,
							   	"arguments" => $methodargs,
						 		"duration" => $duration,
								"invocations" => $invocations);

		return $duration["average"];
	}

	/**
	 * @return string
	 */
	private function invokedMethod() {
		return "{$this->details["class"]}::{$this->details["method"]}(" .
			 join(", ", $this->details["arguments"]) . ")";
	}

	/**
	 * Prints out details about the last profiled method
	 */
	public function printDetails() {
		$methodString = $this->invokedMethod();
		$numInvoked = $this->details["invocations"];

		if($numInvoked == 1) {
			echo "{$methodString} took {$this->details["duration"]["average"]}s\n";
		}

		else {
			echo "{$methodString} was invoked {$numInvoked} times\n";
			echo "Total duration:   {$this->details["duration"]["total"]}s\n";
			echo "Average duration: {$this->details["duration"]["average"]}s\n";
			echo "Worst duration:   {$this->details["duration"]["worst"]}s\n";
		}
	}
}

?>

Howto cd to a dir starting with -

I’ve been using Linux for over 10 years now, but I ran into a seemingly simple problem today that had me stumped. I wanted to change directory whose name started with a “-” character (a hypen, or dash). Here’s what happened:

bmd /dir: ls
-folder
bmd /dir: cd -folder
-bash: cd: -f: invalid option
cd: usage: cd [-L|-P] [dir]
bmd /dir: cd \-folder
-bash: cd: -f: invalid option
cd: usage: cd [-L|-P] [dir]
bmd /dir: cd “-folder”
-bash: cd: -f: invalid option
cd: usage: cd [-L|-P] [dir]

I wasn’t having much luck! It turns out solution is to pass “--” (two hypens) as the first argument to cd:

bmd /dir: cd — -folder
/home/bmd/dir/-folder
bmd /-folder:

I think I remember having to use this trick in the past, but it’s such an infrequent problem that I obviously forgot about it. Hopefully by posting about it I’ll have a better change of remembering it for next time! :)

Automatic SQL generation using Dia

September 14, 2008

If you don’t already know about it, Dia is an open source diagram drawing application, similar to Microsoft’s Visio. It makes it really simple to create all sorts of diagrams, including UML diagrams, flowcharts, and network diagrams. It also allows diagrams to be exported to a number of formats, including PNG images (which can easily be converted to GIF images using my handy PNG2GIF utility).

Using a third party tool tedia2sql, you can automatically generate SQL schema for a number of databases, including Oracle, MySQL, Postgres, based on your database diagram. In this post I’ll describe exactly how.

The diagram

To create the database diagram we’ll be using Dia’s UML tools. Classes represent database tables, and associations between classes represent foreign key constrains.

In this post I’ll go over a simple example database with just three tables: Film, Film_Actor and Actor.

To create our database diagram we’re going to use Dia’s UML shapes. When you first start Dia it defaults to displaying the “Assorted” set of shapes, so you’ll need to change the drop down to “UML”

Then we can click on the UML class shape, which is at the top left.Once we’ve done that we should have a diagram that looks like the one below:

Double clicking on the class will bring up a properties window, which has several tabs. On the first “Class” tab we can set the name. For the first table that is “Film”. On the “Attributes” tab we can enter the rows that this table will have. Setting the attribute visibility to protected signifies that it is a primary key.

After adding all three tables and attributes we end up with a diagram like the one below:

At this point we could already generate a database schema, but it wouldn’t contain any foreign key contraints. If you’re not interested in adding them then you can skip straight to the generation part.

Foreign key contraints must be modelled using the UML aggregation tool: The line with the white diamond on one end. Select that tool, and then click on one table and drag and drop onto another table. When you let go of the mouse button a link should be drawn between the two. Double clicking on that line brings up a properties dialog.

Enter the foreign key in one end, and the row to which the foreign key referrs. The link between the Film_Actors table and the Films table is shown above, where the link is from film_id to id.

After adding a similar link between the Film_Actors and Actor table we end up with our complete diagram:

Generating the SQL

Now that our database diagram is compete we can generate the SQL commands to create the database. The command to generate the code for MySQL InnoDB is:

tedia2sql -i diagram.dia -o schema.sql -t innodb -f

If we want to generate SQL for a different database (such as Oracle) then we just need to change the -t argument. The generated SQL contains lots of comments, but the main sections are shown below:

-- Film
create table Film (
  id                        int not null,
  title                     varchar,
  year                      int,
  constraint pk_Film primary key (id)
) type = InnoDB ;

-- Actor
create table Actor (
  id                        int not null,
  name                      varchar,
  dob                       date,
  constraint pk_Actor primary key (id)
) type = InnoDB ;

-- Film_Actors
create table Film_Actors (
  film_id                   int not null,
  actor_id                  int not null,
  constraint pk_Film_Actors primary key (film_id,actor_id)
) type = InnoDB ;

alter table Film_Actors add constraint film_Actors_fk_Film_id
  foreign key (film_id)
  references Film (id)  ;
alter table Film_Actors add constraint film_Actors_fk_Actor_id
  foreign key (actor_id)
  references Actor (id)  ;

Other Dia tools

In this post I described how easy it is to generate SQL from a Dia diagram using tedia2sql. There are lots of other great third party tools to automatically generate output based on your diagram. The official Dia links page lists many of them.

Ubuntu Jaunty Jackalope

September 10, 2008

I previously blogged about Ubuntu Linux Codenames, right after it was announced that the 8.10 release would be known as Intrepid Ibex. At the time I discussed possible names for the 9.04 release, my favourite being Jiggly Jellyfish. It wasn’t to be! A recent email on the Ubuntu mailing list revealed:

As we approach the launch of Ubuntu 8.10, it’s time to create space for future plans, and so I’m writing to introduce you to The Jaunty Jackalope

I’d never heard of a Jackalope before, and quickly looked it up on Wikipedia. It turns out it is a fictional animal that is a cross between a jackrabbit and an antelope. How strange!

I think this is going to be the first release of Ubuntu named after a fictional animal. Personally I think they should have stuck to real animals. What’s wrong with a normal jackrabbit?

As for the next release? I think Kicking Kangaroo would be brilliant! Based on the Jackalope release though, I’d say Kooky Kinnara is more likely.