From 27dda14dbe778b3d448ad82218e97611bee9302d Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Tue, 28 May 2013 09:44:45 -0400 Subject: [PATCH] Add base/profiling.php and data/profiling_pdo.php. --- CHANGES | 2 + base/profiling.php | 72 +++++++++++++++++ data/profiling_pdo.php | 171 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 base/profiling.php create mode 100644 data/profiling_pdo.php diff --git a/CHANGES b/CHANGES index 212018f..c88a804 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,7 @@ Version 1.1 ================================================================================ +- Add hoplite\data\ProfilingPDO to add profiling to database access. +- Add hoplite\base\Profiling static utility class. - Add hoplite\http\Input for request data sanitization. Version 1.0 diff --git a/base/profiling.php b/base/profiling.php new file mode 100644 index 0000000..5cf89f0 --- /dev/null +++ b/base/profiling.php @@ -0,0 +1,72 @@ +. + +namespace hoplite\base; + +/*! + A static class used to help profile Hoplite components. +*/ +class Profiling +{ + /*! @var bool Enable profiling. */ + static private $profiling_enabled = FALSE; + + /*! Enables profiling globally. */ + static public function EnableProfiling() + { + self::$profiling_enabled = TRUE; + } + + /*! Gets the current state for profiling. */ + static public function IsProfilingEnabled() + { + return self::$profiling_enabled; + } + + /*! + Prepares a debug_backtrace() array for output to the browser by + compressing the array into visible text + + @param array The return value from debug_backtrace(). + + @return array Array of strings that can be imploded() for display. + */ + static public function FormatDebugBacktrace($backtrace) + { + $trace = array(); + foreach ($backtrace AS $i => $frame) { + $args = ''; + $file = $frame['file'] . ':' . $frame['line']; + $file = str_replace($_SERVER['DOCUMENT_ROOT'], '', $file); + $funct = (isset($frame['class']) ? $frame['class'] . '::' . $frame['function'] : $frame['function']); + + if (isset($frame['args']) && is_array($frame['args'])) { + // Convert arrays and objects to strings. + foreach ($frame['args'] AS $id => $arg) { + if (is_array($arg)) + $frame['args']["$id"] = 'Array'; + else if (is_object($arg)) + $frame['args']["$id"] = get_class($arg); + } + $args = implode(', ', $frame['args']); + } + + $trace[] = "#$i $funct($args) called at [$file]"; + } + + return $trace; + } +} diff --git a/data/profiling_pdo.php b/data/profiling_pdo.php new file mode 100644 index 0000000..f415020 --- /dev/null +++ b/data/profiling_pdo.php @@ -0,0 +1,171 @@ +. + +namespace hoplite\data; + +use \hoplite\base\Profiling; + +require_once HOPLITE_ROOT . '/base/profiling.php'; + +/*! + A wrapper class around PDO that profiles all calls to the database. + + This can be used as a drop-in for \PDO, though it sets the + PDO::ATTR_STATEMENT_CLASS to a custom instance. +*/ +class ProfilingPDO extends \PDO +{ + private $traces = array(); + + private $statements = array(); + + public function __construct() + { + $args = func_get_args(); + call_user_func_array('parent::__construct', $args); + $this->SetAttribute(\PDO::ATTR_STATEMENT_CLASS, + array('\\hoplite\\data\\ProfilingPDOStatement', array($this))); + } + + public function exec() + { + $start = microtime(true); + + $args = func_get_args(); + $r = call_user_func_array('parent::exec', $args); + + $this->traces[] = array( + 'start' => $start, + 'end' => microtime(true), + 'trace' => debug_backtrace(), + 'query' => $statement, + ); + return $r; + } + + public function query() + { + $start = microtime(true); + + $args = func_get_args(); + $r = call_user_func_array('parent::query', $args); + + $this->traces[] = array( + 'start' => $start, + 'end' => microtime(true), + 'trace' => debug_backtrace(), + 'query' => $args[0], + ); + + return $r; + } + + public function prepare() + { + $start = microtime(true); + + $args = func_get_args(); + $r = call_user_func_array('parent::prepare', $args); + + $this->traces[] = array( + 'start' => $start, + 'end' => microtime(true), + 'trace' => debug_backtrace(), + 'query' => $args[0], + 'prepare' => TRUE, + ); + + return $r; + } + + /*! + Returns the array of all the traces. + */ + public function GetTraces() + { + return $this->traces; + } + + /*! + Generates a block of HTML that displays information about the query traces. + */ + public function ConstructHTMLDebugBlock() + { + $debug = ''; + + $debug .= "
\n"; + $debug .= '' . "\n\t"; + $debug .= ''; + foreach ($this->traces as $query) { + $italic = isset($query['prepare']) && $query['prepare'] ? 'font-style: italic' : ''; + $debug .= "\n\t"; + $debug .= "\n\t\t\n\t"; + } + + $debug .= "\n
Query Debug: ' . sizeof($this->traces) . ' Total
"; + $debug .= "\n\t\t\t$query[query]\n\n"; + if (isset($query['params'])) { + $debug .= "\t\t\t
    \n\t\t\t\t
  1. "; + $debug .= implode("
  2. \n\t\t\t\t
  3. ", $query['params']); + $debug .= "
  4. \n\t\t\t
\n"; + } + $debug .= "\n\t\t\t
(" . + ($query['end'] - $query['start']) . ")
\n"; + $debug .= "\n\t\t
\n\n\n"; + + return $debug; + } + + /*! + Internal function that records a query trace. This is public only for use b + ProfilingPDOStatement. + */ + public function RecordTrace(array $trace) + { + $this->traces[] = $trace; + } +} + +/*! + A companion to ProfilingPDO that profiles prepared statements. +*/ +class ProfilingPDOStatement extends \PDOStatement +{ + private $pdo = NULL; + + protected function __construct(ProfilingPDO $pdo) + { + $this->pdo = $pdo; + } + + public function execute() + { + $start = microtime(true); + + $args = func_get_args(); + $r = call_user_func_array('parent::execute', $args); + + $this->pdo->RecordTrace(array( + 'start' => $start, + 'end' => microtime(true), + 'trace' => debug_backtrace(), + 'query' => $this->queryString, + 'params' => $args[0], + )); + } +} -- 2.22.5