|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_Db |
|
17 * @subpackage Profiler |
|
18 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
19 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
20 * @version $Id: Profiler.php 20096 2010-01-06 02:05:09Z bkarwin $ |
|
21 */ |
|
22 |
|
23 |
|
24 /** |
|
25 * @category Zend |
|
26 * @package Zend_Db |
|
27 * @subpackage Profiler |
|
28 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
29 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
30 */ |
|
31 class Zend_Db_Profiler |
|
32 { |
|
33 |
|
34 /** |
|
35 * A connection operation or selecting a database. |
|
36 */ |
|
37 const CONNECT = 1; |
|
38 |
|
39 /** |
|
40 * Any general database query that does not fit into the other constants. |
|
41 */ |
|
42 const QUERY = 2; |
|
43 |
|
44 /** |
|
45 * Adding new data to the database, such as SQL's INSERT. |
|
46 */ |
|
47 const INSERT = 4; |
|
48 |
|
49 /** |
|
50 * Updating existing information in the database, such as SQL's UPDATE. |
|
51 * |
|
52 */ |
|
53 const UPDATE = 8; |
|
54 |
|
55 /** |
|
56 * An operation related to deleting data in the database, |
|
57 * such as SQL's DELETE. |
|
58 */ |
|
59 const DELETE = 16; |
|
60 |
|
61 /** |
|
62 * Retrieving information from the database, such as SQL's SELECT. |
|
63 */ |
|
64 const SELECT = 32; |
|
65 |
|
66 /** |
|
67 * Transactional operation, such as start transaction, commit, or rollback. |
|
68 */ |
|
69 const TRANSACTION = 64; |
|
70 |
|
71 /** |
|
72 * Inform that a query is stored (in case of filtering) |
|
73 */ |
|
74 const STORED = 'stored'; |
|
75 |
|
76 /** |
|
77 * Inform that a query is ignored (in case of filtering) |
|
78 */ |
|
79 const IGNORED = 'ignored'; |
|
80 |
|
81 /** |
|
82 * Array of Zend_Db_Profiler_Query objects. |
|
83 * |
|
84 * @var array |
|
85 */ |
|
86 protected $_queryProfiles = array(); |
|
87 |
|
88 /** |
|
89 * Stores enabled state of the profiler. If set to False, calls to |
|
90 * queryStart() will simply be ignored. |
|
91 * |
|
92 * @var boolean |
|
93 */ |
|
94 protected $_enabled = false; |
|
95 |
|
96 /** |
|
97 * Stores the number of seconds to filter. NULL if filtering by time is |
|
98 * disabled. If an integer is stored here, profiles whose elapsed time |
|
99 * is less than this value in seconds will be unset from |
|
100 * the self::$_queryProfiles array. |
|
101 * |
|
102 * @var integer |
|
103 */ |
|
104 protected $_filterElapsedSecs = null; |
|
105 |
|
106 /** |
|
107 * Logical OR of any of the filter constants. NULL if filtering by query |
|
108 * type is disable. If an integer is stored here, it is the logical OR of |
|
109 * any of the query type constants. When the query ends, if it is not |
|
110 * one of the types specified, it will be unset from the |
|
111 * self::$_queryProfiles array. |
|
112 * |
|
113 * @var integer |
|
114 */ |
|
115 protected $_filterTypes = null; |
|
116 |
|
117 /** |
|
118 * Class constructor. The profiler is disabled by default unless it is |
|
119 * specifically enabled by passing in $enabled here or calling setEnabled(). |
|
120 * |
|
121 * @param boolean $enabled |
|
122 * @return void |
|
123 */ |
|
124 public function __construct($enabled = false) |
|
125 { |
|
126 $this->setEnabled($enabled); |
|
127 } |
|
128 |
|
129 /** |
|
130 * Enable or disable the profiler. If $enable is false, the profiler |
|
131 * is disabled and will not log any queries sent to it. |
|
132 * |
|
133 * @param boolean $enable |
|
134 * @return Zend_Db_Profiler Provides a fluent interface |
|
135 */ |
|
136 public function setEnabled($enable) |
|
137 { |
|
138 $this->_enabled = (boolean) $enable; |
|
139 |
|
140 return $this; |
|
141 } |
|
142 |
|
143 /** |
|
144 * Get the current state of enable. If True is returned, |
|
145 * the profiler is enabled. |
|
146 * |
|
147 * @return boolean |
|
148 */ |
|
149 public function getEnabled() |
|
150 { |
|
151 return $this->_enabled; |
|
152 } |
|
153 |
|
154 /** |
|
155 * Sets a minimum number of seconds for saving query profiles. If this |
|
156 * is set, only those queries whose elapsed time is equal or greater than |
|
157 * $minimumSeconds will be saved. To save all queries regardless of |
|
158 * elapsed time, set $minimumSeconds to null. |
|
159 * |
|
160 * @param integer $minimumSeconds OPTIONAL |
|
161 * @return Zend_Db_Profiler Provides a fluent interface |
|
162 */ |
|
163 public function setFilterElapsedSecs($minimumSeconds = null) |
|
164 { |
|
165 if (null === $minimumSeconds) { |
|
166 $this->_filterElapsedSecs = null; |
|
167 } else { |
|
168 $this->_filterElapsedSecs = (integer) $minimumSeconds; |
|
169 } |
|
170 |
|
171 return $this; |
|
172 } |
|
173 |
|
174 /** |
|
175 * Returns the minimum number of seconds for saving query profiles, or null if |
|
176 * query profiles are saved regardless of elapsed time. |
|
177 * |
|
178 * @return integer|null |
|
179 */ |
|
180 public function getFilterElapsedSecs() |
|
181 { |
|
182 return $this->_filterElapsedSecs; |
|
183 } |
|
184 |
|
185 /** |
|
186 * Sets the types of query profiles to save. Set $queryType to one of |
|
187 * the Zend_Db_Profiler::* constants to only save profiles for that type of |
|
188 * query. To save more than one type, logical OR them together. To |
|
189 * save all queries regardless of type, set $queryType to null. |
|
190 * |
|
191 * @param integer $queryTypes OPTIONAL |
|
192 * @return Zend_Db_Profiler Provides a fluent interface |
|
193 */ |
|
194 public function setFilterQueryType($queryTypes = null) |
|
195 { |
|
196 $this->_filterTypes = $queryTypes; |
|
197 |
|
198 return $this; |
|
199 } |
|
200 |
|
201 /** |
|
202 * Returns the types of query profiles saved, or null if queries are saved regardless |
|
203 * of their types. |
|
204 * |
|
205 * @return integer|null |
|
206 * @see Zend_Db_Profiler::setFilterQueryType() |
|
207 */ |
|
208 public function getFilterQueryType() |
|
209 { |
|
210 return $this->_filterTypes; |
|
211 } |
|
212 |
|
213 /** |
|
214 * Clears the history of any past query profiles. This is relentless |
|
215 * and will even clear queries that were started and may not have |
|
216 * been marked as ended. |
|
217 * |
|
218 * @return Zend_Db_Profiler Provides a fluent interface |
|
219 */ |
|
220 public function clear() |
|
221 { |
|
222 $this->_queryProfiles = array(); |
|
223 |
|
224 return $this; |
|
225 } |
|
226 |
|
227 /** |
|
228 * @param integer $queryId |
|
229 * @return integer or null |
|
230 */ |
|
231 public function queryClone(Zend_Db_Profiler_Query $query) |
|
232 { |
|
233 $this->_queryProfiles[] = clone $query; |
|
234 |
|
235 end($this->_queryProfiles); |
|
236 |
|
237 return key($this->_queryProfiles); |
|
238 } |
|
239 |
|
240 /** |
|
241 * Starts a query. Creates a new query profile object (Zend_Db_Profiler_Query) |
|
242 * and returns the "query profiler handle". Run the query, then call |
|
243 * queryEnd() and pass it this handle to make the query as ended and |
|
244 * record the time. If the profiler is not enabled, this takes no |
|
245 * action and immediately returns null. |
|
246 * |
|
247 * @param string $queryText SQL statement |
|
248 * @param integer $queryType OPTIONAL Type of query, one of the Zend_Db_Profiler::* constants |
|
249 * @return integer|null |
|
250 */ |
|
251 public function queryStart($queryText, $queryType = null) |
|
252 { |
|
253 if (!$this->_enabled) { |
|
254 return null; |
|
255 } |
|
256 |
|
257 // make sure we have a query type |
|
258 if (null === $queryType) { |
|
259 switch (strtolower(substr(ltrim($queryText), 0, 6))) { |
|
260 case 'insert': |
|
261 $queryType = self::INSERT; |
|
262 break; |
|
263 case 'update': |
|
264 $queryType = self::UPDATE; |
|
265 break; |
|
266 case 'delete': |
|
267 $queryType = self::DELETE; |
|
268 break; |
|
269 case 'select': |
|
270 $queryType = self::SELECT; |
|
271 break; |
|
272 default: |
|
273 $queryType = self::QUERY; |
|
274 break; |
|
275 } |
|
276 } |
|
277 |
|
278 /** |
|
279 * @see Zend_Db_Profiler_Query |
|
280 */ |
|
281 require_once 'Zend/Db/Profiler/Query.php'; |
|
282 $this->_queryProfiles[] = new Zend_Db_Profiler_Query($queryText, $queryType); |
|
283 |
|
284 end($this->_queryProfiles); |
|
285 |
|
286 return key($this->_queryProfiles); |
|
287 } |
|
288 |
|
289 /** |
|
290 * Ends a query. Pass it the handle that was returned by queryStart(). |
|
291 * This will mark the query as ended and save the time. |
|
292 * |
|
293 * @param integer $queryId |
|
294 * @throws Zend_Db_Profiler_Exception |
|
295 * @return void |
|
296 */ |
|
297 public function queryEnd($queryId) |
|
298 { |
|
299 // Don't do anything if the Zend_Db_Profiler is not enabled. |
|
300 if (!$this->_enabled) { |
|
301 return self::IGNORED; |
|
302 } |
|
303 |
|
304 // Check for a valid query handle. |
|
305 if (!isset($this->_queryProfiles[$queryId])) { |
|
306 /** |
|
307 * @see Zend_Db_Profiler_Exception |
|
308 */ |
|
309 require_once 'Zend/Db/Profiler/Exception.php'; |
|
310 throw new Zend_Db_Profiler_Exception("Profiler has no query with handle '$queryId'."); |
|
311 } |
|
312 |
|
313 $qp = $this->_queryProfiles[$queryId]; |
|
314 |
|
315 // Ensure that the query profile has not already ended |
|
316 if ($qp->hasEnded()) { |
|
317 /** |
|
318 * @see Zend_Db_Profiler_Exception |
|
319 */ |
|
320 require_once 'Zend/Db/Profiler/Exception.php'; |
|
321 throw new Zend_Db_Profiler_Exception("Query with profiler handle '$queryId' has already ended."); |
|
322 } |
|
323 |
|
324 // End the query profile so that the elapsed time can be calculated. |
|
325 $qp->end(); |
|
326 |
|
327 /** |
|
328 * If filtering by elapsed time is enabled, only keep the profile if |
|
329 * it ran for the minimum time. |
|
330 */ |
|
331 if (null !== $this->_filterElapsedSecs && $qp->getElapsedSecs() < $this->_filterElapsedSecs) { |
|
332 unset($this->_queryProfiles[$queryId]); |
|
333 return self::IGNORED; |
|
334 } |
|
335 |
|
336 /** |
|
337 * If filtering by query type is enabled, only keep the query if |
|
338 * it was one of the allowed types. |
|
339 */ |
|
340 if (null !== $this->_filterTypes && !($qp->getQueryType() & $this->_filterTypes)) { |
|
341 unset($this->_queryProfiles[$queryId]); |
|
342 return self::IGNORED; |
|
343 } |
|
344 |
|
345 return self::STORED; |
|
346 } |
|
347 |
|
348 /** |
|
349 * Get a profile for a query. Pass it the same handle that was returned |
|
350 * by queryStart() and it will return a Zend_Db_Profiler_Query object. |
|
351 * |
|
352 * @param integer $queryId |
|
353 * @throws Zend_Db_Profiler_Exception |
|
354 * @return Zend_Db_Profiler_Query |
|
355 */ |
|
356 public function getQueryProfile($queryId) |
|
357 { |
|
358 if (!array_key_exists($queryId, $this->_queryProfiles)) { |
|
359 /** |
|
360 * @see Zend_Db_Profiler_Exception |
|
361 */ |
|
362 require_once 'Zend/Db/Profiler/Exception.php'; |
|
363 throw new Zend_Db_Profiler_Exception("Query handle '$queryId' not found in profiler log."); |
|
364 } |
|
365 |
|
366 return $this->_queryProfiles[$queryId]; |
|
367 } |
|
368 |
|
369 /** |
|
370 * Get an array of query profiles (Zend_Db_Profiler_Query objects). If $queryType |
|
371 * is set to one of the Zend_Db_Profiler::* constants then only queries of that |
|
372 * type will be returned. Normally, queries that have not yet ended will |
|
373 * not be returned unless $showUnfinished is set to True. If no |
|
374 * queries were found, False is returned. The returned array is indexed by the query |
|
375 * profile handles. |
|
376 * |
|
377 * @param integer $queryType |
|
378 * @param boolean $showUnfinished |
|
379 * @return array|false |
|
380 */ |
|
381 public function getQueryProfiles($queryType = null, $showUnfinished = false) |
|
382 { |
|
383 $queryProfiles = array(); |
|
384 foreach ($this->_queryProfiles as $key => $qp) { |
|
385 if ($queryType === null) { |
|
386 $condition = true; |
|
387 } else { |
|
388 $condition = ($qp->getQueryType() & $queryType); |
|
389 } |
|
390 |
|
391 if (($qp->hasEnded() || $showUnfinished) && $condition) { |
|
392 $queryProfiles[$key] = $qp; |
|
393 } |
|
394 } |
|
395 |
|
396 if (empty($queryProfiles)) { |
|
397 $queryProfiles = false; |
|
398 } |
|
399 |
|
400 return $queryProfiles; |
|
401 } |
|
402 |
|
403 /** |
|
404 * Get the total elapsed time (in seconds) of all of the profiled queries. |
|
405 * Only queries that have ended will be counted. If $queryType is set to |
|
406 * one or more of the Zend_Db_Profiler::* constants, the elapsed time will be calculated |
|
407 * only for queries of the given type(s). |
|
408 * |
|
409 * @param integer $queryType OPTIONAL |
|
410 * @return float |
|
411 */ |
|
412 public function getTotalElapsedSecs($queryType = null) |
|
413 { |
|
414 $elapsedSecs = 0; |
|
415 foreach ($this->_queryProfiles as $key => $qp) { |
|
416 if (null === $queryType) { |
|
417 $condition = true; |
|
418 } else { |
|
419 $condition = ($qp->getQueryType() & $queryType); |
|
420 } |
|
421 if (($qp->hasEnded()) && $condition) { |
|
422 $elapsedSecs += $qp->getElapsedSecs(); |
|
423 } |
|
424 } |
|
425 return $elapsedSecs; |
|
426 } |
|
427 |
|
428 /** |
|
429 * Get the total number of queries that have been profiled. Only queries that have ended will |
|
430 * be counted. If $queryType is set to one of the Zend_Db_Profiler::* constants, only queries of |
|
431 * that type will be counted. |
|
432 * |
|
433 * @param integer $queryType OPTIONAL |
|
434 * @return integer |
|
435 */ |
|
436 public function getTotalNumQueries($queryType = null) |
|
437 { |
|
438 if (null === $queryType) { |
|
439 return count($this->_queryProfiles); |
|
440 } |
|
441 |
|
442 $numQueries = 0; |
|
443 foreach ($this->_queryProfiles as $qp) { |
|
444 if ($qp->hasEnded() && ($qp->getQueryType() & $queryType)) { |
|
445 $numQueries++; |
|
446 } |
|
447 } |
|
448 |
|
449 return $numQueries; |
|
450 } |
|
451 |
|
452 /** |
|
453 * Get the Zend_Db_Profiler_Query object for the last query that was run, regardless if it has |
|
454 * ended or not. If the query has not ended, its end time will be null. If no queries have |
|
455 * been profiled, false is returned. |
|
456 * |
|
457 * @return Zend_Db_Profiler_Query|false |
|
458 */ |
|
459 public function getLastQueryProfile() |
|
460 { |
|
461 if (empty($this->_queryProfiles)) { |
|
462 return false; |
|
463 } |
|
464 |
|
465 end($this->_queryProfiles); |
|
466 |
|
467 return current($this->_queryProfiles); |
|
468 } |
|
469 |
|
470 } |
|
471 |