Add base/profiling.php and data/profiling_pdo.php.
authorRobert Sesek <rsesek@bluestatic.org>
Tue, 28 May 2013 13:44:45 +0000 (09:44 -0400)
committerRobert Sesek <rsesek@bluestatic.org>
Tue, 28 May 2013 13:44:45 +0000 (09:44 -0400)
CHANGES
base/profiling.php [new file with mode: 0644]
data/profiling_pdo.php [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index 212018f13a932a8e068f557745f5d5f8f980623b..c88a8044cdd52611cf2dc991207e28549055c32a 100644 (file)
--- 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 (file)
index 0000000..5cf89f0
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+// Hoplite
+// Copyright (c) 2013 Blue Static
+//
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation, either version 3 of the License, or any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program.  If not, see <http://www.gnu.org/licenses/>.
+
+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 (file)
index 0000000..f415020
--- /dev/null
@@ -0,0 +1,171 @@
+<?php
+// Hoplite
+// Copyright (c) 2013 Blue Static
+//
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation, either version 3 of the License, or any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program.  If not, see <http://www.gnu.org/licenses/>.
+
+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 .= "<br />\n";
+    $debug .= '<table cellpadding="4" cellspacing="1" border="0" align="center" width="30%" ' .
+        'style="background-color: rgb(60, 60, 60); color: white">' . "\n\t";
+    $debug .= '<tr><td><strong>Query Debug: ' . sizeof($this->traces) . ' Total </strong></td></tr>';
+    foreach ($this->traces as $query) {
+      $italic = isset($query['prepare']) && $query['prepare'] ? 'font-style: italic' : '';
+      $debug .= "\n\t<tr style=\"background-color: rgb(230, 230, 230); color: black; $italic\">";
+      $debug .= "\n\t\t<td>";
+      $debug .= "\n\t\t\t$query[query]\n\n";
+      if (isset($query['params'])) {
+        $debug .= "\t\t\t<ol>\n\t\t\t\t<li>";
+        $debug .= implode("</li>\n\t\t\t\t<li>", $query['params']);
+        $debug .= "</li>\n\t\t\t</ol>\n";
+      }
+      $debug .= "\n\t\t\t<div style=\"font-size: 9px;\">(" .
+          ($query['end'] - $query['start']) . ")</div>\n";
+      $debug .= "<!--\n" . implode("\n", Profiling::FormatDebugBacktrace($query['trace'])) .
+          "\n-->\n\t\t</td>\n\t</tr>";
+    }
+
+    $debug .= "\n</table>\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],
+    ));
+  }
+}