Add base/profiling.php and data/profiling_pdo.php.
[hoplite.git] / data / profiling_pdo.php
1 <?php
2 // Hoplite
3 // Copyright (c) 2013 Blue Static
4 //
5 // This program is free software: you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or any later version.
8 //
9 // This program is distributed in the hope that it will be useful, but WITHOUT
10 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 // more details.
13 //
14 // You should have received a copy of the GNU General Public License along with
15 // this program. If not, see <http://www.gnu.org/licenses/>.
16
17 namespace hoplite\data;
18
19 use \hoplite\base\Profiling;
20
21 require_once HOPLITE_ROOT . '/base/profiling.php';
22
23 /*!
24 A wrapper class around PDO that profiles all calls to the database.
25
26 This can be used as a drop-in for \PDO, though it sets the
27 PDO::ATTR_STATEMENT_CLASS to a custom instance.
28 */
29 class ProfilingPDO extends \PDO
30 {
31 private $traces = array();
32
33 private $statements = array();
34
35 public function __construct()
36 {
37 $args = func_get_args();
38 call_user_func_array('parent::__construct', $args);
39 $this->SetAttribute(\PDO::ATTR_STATEMENT_CLASS,
40 array('\\hoplite\\data\\ProfilingPDOStatement', array($this)));
41 }
42
43 public function exec()
44 {
45 $start = microtime(true);
46
47 $args = func_get_args();
48 $r = call_user_func_array('parent::exec', $args);
49
50 $this->traces[] = array(
51 'start' => $start,
52 'end' => microtime(true),
53 'trace' => debug_backtrace(),
54 'query' => $statement,
55 );
56 return $r;
57 }
58
59 public function query()
60 {
61 $start = microtime(true);
62
63 $args = func_get_args();
64 $r = call_user_func_array('parent::query', $args);
65
66 $this->traces[] = array(
67 'start' => $start,
68 'end' => microtime(true),
69 'trace' => debug_backtrace(),
70 'query' => $args[0],
71 );
72
73 return $r;
74 }
75
76 public function prepare()
77 {
78 $start = microtime(true);
79
80 $args = func_get_args();
81 $r = call_user_func_array('parent::prepare', $args);
82
83 $this->traces[] = array(
84 'start' => $start,
85 'end' => microtime(true),
86 'trace' => debug_backtrace(),
87 'query' => $args[0],
88 'prepare' => TRUE,
89 );
90
91 return $r;
92 }
93
94 /*!
95 Returns the array of all the traces.
96 */
97 public function GetTraces()
98 {
99 return $this->traces;
100 }
101
102 /*!
103 Generates a block of HTML that displays information about the query traces.
104 */
105 public function ConstructHTMLDebugBlock()
106 {
107 $debug = '';
108
109 $debug .= "<br />\n";
110 $debug .= '<table cellpadding="4" cellspacing="1" border="0" align="center" width="30%" ' .
111 'style="background-color: rgb(60, 60, 60); color: white">' . "\n\t";
112 $debug .= '<tr><td><strong>Query Debug: ' . sizeof($this->traces) . ' Total </strong></td></tr>';
113 foreach ($this->traces as $query) {
114 $italic = isset($query['prepare']) && $query['prepare'] ? 'font-style: italic' : '';
115 $debug .= "\n\t<tr style=\"background-color: rgb(230, 230, 230); color: black; $italic\">";
116 $debug .= "\n\t\t<td>";
117 $debug .= "\n\t\t\t$query[query]\n\n";
118 if (isset($query['params'])) {
119 $debug .= "\t\t\t<ol>\n\t\t\t\t<li>";
120 $debug .= implode("</li>\n\t\t\t\t<li>", $query['params']);
121 $debug .= "</li>\n\t\t\t</ol>\n";
122 }
123 $debug .= "\n\t\t\t<div style=\"font-size: 9px;\">(" .
124 ($query['end'] - $query['start']) . ")</div>\n";
125 $debug .= "<!--\n" . implode("\n", Profiling::FormatDebugBacktrace($query['trace'])) .
126 "\n-->\n\t\t</td>\n\t</tr>";
127 }
128
129 $debug .= "\n</table>\n\n\n";
130
131 return $debug;
132 }
133
134 /*!
135 Internal function that records a query trace. This is public only for use b
136 ProfilingPDOStatement.
137 */
138 public function RecordTrace(array $trace)
139 {
140 $this->traces[] = $trace;
141 }
142 }
143
144 /*!
145 A companion to ProfilingPDO that profiles prepared statements.
146 */
147 class ProfilingPDOStatement extends \PDOStatement
148 {
149 private $pdo = NULL;
150
151 protected function __construct(ProfilingPDO $pdo)
152 {
153 $this->pdo = $pdo;
154 }
155
156 public function execute()
157 {
158 $start = microtime(true);
159
160 $args = func_get_args();
161 $r = call_user_func_array('parent::execute', $args);
162
163 $this->pdo->RecordTrace(array(
164 'start' => $start,
165 'end' => microtime(true),
166 'trace' => debug_backtrace(),
167 'query' => $this->queryString,
168 'params' => $args[0],
169 ));
170 }
171 }