Subversion Repositories PHPX

Rev

Rev 52 | Rev 61 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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