DataReader.php 7.6 KB

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