Simpel voorbeeldje:
<?php
$pdo = new Debug_PDO('mysql:host=localhost;dbname=test', 'root', '');
register_shutdown_function(array($pdo, 'dump'));
?>

Advanced voorbeeldje (gebruikt mijn log_socket scriptje http://phphulp.nl/php/scripts/11/1465/):
<?php
$pdo = new Debug_PDO('mysql:host=localhost;dbname=test', 'root', '');

/* Alle INSERT INTO queries niet uitvoeren, maar wel als succesvol merken */
$pdo->add_filter(function($snapshot, &$success) {
	if(strstr($snapshot->query, 'INSERT INTO')) {
		$success = true;
		return false;
	} 
	
	return true;
});

/* In het antwoord van Debug_PDO_Snapshot::query_as_sql() de argumenten met
 * wat terminal-kleurtjes versieren, zodat ze goed opvallen */
$pdo->add_decorator(function($value) {
	if($value === null) {
		return chr(0x1B) . "[0;37;45mNULL" . chr(0x1B). "[0;32;40m";
	} else {
		return chr(0x1B) . "[0;37;41m" . $value . chr(0x1B). "[0;32;40m";
	}
});

/* Alle INSERT INTO queries (die dus niet uitgevoerd worden) over de log_socket
 * sturen zodat ze gekleurd en wel in mijn recieve programmaatje verschijnen */
$pdo->add_listener(function($snapshot) {
	if(strstr($snapshot->query, 'INSERT INTO')) {
		log_send((string) $snapshot);
	}
});
?>


De klassen:
<?php

<?php

class Debug_PDO extends PDO {
	
	public $snapshots = array();
	
	protected $_filters = array();
	
	protected $_listeners = array();
	
	protected $_decorators = array();
	
	public function __construct($uri, $username = null, $password = null) {
		parent::__construct($uri, $username, $password);
		$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('Debug_PDO_Statement', array($this)));
	}
	
	public function dump() {
		foreach($this->snapshots as $snapshot) {
			echo "<pre>" . $snapshot . "</pre><br><br>\n";
		}
	}
	
	public function add_listener($listener) {
		if(!is_callable($listener)) {
			throw new InvalidArgumentException('add_listener expects a valid callback');
		}
		
		$this->_listeners[] = $listener;
	}
	
	public function add_filter($filter) {
		if(!is_callable($filter)) {
			throw new InvalidArgumentException('add_filter expects a valid callback');
		}
		
		$this->_filters[] = $filter;
	}
	
	public function add_decorator($decorator) {
		if(!is_callable($decorator)) {
			throw new InvalidArgumentException('add_decorator expects a valid callback');
		}

		$this->_decorators[] = $decorator;
	}
	
	public function should_execute_statement(Debug_PDO_Snapshot $snapshot, &$success) {
		foreach($this->_filters as $filter) {
			if(!call_user_func_array($filter, array($snapshot, &$success))) return false;
		}
		
		return true;
	}
	
	public function add_snapshot(Debug_PDO_Snapshot $snapshot) {
		$this->snapshots[] = $snapshot;

		$snapshot->set_decorators($this->_decorators);
		
		foreach($this->_listeners as $listener) {
			call_user_func($listener, $snapshot, $this);
		}
	}

	/*public function lastInsertId() {
		return $this->snapshots[count($this->snapshots) - 1]->insert_id;
	}*/
}

class Debug_PDO_Statement extends PDOStatement {
	
	protected $_delegate;
	
	protected $_parameters = array();
	
	protected function __construct($delegate) {
		$this->_delegate = $delegate;
	}
	
	public function execute($arguments = null) {
		
		$snapshot = $this->make_snapshot($arguments);
		
		$this->_delegate->add_snapshot($snapshot);
		
		if($this->_delegate->should_execute_statement($snapshot, $success)) {
		
			$snapshot->timer->start();
		
			$success = parent::execute($arguments);
		
			$snapshot->timer->stop();
			
			$snapshot->insert_id = $this->_delegate->lastInsertId();
		
			$snapshot->affected_rows = $this->rowCount();
		}
		
		return $success;
	}
	
	public function bindParam($name, &$value, $data_type = null, $length = null, $driver_options = null) {
		$this->_parameters[$name] = $this->_delegate->quote($value);
		
		return parent::bindParam($name, $value, $data_type, $length, $driver_options);
	}
	
	public function bindValue($name, $value, $data_type = null, $length = null, $driver_options = null) {
		return $this->bindParam($name, $value, $data_type, $length, $driver_options);
	}
	
	public function make_snapshot($arguments) {
		$snapshot = new Debug_PDO_Snapshot();
		$snapshot->query = $this->queryString;
		$snapshot->parameters = $this->_parameters;
		$snapshot->anonymous_parameters = $arguments;
		
		return $snapshot;
	}
}

class Debug_PDO_Snapshot {
	
	public $query;
	
	public $parameters;
	
	public $insert_id;
	
	public $anonymous_parameters;
	
	private $_anonymous_parameter_index = 0;
	
	public $affected_rows;
	
	public $timer;
	
	protected $_decorators = array();
	
	public function __construct() {
		$this->timer = new Debug_PDO_Timer();
	}
		
	public function query_as_sql() {
		$this->_anonymous_parameter_index = 0;
		
		$query = self::_remove_indent(preg_replace_callback('/(?:(\:([a-zA-Z0-9\_]+))|(\?))/', array($this, '_replace_placeholder'), $this->query));
		
		return $query;
	}
	
	public function execution_time() {
		return $this->timer->delta();
	}
	
	public function set_decorators(array $decorators) {
		$this->_decorators = $decorators;
	}
	
	public function __toString() {
		return sprintf("%s\n%f seconds, %d affected rows\n",
			$this->query_as_sql(), $this->execution_time(), $this->affected_rows);
	}
	
	protected function _replace_placeholder($matches) {
		if($matches[0] == '?') {
			$value = $this->anonymous_parameters[$this->_anonymous_parameter_index++];
		} else {
			$value = $this->parameters[$matches[1]];
		}
		
		foreach($this->_decorators as $decorator) {
			$value = call_user_func($decorator, $value);
		}
		
		return $value;
	}
	
	static protected function _remove_indent($text) {
		$lines = explode("\n", $text);
		
		$smallest_indent = INF;
		
		foreach($lines as $line) {
			
			if(trim($line) == '') continue;
			
			$leading_tabs = 0;
			while($line[$leading_tabs] == "\t") $leading_tabs++;
			
			if($leading_tabs < $smallest_indent) {
				$smallest_indent = $leading_tabs;
			}
		}
		
		foreach($lines as $line_number => $line) {
			if(trim($line) == '') continue;
			 
			$lines[$line_number] = substr($line, $smallest_indent);
		}
		
		return implode("\n", $lines);
	}
}

class Debug_PDO_Timer {
	
	protected $_start_time;
	
	protected $_stop_time;
	
	public function start() {
		$this->_start_time = microtime(true);
	}
	
	public function stop() {
		$this->_stop_time = microtime(true);
	}
	
	public function delta() {
		return $this->_start_time && $this->_stop_time ? $this->_stop_time - $this->_start_time : false;
	}
}
?>