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.

185 lines
4.5 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. * @version SVN: $Id$
  9. * @filesource $URL$
  10. */
  11. abstract class DbStatement
  12. {
  13. /**
  14. * @var DbDriver
  15. */
  16. protected $driver;
  17. /**
  18. * @var string
  19. */
  20. protected $sql;
  21. protected $map = null;
  22. protected $params = array();
  23. protected $result;
  24. public function __construct($driver, $sql)
  25. {
  26. $this->driver = $driver;
  27. $this->sql = $sql;
  28. }
  29. public function bindParam($param, &$value)
  30. {
  31. if ($this->map === null) {
  32. $this->mapPlaceholders();
  33. }
  34. if (count($this->map) > 0) {
  35. if (!is_string($param) && !is_int($param)) {
  36. throw new Exception('Placeholder must be an array or string');
  37. }
  38. if (is_object($value) && ! ($value instanceof DbExpr)) {
  39. throw new Exception('Objects excepts DbExpr not allowed.');
  40. }
  41. if (isset($this->map[$param])) {
  42. $this->params[$param] = &$value;
  43. return true;
  44. }
  45. }
  46. return false;
  47. }
  48. /**
  49. * @param array $params
  50. * @return bool
  51. */
  52. public function execute($params = null)
  53. {
  54. if (is_array($params)) {
  55. foreach ($params as $param => &$value) {
  56. $this->bindParam($param, $value);
  57. }
  58. }
  59. return $this->driverExecute($this->assemble());
  60. }
  61. protected function mapPlaceholders()
  62. {
  63. $matches = array();
  64. if(preg_match_all('/(\?|:[A-z0-9_]+)/u', $this->sql, $matches, PREG_OFFSET_CAPTURE)) {
  65. $noname = 0;
  66. foreach ($matches[0] as $id=>$match) {
  67. $match[2] = $matches[1][$id][0];
  68. $name = ($match[2][0] === ':') ? ltrim($match[2], ':') : $noname++;
  69. $this->map[$name]['placeholder'] = $match[0];
  70. $this->map[$name]['offset'][] = $match[1];
  71. }
  72. }
  73. }
  74. protected function assemble()
  75. {
  76. if (empty($this->map)) {
  77. return $this->sql;
  78. }
  79. $query = $this->sql;
  80. $placeholders = array();
  81. foreach($this->map as $name => $place) {
  82. $value = $this->driver->quote($this->params[$name]);
  83. foreach ($place['offset'] as $offset) {
  84. $placeholders[$offset] = array('placeholder' => $place['placeholder'], 'value' => $value);
  85. }
  86. }
  87. ksort($placeholders);
  88. $increment = 0;
  89. foreach($placeholders as $current_offset => $placeholder) {
  90. $offset = $current_offset + $increment;
  91. $length = mb_strlen($placeholder['placeholder']);
  92. $query = mb_substr($query, 0, $offset) . $placeholder['value'] . mb_substr($query, $offset + $length);
  93. $increment = (($increment - $length) + mb_strlen($placeholder['value']));
  94. }
  95. return $query;
  96. }
  97. public function __destruct()
  98. {
  99. $this->close();
  100. }
  101. /**
  102. * @param mixed $style
  103. * @return array
  104. */
  105. public function fetchAll($style = Db::FETCH_OBJ)
  106. {
  107. $data = array();
  108. while ($row = $this->fetch($style)) {
  109. $data[] = $row;
  110. }
  111. return $data;
  112. }
  113. /**
  114. * @param string $field
  115. */
  116. public function fetchColumn($field)
  117. {
  118. $data = array();
  119. while ($row = $this->fetch(Db::FETCH_ASSOC)) {
  120. $data[] = $row[$field];
  121. }
  122. return $data;
  123. }
  124. /**
  125. * @param string $field
  126. */
  127. public function fetchField($field)
  128. {
  129. $row = $this->fetch(Db::FETCH_ASSOC);
  130. if (isset($row[$field])) {
  131. return $row[$field];
  132. }
  133. return false;
  134. }
  135. /**
  136. * @return array
  137. */
  138. public function fetchPairs()
  139. {
  140. $data = array();
  141. while ($row = $this->fetch(Db::FETCH_NUM)) {
  142. $data[$row[0]] = $row[1];
  143. }
  144. return $data;
  145. }
  146. /* Abstract methods */
  147. abstract public function fetch($style = Db::FETCH_OBJ);
  148. abstract public function fetchObject($class = 'stdClass');
  149. abstract public function close();
  150. /**
  151. * @return int
  152. */
  153. abstract public function affectedRows();
  154. abstract public function numRows();
  155. /**
  156. * @return bool
  157. */
  158. abstract protected function driverExecute($sql);
  159. }