1: <?php
2:
3: /**
4: * @author Lorenz Weber <mail@phryneas.de>
5: * @copyright (c) 2013, Lorenz Weber
6: * @package loggedPDO
7: *
8: * The MIT License (MIT)
9: *
10: * @copyright (c) 2013, Lorenz Weber
11: *
12: * Permission is hereby granted, free of charge, to any person obtaining a copy
13: * of this software and associated documentation files (the "Software"), to deal
14: * in the Software without restriction, including without limitation the rights
15: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16: * copies of the Software, and to permit persons to whom the Software is
17: * furnished to do so, subject to the following conditions:
18: *
19: * The above copyright notice and this permission notice shall be included in
20: * all copies or substantial portions of the Software.
21: *
22: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25: * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28: * THE SOFTWARE.
29: */
30:
31: namespace LoggedPDO;
32:
33: if (stream_resolve_include_path('Log.php'))
34: require_once 'Log.php';
35: else
36: die("Failure including Log.php\nplease install PEAR::Log or check your include_path\n");
37:
38: require_once __DIR__ . DIRECTORY_SEPARATOR . 'PDOStatement.php';
39:
40: /**
41: * Class extending the php PDO Class for logging purpose
42: *
43: * @uses Log.php
44: *
45: * simple usage:
46: * <code>
47: * require_once 'loggedPDO/PDO.php';
48: * $logger = Log::factory('console', '', 'PDO');
49: * $pdo = new \LoggedPDO\PDO($connstr, DB_USERNAME, DB_PASSWORD, null, $logger);
50: * </code>
51: *
52: * users of a firebug logger might alternatively like the Log_firebugJSON class
53: * <code>
54: * require_once 'loggedPDO/Log_firebugJSON.php';
55: * $logger = Log::factory('console', '', 'PDO');
56: * </code>
57: *
58: *
59: * {@inheritdoc}
60: */
61: class PDO extends \PDO {
62:
63: /**
64: * PEAR Log object that will be used for logging
65: * @var \Log
66: */
67: public $log;
68: private $logFullTime;
69: private $logCount;
70: public static $LOG_QUERY = "query";
71: public static $LOG_TIME = "time";
72: public static $LOG_TYPE = "method";
73: public static $LOG_PARAMS = "parameters";
74:
75: /**
76: * If true, parameters will be inserted into query for logging.
77: * If false, query and parameters will be logged separately.
78: * @var boolean
79: */
80: public $log_replace_params = true;
81:
82: /**
83: * {@inheritdoc}
84: *
85: * @param \Log $log a PEAR Log object that will be used for logging
86: * @throws \Exception if there is no PEAR Log object specified
87: */
88: public function __construct($dsn, $username = null, $password = null, $options = null, \Log $log = null) {
89: if ($log == null) {
90: throw new \Exception("We need a PEAR Log object, parameter order has just been kept due to consistency, last element is NOT optional.\n"
91: . "Please call this class as.\n"
92: . "new \LoggedPDO\PDO(\$dsn, null, null, null, \$log);.\n"
93: . "if you have no \$username, \$password or \$options to specify.");
94: }
95: parent::__construct($dsn, $username, $password, $options);
96: $this->log = $log;
97: $this->setAttribute(\PDO::ATTR_STATEMENT_CLASS, array('\LoggedPDO\PDOStatement', array()));
98: }
99:
100: /**
101: * {@inheritdoc}
102: */
103: public function prepare($statement, $driver_options = array()) {
104: $pdostatement = parent::prepare($statement, $driver_options);
105:
106: $pdostatement->pdo = $this;
107: return $pdostatement;
108: }
109:
110: /**
111: * {@inheritdoc}
112: */
113: public function query($statement) {
114: $start = microtime(true);
115: $pdostatement = parent::query($statement);
116: $time = microtime(true) - $start;
117: $this->log($statement, round($time * 1000, 3));
118:
119: $pdostatement->pdo = $this;
120: return $pdostatement;
121: }
122:
123: /**
124: * {@inheritdoc}
125: */
126: public function exec($statement) {
127: $start = microtime(true);
128: parent::exec($statement);
129: $time = microtime(true) - $start;
130: $this->log($statement, round($time * 1000, 3));
131: }
132:
133: /**
134: * Returns the assigned PEAR logger
135: * @return \Log
136: */
137: public function getLogger() {
138: return $this->log;
139: }
140:
141: /**
142: * Log a query.
143: * @param type $query Logged Query
144: * @param type $time Time used by query
145: * @param type $params Params, only if $log_replace_params is true
146: */
147: public function log($query, $time, $params = null) {
148:
149: $this->logFullTime+=$time;
150: $this->logCount++;
151:
152: $trace = debug_backtrace();
153: $stackdepth = 1;
154: $called_from = sprintf('%1$s->%2$s in %3$s on line %4$d'
155: , $trace[$stackdepth]['class']
156: , $trace[$stackdepth]['function']
157: , $trace[$stackdepth]['file']
158: , $trace[$stackdepth]['line']
159: );
160:
161:
162: $log = array(
163: self::$LOG_TIME => $time,
164: self::$LOG_QUERY => $query,
165: self::$LOG_TYPE => $called_from);
166: if ($this->log_replace_params == false) {
167: $log[self::$LOG_PARAMS] = $params;
168: }
169:
170: $this->log->log(
171: array(sprintf('query took %s ms', $time), $log)
172: , PEAR_LOG_DEBUG);
173: }
174:
175: /**
176: * Get time spent by all logged querys until now.
177: * @return float
178: */
179: public function getFullTime() {
180: return $this->logFullTime;
181: }
182:
183: /**
184: * Get count of querys executed until now.
185: * @return integer
186: */
187: public function getQueryCount() {
188: return $this->logCount;
189: }
190:
191: }
192:
193: ?>
194: