DataReader.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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. * ```php
  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](https://secure.php.net/manual/en/function.PDOStatement-setFetchMode.php)
  38. * for more details about possible fetch mode.
  39. *
  40. * @property int $columnCount The number of columns in the result set. This property is read-only.
  41. * @property int $fetchMode Fetch mode. This property is write-only.
  42. * @property bool $isClosed Whether the reader is closed or not. This property is read-only.
  43. * @property int $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\BaseObject 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 int|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 int $dataType Data type of the parameter
  77. * @see https://secure.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. *
  90. * @param int $mode fetch mode
  91. * @see https://secure.php.net/manual/en/function.PDOStatement-setFetchMode.php
  92. */
  93. public function setFetchMode($mode)
  94. {
  95. $params = func_get_args();
  96. call_user_func_array([$this->_statement, 'setFetchMode'], $params);
  97. }
  98. /**
  99. * Advances the reader to the next row in a result set.
  100. * @return array the current row, false if no more row available
  101. */
  102. public function read()
  103. {
  104. return $this->_statement->fetch();
  105. }
  106. /**
  107. * Returns a single column from the next row of a result set.
  108. * @param int $columnIndex zero-based column index
  109. * @return mixed the column of the current row, false if no more rows available
  110. */
  111. public function readColumn($columnIndex)
  112. {
  113. return $this->_statement->fetchColumn($columnIndex);
  114. }
  115. /**
  116. * Returns an object populated with the next row of data.
  117. * @param string $className class name of the object to be created and populated
  118. * @param array $fields Elements of this array are passed to the constructor
  119. * @return mixed the populated object, false if no more row of data available
  120. */
  121. public function readObject($className, $fields)
  122. {
  123. return $this->_statement->fetchObject($className, $fields);
  124. }
  125. /**
  126. * Reads the whole result set into an array.
  127. * @return array the result set (each array element represents a row of data).
  128. * An empty array will be returned if the result contains no row.
  129. */
  130. public function readAll()
  131. {
  132. return $this->_statement->fetchAll();
  133. }
  134. /**
  135. * Advances the reader to the next result when reading the results of a batch of statements.
  136. * This method is only useful when there are multiple result sets
  137. * returned by the query. Not all DBMS support this feature.
  138. * @return bool Returns true on success or false on failure.
  139. */
  140. public function nextResult()
  141. {
  142. if (($result = $this->_statement->nextRowset()) !== false) {
  143. $this->_index = -1;
  144. }
  145. return $result;
  146. }
  147. /**
  148. * Closes the reader.
  149. * This frees up the resources allocated for executing this SQL statement.
  150. * Read attempts after this method call are unpredictable.
  151. */
  152. public function close()
  153. {
  154. $this->_statement->closeCursor();
  155. $this->_closed = true;
  156. }
  157. /**
  158. * whether the reader is closed or not.
  159. * @return bool whether the reader is closed or not.
  160. */
  161. public function getIsClosed()
  162. {
  163. return $this->_closed;
  164. }
  165. /**
  166. * Returns the number of rows in the result set.
  167. * Note, most DBMS may not give a meaningful count.
  168. * In this case, use "SELECT COUNT(*) FROM tableName" to obtain the number of rows.
  169. * @return int number of rows contained in the result.
  170. */
  171. public function getRowCount()
  172. {
  173. return $this->_statement->rowCount();
  174. }
  175. /**
  176. * Returns the number of rows in the result set.
  177. * This method is required by the Countable interface.
  178. * Note, most DBMS may not give a meaningful count.
  179. * In this case, use "SELECT COUNT(*) FROM tableName" to obtain the number of rows.
  180. * @return int number of rows contained in the result.
  181. */
  182. public function count()
  183. {
  184. return $this->getRowCount();
  185. }
  186. /**
  187. * Returns the number of columns in the result set.
  188. * Note, even there's no row in the reader, this still gives correct column number.
  189. * @return int the number of columns in the result set.
  190. */
  191. public function getColumnCount()
  192. {
  193. return $this->_statement->columnCount();
  194. }
  195. /**
  196. * Resets the iterator to the initial state.
  197. * This method is required by the interface [[\Iterator]].
  198. * @throws InvalidCallException if this method is invoked twice
  199. */
  200. public function rewind()
  201. {
  202. if ($this->_index < 0) {
  203. $this->_row = $this->_statement->fetch();
  204. $this->_index = 0;
  205. } else {
  206. throw new InvalidCallException('DataReader cannot rewind. It is a forward-only reader.');
  207. }
  208. }
  209. /**
  210. * Returns the index of the current row.
  211. * This method is required by the interface [[\Iterator]].
  212. * @return int the index of the current row.
  213. */
  214. public function key()
  215. {
  216. return $this->_index;
  217. }
  218. /**
  219. * Returns the current row.
  220. * This method is required by the interface [[\Iterator]].
  221. * @return mixed the current row.
  222. */
  223. public function current()
  224. {
  225. return $this->_row;
  226. }
  227. /**
  228. * Moves the internal pointer to the next row.
  229. * This method is required by the interface [[\Iterator]].
  230. */
  231. public function next()
  232. {
  233. $this->_row = $this->_statement->fetch();
  234. $this->_index++;
  235. }
  236. /**
  237. * Returns whether there is a row of data at current position.
  238. * This method is required by the interface [[\Iterator]].
  239. * @return bool whether there is a row of data at current position.
  240. */
  241. public function valid()
  242. {
  243. return $this->_row !== false;
  244. }
  245. }