Subversion Repositories PHPX

Rev

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

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