1<?php 2/** 3 * $Header$ 4 * $Horde: horde/lib/Log/sql.php,v 1.12 2000/08/16 20:27:34 chuck Exp $ 5 * 6 * @version $Revision$ 7 * @package Log 8 */ 9 10/** 11 * We require the PEAR DB class. This is generally defined in the DB.php file, 12 * but it's possible that the caller may have provided the DB class, or a 13 * compatible wrapper (such as the one shipped with MDB2), so we first check 14 * for an existing 'DB' class before including 'DB.php'. 15 */ 16if (!class_exists('DB')) { 17 require_once 'DB.php'; 18} 19 20/** 21 * The Log_sql class is a concrete implementation of the Log:: 22 * abstract class which sends messages to an SQL server. Each entry 23 * occupies a separate row in the database. 24 * 25 * This implementation uses PHP's PEAR database abstraction layer. 26 * 27 * CREATE TABLE log_table ( 28 * id INT NOT NULL, 29 * logtime TIMESTAMP NOT NULL, 30 * ident CHAR(16) NOT NULL, 31 * priority INT NOT NULL, 32 * message VARCHAR(200), 33 * PRIMARY KEY (id) 34 * ); 35 * 36 * @author Jon Parise <jon@php.net> 37 * @since Horde 1.3 38 * @since Log 1.0 39 * @package Log 40 * 41 * @example sql.php Using the SQL handler. 42 */ 43class Log_sql extends Log 44{ 45 /** 46 * Variable containing the DSN information. 47 * @var mixed 48 * @access private 49 */ 50 var $_dsn = ''; 51 52 /** 53 * String containing the SQL insertion statement. 54 * 55 * @var string 56 * @access private 57 */ 58 var $_sql = ''; 59 60 /** 61 * Array containing our set of DB configuration options. 62 * @var array 63 * @access private 64 */ 65 var $_options = array('persistent' => true); 66 67 /** 68 * Object holding the database handle. 69 * @var object 70 * @access private 71 */ 72 var $_db = null; 73 74 /** 75 * Resource holding the prepared statement handle. 76 * @var resource 77 * @access private 78 */ 79 var $_statement = null; 80 81 /** 82 * Flag indicating that we're using an existing database connection. 83 * @var boolean 84 * @access private 85 */ 86 var $_existingConnection = false; 87 88 /** 89 * String holding the database table to use. 90 * @var string 91 * @access private 92 */ 93 var $_table = 'log_table'; 94 95 /** 96 * String holding the name of the ID sequence. 97 * @var string 98 * @access private 99 */ 100 var $_sequence = 'log_id'; 101 102 /** 103 * Maximum length of the $ident string. This corresponds to the size of 104 * the 'ident' column in the SQL table. 105 * @var integer 106 * @access private 107 */ 108 var $_identLimit = 16; 109 110 /** 111 * Constructs a new sql logging object. 112 * 113 * @param string $name The target SQL table. 114 * @param string $ident The identification field. 115 * @param array $conf The connection configuration array. 116 * @param int $level Log messages up to and including this level. 117 * @access public 118 */ 119 public function __construct($name, $ident = '', $conf = array(), 120 $level = PEAR_LOG_DEBUG) 121 { 122 $this->_id = md5(microtime().rand()); 123 $this->_table = $name; 124 $this->_mask = Log::UPTO($level); 125 126 /* Now that we have a table name, assign our SQL statement. */ 127 if (!empty($conf['sql'])) { 128 $this->_sql = $conf['sql']; 129 } else { 130 $this->_sql = 'INSERT INTO ' . $this->_table . 131 ' (id, logtime, ident, priority, message)' . 132 ' VALUES(?, CURRENT_TIMESTAMP, ?, ?, ?)'; 133 } 134 135 /* If an options array was provided, use it. */ 136 if (isset($conf['options']) && is_array($conf['options'])) { 137 $this->_options = $conf['options']; 138 } 139 140 /* If a specific sequence name was provided, use it. */ 141 if (!empty($conf['sequence'])) { 142 $this->_sequence = $conf['sequence']; 143 } 144 145 /* If a specific sequence name was provided, use it. */ 146 if (isset($conf['identLimit'])) { 147 $this->_identLimit = $conf['identLimit']; 148 } 149 150 /* Now that the ident limit is confirmed, set the ident string. */ 151 $this->setIdent($ident); 152 153 /* If an existing database connection was provided, use it. */ 154 if (isset($conf['db'])) { 155 $this->_db = &$conf['db']; 156 $this->_existingConnection = true; 157 $this->_opened = true; 158 } else { 159 $this->_dsn = $conf['dsn']; 160 } 161 } 162 163 /** 164 * Opens a connection to the database, if it has not already 165 * been opened. This is implicitly called by log(), if necessary. 166 * 167 * @return boolean True on success, false on failure. 168 * @access public 169 */ 170 function open() 171 { 172 if (!$this->_opened) { 173 /* Use the DSN and options to create a database connection. */ 174 $this->_db = &DB::connect($this->_dsn, $this->_options); 175 if (DB::isError($this->_db)) { 176 return false; 177 } 178 179 /* Create a prepared statement for repeated use in log(). */ 180 if (!$this->_prepareStatement()) { 181 return false; 182 } 183 184 /* We now consider out connection open. */ 185 $this->_opened = true; 186 } 187 188 return $this->_opened; 189 } 190 191 /** 192 * Closes the connection to the database if it is still open and we were 193 * the ones that opened it. It is the caller's responsible to close an 194 * existing connection that was passed to us via $conf['db']. 195 * 196 * @return boolean True on success, false on failure. 197 * @access public 198 */ 199 function close() 200 { 201 if ($this->_opened && !$this->_existingConnection) { 202 $this->_opened = false; 203 $this->_db->freePrepared($this->_statement); 204 return $this->_db->disconnect(); 205 } 206 207 return ($this->_opened === false); 208 } 209 210 /** 211 * Sets this Log instance's identification string. Note that this 212 * SQL-specific implementation will limit the length of the $ident string 213 * to sixteen (16) characters. 214 * 215 * @param string $ident The new identification string. 216 * 217 * @access public 218 * @since Log 1.8.5 219 */ 220 function setIdent($ident) 221 { 222 $this->_ident = substr($ident, 0, $this->_identLimit); 223 } 224 225 /** 226 * Inserts $message to the currently open database. Calls open(), 227 * if necessary. Also passes the message along to any Log_observer 228 * instances that are observing this Log. 229 * 230 * @param mixed $message String or object containing the message to log. 231 * @param string $priority The priority of the message. Valid 232 * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, 233 * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, 234 * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. 235 * @return boolean True on success or false on failure. 236 * @access public 237 */ 238 function log($message, $priority = null) 239 { 240 /* If a priority hasn't been specified, use the default value. */ 241 if ($priority === null) { 242 $priority = $this->_priority; 243 } 244 245 /* Abort early if the priority is above the maximum logging level. */ 246 if (!$this->_isMasked($priority)) { 247 return false; 248 } 249 250 /* If the connection isn't open and can't be opened, return failure. */ 251 if (!$this->_opened && !$this->open()) { 252 return false; 253 } 254 255 /* If we don't already have our statement object yet, create it. */ 256 if (!is_object($this->_statement) && !$this->_prepareStatement()) { 257 return false; 258 } 259 260 /* Extract the string representation of the message. */ 261 $message = $this->_extractMessage($message); 262 263 /* Build our set of values for this log entry. */ 264 $id = $this->_db->nextId($this->_sequence); 265 $values = array($id, $this->_ident, $priority, $message); 266 267 /* Execute the SQL query for this log entry insertion. */ 268 $result =& $this->_db->execute($this->_statement, $values); 269 if (DB::isError($result)) { 270 return false; 271 } 272 273 $this->_announce(array('priority' => $priority, 'message' => $message)); 274 275 return true; 276 } 277 278 /** 279 * Prepare the SQL insertion statement. 280 * 281 * @return boolean True if the statement was successfully created. 282 * 283 * @access private 284 * @since Log 1.9.1 285 */ 286 function _prepareStatement() 287 { 288 $this->_statement = $this->_db->prepare($this->_sql); 289 290 /* Return success if we didn't generate an error. */ 291 return (DB::isError($this->_statement) === false); 292 } 293} 294