You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

268 line
8.3KB

  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\db;
  8. use yii\base\InvalidCallException;
  9. /**
  10. * DataReader represents a forward-only stream of rows from a query result set.
  11. *
  12. * To read the current row of data, call [[read()]]. The method [[readAll()]]
  13. * returns all the rows in a single array. Rows of data can also be read by
  14. * iterating through the reader. For example,
  15. *
  16. * ~~~
  17. * $command = $connection->createCommand('SELECT * FROM post');
  18. * $reader = $command->query();
  19. *
  20. * while ($row = $reader->read()) {
  21. * $rows[] = $row;
  22. * }
  23. *
  24. * // equivalent to:
  25. * foreach ($reader as $row) {
  26. * $rows[] = $row;
  27. * }
  28. *
  29. * // equivalent to:
  30. * $rows = $reader->readAll();
  31. * ~~~
  32. *
  33. * Note that since DataReader is a forward-only stream, you can only traverse it once.
  34. * Doing it the second time will throw an exception.
  35. *
  36. * It is possible to use a specific mode of data fetching by setting
  37. * [[fetchMode]]. See the [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php)
  38. * for more details about possible fetch mode.
  39. *
  40. * @property integer $columnCount The number of columns in the result set. This property is read-only.
  41. * @property integer $fetchMode Fetch mode. This property is write-only.
  42. * @property boolean $isClosed Whether the reader is closed or not. This property is read-only.
  43. * @property integer $rowCount Number of rows contained in the result. This property is read-only.
  44. *
  45. * @author Qiang Xue <qiang.xue@gmail.com>
  46. * @since 2.0
  47. */
  48. class DataReader extends \yii\base\Object implements \Iterator, \Countable
  49. {
  50. /**
  51. * @var \PDOStatement the PDOStatement associated with the command
  52. */
  53. private $_statement;
  54. private $_closed = false;
  55. private $_row;
  56. private $_index = -1;
  57. /**
  58. * Constructor.
  59. * @param Command $command the command generating the query result
  60. * @param array $config name-value pairs that will be used to initialize the object properties
  61. */
  62. public function __construct(Command $command, $config = [])
  63. {
  64. $this->_statement = $command->pdoStatement;
  65. $this->_statement->setFetchMode(\PDO::FETCH_ASSOC);
  66. parent::__construct($config);
  67. }
  68. /**
  69. * Binds a column to a PHP variable.
  70. * When rows of data are being fetched, the corresponding column value
  71. * will be set in the variable. Note, the fetch mode must include PDO::FETCH_BOUND.
  72. * @param integer|string $column Number of the column (1-indexed) or name of the column
  73. * in the result set. If using the column name, be aware that the name
  74. * should match the case of the column, as returned by the driver.
  75. * @param mixed $value Name of the PHP variable to which the column will be bound.
  76. * @param integer $dataType Data type of the parameter
  77. * @see http://www.php.net/manual/en/function.PDOStatement-bindColumn.php
  78. */
  79. public function bindColumn($column, &$value, $dataType = null)
  80. {
  81. if ($dataType === null) {
  82. $this->_statement->bindColumn($column, $value);
  83. } else {
  84. $this->_statement->bindColumn($column, $value, $dataType);
  85. }
  86. }
  87. /**
  88. * Set the default fetch mode for this statement
  89. * @param integer $mode fetch mode
  90. * @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php
  91. */
  92. public function setFetchMode($mode)
  93. {
  94. $params = func_get_args();
  95. call_user_func_array([$this->_statement, 'setFetchMode'], $params);
  96. }
  97. /**
  98. * Advances the reader to the next row in a result set.
  99. * @return array the current row, false if no more row available
  100. */
  101. public function read()
  102. {
  103. return $this->_statement->fetch();
  104. }
  105. /**
  106. * Returns a single column from the next row of a result set.
  107. * @param integer $columnIndex zero-based column index
  108. * @return mixed the column of the current row, false if no more rows available
  109. */
  110. public function readColumn($columnIndex)
  111. {
  112. return $this->_statement->fetchColumn($columnIndex);
  113. }
  114. /**
  115. * Returns an object populated with the next row of data.
  116. * @param string $className class name of the object to be created and populated
  117. * @param array $fields Elements of this array are passed to the constructor
  118. * @return mixed the populated object, false if no more row of data available
  119. */
  120. public function readObject($className, $fields)
  121. {
  122. return $this->_statement->fetchObject($className, $fields);
  123. }
  124. /**
  125. * Reads the whole result set into an array.
  126. * @return array the result set (each array element represents a row of data).
  127. * An empty array will be returned if the result contains no row.
  128. */
  129. public function readAll()
  130. {
  131. return $this->_statement->fetchAll();
  132. }
  133. /**
  134. * Advances the reader to the next result when reading the results of a batch of statements.
  135. * This method is only useful when there are multiple result sets
  136. * returned by the query. Not all DBMS support this feature.
  137. * @return boolean Returns true on success or false on failure.
  138. */
  139. public function nextResult()
  140. {
  141. if (($result = $this->_statement->nextRowset()) !== false) {
  142. $this->_index = -1;
  143. }
  144. return $result;
  145. }
  146. /**
  147. * Closes the reader.
  148. * This frees up the resources allocated for executing this SQL statement.
  149. * Read attempts after this method call are unpredictable.
  150. */
  151. public function close()
  152. {
  153. $this->_statement->closeCursor();
  154. $this->_closed = true;
  155. }
  156. /**
  157. * whether the reader is closed or not.
  158. * @return boolean whether the reader is closed or not.
  159. */
  160. public function getIsClosed()
  161. {
  162. return $this->_closed;
  163. }
  164. /**
  165. * Returns the number of rows in the result set.
  166. * Note, most DBMS may not give a meaningful count.
  167. * In this case, use "SELECT COUNT(*) FROM tableName" to obtain the number of rows.
  168. * @return integer number of rows contained in the result.
  169. */
  170. public function getRowCount()
  171. {
  172. return $this->_statement->rowCount();
  173. }
  174. /**
  175. * Returns the number of rows in the result set.
  176. * This method is required by the Countable interface.
  177. * Note, most DBMS may not give a meaningful count.
  178. * In this case, use "SELECT COUNT(*) FROM tableName" to obtain the number of rows.
  179. * @return integer number of rows contained in the result.
  180. */
  181. public function count()
  182. {
  183. return $this->getRowCount();
  184. }
  185. /**
  186. * Returns the number of columns in the result set.
  187. * Note, even there's no row in the reader, this still gives correct column number.
  188. * @return integer the number of columns in the result set.
  189. */
  190. public function getColumnCount()
  191. {
  192. return $this->_statement->columnCount();
  193. }
  194. /**
  195. * Resets the iterator to the initial state.
  196. * This method is required by the interface Iterator.
  197. * @throws InvalidCallException if this method is invoked twice
  198. */
  199. public function rewind()
  200. {
  201. if ($this->_index < 0) {
  202. $this->_row = $this->_statement->fetch();
  203. $this->_index = 0;
  204. } else {
  205. throw new InvalidCallException('DataReader cannot rewind. It is a forward-only reader.');
  206. }
  207. }
  208. /**
  209. * Returns the index of the current row.
  210. * This method is required by the interface Iterator.
  211. * @return integer the index of the current row.
  212. */
  213. public function key()
  214. {
  215. return $this->_index;
  216. }
  217. /**
  218. * Returns the current row.
  219. * This method is required by the interface Iterator.
  220. * @return mixed the current row.
  221. */
  222. public function current()
  223. {
  224. return $this->_row;
  225. }
  226. /**
  227. * Moves the internal pointer to the next row.
  228. * This method is required by the interface Iterator.
  229. */
  230. public function next()
  231. {
  232. $this->_row = $this->_statement->fetch();
  233. $this->_index++;
  234. }
  235. /**
  236. * Returns whether there is a row of data at current position.
  237. * This method is required by the interface Iterator.
  238. * @return boolean whether there is a row of data at current position.
  239. */
  240. public function valid()
  241. {
  242. return $this->_row !== false;
  243. }
  244. }