Escape the callstack function args in ProfilingPDO::ConstructHTMLDebugBlock().
[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/filter.php';
22 require_once HOPLITE_ROOT . '/base/profiling.php';
23
24 /*!
25 A wrapper class around PDO that profiles all calls to the database.
26
27 This can be used as a drop-in for \PDO, though it sets the
28 PDO::ATTR_STATEMENT_CLASS to a custom instance.
29 */
30 class ProfilingPDO extends \PDO
31 {
32 private $traces = array();
33
34 private $statements = array();
35
36 public function __construct()
37 {
38 $args = func_get_args();
39 call_user_func_array('parent::__construct', $args);
40 $this->SetAttribute(\PDO::ATTR_STATEMENT_CLASS,
41 array('\\hoplite\\data\\ProfilingPDOStatement', array($this)));
42 }
43
44 public function exec()
45 {
46 $start = microtime(true);
47
48 $args = func_get_args();
49 $r = call_user_func_array('parent::exec', $args);
50
51 $this->traces[] = array(
52 'start' => $start,
53 'end' => microtime(true),
54 'trace' => debug_backtrace(),
55 'query' => $statement,
56 );
57 return $r;
58 }
59
60 public function query()
61 {
62 $start = microtime(true);
63
64 $args = func_get_args();
65 $r = call_user_func_array('parent::query', $args);
66
67 $this->traces[] = array(
68 'start' => $start,
69 'end' => microtime(true),
70 'trace' => debug_backtrace(),
71 'query' => $args[0],
72 );
73
74 return $r;
75 }
76
77 public function prepare()
78 {
79 $start = microtime(true);
80
81 $args = func_get_args();
82 $r = call_user_func_array('parent::prepare', $args);
83
84 $this->traces[] = array(
85 'start' => $start,
86 'end' => microtime(true),
87 'trace' => debug_backtrace(),
88 'query' => $args[0],
89 'prepare' => TRUE,
90 );
91
92 return $r;
93 }
94
95 /*!
96 Returns the array of all the traces.
97 */
98 public function GetTraces()
99 {
100 return $this->traces;
101 }
102
103 /*!
104 Generates a block of HTML that displays information about the query traces.
105 */
106 public function ConstructHTMLDebugBlock()
107 {
108 $debug = '';
109
110 $debug .= "<br />\n";
111 $debug .= '<table cellpadding="4" cellspacing="1" border="0" align="center" width="30%" ' .
112 'style="background-color: rgb(60, 60, 60); color: white">' . "\n\t";
113 $debug .= '<tr><td><strong>Query Debug: ' . sizeof($this->traces) . ' Total </strong></td></tr>';
114 foreach ($this->traces as $query) {
115 $italic = isset($query['prepare']) && $query['prepare'] ? 'font-style: italic' : '';
116 $debug .= "\n\t<tr style=\"background-color: rgb(230, 230, 230); color: black; $italic\">";
117 $debug .= "\n\t\t<td>";
118 $debug .= "\n\t\t\t$query[query]\n\n";
119 if (isset($query['params'])) {
120 $debug .= "\t\t\t<ol>\n\t\t\t\t<li>";
121 $debug .= implode("</li>\n\t\t\t\t<li>", \hoplite\base\filter\String($query['params']));
122 $debug .= "</li>\n\t\t\t</ol>\n";
123 }
124 $debug .= "\n\t\t\t<div style=\"font-size: 9px;\">(" .
125 ($query['end'] - $query['start']) . ")</div>\n";
126 $debug .= "<!--\n" . implode("\n", Profiling::FormatDebugBacktrace($query['trace'])) .
127 "\n-->\n\t\t</td>\n\t</tr>";
128 }
129
130 $debug .= "\n</table>\n\n\n";
131
132 return $debug;
133 }
134
135 /*!
136 Internal function that records a query trace. This is public only for use b
137 ProfilingPDOStatement.
138 */
139 public function RecordTrace(array $trace)
140 {
141 $this->traces[] = $trace;
142 }
143 }
144
145 /*!
146 A companion to ProfilingPDO that profiles prepared statements.
147 */
148 class ProfilingPDOStatement extends \PDOStatement
149 {
150 private $pdo = NULL;
151
152 protected function __construct(ProfilingPDO $pdo)
153 {
154 $this->pdo = $pdo;
155 }
156
157 public function execute()
158 {
159 $start = microtime(true);
160
161 $args = func_get_args();
162 $r = call_user_func_array('parent::execute', $args);
163
164 $this->pdo->RecordTrace(array(
165 'start' => $start,
166 'end' => microtime(true),
167 'trace' => debug_backtrace(),
168 'query' => $this->queryString,
169 'params' => $args[0],
170 ));
171 }
172 }