Subversion Repositories PHPX

Rev

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

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