Subversion Repositories PHPX

Rev

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

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