Subversion Repositories PHPX

Rev

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

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