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.

160 lines
4.0 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. /**
  30. * @param mixed $style
  31. * @return array
  32. */
  33. public function fetchAll($style = Db::FETCH_OBJ)
  34. {
  35. $data = array();
  36. while ($row = $this->fetch($style)) {
  37. $data[] = $row;
  38. }
  39. return $data;
  40. }
  41. /**
  42. * @param string $field
  43. */
  44. public function fetchField($field)
  45. {
  46. $row = $this->fetch(Db::FETCH_ASSOC);
  47. if (isset($row[$field])) {
  48. return $row[$field];
  49. }
  50. return false;
  51. }
  52. public function bindParam($param, &$value)
  53. {
  54. if ($this->map === null) {
  55. $this->mapPlaceholders();
  56. }
  57. if (count($this->map) > 0) {
  58. if (!is_string($param) && !is_int($param)) {
  59. throw new Exception('Placeholder must be an array or string');
  60. }
  61. if (is_object($value) && ! ($value instanceof DbExpr)) {
  62. throw new Exception('Objects excepts DbExpr not allowed.');
  63. }
  64. if (isset($this->map[$param])) {
  65. $this->params[$param] = &$value;
  66. return true;
  67. }
  68. }
  69. return false;
  70. }
  71. /**
  72. * @param array $params
  73. * @return bool
  74. */
  75. public function execute($params = null)
  76. {
  77. if (is_array($params)) {
  78. foreach ($params as $param => &$value) {
  79. $this->bindParam($param, $value);
  80. }
  81. }
  82. return $this->driverExecute($this->assemble());
  83. }
  84. protected function mapPlaceholders()
  85. {
  86. $matches = array();
  87. if(preg_match_all('/(\?|:[A-z0-9_]+)/u', $this->sql, $matches, PREG_OFFSET_CAPTURE)) {
  88. $noname = 0;
  89. foreach ($matches[0] as $id=>$match) {
  90. $match[2] = $matches[1][$id][0];
  91. $name = ($match[2][0] === ':') ? ltrim($match[2], ':') : $noname++;
  92. $this->map[$name]['placeholder'] = $match[0];
  93. $this->map[$name]['offset'][] = $match[1];
  94. }
  95. }
  96. }
  97. protected function assemble()
  98. {
  99. if (empty($this->map)) {
  100. return $this->sql;
  101. }
  102. $query = $this->sql;
  103. $placeholders = array();
  104. foreach($this->map as $name => $place) {
  105. $value = $this->driver->quote($this->params[$name]);
  106. foreach ($place['offset'] as $offset) {
  107. $placeholders[$offset] = array('placeholder' => $place['placeholder'], 'value' => $value);
  108. }
  109. }
  110. ksort($placeholders);
  111. $increment = 0;
  112. foreach($placeholders as $current_offset => $placeholder) {
  113. $offset = $current_offset + $increment;
  114. $length = mb_strlen($placeholder['placeholder']);
  115. $query = mb_substr($query, 0, $offset) . $placeholder['value'] . mb_substr($query, $offset + $length);
  116. $increment = (($increment - $length) + mb_strlen($placeholder['value']));
  117. }
  118. return $query;
  119. }
  120. public function __destruct()
  121. {
  122. $this->close();
  123. }
  124. /* Abstract methods */
  125. abstract public function fetch($style = Db::FETCH_OBJ);
  126. abstract public function fetchObject($class = 'stdClass');
  127. abstract public function close();
  128. /**
  129. * @return int
  130. */
  131. abstract public function affectedRows();
  132. abstract public function numRows();
  133. /**
  134. * @return bool
  135. */
  136. abstract protected function driverExecute($sql);
  137. }