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.

175 lines
4.8 KiB

  1. <?php
  2. /**
  3. * @copyright NetMonsters <team@netmonsters.ru>
  4. * @link http://netmonsters.ru
  5. * @package Majestic
  6. * @subpackage db
  7. * @since 2010-02-19
  8. */
  9. /**
  10. * @property MySQLiDriver $driver
  11. * @property MySQLi_Result $result
  12. */
  13. class MySQLiStatement extends DbStatement
  14. {
  15. protected $map = null;
  16. public function bindParam($param, &$value)
  17. {
  18. if ($this->map === null) {
  19. $this->mapPlaceholders();
  20. }
  21. if (count($this->map) > 0) {
  22. if (!is_string($param) && !is_int($param)) {
  23. throw new GeneralException('Placeholder must be an integer or string');
  24. }
  25. if (is_object($value) && ! ($value instanceof DbExpr)) {
  26. throw new GeneralException('Objects excepts DbExpr not allowed.');
  27. }
  28. if (isset($this->map[$param])) {
  29. $this->params[$param] = &$value;
  30. return true;
  31. }
  32. }
  33. return false;
  34. }
  35. protected function mapPlaceholders()
  36. {
  37. $matches = array();
  38. if(preg_match_all('/(\?|:[A-z0-9_]+)/u', $this->request, $matches, PREG_OFFSET_CAPTURE)) {
  39. $noname = 0;
  40. foreach ($matches[0] as $id=>$match) {
  41. $match[2] = $matches[1][$id][0];
  42. $name = ($match[2][0] === ':') ? ltrim($match[2], ':') : $noname++;
  43. $this->map[$name]['placeholder'] = $match[0];
  44. $this->map[$name]['offset'][] = $match[1];
  45. }
  46. }
  47. }
  48. protected function assemble()
  49. {
  50. if (empty($this->map)) {
  51. return $this->request;
  52. }
  53. $query = $this->request;
  54. $placeholders = array();
  55. foreach($this->map as $name => $place) {
  56. $value = $this->driver->quote($this->params[$name]);
  57. foreach ($place['offset'] as $offset) {
  58. $placeholders[$offset] = array('placeholder' => $place['placeholder'], 'value' => $value);
  59. }
  60. }
  61. ksort($placeholders);
  62. $increment = 0;
  63. foreach($placeholders as $current_offset => $placeholder) {
  64. $offset = $current_offset + $increment;
  65. $length = mb_strlen($placeholder['placeholder']);
  66. $query = mb_substr($query, 0, $offset) . $placeholder['value'] . mb_substr($query, $offset + $length);
  67. $increment = (($increment - $length) + mb_strlen($placeholder['value']));
  68. }
  69. return $query;
  70. }
  71. /**
  72. * Fetches single row
  73. *
  74. * @param mixed $style
  75. * @return mixed
  76. * @throws GeneralException
  77. */
  78. public function fetch($style = Db::FETCH_OBJ)
  79. {
  80. if (!$this->result) {
  81. return false;
  82. }
  83. switch ($style) {
  84. case Db::FETCH_OBJ:
  85. $row = $this->result->fetch_object();
  86. break;
  87. case Db::FETCH_NUM:
  88. $row = $this->result->fetch_array(MYSQLI_NUM);
  89. break;
  90. case Db::FETCH_ASSOC:
  91. $row = $this->result->fetch_assoc();
  92. break;
  93. case Db::FETCH_BOTH:
  94. $row = $this->result->fetch_array(MYSQLI_BOTH);
  95. break;
  96. default:
  97. throw new GeneralException('Invalid fetch mode "' . $style . '" specified');
  98. }
  99. return $row;
  100. }
  101. /**
  102. * @param string $class
  103. * @return object
  104. */
  105. public function fetchObject($class = 'stdClass')
  106. {
  107. return $this->result->fetch_object($class);
  108. }
  109. /**
  110. * @return array
  111. */
  112. public function fetchPairs()
  113. {
  114. $data = array();
  115. while ($row = $this->fetch(Db::FETCH_NUM)) {
  116. $data[$row[0]] = $row[1];
  117. }
  118. return $data;
  119. }
  120. public function close()
  121. {
  122. if ($this->result !== null) {
  123. $this->result->close();
  124. $this->result = null;
  125. }
  126. }
  127. public function affectedRows()
  128. {
  129. return $this->driver->getConnection()->affected_rows;
  130. }
  131. public function numRows()
  132. {
  133. if ($this->result) {
  134. return $this->result->num_rows;
  135. }
  136. return false;
  137. }
  138. protected function driverExecute($request)
  139. {
  140. /**
  141. * @var MySQLi
  142. */
  143. $mysqli = $this->driver->getConnection();
  144. if (Config::get('PROFILER_DETAILS')) {
  145. $profiler = Profiler::getInstance()->profilerCommand('MySQL', $request);
  146. $result = $mysqli->query($request);
  147. $profiler->end();
  148. } else {
  149. $result = $mysqli->query($request);
  150. }
  151. if ($result === false) {
  152. $message = $mysqli->error . "\nQuery: \"" . $request . '"';
  153. throw new GeneralException($message, $mysqli->errno);
  154. }
  155. if ($result instanceof MySQLi_Result) {
  156. $this->result = $result;
  157. }
  158. return true;
  159. }
  160. }