/library/Zend/Db/Adapter/Driver/Mysqli/Result.php

https://github.com/necrogami/zf2 · PHP · 307 lines · 160 code · 37 blank · 110 comment · 31 complexity · d9a0ceb073f8c06ab436dbbc0e04af4c MD5 · raw file

  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. * @package Zend_Db
  9. */
  10. namespace Zend\Db\Adapter\Driver\Mysqli;
  11. use Zend\Db\Adapter\Driver\ResultInterface,
  12. Zend\Db\Adapter\Exception;
  13. /**
  14. * @category Zend
  15. * @package Zend_Db
  16. * @subpackage Adapter
  17. */
  18. class Result implements \Iterator, ResultInterface
  19. {
  20. /**
  21. * @var \mysqli|\mysqli_result|\mysqli_stmt
  22. */
  23. protected $resource = null;
  24. /**
  25. * @var bool
  26. */
  27. protected $isBuffered = null;
  28. /**
  29. * Cursor position
  30. * @var int
  31. */
  32. protected $position = 0;
  33. /**
  34. * Number of known rows
  35. * @var int
  36. */
  37. protected $numberOfRows = -1;
  38. /**
  39. * Is the current() operation already complete for this pointer position?
  40. * @var bool
  41. */
  42. protected $currentComplete = false;
  43. /**
  44. * @var bool
  45. */
  46. protected $nextComplete = false;
  47. /**
  48. * @var bool
  49. */
  50. protected $currentData = false;
  51. /**
  52. *
  53. * @var array
  54. */
  55. protected $statementBindValues = array('keys' => null, 'values' => array());
  56. /**
  57. * @var mixed
  58. */
  59. protected $generatedValue = null;
  60. /**
  61. * Initialize
  62. * @param mixed $resource
  63. * @param mixed $generatedValue
  64. * @param bool|null $isBuffered
  65. * @return Result
  66. */
  67. public function initialize($resource, $generatedValue, $isBuffered = null)
  68. {
  69. if (!$resource instanceof \mysqli && !$resource instanceof \mysqli_result && !$resource instanceof \mysqli_stmt) {
  70. throw new Exception\InvalidArgumentException('Invalid resource provided.');
  71. }
  72. if ($isBuffered !== null) {
  73. $this->isBuffered = $isBuffered;
  74. } else {
  75. if ($resource instanceof \mysqli || $resource instanceof \mysqli_result
  76. || $resource instanceof \mysqli_stmt && $resource->num_rows != 0) {
  77. $this->isBuffered = true;
  78. }
  79. }
  80. $this->resource = $resource;
  81. $this->generatedValue = $generatedValue;
  82. return $this;
  83. }
  84. /**
  85. * Force buffering
  86. * @throws Exception\RuntimeException
  87. */
  88. public function buffer()
  89. {
  90. if ($this->resource instanceof \mysqli_stmt && $this->isBuffered !== true) {
  91. if ($this->position > 0) {
  92. throw new Exception\RuntimeException('Cannot buffer a result set that has started iteration.');
  93. }
  94. $this->resource->store_result();
  95. $this->isBuffered = true;
  96. }
  97. }
  98. /**
  99. * Return the resource
  100. * @return mixed
  101. */
  102. public function getResource()
  103. {
  104. return $this->resource;
  105. }
  106. /**
  107. * Is query result?
  108. *
  109. * @return boolean
  110. */
  111. public function isQueryResult()
  112. {
  113. return ($this->resource->field_count > 0);
  114. }
  115. /**
  116. * Get affected rows
  117. * @return integer
  118. */
  119. public function getAffectedRows()
  120. {
  121. if ($this->resource instanceof \mysqli || $this->resource instanceof \mysqli_stmt) {
  122. return $this->resource->affected_rows;
  123. } else {
  124. return $this->resource->num_rows;
  125. }
  126. }
  127. /**
  128. * Current
  129. * @return mixed
  130. */
  131. public function current()
  132. {
  133. if ($this->currentComplete) {
  134. return $this->currentData;
  135. }
  136. if ($this->resource instanceof \mysqli_stmt) {
  137. $this->loadDataFromMysqliStatement();
  138. return $this->currentData;
  139. } else {
  140. $this->loadFromMysqliResult();
  141. return $this->currentData;
  142. }
  143. }
  144. /**
  145. * Mysqli's binding and returning of statement values
  146. *
  147. * Mysqli requires you to bind variables to the extension in order to
  148. * get data out. These values have to be references:
  149. * @see http://php.net/manual/en/mysqli-stmt.bind-result.php
  150. *
  151. * @throws \RuntimeException
  152. * @return bool
  153. */
  154. protected function loadDataFromMysqliStatement()
  155. {
  156. $data = null;
  157. // build the default reference based bind strutcure, if it does not already exist
  158. if ($this->statementBindValues['keys'] === null) {
  159. $this->statementBindValues['keys'] = array();
  160. $resultResource = $this->resource->result_metadata();
  161. foreach ($resultResource->fetch_fields() as $col) {
  162. $this->statementBindValues['keys'][] = $col->name;
  163. }
  164. $this->statementBindValues['values'] = array_fill(0, count($this->statementBindValues['keys']), null);
  165. $refs = array();
  166. foreach ($this->statementBindValues['values'] as $i => &$f) {
  167. $refs[$i] = &$f;
  168. }
  169. call_user_func_array(array($this->resource, 'bind_result'), $this->statementBindValues['values']);
  170. }
  171. if (($r = $this->resource->fetch()) === null) {
  172. $this->resource->close();
  173. return false;
  174. } elseif ($r === false) {
  175. throw new Exception\RuntimeException($this->resource->error);
  176. }
  177. // dereference
  178. for ($i = 0; $i < count($this->statementBindValues['keys']); $i++) {
  179. $this->currentData[$this->statementBindValues['keys'][$i]] = $this->statementBindValues['values'][$i];
  180. }
  181. $this->currentComplete = true;
  182. $this->nextComplete = true;
  183. $this->position++;
  184. return true;
  185. }
  186. /**
  187. * Load from mysqli result
  188. *
  189. * @return boolean
  190. */
  191. protected function loadFromMysqliResult()
  192. {
  193. $this->currentData = null;
  194. if (($data = $this->resource->fetch_assoc()) === null) {
  195. return false;
  196. }
  197. $this->position++;
  198. $this->currentData = $data;
  199. $this->currentComplete = true;
  200. $this->nextComplete = true;
  201. $this->position++;
  202. return true;
  203. }
  204. /**
  205. * Next
  206. */
  207. public function next()
  208. {
  209. $this->currentComplete = false;
  210. if ($this->nextComplete == false) {
  211. $this->position++;
  212. }
  213. $this->nextComplete = false;
  214. }
  215. /**
  216. * Key
  217. * @return mixed
  218. */
  219. public function key()
  220. {
  221. return $this->position;
  222. }
  223. /**
  224. * Rewind
  225. */
  226. public function rewind()
  227. {
  228. if ($this->position !== 0) {
  229. if ($this->isBuffered === false) {
  230. throw new Exception\RuntimeException('Unbuffered results cannot be rewound for multiple iterations');
  231. }
  232. }
  233. $this->resource->data_seek(0); // works for both mysqli_result & mysqli_stmt
  234. $this->currentComplete = false;
  235. $this->position = 0;
  236. }
  237. /**
  238. * Valid
  239. * @return boolean
  240. */
  241. public function valid()
  242. {
  243. if ($this->currentComplete) {
  244. return true;
  245. }
  246. if ($this->resource instanceof \mysqli_stmt) {
  247. return $this->loadDataFromMysqliStatement();
  248. } else {
  249. return $this->loadFromMysqliResult();
  250. }
  251. }
  252. /**
  253. * Count
  254. * @return integer
  255. */
  256. public function count()
  257. {
  258. if ($this->isBuffered === false) {
  259. throw new Exception\RuntimeException('Row count is not availabe in unbuffered result sets.');
  260. }
  261. return $this->resource->num_rows;
  262. }
  263. /**
  264. * @return mixed|null
  265. */
  266. public function getGeneratedValue()
  267. {
  268. return $this->generatedValue;
  269. }
  270. }