Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
47.62% |
10 / 21 |
CRAP | |
59.64% |
99 / 166 |
| StatementPrefetch | |
0.00% |
0 / 1 |
|
47.62% |
10 / 21 |
372.03 | |
59.64% |
99 / 166 |
| __construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
5 / 5 |
|||
| execute | |
0.00% |
0 / 1 |
10.00 | |
76.92% |
20 / 26 |
|||
| throwPDOException | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 4 |
|||
| getStatement | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| getQueryString | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| setFetchMode | |
0.00% |
0 / 1 |
8.79 | |
46.67% |
7 / 15 |
|||
| current | |
0.00% |
0 / 1 |
111.05 | |
28.12% |
9 / 32 |
|||
| key | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| rewind | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| next | |
100.00% |
1 / 1 |
2 | |
100.00% |
6 / 6 |
|||
| valid | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| rowCount | |
0.00% |
0 / 1 |
2.15 | |
66.67% |
2 / 3 |
|||
| fetch | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 9 |
|||
| fetchColumn | |
100.00% |
1 / 1 |
3 | |
100.00% |
5 / 5 |
|||
| fetchField | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| fetchObject | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 11 |
|||
| fetchAssoc | |
100.00% |
1 / 1 |
2 | |
100.00% |
5 / 5 |
|||
| fetchAll | |
0.00% |
0 / 1 |
5.09 | |
84.62% |
11 / 13 |
|||
| fetchCol | |
100.00% |
1 / 1 |
3 | |
100.00% |
7 / 7 |
|||
| fetchAllKeyed | |
0.00% |
0 / 1 |
4.02 | |
88.89% |
8 / 9 |
|||
| fetchAllAssoc | |
100.00% |
1 / 1 |
3 | |
100.00% |
10 / 10 |
|||
| <?php | |
| /** | |
| * @file | |
| * Contains \Drupal\Core\Database\StatementPrefetch. | |
| */ | |
| namespace Drupal\Core\Database; | |
| /** | |
| * An implementation of StatementInterface that prefetches all data. | |
| * | |
| * This class behaves very similar to a \PDOStatement but as it always fetches | |
| * every row it is possible to manipulate those results. | |
| */ | |
| class StatementPrefetch implements \Iterator, StatementInterface { | |
| /** | |
| * The query string. | |
| * | |
| * @var string | |
| */ | |
| protected $queryString; | |
| /** | |
| * Driver-specific options. Can be used by child classes. | |
| * | |
| * @var Array | |
| */ | |
| protected $driverOptions; | |
| /** | |
| * Reference to the Drupal database connection object for this statement. | |
| * | |
| * @var \Drupal\Core\Database\Connection | |
| */ | |
| public $dbh; | |
| /** | |
| * Reference to the PDO connection object for this statement. | |
| * | |
| * @var \PDO | |
| */ | |
| protected $pdoConnection; | |
| /** | |
| * Main data store. | |
| * | |
| * @var Array | |
| */ | |
| protected $data = array(); | |
| /** | |
| * The current row, retrieved in \PDO::FETCH_ASSOC format. | |
| * | |
| * @var Array | |
| */ | |
| protected $currentRow = NULL; | |
| /** | |
| * The key of the current row. | |
| * | |
| * @var int | |
| */ | |
| protected $currentKey = NULL; | |
| /** | |
| * The list of column names in this result set. | |
| * | |
| * @var Array | |
| */ | |
| protected $columnNames = NULL; | |
| /** | |
| * The number of rows affected by the last query. | |
| * | |
| * @var int | |
| */ | |
| protected $rowCount = NULL; | |
| /** | |
| * The number of rows in this result set. | |
| * | |
| * @var int | |
| */ | |
| protected $resultRowCount = 0; | |
| /** | |
| * Holds the current fetch style (which will be used by the next fetch). | |
| * @see \PDOStatement::fetch() | |
| * | |
| * @var int | |
| */ | |
| protected $fetchStyle = \PDO::FETCH_OBJ; | |
| /** | |
| * Holds supplementary current fetch options (which will be used by the next fetch). | |
| * | |
| * @var Array | |
| */ | |
| protected $fetchOptions = array( | |
| 'class' => 'stdClass', | |
| 'constructor_args' => array(), | |
| 'object' => NULL, | |
| 'column' => 0, | |
| ); | |
| /** | |
| * Holds the default fetch style. | |
| * | |
| * @var int | |
| */ | |
| protected $defaultFetchStyle = \PDO::FETCH_OBJ; | |
| /** | |
| * Holds supplementary default fetch options. | |
| * | |
| * @var Array | |
| */ | |
| protected $defaultFetchOptions = array( | |
| 'class' => 'stdClass', | |
| 'constructor_args' => array(), | |
| 'object' => NULL, | |
| 'column' => 0, | |
| ); | |
| /** | |
| * Is rowCount() execution allowed. | |
| * | |
| * @var bool | |
| */ | |
| public $allowRowCount = FALSE; | |
| public function __construct(\PDO $pdo_connection, Connection $connection, $query, array $driver_options = array()) { | |
| $this->pdoConnection = $pdo_connection; | |
| $this->dbh = $connection; | |
| $this->queryString = $query; | |
| $this->driverOptions = $driver_options; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function execute($args = array(), $options = array()) { | |
| if (isset($options['fetch'])) { | |
| if (is_string($options['fetch'])) { | |
| // Default to an object. Note: db fields will be added to the object | |
| // before the constructor is run. If you need to assign fields after | |
| // the constructor is run. See https://www.drupal.org/node/315092. | |
| $this->setFetchMode(\PDO::FETCH_CLASS, $options['fetch']); | |
| } | |
| else { | |
| $this->setFetchMode($options['fetch']); | |
| } | |
| } | |
| $logger = $this->dbh->getLogger(); | |
| if (!empty($logger)) { | |
| $query_start = microtime(TRUE); | |
| } | |
| // Prepare the query. | |
| $statement = $this->getStatement($this->queryString, $args); | |
| if (!$statement) { | |
| $this->throwPDOException(); | |
| } | |
| $return = $statement->execute($args); | |
| if (!$return) { | |
| $this->throwPDOException(); | |
| } | |
| if ($options['return'] == Database::RETURN_AFFECTED) { | |
| $this->rowCount = $statement->rowCount(); | |
| } | |
| // Fetch all the data from the reply, in order to release any lock | |
| // as soon as possible. | |
| $this->data = $statement->fetchAll(\PDO::FETCH_ASSOC); | |
| // Destroy the statement as soon as possible. See the documentation of | |
| // \Drupal\Core\Database\Driver\sqlite\Statement for an explanation. | |
| unset($statement); | |
| $this->resultRowCount = count($this->data); | |
| if ($this->resultRowCount) { | |
| $this->columnNames = array_keys($this->data[0]); | |
| } | |
| else { | |
| $this->columnNames = array(); | |
| } | |
| if (!empty($logger)) { | |
| $query_end = microtime(TRUE); | |
| $logger->log($this, $args, $query_end - $query_start); | |
| } | |
| // Initialize the first row in $this->currentRow. | |
| $this->next(); | |
| return $return; | |
| } | |
| /** | |
| * Throw a PDO Exception based on the last PDO error. | |
| */ | |
| protected function throwPDOException() { | |
| $error_info = $this->dbh->errorInfo(); | |
| // We rebuild a message formatted in the same way as PDO. | |
| $exception = new \PDOException("SQLSTATE[" . $error_info[0] . "]: General error " . $error_info[1] . ": " . $error_info[2]); | |
| $exception->errorInfo = $error_info; | |
| throw $exception; | |
| } | |
| /** | |
| * Grab a PDOStatement object from a given query and its arguments. | |
| * | |
| * Some drivers (including SQLite) will need to perform some preparation | |
| * themselves to get the statement right. | |
| * | |
| * @param $query | |
| * The query. | |
| * @param array $args | |
| * An array of arguments. | |
| * @return \PDOStatement | |
| * A PDOStatement object. | |
| */ | |
| protected function getStatement($query, &$args = array()) { | |
| return $this->dbh->prepare($query); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getQueryString() { | |
| return $this->queryString; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function setFetchMode($mode, $a1 = NULL, $a2 = array()) { | |
| $this->defaultFetchStyle = $mode; | |
| switch ($mode) { | |
| case \PDO::FETCH_CLASS: | |
| $this->defaultFetchOptions['class'] = $a1; | |
| if ($a2) { | |
| $this->defaultFetchOptions['constructor_args'] = $a2; | |
| } | |
| break; | |
| case \PDO::FETCH_COLUMN: | |
| $this->defaultFetchOptions['column'] = $a1; | |
| break; | |
| case \PDO::FETCH_INTO: | |
| $this->defaultFetchOptions['object'] = $a1; | |
| break; | |
| } | |
| // Set the values for the next fetch. | |
| $this->fetchStyle = $this->defaultFetchStyle; | |
| $this->fetchOptions = $this->defaultFetchOptions; | |
| } | |
| /** | |
| * Return the current row formatted according to the current fetch style. | |
| * | |
| * This is the core method of this class. It grabs the value at the current | |
| * array position in $this->data and format it according to $this->fetchStyle | |
| * and $this->fetchMode. | |
| * | |
| * @return mixed | |
| * The current row formatted as requested. | |
| */ | |
| public function current() { | |
| if (isset($this->currentRow)) { | |
| switch ($this->fetchStyle) { | |
| case \PDO::FETCH_ASSOC: | |
| return $this->currentRow; | |
| case \PDO::FETCH_BOTH: | |
| // \PDO::FETCH_BOTH returns an array indexed by both the column name | |
| // and the column number. | |
| return $this->currentRow + array_values($this->currentRow); | |
| case \PDO::FETCH_NUM: | |
| return array_values($this->currentRow); | |
| case \PDO::FETCH_LAZY: | |
| // We do not do lazy as everything is fetched already. Fallback to | |
| // \PDO::FETCH_OBJ. | |
| case \PDO::FETCH_OBJ: | |
| return (object) $this->currentRow; | |
| case \PDO::FETCH_CLASS | \PDO::FETCH_CLASSTYPE: | |
| $class_name = array_unshift($this->currentRow); | |
| // Deliberate no break. | |
| case \PDO::FETCH_CLASS: | |
| if (!isset($class_name)) { | |
| $class_name = $this->fetchOptions['class']; | |
| } | |
| if (count($this->fetchOptions['constructor_args'])) { | |
| $reflector = new \ReflectionClass($class_name); | |
| $result = $reflector->newInstanceArgs($this->fetchOptions['constructor_args']); | |
| } | |
| else { | |
| $result = new $class_name(); | |
| } | |
| foreach ($this->currentRow as $k => $v) { | |
| $result->$k = $v; | |
| } | |
| return $result; | |
| case \PDO::FETCH_INTO: | |
| foreach ($this->currentRow as $k => $v) { | |
| $this->fetchOptions['object']->$k = $v; | |
| } | |
| return $this->fetchOptions['object']; | |
| case \PDO::FETCH_COLUMN: | |
| if (isset($this->columnNames[$this->fetchOptions['column']])) { | |
| return $this->currentRow[$k][$this->columnNames[$this->fetchOptions['column']]]; | |
| } | |
| else { | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function key() { | |
| return $this->currentKey; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function rewind() { | |
| // Nothing to do: our DatabaseStatement can't be rewound. | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function next() { | |
| if (!empty($this->data)) { | |
| $this->currentRow = reset($this->data); | |
| $this->currentKey = key($this->data); | |
| unset($this->data[$this->currentKey]); | |
| } | |
| else { | |
| $this->currentRow = NULL; | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function valid() { | |
| return isset($this->currentRow); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function rowCount() { | |
| // SELECT query should not use the method. | |
| if ($this->allowRowCount) { | |
| return $this->rowCount; | |
| } | |
| else { | |
| throw new RowCountException(); | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function fetch($fetch_style = NULL, $cursor_orientation = \PDO::FETCH_ORI_NEXT, $cursor_offset = NULL) { | |
| if (isset($this->currentRow)) { | |
| // Set the fetch parameter. | |
| $this->fetchStyle = isset($fetch_style) ? $fetch_style : $this->defaultFetchStyle; | |
| $this->fetchOptions = $this->defaultFetchOptions; | |
| // Grab the row in the format specified above. | |
| $return = $this->current(); | |
| // Advance the cursor. | |
| $this->next(); | |
| // Reset the fetch parameters to the value stored using setFetchMode(). | |
| $this->fetchStyle = $this->defaultFetchStyle; | |
| $this->fetchOptions = $this->defaultFetchOptions; | |
| return $return; | |
| } | |
| else { | |
| return FALSE; | |
| } | |
| } | |
| public function fetchColumn($index = 0) { | |
| if (isset($this->currentRow) && isset($this->columnNames[$index])) { | |
| // We grab the value directly from $this->data, and format it. | |
| $return = $this->currentRow[$this->columnNames[$index]]; | |
| $this->next(); | |
| return $return; | |
| } | |
| else { | |
| return FALSE; | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function fetchField($index = 0) { | |
| return $this->fetchColumn($index); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function fetchObject($class_name = NULL, $constructor_args = array()) { | |
| if (isset($this->currentRow)) { | |
| if (!isset($class_name)) { | |
| // Directly cast to an object to avoid a function call. | |
| $result = (object) $this->currentRow; | |
| } | |
| else { | |
| $this->fetchStyle = \PDO::FETCH_CLASS; | |
| $this->fetchOptions = array('constructor_args' => $constructor_args); | |
| // Grab the row in the format specified above. | |
| $result = $this->current(); | |
| // Reset the fetch parameters to the value stored using setFetchMode(). | |
| $this->fetchStyle = $this->defaultFetchStyle; | |
| $this->fetchOptions = $this->defaultFetchOptions; | |
| } | |
| $this->next(); | |
| return $result; | |
| } | |
| else { | |
| return FALSE; | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function fetchAssoc() { | |
| if (isset($this->currentRow)) { | |
| $result = $this->currentRow; | |
| $this->next(); | |
| return $result; | |
| } | |
| else { | |
| return FALSE; | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function fetchAll($mode = NULL, $column_index = NULL, $constructor_arguments = NULL) { | |
| $this->fetchStyle = isset($mode) ? $mode : $this->defaultFetchStyle; | |
| $this->fetchOptions = $this->defaultFetchOptions; | |
| if (isset($column_index)) { | |
| $this->fetchOptions['column'] = $column_index; | |
| } | |
| if (isset($constructor_arguments)) { | |
| $this->fetchOptions['constructor_args'] = $constructor_arguments; | |
| } | |
| $result = array(); | |
| // Traverse the array as PHP would have done. | |
| while (isset($this->currentRow)) { | |
| // Grab the row in the format specified above. | |
| $result[] = $this->current(); | |
| $this->next(); | |
| } | |
| // Reset the fetch parameters to the value stored using setFetchMode(). | |
| $this->fetchStyle = $this->defaultFetchStyle; | |
| $this->fetchOptions = $this->defaultFetchOptions; | |
| return $result; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function fetchCol($index = 0) { | |
| if (isset($this->columnNames[$index])) { | |
| $result = array(); | |
| // Traverse the array as PHP would have done. | |
| while (isset($this->currentRow)) { | |
| $result[] = $this->currentRow[$this->columnNames[$index]]; | |
| $this->next(); | |
| } | |
| return $result; | |
| } | |
| else { | |
| return array(); | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function fetchAllKeyed($key_index = 0, $value_index = 1) { | |
| if (!isset($this->columnNames[$key_index]) || !isset($this->columnNames[$value_index])) | |
| return array(); | |
| $key = $this->columnNames[$key_index]; | |
| $value = $this->columnNames[$value_index]; | |
| $result = array(); | |
| // Traverse the array as PHP would have done. | |
| while (isset($this->currentRow)) { | |
| $result[$this->currentRow[$key]] = $this->currentRow[$value]; | |
| $this->next(); | |
| } | |
| return $result; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function fetchAllAssoc($key, $fetch_style = NULL) { | |
| $this->fetchStyle = isset($fetch_style) ? $fetch_style : $this->defaultFetchStyle; | |
| $this->fetchOptions = $this->defaultFetchOptions; | |
| $result = array(); | |
| // Traverse the array as PHP would have done. | |
| while (isset($this->currentRow)) { | |
| // Grab the row in its raw \PDO::FETCH_ASSOC format. | |
| $result_row = $this->current(); | |
| $result[$this->currentRow[$key]] = $result_row; | |
| $this->next(); | |
| } | |
| // Reset the fetch parameters to the value stored using setFetchMode(). | |
| $this->fetchStyle = $this->defaultFetchStyle; | |
| $this->fetchOptions = $this->defaultFetchOptions; | |
| return $result; | |
| } | |
| } |