Subversion Repositories PHPX

Rev

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

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