BufferedStatement.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Database\Statement;
  16. use Cake\Database\StatementInterface;
  17. use Cake\Database\TypeConverterTrait;
  18. use Iterator;
  19. /**
  20. * A statement decorator that implements buffered results.
  21. *
  22. * This statement decorator will save fetched results in memory, allowing
  23. * the iterator to be rewound and reused.
  24. */
  25. class BufferedStatement implements Iterator, StatementInterface
  26. {
  27. use TypeConverterTrait;
  28. /**
  29. * If true, all rows were fetched
  30. *
  31. * @var bool
  32. */
  33. protected $_allFetched = false;
  34. /**
  35. * The decorated statement
  36. *
  37. * @var \Cake\Database\StatementInterface
  38. */
  39. protected $statement;
  40. /**
  41. * The driver for the statement
  42. *
  43. * @var \Cake\Database\DriverInterface
  44. */
  45. protected $_driver;
  46. /**
  47. * The in-memory cache containing results from previous iterators
  48. *
  49. * @var array
  50. */
  51. protected $buffer = [];
  52. /**
  53. * Whether or not this statement has already been executed
  54. *
  55. * @var bool
  56. */
  57. protected $_hasExecuted = false;
  58. /**
  59. * The current iterator index.
  60. *
  61. * @var int
  62. */
  63. protected $index = 0;
  64. /**
  65. * Constructor
  66. *
  67. * @param \Cake\Database\StatementInterface $statement Statement implementation such as PDOStatement
  68. * @param \Cake\Database\Driver $driver Driver instance
  69. */
  70. public function __construct(StatementInterface $statement, $driver)
  71. {
  72. $this->statement = $statement;
  73. $this->_driver = $driver;
  74. }
  75. /**
  76. * Magic getter to return $queryString as read-only.
  77. *
  78. * @param string $property internal property to get
  79. * @return mixed
  80. */
  81. public function __get($property)
  82. {
  83. if ($property === 'queryString') {
  84. return $this->statement->queryString;
  85. }
  86. }
  87. /**
  88. * {@inheritDoc}
  89. */
  90. public function bindValue($column, $value, $type = 'string')
  91. {
  92. $this->statement->bindValue($column, $value, $type);
  93. }
  94. /**
  95. * {@inheritDoc}
  96. */
  97. public function closeCursor()
  98. {
  99. $this->statement->closeCursor();
  100. }
  101. /**
  102. * {@inheritDoc}
  103. */
  104. public function columnCount()
  105. {
  106. return $this->statement->columnCount();
  107. }
  108. /**
  109. * {@inheritDoc}
  110. */
  111. public function errorCode()
  112. {
  113. return $this->statement->errorCode();
  114. }
  115. /**
  116. * {@inheritDoc}
  117. */
  118. public function errorInfo()
  119. {
  120. return $this->statement->errorInfo();
  121. }
  122. /**
  123. * {@inheritDoc}
  124. */
  125. public function execute($params = null)
  126. {
  127. $this->_reset();
  128. $this->_hasExecuted = true;
  129. return $this->statement->execute($params);
  130. }
  131. /**
  132. * {@inheritDoc}
  133. */
  134. public function fetchColumn($position)
  135. {
  136. $result = $this->fetch(static::FETCH_TYPE_NUM);
  137. if (isset($result[$position])) {
  138. return $result[$position];
  139. }
  140. return false;
  141. }
  142. /**
  143. * Statements can be passed as argument for count() to return the number
  144. * for affected rows from last execution.
  145. *
  146. * @return int
  147. */
  148. public function count()
  149. {
  150. return $this->rowCount();
  151. }
  152. /**
  153. * {@inheritDoc}
  154. */
  155. public function bind($params, $types)
  156. {
  157. $this->statement->bind($params, $types);
  158. }
  159. /**
  160. * {@inheritDoc}
  161. */
  162. public function lastInsertId($table = null, $column = null)
  163. {
  164. return $this->statement->lastInsertId($table, $column);
  165. }
  166. /**
  167. * {@inheritDoc}
  168. *
  169. * @param string $type The type to fetch.
  170. * @return array|false
  171. */
  172. public function fetch($type = self::FETCH_TYPE_NUM)
  173. {
  174. if ($this->_allFetched) {
  175. $row = false;
  176. if (isset($this->buffer[$this->index])) {
  177. $row = $this->buffer[$this->index];
  178. }
  179. $this->index += 1;
  180. if ($row && $type === static::FETCH_TYPE_NUM) {
  181. return array_values($row);
  182. }
  183. return $row;
  184. }
  185. $record = $this->statement->fetch($type);
  186. if ($record === false) {
  187. $this->_allFetched = true;
  188. $this->statement->closeCursor();
  189. return false;
  190. }
  191. $this->buffer[] = $record;
  192. return $record;
  193. }
  194. /**
  195. * {@inheritdoc}
  196. */
  197. public function fetchAssoc()
  198. {
  199. $result = $this->fetch(static::FETCH_TYPE_ASSOC);
  200. return $result ?: [];
  201. }
  202. /**
  203. * {@inheritDoc}
  204. *
  205. * @param string $type The type to fetch.
  206. * @return array
  207. */
  208. public function fetchAll($type = self::FETCH_TYPE_NUM)
  209. {
  210. if ($this->_allFetched) {
  211. return $this->buffer;
  212. }
  213. $results = $this->statement->fetchAll($type);
  214. if ($results !== false) {
  215. $this->buffer = array_merge($this->buffer, $results);
  216. }
  217. $this->_allFetched = true;
  218. $this->statement->closeCursor();
  219. return $this->buffer;
  220. }
  221. /**
  222. * {@inheritDoc}
  223. */
  224. public function rowCount()
  225. {
  226. if (!$this->_allFetched) {
  227. $this->fetchAll(static::FETCH_TYPE_ASSOC);
  228. }
  229. return count($this->buffer);
  230. }
  231. /**
  232. * Reset all properties
  233. *
  234. * @return void
  235. */
  236. protected function _reset()
  237. {
  238. $this->buffer = [];
  239. $this->_allFetched = false;
  240. $this->index = 0;
  241. }
  242. /**
  243. * Returns the current key in the iterator
  244. *
  245. * @return mixed
  246. */
  247. public function key()
  248. {
  249. return $this->index;
  250. }
  251. /**
  252. * Returns the current record in the iterator
  253. *
  254. * @return mixed
  255. */
  256. public function current()
  257. {
  258. return $this->buffer[$this->index];
  259. }
  260. /**
  261. * Rewinds the collection
  262. *
  263. * @return void
  264. */
  265. public function rewind()
  266. {
  267. $this->index = 0;
  268. }
  269. /**
  270. * Returns whether or not the iterator has more elements
  271. *
  272. * @return bool
  273. */
  274. public function valid()
  275. {
  276. $old = $this->index;
  277. $row = $this->fetch(self::FETCH_TYPE_ASSOC);
  278. // Restore the index as fetch() increments during
  279. // the cache scenario.
  280. $this->index = $old;
  281. return $row !== false;
  282. }
  283. /**
  284. * Advances the iterator pointer to the next element
  285. *
  286. * @return void
  287. */
  288. public function next()
  289. {
  290. $this->index += 1;
  291. }
  292. /**
  293. * Get the wrapped statement
  294. *
  295. * @return \Cake\Database\StatementInterface
  296. */
  297. public function getInnerStatement()
  298. {
  299. return $this->statement;
  300. }
  301. }