Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * $Header$
4
 *
5
 * @version $Revision: 204814 $
6
 * @package Log
7
 */
8
 
9
/** PEAR's MDB2 package */
10
require_once 'MDB2.php';
11
MDB2::loadFile('Date');
12
 
13
/**
14
 * The Log_mdb2 class is a concrete implementation of the Log:: abstract class
15
 * which sends messages to an SQL server.  Each entry occupies a separate row
16
 * in the database.
17
 *
18
 * This implementation uses PEAR's MDB2 database abstraction layer.
19
 *
20
 * CREATE TABLE log_table (
21
 *  id          INT NOT NULL,
22
 *  logtime     TIMESTAMP NOT NULL,
23
 *  ident       CHAR(16) NOT NULL,
24
 *  priority    INT NOT NULL,
25
 *  message     VARCHAR(200),
26
 *  PRIMARY KEY (id)
27
 * );
28
 *
29
 * @author  Lukas Smith <smith@backendmedia.com>
30
 * @author  Jon Parise <jon@php.net>
31
 * @since   Log 1.9.0
32
 * @package Log
33
 */
34
class Log_mdb2 extends Log
35
{
36
    /**
37
     * Variable containing the DSN information.
38
     * @var mixed
39
     * @access private
40
     */
41
    var $_dsn = '';
42
 
43
    /**
44
     * Array containing our set of DB configuration options.
45
     * @var array
46
     * @access private
47
     */
48
    var $_options = array('persistent' => true);
49
 
50
    /**
51
     * Object holding the database handle.
52
     * @var object
53
     * @access private
54
     */
55
    var $_db = null;
56
 
57
    /**
58
     * Resource holding the prepared statement handle.
59
     * @var resource
60
     * @access private
61
     */
62
    var $_statement = null;
63
 
64
    /**
65
     * Flag indicating that we're using an existing database connection.
66
     * @var boolean
67
     * @access private
68
     */
69
    var $_existingConnection = false;
70
 
71
    /**
72
     * String holding the database table to use.
73
     * @var string
74
     * @access private
75
     */
76
    var $_table = 'log_table';
77
 
78
    /**
79
     * String holding the name of the ID sequence.
80
     * @var string
81
     * @access private
82
     */
83
    var $_sequence = 'log_id';
84
 
85
    /**
86
     * Maximum length of the $ident string.  This corresponds to the size of
87
     * the 'ident' column in the SQL table.
88
     * @var integer
89
     * @access private
90
     */
91
    var $_identLimit = 16;
92
 
93
    /**
94
     * Set of field types used in the database table.
95
     * @var array
96
     * @access private
97
     */
98
    var $_types = array(
99
        'id'        => 'integer',
100
        'logtime'   => 'timestamp',
101
        'ident'     => 'text',
102
        'priority'  => 'text',
103
        'message'   => 'clob'
104
    );
105
 
106
    /**
107
     * Constructs a new sql logging object.
108
     *
109
     * @param string $name         The target SQL table.
110
     * @param string $ident        The identification field.
111
     * @param array $conf          The connection configuration array.
112
     * @param int $level           Log messages up to and including this level.
113
     * @access public
114
     */
115
    function Log_mdb2($name, $ident = '', $conf = array(),
116
                     $level = PEAR_LOG_DEBUG)
117
    {
118
        $this->_id = md5(microtime());
119
        $this->_table = $name;
120
        $this->_mask = Log::UPTO($level);
121
 
122
        /* If an options array was provided, use it. */
123
        if (isset($conf['options']) && is_array($conf['options'])) {
124
            $this->_options = $conf['options'];
125
        }
126
 
127
        /* If a specific sequence name was provided, use it. */
128
        if (!empty($conf['sequence'])) {
129
            $this->_sequence = $conf['sequence'];
130
        }
131
 
132
        /* If a specific sequence name was provided, use it. */
133
        if (isset($conf['identLimit'])) {
134
            $this->_identLimit = $conf['identLimit'];
135
        }
136
 
137
        /* Now that the ident limit is confirmed, set the ident string. */
138
        $this->setIdent($ident);
139
 
140
        /* If an existing database connection was provided, use it. */
141
        if (isset($conf['db'])) {
142
            $this->_db = &$conf['db'];
143
            $this->_existingConnection = true;
144
            $this->_opened = true;
145
        } elseif (isset($conf['singleton'])) {
146
            $this->_db = &MDB2::singleton($conf['singleton'], $this->_options);
147
            $this->_existingConnection = true;
148
            $this->_opened = true;
149
        } else {
150
            $this->_dsn = $conf['dsn'];
151
        }
152
    }
153
 
154
    /**
155
     * Opens a connection to the database, if it has not already
156
     * been opened. This is implicitly called by log(), if necessary.
157
     *
158
     * @return boolean   True on success, false on failure.
159
     * @access public
160
     */
161
    function open()
162
    {
163
        if (!$this->_opened) {
164
            /* Use the DSN and options to create a database connection. */
165
            $this->_db = &MDB2::connect($this->_dsn, $this->_options);
166
            if (PEAR::isError($this->_db)) {
167
                return false;
168
            }
169
 
170
            /* Create a prepared statement for repeated use in log(). */
171
            if (!$this->_prepareStatement()) {
172
                return false;
173
            }
174
 
175
            /* We now consider out connection open. */
176
            $this->_opened = true;
177
        }
178
 
179
        return $this->_opened;
180
    }
181
 
182
    /**
183
     * Closes the connection to the database if it is still open and we were
184
     * the ones that opened it.  It is the caller's responsible to close an
185
     * existing connection that was passed to us via $conf['db'].
186
     *
187
     * @return boolean   True on success, false on failure.
188
     * @access public
189
     */
190
    function close()
191
    {
192
        /* If we have a statement object, free it. */
193
        if (is_object($this->_statement)) {
194
            $this->_statement->free();
195
            $this->_statement = null;
196
        }
197
 
198
        /* If we opened the database connection, disconnect it. */
199
        if ($this->_opened && !$this->_existingConnection) {
200
            $this->_opened = false;
201
            return $this->_db->disconnect();
202
        }
203
 
204
        return ($this->_opened === false);
205
    }
206
 
207
    /**
208
     * Sets this Log instance's identification string.  Note that this
209
     * SQL-specific implementation will limit the length of the $ident string
210
     * to sixteen (16) characters.
211
     *
212
     * @param string    $ident      The new identification string.
213
     *
214
     * @access  public
215
     * @since   Log 1.8.5
216
     */
217
    function setIdent($ident)
218
    {
219
        $this->_ident = substr($ident, 0, $this->_identLimit);
220
    }
221
 
222
    /**
223
     * Inserts $message to the currently open database.  Calls open(),
224
     * if necessary.  Also passes the message along to any Log_observer
225
     * instances that are observing this Log.
226
     *
227
     * @param mixed  $message  String or object containing the message to log.
228
     * @param string $priority The priority of the message.  Valid
229
     *                  values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT,
230
     *                  PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING,
231
     *                  PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG.
232
     * @return boolean  True on success or false on failure.
233
     * @access public
234
     */
235
    function log($message, $priority = null)
236
    {
237
        /* If a priority hasn't been specified, use the default value. */
238
        if ($priority === null) {
239
            $priority = $this->_priority;
240
        }
241
 
242
        /* Abort early if the priority is above the maximum logging level. */
243
        if (!$this->_isMasked($priority)) {
244
            return false;
245
        }
246
 
247
        /* If the connection isn't open and can't be opened, return failure. */
248
        if (!$this->_opened && !$this->open()) {
249
            return false;
250
        }
251
 
252
        /* If we don't already have a statement object, create one. */
253
        if (!is_object($this->_statement) && !$this->_prepareStatement()) {
254
            return false;
255
        }
256
 
257
        /* Extract the string representation of the message. */
258
        $message = $this->_extractMessage($message);
259
 
260
        /* Build our set of values for this log entry. */
261
        $values = array(
262
            'id'       => $this->_db->nextId($this->_sequence),
263
            'logtime'  => MDB2_Date::mdbNow(),
264
            'ident'    => $this->_ident,
265
            'priority' => $priority,
266
            'message'  => $message
267
        );
268
 
269
        /* Execute the SQL query for this log entry insertion. */
270
        $this->_db->expectError(MDB2_ERROR_NOSUCHTABLE);
271
        $result = &$this->_statement->execute($values);
272
        $this->_db->popExpect();
273
 
274
        /* Attempt to handle any errors. */
275
        if (PEAR::isError($result)) {
276
            /* We can only handle MDB2_ERROR_NOSUCHTABLE errors. */
277
            if ($result->getCode() != MDB2_ERROR_NOSUCHTABLE) {
278
                return false;
279
            }
280
 
281
            /* Attempt to create the target table. */
282
            if (!$this->_createTable()) {
283
                return false;
284
            }
285
 
286
            /* Recreate our prepared statement resource. */
287
            $this->_statement->free();
288
            if (!$this->_prepareStatement()) {
289
                return false;
290
            }
291
 
292
            /* Attempt to re-execute the insertion query. */
293
            $result = $this->_statement->execute($values);
294
            if (PEAR::isError($result)) {
295
                return false;
296
            }
297
        }
298
 
299
        $this->_announce(array('priority' => $priority, 'message' => $message));
300
 
301
        return true;
302
    }
303
 
304
    /**
305
     * Create the log table in the database.
306
     *
307
     * @return boolean  True on success or false on failure.
308
     * @access private
309
     */
310
    function _createTable()
311
    {
312
        $this->_db->loadModule('Manager', null, true);
313
        $result = $this->_db->manager->createTable(
314
            $this->_table,
315
            array(
316
                'id'        => array('type' => $this->_types['id']),
317
                'logtime'   => array('type' => $this->_types['logtime']),
318
                'ident'     => array('type' => $this->_types['ident']),
319
                'priority'  => array('type' => $this->_types['priority']),
320
                'message'   => array('type' => $this->_types['message'])
321
            )
322
        );
323
        if (PEAR::isError($result)) {
324
            return false;
325
        }
326
 
327
        $result = $this->_db->manager->createIndex(
328
            $this->_table,
329
            'unique_id',
330
            array('fields' => array('id' => true), 'unique' => true)
331
        );
332
        if (PEAR::isError($result)) {
333
            return false;
334
        }
335
 
336
        return true;
337
    }
338
 
339
    /**
340
     * Prepare the SQL insertion statement.
341
     *
342
     * @return boolean  True if the statement was successfully created.
343
     *
344
     * @access  private
345
     * @since   Log 1.9.0
346
     */
347
    function _prepareStatement()
348
    {
349
        $this->_statement = &$this->_db->prepare(
350
                'INSERT INTO ' . $this->_table .
351
                ' (id, logtime, ident, priority, message)' .
352
                ' VALUES(:id, :logtime, :ident, :priority, :message)',
353
                $this->_types, MDB2_PREPARE_MANIP);
354
 
355
        /* Return success if we didn't generate an error. */
356
        return (PEAR::isError($this->_statement) === false);
357
    }
358
}