forked from MythTV/mythweb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDatabase.php
533 lines (496 loc) · 17.1 KB
/
Database.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
<?php
/**
* This file was originally written by Chris Petersen for several different open
* source projects. It is distrubuted under the GNU General Public License.
* I (Chris Petersen) have also granted a special LGPL license for this code to
* several companies I do work for on the condition that these companies will
* release any changes to this back to me and the open source community as GPL,
* thus continuing to improve the open source version of the library. If you
* would like to inquire about the status of this arrangement, please contact
* me personally.
*
* ---
*
* The main Database superclass. The primary function of this class is to act
* as a constructor for various engine-specific subclasses. It is intended to
* work somewhat like perl's DBI library.
*
* Do not create any instances of this class. Instead, use the public
* constructor wrapper Database::connect() so that it can determine the correct
* database engine subclass and return it to you.
*
* Subclasses are expected to define a prepare() method, along with a matching
* execute() method in the query class that prepare() returns. They must also
* define _errstr() and _errno() methods to return the appropriate database
* engine error string/number.
*
* @copyright Silicon Mechanics
* @license GPL
*
* @package MythWeb
* @subpackage Database
*
/**/
/**
* Abstract superclass for all database connection types. This also defines the
* Database::connect() function that handles creating instances of the
* appropriate database handle for the requested database engine.
/**/
class Database {
/** @var resource Resource handle for this database connection */
var $dbh;
/** @var string A full error message generated by the coder */
var $error;
/** @var string The database-generated error string */
var $err;
/** @var int The database-generated error number */
var $errno;
/** @var resource The last statement handle created by this object */
var $last_sh;
/** @var bool This controls if the mysql query errors are fatal or just stored in the mysql error string */
var $fatal_errors = true;
/** @var int Total number of queries executed. */
var $query_count = 0;
/** @var float Total time spent waiting for a query to run. */
var $query_time = 0;
/** @var string The registered global name of this object instance. */
var $global_name = '';
/** @var array Collection of functions and parameters to be called on object destruction */
var $destruct_handlers = array();
/******************************************************************************/
/**
* Legacy constructor to catch things that the abstract classification won't
/**/
function __construct() {
trigger_error('The Database class should never be created as an object. Use Database::connect() instead.', E_USER_ERROR);
}
/**
* This takes the place of a database constructor. It should be called directly
* without an object as:
*
* Database::connect(....)
*
* This assumes that you are either using a class autoloader (php5) or have
* already require_once'd the appropriate database engine file
* (eg. Database_mysql.php).
*
* @param string $db_name Name of the database we're connecting to
* @param string $login Login name to use when connecting
* @param string $password Password to use when connecting
* @param string $server Database server to connect to (default: localhost)
* @param string $port Port or socket address to connect to
* @param string $engine Database engine to use (default: mysql_detect)
* @param array $options Hash of var=>value pairs of server options for
* engines that support them
*
* @return object Database subclass based on requested $engine
/**/
static function &connect($db_name, $login, $password, $server='localhost', $port=NULL, $engine='mysql_detect', $options=array()) {
// For consistency, engine names are all lower case.
$engine = strtolower($engine);
// There are two versions of the mysql driver in php. We have special
// consideration here for people who want auto-detection.
if ($engine == 'mysql_detect') {
$dbh = new Database_mysql($db_name, $login, $password, $server, $port);
// MySQL gets some extra smarts to try to use mysqli if it's available
if ($dbh && function_exists('mysqli_connect')) {
$version = preg_replace('/^(\d+\.\d).*$/', '$1', $dbh->server_info());
if ($version >= 4.1) {
$dbh->close();
$dbh = new Database_mysqlicompat($db_name, $login, $password, $server, $port);
}
}
}
// Do our best to load the requested class
else {
$class = "Database_$engine";
$dbh = new $class($db_name, $login, $password, $server, $port, $options);
}
// Set database connection to utf8
$dbh->query('SET NAMES utf8;');
// Make sure UNIX_TIMESTAMP AND FROM_UNIXTIME do the right things
$dbh->query('SET time_zone="+0:00";');
// Return
return $dbh;
}
/**
* I like how in perl you can pass variables into functions in lists or arrays,
* and they all show up to the function as one giant list. This takes an array
* containing scalars and arrays of scalars, and returns one cleanarray of all
* values.
*
* @param mixed $args Scalar or nested array to be "flattened" into a single array.
*
* @return array Single array comprised of all scalars present in $args.
/**/
static function smart_args($args) {
$new_args = array();
// Not an array
if (!is_array($args))
return array($args);
// Loop
foreach ($args as $arg) {
if (is_array($arg)) {
$new_args += Database::smart_args($arg);
}
else {
$new_args[] = $arg;
}
}
// Return
return $new_args;
}
/**
* Calls $this->escape() on an array of strings.
*
* @return string
/**/
function escape_array($array) {
$new = array();
foreach ($array as $string) {
$new[] = $this->escape($string);
}
return $new;
}
/******************************************************************************/
/**
* Execute any destruct handler functions.
/**/
function __destruct() {
// Globals already destroyed?
if ($this->global_name && empty($GLOBALS[$this->global_name]))
$GLOBALS[$this->global_name] =& $this;
// Process any destruct handlers
if (is_array($this->destruct_handlers)) {
foreach ($this->destruct_handlers as $call) {
if (is_array($call['p']))
call_user_func_array($call['f'], $call['p']);
else
call_user_func($call['f']);
}
}
}
/**
* PHP 5.2 destroys the global variable name space before calling destruct
* handlers, so we'll need to keep track of the name if any destruct handlers
* might need to use it.
*
* @param string $name The global name this instance is registered as.
/**/
function register_global_name($name) {
if ($GLOBALS[$name] === $this) {
$this->global_name = $name;
return true;
}
return false;
}
/**
* Because of changes in PHP that make it destroy objects before saving session
* data, this function was added to make sure that certain code (like
* session_write_close) gets executed before in time.
*
* @link http://us2.php.net/session_set_save_handler
/**/
function register_destruct_handler($func, $params=null) {
$this->destruct_handlers[] = array('f' => $func,
'p' => $params);
}
/**
* Fill the error variables
*
* @param string $error The string to set the error message to. Set to
* false if you want to wipe out the existing errors.
* @param bool $backtrace Include a backtrace along with the error message.
/**/
function error($error='', $backtrace=true) {
if ($error === false) {
$this->err = null;
$this->errno = null;
$this->error = null;
}
else {
$this->err = $this->_errstr();
$this->errno = $this->_errno();
$this->error = ($error ? "$error\n\n" : '')."$this->err [#$this->errno]";
if ($backtrace)
$this->error .= "\n\nBacktrace\n".print_r(debug_backtrace(), true);
}
}
/**
* Perform a database query and return a handle. Usage:
*
* <pre>
* $sh =& $db->query('SELECT * FROM foo WHERE x=? AND y=? AND z="bar\\?"',
* $x_value, $y_value);
* </pre>
*
* @param string $query The query string
* @param mixed $arg Query arguments to escape and insert at ? placeholders in $query
* @param mixed ... Additional arguments
*
* @return mixed Statement handle for the current type of database connection
/**/
function &query($query) {
// Hack to get query_row and query_assoc working correctly
$args = array_slice(func_get_args(), 1);
// Create and return a database query
$start_time = microtime(true);
$this->last_sh =& $this->prepare($query);
$this->last_sh->execute($args);
$this->query_time += microtime(true) - $start_time;
$this->query_count++;
// PHP 5 doesn't like us returning NULL by reference
if (!$this->last_sh->sh)
$this->last_sh = NULL;
return $this->last_sh;
}
/**
* Returns a single row from the database and frees the result.
*
* @param string $query The query string
* @param mixed $arg Query arguments to escape and insert at ? placeholders in $query
* @param mixed ... Additional arguments
*
* @return array
/**/
function query_row($query) {
// Query and return
$args = array_slice(func_get_args(), 1);
$sh = $this->query($query, $args);
if ($sh) {
$return = $sh->fetch_row();
$sh->finish();
return $return;
}
return null;
}
/**
* Returns a single assoc row from the database and frees the result.
*
* @param string $query The query string
* @param mixed $arg Query arguments to escape and insert at ? placeholders in $query
* @param mixed ... Additional arguments
*
* @return assoc
/**/
function query_assoc($query) {
// Query and return
$args = array_slice(func_get_args(), 1);
$sh = $this->query($query, $args);
if ($sh) {
$return = $sh->fetch_assoc();
$sh->finish();
return $return;
}
return null;
}
/**
* Returns a single column from the database and frees the result.
*
* @param string $query The query string
* @param mixed $arg Query arguments to escape and insert at ? placeholders in $query
* @param mixed ... Additional arguments
*
* @return mixed
/**/
function query_col($query) {
// Query and return
$args = array_slice(func_get_args(), 1);
$sh = $this->query($query, $args);
if ($sh) {
list($return) = $sh->fetch_row();
$sh->finish();
return $return;
}
return null;
}
/**
* Returns an array of all first colums returned from the specified query.
*
* @param string $query The query string
* @param mixed $arg Query arguments to escape and insert at ? placeholders in $query
* @param mixed ... Additional arguments
*
* @return array
/**/
function query_list($query) {
// Query and return
$args = array_slice(func_get_args(), 1);
$sh = $this->query($query, $args);
if ($sh) {
$return = array();
while ($row = $sh->fetch_array()) {
$return[] = $row[0];
}
$sh->finish();
return $return;
}
return null;
}
/**
* Returns an array of the results from the specified query. Each result is
* stored in an array.
*
* @param string $query The query string
* @param mixed $arg Query arguments to escape and insert at ? placeholders in $query
* @param mixed ... Additional arguments
*
* @return array
/**/
function query_list_array($query) {
// Query and return
$args = array_slice(func_get_args(), 1);
$sh = $this->query($query, $args);
if ($sh) {
$return = array();
while ($row = $sh->fetch_array()) {
$return[] = $row;
}
$sh->finish();
return $return;
}
return null;
}
/**
* Returns an array of the results from the specified query. Each result is
* stored in an assoc.
*
* @param string $query The query string
* @param mixed $arg Query arguments to escape and insert at ? placeholders in $query
* @param mixed ... Additional arguments
*
* @return array
/**/
function query_list_assoc($query) {
// Query and return
$args = array_slice(func_get_args(), 1);
$sh = $this->query($query, $args);
if ($sh) {
$return = array();
while ($row = $sh->fetch_assoc()) {
$return[] = $row;
}
$sh->finish();
return $return;
}
return null;
}
/**
* Returns an array of the results from the specified query. Each result is
* stored in an array. The array returned will be indexed by the value of the
* column specified by $key.
*
* @param string $key Column to use as the returned list's key
* @param string $query The query string
* @param mixed $arg Query arguments to escape and insert at ? placeholders in $query
* @param mixed ... Additional arguments
*
* @return array
/**/
function query_keyed_list_array($key, $query) {
// Query and return
$args = array_slice(func_get_args(), 2);
$sh = $this->query($query, $args);
if ($sh) {
$return = array();
while ($row = $sh->fetch_array()) {
$return[$row[$key]] = $row;
}
$sh->finish();
return $return;
}
return null;
}
/**
* Returns an array of the results from the specified query. Each result is
* stored in an assoc. The array returned will be indexed by the value of the
* column specified by $key.
*
* @param string $key Column to use as the returned list's key
* @param string $query The query string
* @param mixed $arg Query arguments to escape and insert at ? placeholders in $query
* @param mixed ... Additional arguments
*
* @return array
/**/
function query_keyed_list_assoc($key, $query) {
// Query and return
$args = array_slice(func_get_args(), 2);
$sh = $this->query($query, $args);
if ($sh) {
$return = array();
while ($row = $sh->fetch_assoc()) {
$return[$row[$key]] = $row;
}
$sh->finish();
return $return;
}
return null;
}
/**
* Returns the row count from the query and frees the result.
*
* @param string $query The query string
* @param mixed $arg Query arguments to escape and insert at ? placeholders in $query
* @param mixed ... Additional arguments
*
* @return int The number of rows affected by the requested query.
/**/
function query_num_rows($query) {
// Query and return
$args = array_slice(func_get_args(), 1);
$sh = $this->query($query, $args);
if ($sh) {
$return = $sh->num_rows();
$sh->finish();
return $return;
}
return null;
}
/**
* Returns the inserted id from the query and frees the result
*
* @param string $query The query string
* @param mixed $arg Query arguments to escape and insert at ? placeholders in $query
* @param mixed ... Additional arguments
*
* @return int The insert_id generated by the requested query.
/**/
function query_insert_id($query) {
// Query and return
$args = array_slice(func_get_args(), 1);
$sh = $this->query($query, $args);
if ($sh) {
$return = $this->insert_id;
$sh->finish();
return $return;
}
return null;
}
/**
* Wrapper for the last query statement's insert_id method.
* @return int
/**/
function insert_id() {
return $this->last_sh->insert_id();
}
/**
* Wrapper for the last query statement's affected_rows method.
* @return int
/**/
function affected_rows() {
return $this->last_sh->affected_rows();
}
/**
* This function and the next one control if the mysql_query throws a fatal error or not
/**/
function enable_fatal_errors() {
$this->fatal_errors = true;
}
/**
* This function disables the fatal error trigger code
/**/
function disable_fatal_errors() {
$this->fatal_errors = false;
}
}