Subversion Repositories PHPX

Rev

Rev 41 | Rev 47 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
27 PointedEar 1
<?php
2
 
29 PointedEar 3
require_once __DIR__ . '/../global.inc';
4
require_once __DIR__ . '/../AbstractModel.php';
27 PointedEar 5
 
6
/**
7
 * Generic database model class using PDO (PHP Data Objects)
8
 *
41 PointedEar 9
 * @property-read PDO $connection
10
 *   Database connection.  Established on read access to this
11
 *   property if not yet established.
27 PointedEar 12
 * @property-read array $lastError
13
 *   Last error information of the database operation.
14
 *   See {@link PDOStatement::errorInfo()}.
15
 * @property-read string $lastInsertId
16
 *   ID of the last inserted row, or the last value from a sequence object,
17
 *   depending on the underlying driver. May not be supported by all databases.
18
 * @property-read array $lastResult
19
 *   Last result of the database operation
20
 * @property-read boolean $lastSuccess
21
 *   Last success value of the database operation
22
 * @author Thomas Lahn
23
 */
24
class Database extends AbstractModel
25
{
32 PointedEar 26
  /* Access properties */
41 PointedEar 27
 
27 PointedEar 28
  /**
29
   * DSN of the database
30
   * @var string
31
   */
32
  protected $_dsn = '';
41 PointedEar 33
 
27 PointedEar 34
  /**
35
   * Username to access the database
36
   * @var string
37
   */
38
  protected $_username;
41 PointedEar 39
 
27 PointedEar 40
  /**
41
   * Password to access the database
42
   * @var string
43
   */
44
  protected $_password;
41 PointedEar 45
 
27 PointedEar 46
  /**
47
   * PDO driver-specific options
48
   * @var array
49
   */
50
  protected $_options = array();
32 PointedEar 51
 
52
  /**
53
   * Database-specific string to use for quoting a name or value
54
   * left-hand side (for security reasons and to prevent a name
55
   * from being parsed as a keyword).
56
   * @var string
57
   */
58
  protected $_leftQuote = '';
41 PointedEar 59
 
27 PointedEar 60
  /**
32 PointedEar 61
   * Database-specific string to use for quoting a name or value
62
   * left-hand side (for security reasons and to prevent a name
63
   * from being parsed as a keyword).
64
   * @var string
65
   */
66
  protected $_rightQuote = '';
41 PointedEar 67
 
32 PointedEar 68
  /* Status properties */
41 PointedEar 69
 
32 PointedEar 70
  /**
27 PointedEar 71
   * Database connection
72
   * @var PDO
73
   */
74
  protected $_connection;
41 PointedEar 75
 
27 PointedEar 76
  /**
77
   * Last success value of the database operation
78
   * @var boolean
79
   */
80
  protected $_lastSuccess;
81
 
82
  /**
83
   * Last error information of the database operation
84
   * @var array
85
   */
86
  protected $_lastError;
41 PointedEar 87
 
27 PointedEar 88
  /**
89
   * Last result of the database operation
90
   * @var array
91
   */
92
  protected $_lastResult;
93
 
94
  /**
95
  * ID of the last inserted row, or the last value from a sequence object,
96
  * depending on the underlying driver. May not be supported by all databases.
97
  * @var string
98
  */
99
  protected $_lastInsertId = '';
41 PointedEar 100
 
34 PointedEar 101
  /**
102
   * Creates a new <code>Database</code> instance.
103
   *
104
   * Each of the parameters is optional and can also be given
105
   * by a protected property where the parameter name is preceded
106
   * by <code>_</code>.  Parameter values overwrite the default
107
   * property values.  It is recommended to use default property
108
   * values of inheriting classes except for small applications
109
   * and testing purposes.
110
   *
111
   * @param string $dsn
112
   * @param string $username
113
   * @param string $password
44 PointedEar 114
   * @param array $options
115
   * @see PDO::__construct()
34 PointedEar 116
   */
44 PointedEar 117
  public function __construct ($dsn = '', $username = null,
34 PointedEar 118
    $password = null, array $options = array())
27 PointedEar 119
  {
34 PointedEar 120
    if ($dsn !== '')
121
    {
122
      $this->_dsn = $dsn;
123
    }
41 PointedEar 124
 
34 PointedEar 125
    if ($username !== null)
126
    {
127
      $this->_username = $username;
128
    }
41 PointedEar 129
 
34 PointedEar 130
    if ($password !== null)
131
    {
132
      $this->_password = $password;
133
    }
41 PointedEar 134
 
34 PointedEar 135
    if ($options)
136
    {
137
      $this->_options = $options;
138
    }
27 PointedEar 139
  }
41 PointedEar 140
 
27 PointedEar 141
  /**
41 PointedEar 142
   * @return PDO
143
   */
144
  public function getConnection ()
145
  {
146
    if ($this->_connection === null)
147
    {
148
      $this->_connection =
149
        new PDO($this->_dsn, $this->_username, $this->_password, $this->_options);
150
    }
151
 
152
    return $this->_connection;
153
  }
154
 
155
  /**
44 PointedEar 156
   * Creates a database according to the specified parameters
157
   *
158
   * Should be overwritten and called by inheriting classes.
159
   *
160
   * @param string $dsn
161
   *   Connection DSN (required; must not include the database
162
   *   name).
163
   * @param string $username = null
164
   *   Connection username.  The default is specified by the
165
   *   <code>$_username</code> property.  Note that creating
166
   *   the database usually requires a user with more privileges
167
   *   than the one accessing the database or its tables.
168
   * @param string $password = null
169
   *   Connection password.  The default is specified by the
170
   *   <code>$_password</code> property.
171
   * @param array? $options = null
172
   *   Connection options.  The default is specified by the
173
   *   <code>$_options</code> property.
174
   * @param string $spec = null
175
   *   Additional database specifications, like character encoding
176
   *   and collation.
177
   * @param boolean $force = false
178
   *   If a true-value, the database will be attempted to be
179
   *   created even if there is a database of the name specified
180
   *   by the <code>$_dbname</code> property.
181
   * @return int
182
   *   The number of rows affected by the CREATE DATABASE statement.
183
   * @see PDO::__construct()
184
   * @see PDO::exec()
185
   */
186
  public function create ($dsn, $username = null, $password = null,
187
    array $options = null, $dbspec = null, $force = false)
188
  {
189
    $connection = new PDO($dsn,
190
      $username !== null ? $username : $this->_username,
191
      $password !== null ? $password : $this->_password,
192
      $options !== null ? $options : $this->_options);
193
 
194
    $query = 'CREATE DATABASE'
195
           . (!$force ? ' IF NOT EXISTS' : '')
196
           . ' ' . $this->escapeName($this->_dbname)
197
           . ($dbspec ? ' ' . $dbspec : '');
198
 
199
    return $connection->exec($query);
200
  }
201
 
202
  /**
27 PointedEar 203
   * Initiates a transaction
204
   *
205
   * @return bool
206
   * @see PDO::beginTransaction()
207
   */
208
  public function beginTransaction()
209
  {
41 PointedEar 210
    return $this->connection->beginTransaction();
27 PointedEar 211
  }
41 PointedEar 212
 
27 PointedEar 213
  /**
214
   * Rolls back a transaction
215
   *
216
   * @return bool
217
   * @see PDO::rollBack()
218
   */
219
  public function rollBack()
220
  {
41 PointedEar 221
    return $this->connection->rollBack();
27 PointedEar 222
  }
41 PointedEar 223
 
27 PointedEar 224
  /**
225
   * Commits a transaction
226
   *
227
   * @return bool
228
   * @see PDO::commit()
229
   */
230
  public function commit()
231
  {
41 PointedEar 232
    return $this->connection->commit();
27 PointedEar 233
  }
41 PointedEar 234
 
27 PointedEar 235
  /**
236
   * Prepares a statement for execution with the database
237
   * @param string $query
238
   */
239
  public function prepare($query, array $driver_options = array())
240
  {
41 PointedEar 241
    return $this->connection->prepare($query, $driver_options);
27 PointedEar 242
  }
41 PointedEar 243
 
27 PointedEar 244
  /**
245
   * Returns the ID of the last inserted row, or the last value from
246
   * a sequence object, depending on the underlying driver.
247
   *
248
   * @return int
249
   */
250
  public function getLastInsertId()
251
  {
252
    return $this->_lastInsertId;
253
  }
41 PointedEar 254
 
27 PointedEar 255
  /**
256
   * Escapes a database name so that it can be used in a query.
257
   *
258
   * @param string $name
259
   *   The name to be escaped
260
   * @return string
261
   *   The escaped name
262
   */
263
  public function escapeName($name)
264
  {
32 PointedEar 265
    return $this->_leftQuote . $name . $this->_rightQuote;
27 PointedEar 266
  }
41 PointedEar 267
 
27 PointedEar 268
  /**
269
   * Determines if an array is associative (has not all integer keys).
270
   *
271
   * @author
272
   *   Algorithm courtesy of squirrel, <http://stackoverflow.com/a/5969617/855543>.
273
   * @param array $a
274
   * @return boolean
275
   *   <code>true</code> if <var>$a</var> is associative,
276
   *   <code>false</code> otherwise
277
   */
278
  protected function _isAssociativeArray(array $a)
279
  {
280
    for (reset($a); is_int(key($a)); next($a));
281
    return !is_null(key($a));
282
  }
41 PointedEar 283
 
27 PointedEar 284
  /**
285
   * Escapes an associative array so that its string representation can be used
286
   * as list with table or column aliases in a query.
287
   *
288
   * This method does not actually escape anything; it only inserts the
289
   * 'AS' keyword.  It should be overridden by inheriting methods.
290
   *
291
   * NOTE: This method intentionally does not check whether the array actually
292
   * is associative.
293
   *
294
   * @param array &$array
295
   *   The array to be escaped
296
   * @return array
297
   *   The escaped array
298
   */
299
  protected function _escapeAliasArray(array &$array)
300
  {
301
    foreach ($array as $column => &$value)
302
    {
32 PointedEar 303
      $quotedColumn = $column;
304
      if (strpos($column, $this->_leftQuote) === false
305
         && strpos($column, $this->_rightQuote) === false)
306
      {
307
        $quotedColumn = $this->_leftQuote . $column . $this->_rightQuote;
308
      }
41 PointedEar 309
 
32 PointedEar 310
      $value = $value . ' AS ' . $quotedColumn;
27 PointedEar 311
    }
41 PointedEar 312
 
27 PointedEar 313
    return $array;
314
  }
315
 
316
  /**
317
   * @param array $a
318
   * @param string $prefix
319
   */
320
  private static function _expand(array $a, $prefix)
321
  {
322
    $a2 = array();
41 PointedEar 323
 
27 PointedEar 324
    foreach ($a as $key => $value)
325
    {
326
      $a2[] = ':' . $prefix . ($key + 1);
327
    }
41 PointedEar 328
 
27 PointedEar 329
    return $a2;
330
  }
41 PointedEar 331
 
27 PointedEar 332
  /**
333
   * Escapes an associative array so that its string representation can be used
334
   * as value list in a query.
335
   *
336
   * This method should be overridden by inheriting classes to escape
337
   * column names as fitting for the database schema they support.  It is
338
   * strongly recommended that the overriding methods call this method with
339
   * an appropriate <var>$escape</var> parameter, pass all other parameters
340
   * on unchanged, and return its return value.
341
   *
342
   * NOTE: Intentionally does not check whether the array actually is associative!
343
   *
344
   * @param array &$array
345
   *   The array to be escaped
346
   * @param string $suffix
347
   *   The string to be appended to the column name for the value placeholder.
348
   *   The default is the empty string.
349
   * @param array $escape
350
   *   The strings to use left-hand side (index 0) and right-hand side (index 1)
351
   *   of the column name.  The default is the empty string, respectively.
352
   * @return array
353
   *   The escaped array
354
   */
32 PointedEar 355
  protected function _escapeValueArray(array &$array, $suffix = '')
27 PointedEar 356
  {
357
    $result = array();
41 PointedEar 358
 
27 PointedEar 359
    foreach ($array as $column => $value)
360
    {
361
      $op = '=';
362
      $placeholder = ":{$column}";
41 PointedEar 363
 
27 PointedEar 364
      if (is_array($value) && $this->_isAssociativeArray($value))
365
      {
366
        reset($value);
367
        $op = ' ' . key($value) . ' ';
41 PointedEar 368
 
27 PointedEar 369
        $value = $value[key($value)];
370
      }
41 PointedEar 371
 
27 PointedEar 372
      if (is_array($value))
373
      {
33 PointedEar 374
        $placeholder = '(' . implode(', ', self::_expand($value, $column)) . ')';
27 PointedEar 375
      }
41 PointedEar 376
 
32 PointedEar 377
      $result[] = $this->_leftQuote . $column . $this->_rightQuote . "{$op}{$placeholder}{$suffix}";
27 PointedEar 378
    }
41 PointedEar 379
 
27 PointedEar 380
    return $result;
381
  }
41 PointedEar 382
 
27 PointedEar 383
  /**
384
   * Constructs the WHERE part of a query
385
   *
386
   * @param string|array $where
387
   *   Condition
388
   * @param string $suffix
389
   *   The string to be appended to the column name for the value placeholder,
390
   *   passed on to {@link Database::_escapeValueArray()}.  The default is
391
   *   the empty string.
392
   * @return string
393
   * @see Database::_escapeValueArray()
394
   */
395
  protected function _where($where, $suffix = '')
396
  {
397
    if (!is_null($where))
398
    {
399
      if (is_array($where))
400
      {
401
        if (count($where) < 1)
402
        {
403
          return '';
404
        }
41 PointedEar 405
 
27 PointedEar 406
        if ($this->_isAssociativeArray($where))
407
        {
408
          $where = $this->_escapeValueArray($where, $suffix);
409
        }
41 PointedEar 410
 
27 PointedEar 411
        $where = '(' . implode(') AND (', $where) . ')';
412
      }
41 PointedEar 413
 
27 PointedEar 414
      return ' WHERE ' . $where;
415
    }
41 PointedEar 416
 
27 PointedEar 417
    return '';
418
  }
419
 
420
  /**
421
   * Selects data from one or more tables; the resulting records are stored
422
   * in the <code>result</code> property and returned as an associative array,
423
   * where the keys are the column (alias) names.
424
   *
425
   * @param string|array[string] $tables Table(s) to select from
426
   * @param string|array[string] $columns Column(s) to select from (optional)
427
   * @param string|array $where Condition (optional)
428
   * @param string $order Sort order (optional)
429
   *   If provided, MUST start with ORDER BY or GROUP BY
430
   * @param string $limit Limit (optional)
431
   * @param int $fetch_style
432
   *   The mode that should be used for {@link PDOStatement::fetchAll()}.
433
   *   The default is {@link PDO::FETCH_ASSOC}.
434
   * @return array
435
   * @see Database::prepare()
436
   * @see PDOStatement::fetchAll()
437
   */
438
  public function select($tables, $columns = null, $where = null,
439
    $order = null, $limit = null, $fetch_style = PDO::FETCH_ASSOC)
440
  {
441
    if (is_null($columns))
442
    {
443
      $columns = array('*');
444
    }
41 PointedEar 445
 
27 PointedEar 446
    if (is_array($columns))
447
    {
448
      if ($this->_isAssociativeArray($columns))
449
      {
450
        $columns = $this->_escapeAliasArray($columns);
451
      }
452
 
33 PointedEar 453
      $columns = implode(', ', $columns);
27 PointedEar 454
    }
455
 
456
    if (is_array($tables))
457
    {
458
      if ($this->_isAssociativeArray($columns))
459
      {
460
        $columns = $this->_escapeAliasArray($columns);
461
      }
462
 
33 PointedEar 463
      $tables = implode(', ', $tables);
27 PointedEar 464
    }
465
 
466
    $query = "SELECT {$columns} FROM {$tables}" . $this->_where($where);
467
 
468
    if (!is_null($order))
469
    {
470
      if (is_array($order))
471
      {
33 PointedEar 472
        $order = 'ORDER BY ' . implode(', ', $order);
27 PointedEar 473
      }
41 PointedEar 474
 
27 PointedEar 475
      $query .= " $order";
476
    }
477
 
478
    if (!is_null($limit))
479
    {
480
      $query .= " LIMIT $limit";
481
    }
41 PointedEar 482
 
27 PointedEar 483
    $stmt = $this->prepare($query);
484
 
485
    $params = array();
41 PointedEar 486
 
27 PointedEar 487
    if (is_array($where) && $this->_isAssociativeArray($where))
488
    {
34 PointedEar 489
      /* FIXME: Export and reuse this */
27 PointedEar 490
      foreach ($where as $column => $condition)
491
      {
34 PointedEar 492
        /* TODO: Also handle function calls as keys */
27 PointedEar 493
        if (is_array($condition) && $this->_isAssociativeArray($condition))
494
        {
495
          reset($condition);
496
          $condition = $condition[key($condition)];
41 PointedEar 497
 
27 PointedEar 498
          if (is_array($condition))
499
          {
500
            foreach (self::_expand($condition, $column) as $param_index => $param_name)
501
            {
502
              $params[$param_name] = $condition[$param_index];
503
            }
504
          }
505
        }
506
        else
507
        {
508
          $params[":{$column}"] = $condition;
509
        }
510
      }
511
    }
512
 
513
    /* DEBUG */
514
    if (defined('DEBUG') && DEBUG > 1)
515
    {
516
      debug(array(
517
        'query'  => $query,
518
        'params' => $params
519
      ));
520
    }
41 PointedEar 521
 
27 PointedEar 522
    $success =& $this->_lastSuccess;
523
    $success =  $stmt->execute($params);
41 PointedEar 524
 
27 PointedEar 525
    $errorInfo =& $this->_lastError;
526
    $errorInfo =  $stmt->errorInfo();
41 PointedEar 527
 
27 PointedEar 528
    $result =& $this->_lastResult;
529
    $result =  $stmt->fetchAll($fetch_style);
41 PointedEar 530
 
27 PointedEar 531
    if (defined('DEBUG') && DEBUG > 1)
532
    {
533
      debug(array(
534
        '_lastSuccess' => $success,
535
        '_lastError'   => $errorInfo,
536
        '_lastResult'  => $result
537
      ));
538
    }
41 PointedEar 539
 
27 PointedEar 540
    return $result;
541
  }
542
 
543
  /**
544
   * Sets and returns the ID of the last inserted row, or the last value from
545
   * a sequence object, depending on the underlying driver.
546
   *
547
   * @param string $name
548
   *   Name of the sequence object from which the ID should be returned.
549
   * @return string
550
   */
551
  protected function _setLastInsertId($name = null)
552
  {
41 PointedEar 553
    return ($this->_lastInsertId = $this->connection->lastInsertId($name));
27 PointedEar 554
  }
555
 
556
  /**
557
   * Resets the the ID of the last inserted row, or the last value from
558
   * a sequence object, depending on the underlying driver.
559
   *
560
   * @return string
561
   *   The default value
562
   */
563
  protected function _resetLastInsertId()
564
  {
565
    return ($this->_lastInsertId = '');
566
  }
41 PointedEar 567
 
27 PointedEar 568
  /**
569
   * Updates one or more records
570
   *
571
   * @param string|array $tables
572
   *   Table name
573
   * @param array $values
574
   *   Associative array of column-value pairs
575
   * @param array|string $where
576
   *   Only the records matching this condition are updated
577
   * @return bool
578
   */
579
  public function update($tables, $updates, $where = null)
580
  {
581
    if (!$tables)
582
    {
583
      throw new InvalidArgumentException('No table specified');
584
    }
41 PointedEar 585
 
27 PointedEar 586
    if (is_array($tables))
587
    {
33 PointedEar 588
      $tables = implode(', ', $tables);
27 PointedEar 589
    }
41 PointedEar 590
 
27 PointedEar 591
    if (!$updates)
592
    {
593
      throw new InvalidArgumentException('No values specified');
594
    }
595
 
596
    $params = array();
41 PointedEar 597
 
27 PointedEar 598
    if ($this->_isAssociativeArray($updates))
599
    {
600
      foreach ($updates as $key => $condition)
601
      {
602
        $params[":{$key}"] = $condition;
603
      }
604
    }
41 PointedEar 605
 
33 PointedEar 606
    $updates = implode(', ', $this->_escapeValueArray($updates));
41 PointedEar 607
 
27 PointedEar 608
    /* TODO: Should escape table names with escapeName(), but what about aliases? */
609
    $query = "UPDATE {$tables} SET {$updates}" . $this->_where($where, '2');
41 PointedEar 610
 
27 PointedEar 611
    $stmt = $this->prepare($query);
41 PointedEar 612
 
27 PointedEar 613
    if (is_array($where) && $this->_isAssociativeArray($where))
614
    {
615
      foreach ($where as $column => $condition)
616
      {
617
        if (is_array($condition) && $this->_isAssociativeArray($condition))
618
        {
619
          reset($condition);
620
          $condition = $condition[key($condition)];
41 PointedEar 621
 
27 PointedEar 622
          if (is_array($condition))
623
          {
624
            foreach (self::_expand($condition, $column) as $param_index => $param_name)
625
            {
626
              $params[$param_name] = $condition[$param_index];
627
            }
628
          }
629
        }
630
        else
631
        {
632
          $params[":{$column}2"] = $condition;
633
        }
634
      }
635
    }
636
 
637
    /* DEBUG */
638
    if (defined('DEBUG') && DEBUG > 1)
639
    {
640
      debug(array(
641
        'query'  => $query,
642
        'params' => $params
643
      ));
644
    }
41 PointedEar 645
 
27 PointedEar 646
    $success =& $this->_lastSuccess;
647
    $success =  $stmt->execute($params);
41 PointedEar 648
 
27 PointedEar 649
    $errorInfo =& $this->_lastError;
650
    $errorInfo =  $stmt->errorInfo();
41 PointedEar 651
 
27 PointedEar 652
    $this->_resetLastInsertId();
41 PointedEar 653
 
27 PointedEar 654
    $result =& $this->_lastResult;
655
    $result =  $stmt->fetchAll();
41 PointedEar 656
 
27 PointedEar 657
    if (defined('DEBUG') && DEBUG > 1)
658
    {
659
      debug(array(
660
        '_lastSuccess' => $success,
661
        '_lastError'    => $errorInfo,
662
        '_lastResult'  => $result
663
      ));
664
    }
41 PointedEar 665
 
27 PointedEar 666
    return $success;
667
  }
41 PointedEar 668
 
27 PointedEar 669
  /**
670
   * Inserts a record into a table.<p>The AUTO_INCREMENT value of the inserted
671
   * row, if any (> 0), is stored in the {@link $lastInsertId} property of
672
   * the <code>Database</code> instance.</p>
673
   *
674
   * @param string $table
675
   *   Table name
676
   * @param array|string $values
677
   *   Associative array of column-value pairs, indexed array,
678
   *   or comma-separated list of values.  If <var>$values</var> is not
679
   *   an associative array, <var>$cols</var> must be passed if the
680
   *   values are not in column order (see below).
681
   * @param array|string $cols
682
   *   Indexed array, or comma-separated list of column names.
683
   *   Needs only be passed if <var>$values</var> is not an associative array
684
   *   and the values are not in column order (default: <code>null</code>);
685
   *   is ignored otherwise.  <strong>You SHOULD NOT rely on column order.</strong>
686
   * @return bool
687
   *   <code>true</code> if successful, <code>false</code> otherwise
688
   * @see PDOStatement::execute()
689
   */
690
  public function insert($table, $values, $cols = null)
691
  {
692
    if ($cols != null)
693
    {
694
      $cols = ' ('
695
            . (is_array($cols)
33 PointedEar 696
                ? implode(', ', array_map(array($this, 'escapeName'), $cols))
27 PointedEar 697
                : $cols) . ')';
698
    }
699
    else
700
    {
701
      $cols = '';
702
    }
41 PointedEar 703
 
27 PointedEar 704
    /* DEBUG */
705
    if (defined('DEBUG') && DEBUG > 2)
706
    {
707
      debug(array('values' => $values));
708
    }
41 PointedEar 709
 
27 PointedEar 710
    $params = array();
41 PointedEar 711
 
27 PointedEar 712
    if (is_array($values))
713
    {
714
      if ($this->_isAssociativeArray($values))
715
      {
716
        foreach ($values as $key => $condition)
717
        {
718
          $params[":{$key}"] = $condition;
719
        }
41 PointedEar 720
 
27 PointedEar 721
        $values = $this->_escapeValueArray($values);
41 PointedEar 722
 
27 PointedEar 723
        $cols = '';
724
        $values = 'SET ' . implode(', ', $values);
725
      }
726
      else
727
      {
728
        foreach ($values as &$value)
729
        {
730
          if (is_string($value))
731
          {
732
            $value = "'" . $value . "'";
733
          }
734
        }
41 PointedEar 735
 
27 PointedEar 736
        $values = ' VALUES (' . implode(', ', $values) . ')';
737
      }
738
    }
41 PointedEar 739
 
27 PointedEar 740
    /* TODO: Should escape table names with escapeName(), but what about aliases? */
741
    $query = "INSERT INTO {$table} {$cols} {$values}";
41 PointedEar 742
 
27 PointedEar 743
    $stmt = $this->prepare($query);
41 PointedEar 744
 
27 PointedEar 745
      /* DEBUG */
746
    if (defined('DEBUG') && DEBUG > 1)
747
    {
748
       debug(array(
749
         'query'  => $query,
750
         'params' => $params
751
       ));
752
    }
41 PointedEar 753
 
27 PointedEar 754
    $success =& $this->_lastSuccess;
755
    $success = $stmt->execute($params);
41 PointedEar 756
 
27 PointedEar 757
    $errorInfo =& $this->_lastError;
758
    $errorInfo =  $stmt->errorInfo();
41 PointedEar 759
 
27 PointedEar 760
    $this->_setLastInsertId();
41 PointedEar 761
 
27 PointedEar 762
    $result =& $this->_lastResult;
763
    $result =  $stmt->fetchAll();
764
 
765
    if (defined('DEBUG') && DEBUG > 1)
766
    {
767
      debug(array(
768
        '_lastSuccess'  => $success,
769
        '_lastError'    => $errorInfo,
770
        '_lastInsertId' => $this->_lastInsertId,
771
        '_lastResult'   => $result
772
      ));
773
    }
41 PointedEar 774
 
27 PointedEar 775
    return $success;
776
  }
41 PointedEar 777
 
27 PointedEar 778
  /**
779
   * Retrieves all rows from a table
780
   *
781
   * @param int[optional] $fetch_style
782
   * @param int[optional] $column_index
783
   * @param array[optional] $ctor_args
784
   * @return array
785
   * @see PDOStatement::fetchAll()
786
   */
787
  public function fetchAll($table, $fetch_style = null, $column_index = null, array $ctor_args = null)
788
  {
789
    /* NOTE: Cannot use table name as statement parameter */
790
    $stmt = $this->prepare("SELECT * FROM $table");
791
    $this->_lastSuccess = $stmt->execute();
41 PointedEar 792
 
27 PointedEar 793
    $this->_lastError = $stmt->errorInfo();
41 PointedEar 794
 
27 PointedEar 795
    $result =& $this->_lastResult;
41 PointedEar 796
 
27 PointedEar 797
    if (is_null($fetch_style))
798
    {
799
      $fetch_style = PDO::FETCH_ASSOC;
800
    }
41 PointedEar 801
 
27 PointedEar 802
    if (!is_null($ctor_args))
803
    {
804
      $result = $stmt->fetchAll($fetch_style, $column_index, $ctor_args);
805
    }
806
    else if (!is_null($column_index))
807
    {
808
      $result = $stmt->fetchAll($fetch_style, $column_index);
809
    }
810
    else if (!is_null($fetch_style))
811
    {
812
      $result = $stmt->fetchAll($fetch_style);
813
    }
814
    else
815
    {
816
      $result = $stmt->fetchAll();
817
    }
41 PointedEar 818
 
27 PointedEar 819
    return $result;
820
  }
821
 
822
  /**
823
   * Deletes one or more records
824
   *
825
   * @param string|array $tables
826
   *   Table name(s)
827
   * @param array|string $where
828
   *   Only the records matching this condition are deleted
829
   * @return bool
830
   * @see PDOStatement::execute()
831
   */
832
  public function delete($tables, $where = null)
833
  {
834
    if (!$tables)
835
    {
836
      throw new InvalidArgumentException('No table specified');
837
    }
41 PointedEar 838
 
27 PointedEar 839
    if (is_array($tables))
840
    {
33 PointedEar 841
      $tables = implode(', ', $tables);
27 PointedEar 842
    }
41 PointedEar 843
 
27 PointedEar 844
    $params = array();
41 PointedEar 845
 
27 PointedEar 846
    $query = "DELETE FROM {$tables}" . $this->_where($where);
41 PointedEar 847
 
27 PointedEar 848
    $stmt = $this->prepare($query);
41 PointedEar 849
 
27 PointedEar 850
    if ($this->_isAssociativeArray($where))
851
    {
852
      foreach ($where as $column => $condition)
853
      {
854
        if (is_array($condition) && $this->_isAssociativeArray($condition))
855
        {
856
          reset($condition);
857
          $condition = $condition[key($condition)];
41 PointedEar 858
 
27 PointedEar 859
          if (is_array($condition))
860
          {
861
            foreach (self::_expand($condition, $column) as $param_index => $param_name)
862
            {
863
              $params[$param_name] = $condition[$param_index];
864
            }
865
          }
866
        }
867
        else
868
        {
869
          $params[":{$column}"] = $condition;
870
        }
871
      }
872
    }
873
 
874
    /* DEBUG */
875
    if (defined('DEBUG') && DEBUG > 1)
876
    {
877
      debug(array(
878
        'query'  => $query,
879
        'params' => $params
880
      ));
881
    }
41 PointedEar 882
 
27 PointedEar 883
    $success =& $this->_lastSuccess;
884
    $success =  $stmt->execute($params);
41 PointedEar 885
 
27 PointedEar 886
    $result =& $this->_lastResult;
887
    $result =  $stmt->fetchAll();
41 PointedEar 888
 
27 PointedEar 889
    $errorInfo =& $this->_lastError;
890
    $errorInfo =  $stmt->errorInfo();
41 PointedEar 891
 
27 PointedEar 892
    if (defined('DEBUG') && DEBUG > 1)
893
    {
894
      debug(array(
895
        '_lastSuccess' => $success,
896
        '_lastError'   => $errorInfo,
897
        '_lastResult'  => $result
898
      ));
899
    }
41 PointedEar 900
 
27 PointedEar 901
    return $success;
902
  }
903
}