| Drupal | PHP Cross Reference | Content Management Systems |
1 <?php 2 3 /** 4 * @file 5 * Database interface code for engines that need complete control over their 6 * result sets. For example, SQLite will prefix some column names by the name 7 * of the table. We post-process the data, by renaming the column names 8 * using the same convention as MySQL and PostgreSQL. 9 */ 10 11 /** 12 * @addtogroup database 13 * @{ 14 */ 15 16 /** 17 * An implementation of DatabaseStatementInterface that prefetches all data. 18 * 19 * This class behaves very similar to a PDOStatement but as it always fetches 20 * every row it is possible to manipulate those results. 21 */ 22 class DatabaseStatementPrefetch implements Iterator, DatabaseStatementInterface { 23 24 /** 25 * The query string. 26 * 27 * @var string 28 */ 29 protected $queryString; 30 31 /** 32 * Driver-specific options. Can be used by child classes. 33 * 34 * @var Array 35 */ 36 protected $driverOptions; 37 38 /** 39 * Reference to the database connection object for this statement. 40 * 41 * The name $dbh is inherited from PDOStatement. 42 * 43 * @var DatabaseConnection 44 */ 45 public $dbh; 46 47 /** 48 * Main data store. 49 * 50 * @var Array 51 */ 52 protected $data = array(); 53 54 /** 55 * The current row, retrieved in PDO::FETCH_ASSOC format. 56 * 57 * @var Array 58 */ 59 protected $currentRow = NULL; 60 61 /** 62 * The key of the current row. 63 * 64 * @var int 65 */ 66 protected $currentKey = NULL; 67 68 /** 69 * The list of column names in this result set. 70 * 71 * @var Array 72 */ 73 protected $columnNames = NULL; 74 75 /** 76 * The number of rows affected by the last query. 77 * 78 * @var int 79 */ 80 protected $rowCount = NULL; 81 82 /** 83 * The number of rows in this result set. 84 * 85 * @var int 86 */ 87 protected $resultRowCount = 0; 88 89 /** 90 * Holds the current fetch style (which will be used by the next fetch). 91 * @see PDOStatement::fetch() 92 * 93 * @var int 94 */ 95 protected $fetchStyle = PDO::FETCH_OBJ; 96 97 /** 98 * Holds supplementary current fetch options (which will be used by the next fetch). 99 * 100 * @var Array 101 */ 102 protected $fetchOptions = array( 103 'class' => 'stdClass', 104 'constructor_args' => array(), 105 'object' => NULL, 106 'column' => 0, 107 ); 108 109 /** 110 * Holds the default fetch style. 111 * 112 * @var int 113 */ 114 protected $defaultFetchStyle = PDO::FETCH_OBJ; 115 116 /** 117 * Holds supplementary default fetch options. 118 * 119 * @var Array 120 */ 121 protected $defaultFetchOptions = array( 122 'class' => 'stdClass', 123 'constructor_args' => array(), 124 'object' => NULL, 125 'column' => 0, 126 ); 127 128 public function __construct(DatabaseConnection $connection, $query, array $driver_options = array()) { 129 $this->dbh = $connection; 130 $this->queryString = $query; 131 $this->driverOptions = $driver_options; 132 } 133 134 /** 135 * Executes a prepared statement. 136 * 137 * @param $args 138 * An array of values with as many elements as there are bound parameters in the SQL statement being executed. 139 * @param $options 140 * An array of options for this query. 141 * @return 142 * TRUE on success, or FALSE on failure. 143 */ 144 public function execute($args = array(), $options = array()) { 145 if (isset($options['fetch'])) { 146 if (is_string($options['fetch'])) { 147 // Default to an object. Note: db fields will be added to the object 148 // before the constructor is run. If you need to assign fields after 149 // the constructor is run, see http://drupal.org/node/315092. 150 $this->setFetchMode(PDO::FETCH_CLASS, $options['fetch']); 151 } 152 else { 153 $this->setFetchMode($options['fetch']); 154 } 155 } 156 157 $logger = $this->dbh->getLogger(); 158 if (!empty($logger)) { 159 $query_start = microtime(TRUE); 160 } 161 162 // Prepare the query. 163 $statement = $this->getStatement($this->queryString, $args); 164 if (!$statement) { 165 $this->throwPDOException(); 166 } 167 168 $return = $statement->execute($args); 169 if (!$return) { 170 $this->throwPDOException(); 171 } 172 173 // Fetch all the data from the reply, in order to release any lock 174 // as soon as possible. 175 $this->rowCount = $statement->rowCount(); 176 $this->data = $statement->fetchAll(PDO::FETCH_ASSOC); 177 // Destroy the statement as soon as possible. See 178 // DatabaseConnection_sqlite::PDOPrepare() for explanation. 179 unset($statement); 180 181 $this->resultRowCount = count($this->data); 182 183 if ($this->resultRowCount) { 184 $this->columnNames = array_keys($this->data[0]); 185 } 186 else { 187 $this->columnNames = array(); 188 } 189 190 if (!empty($logger)) { 191 $query_end = microtime(TRUE); 192 $logger->log($this, $args, $query_end - $query_start); 193 } 194 195 // Initialize the first row in $this->currentRow. 196 $this->next(); 197 198 return $return; 199 } 200 201 /** 202 * Throw a PDO Exception based on the last PDO error. 203 */ 204 protected function throwPDOException() { 205 $error_info = $this->dbh->errorInfo(); 206 // We rebuild a message formatted in the same way as PDO. 207 $exception = new PDOException("SQLSTATE[" . $error_info[0] . "]: General error " . $error_info[1] . ": " . $error_info[2]); 208 $exception->errorInfo = $error_info; 209 throw $exception; 210 } 211 212 /** 213 * Grab a PDOStatement object from a given query and its arguments. 214 * 215 * Some drivers (including SQLite) will need to perform some preparation 216 * themselves to get the statement right. 217 * 218 * @param $query 219 * The query. 220 * @param array $args 221 * An array of arguments. 222 * @return PDOStatement 223 * A PDOStatement object. 224 */ 225 protected function getStatement($query, &$args = array()) { 226 return $this->dbh->prepare($query); 227 } 228 229 /** 230 * Return the object's SQL query string. 231 */ 232 public function getQueryString() { 233 return $this->queryString; 234 } 235 236 /** 237 * @see PDOStatement::setFetchMode() 238 */ 239 public function setFetchMode($fetchStyle, $a2 = NULL, $a3 = NULL) { 240 $this->defaultFetchStyle = $fetchStyle; 241 switch ($fetchStyle) { 242 case PDO::FETCH_CLASS: 243 $this->defaultFetchOptions['class'] = $a2; 244 if ($a3) { 245 $this->defaultFetchOptions['constructor_args'] = $a3; 246 } 247 break; 248 case PDO::FETCH_COLUMN: 249 $this->defaultFetchOptions['column'] = $a2; 250 break; 251 case PDO::FETCH_INTO: 252 $this->defaultFetchOptions['object'] = $a2; 253 break; 254 } 255 256 // Set the values for the next fetch. 257 $this->fetchStyle = $this->defaultFetchStyle; 258 $this->fetchOptions = $this->defaultFetchOptions; 259 } 260 261 /** 262 * Return the current row formatted according to the current fetch style. 263 * 264 * This is the core method of this class. It grabs the value at the current 265 * array position in $this->data and format it according to $this->fetchStyle 266 * and $this->fetchMode. 267 * 268 * @return 269 * The current row formatted as requested. 270 */ 271 public function current() { 272 if (isset($this->currentRow)) { 273 switch ($this->fetchStyle) { 274 case PDO::FETCH_ASSOC: 275 return $this->currentRow; 276 case PDO::FETCH_BOTH: 277 // PDO::FETCH_BOTH returns an array indexed by both the column name 278 // and the column number. 279 return $this->currentRow + array_values($this->currentRow); 280 case PDO::FETCH_NUM: 281 return array_values($this->currentRow); 282 case PDO::FETCH_LAZY: 283 // We do not do lazy as everything is fetched already. Fallback to 284 // PDO::FETCH_OBJ. 285 case PDO::FETCH_OBJ: 286 return (object) $this->currentRow; 287 case PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE: 288 $class_name = array_unshift($this->currentRow); 289 // Deliberate no break. 290 case PDO::FETCH_CLASS: 291 if (!isset($class_name)) { 292 $class_name = $this->fetchOptions['class']; 293 } 294 if (count($this->fetchOptions['constructor_args'])) { 295 $reflector = new ReflectionClass($class_name); 296 $result = $reflector->newInstanceArgs($this->fetchOptions['constructor_args']); 297 } 298 else { 299 $result = new $class_name(); 300 } 301 foreach ($this->currentRow as $k => $v) { 302 $result->$k = $v; 303 } 304 return $result; 305 case PDO::FETCH_INTO: 306 foreach ($this->currentRow as $k => $v) { 307 $this->fetchOptions['object']->$k = $v; 308 } 309 return $this->fetchOptions['object']; 310 case PDO::FETCH_COLUMN: 311 if (isset($this->columnNames[$this->fetchOptions['column']])) { 312 return $this->currentRow[$k][$this->columnNames[$this->fetchOptions['column']]]; 313 } 314 else { 315 return; 316 } 317 } 318 } 319 } 320 321 /* Implementations of Iterator. */ 322 323 public function key() { 324 return $this->currentKey; 325 } 326 327 public function rewind() { 328 // Nothing to do: our DatabaseStatement can't be rewound. 329 } 330 331 public function next() { 332 if (!empty($this->data)) { 333 $this->currentRow = reset($this->data); 334 $this->currentKey = key($this->data); 335 unset($this->data[$this->currentKey]); 336 } 337 else { 338 $this->currentRow = NULL; 339 } 340 } 341 342 public function valid() { 343 return isset($this->currentRow); 344 } 345 346 /* Implementations of DatabaseStatementInterface. */ 347 348 public function rowCount() { 349 return $this->rowCount; 350 } 351 352 public function fetch($fetch_style = NULL, $cursor_orientation = PDO::FETCH_ORI_NEXT, $cursor_offset = NULL) { 353 if (isset($this->currentRow)) { 354 // Set the fetch parameter. 355 $this->fetchStyle = isset($fetch_style) ? $fetch_style : $this->defaultFetchStyle; 356 $this->fetchOptions = $this->defaultFetchOptions; 357 358 // Grab the row in the format specified above. 359 $return = $this->current(); 360 // Advance the cursor. 361 $this->next(); 362 363 // Reset the fetch parameters to the value stored using setFetchMode(). 364 $this->fetchStyle = $this->defaultFetchStyle; 365 $this->fetchOptions = $this->defaultFetchOptions; 366 return $return; 367 } 368 else { 369 return FALSE; 370 } 371 } 372 373 public function fetchColumn($index = 0) { 374 if (isset($this->currentRow) && isset($this->columnNames[$index])) { 375 // We grab the value directly from $this->data, and format it. 376 $return = $this->currentRow[$this->columnNames[$index]]; 377 $this->next(); 378 return $return; 379 } 380 else { 381 return FALSE; 382 } 383 } 384 385 public function fetchField($index = 0) { 386 return $this->fetchColumn($index); 387 } 388 389 public function fetchObject($class_name = NULL, $constructor_args = array()) { 390 if (isset($this->currentRow)) { 391 if (!isset($class_name)) { 392 // Directly cast to an object to avoid a function call. 393 $result = (object) $this->currentRow; 394 } 395 else { 396 $this->fetchStyle = PDO::FETCH_CLASS; 397 $this->fetchOptions = array('constructor_args' => $constructor_args); 398 // Grab the row in the format specified above. 399 $result = $this->current(); 400 // Reset the fetch parameters to the value stored using setFetchMode(). 401 $this->fetchStyle = $this->defaultFetchStyle; 402 $this->fetchOptions = $this->defaultFetchOptions; 403 } 404 405 $this->next(); 406 407 return $result; 408 } 409 else { 410 return FALSE; 411 } 412 } 413 414 public function fetchAssoc() { 415 if (isset($this->currentRow)) { 416 $result = $this->currentRow; 417 $this->next(); 418 return $result; 419 } 420 else { 421 return FALSE; 422 } 423 } 424 425 public function fetchAll($fetch_style = NULL, $fetch_column = NULL, $constructor_args = NULL) { 426 $this->fetchStyle = isset($fetch_style) ? $fetch_style : $this->defaultFetchStyle; 427 $this->fetchOptions = $this->defaultFetchOptions; 428 if (isset($fetch_column)) { 429 $this->fetchOptions['column'] = $fetch_column; 430 } 431 if (isset($constructor_args)) { 432 $this->fetchOptions['constructor_args'] = $constructor_args; 433 } 434 435 $result = array(); 436 // Traverse the array as PHP would have done. 437 while (isset($this->currentRow)) { 438 // Grab the row in the format specified above. 439 $result[] = $this->current(); 440 $this->next(); 441 } 442 443 // Reset the fetch parameters to the value stored using setFetchMode(). 444 $this->fetchStyle = $this->defaultFetchStyle; 445 $this->fetchOptions = $this->defaultFetchOptions; 446 return $result; 447 } 448 449 public function fetchCol($index = 0) { 450 if (isset($this->columnNames[$index])) { 451 $column = $this->columnNames[$index]; 452 $result = array(); 453 // Traverse the array as PHP would have done. 454 while (isset($this->currentRow)) { 455 $result[] = $this->currentRow[$this->columnNames[$index]]; 456 $this->next(); 457 } 458 return $result; 459 } 460 else { 461 return array(); 462 } 463 } 464 465 public function fetchAllKeyed($key_index = 0, $value_index = 1) { 466 if (!isset($this->columnNames[$key_index]) || !isset($this->columnNames[$value_index])) 467 return array(); 468 469 $key = $this->columnNames[$key_index]; 470 $value = $this->columnNames[$value_index]; 471 472 $result = array(); 473 // Traverse the array as PHP would have done. 474 while (isset($this->currentRow)) { 475 $result[$this->currentRow[$key]] = $this->currentRow[$value]; 476 $this->next(); 477 } 478 return $result; 479 } 480 481 public function fetchAllAssoc($key, $fetch_style = NULL) { 482 $this->fetchStyle = isset($fetch_style) ? $fetch_style : $this->defaultFetchStyle; 483 $this->fetchOptions = $this->defaultFetchOptions; 484 485 $result = array(); 486 // Traverse the array as PHP would have done. 487 while (isset($this->currentRow)) { 488 // Grab the row in its raw PDO::FETCH_ASSOC format. 489 $row = $this->currentRow; 490 // Grab the row in the format specified above. 491 $result_row = $this->current(); 492 $result[$this->currentRow[$key]] = $result_row; 493 $this->next(); 494 } 495 496 // Reset the fetch parameters to the value stored using setFetchMode(). 497 $this->fetchStyle = $this->defaultFetchStyle; 498 $this->fetchOptions = $this->defaultFetchOptions; 499 return $result; 500 } 501 502 } 503 504 /** 505 * @} End of "addtogroup database". 506 */ 507
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title