Expressions.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <?php
  2. /**
  3. * @package ActiveRecord
  4. */
  5. namespace ActiveRecord;
  6. /**
  7. * Templating like class for building SQL statements.
  8. *
  9. * Examples:
  10. * 'name = :name AND author = :author'
  11. * 'id = IN(:ids)'
  12. * 'id IN(:subselect)'
  13. *
  14. * @package ActiveRecord
  15. */
  16. class Expressions
  17. {
  18. const ParameterMarker = '?';
  19. private $expressions;
  20. private $values = array();
  21. private $connection;
  22. public function __construct($connection, $expressions=null /* [, $values ... ] */)
  23. {
  24. $values = null;
  25. $this->connection = $connection;
  26. if (is_array($expressions))
  27. {
  28. $glue = func_num_args() > 2 ? func_get_arg(2) : ' AND ';
  29. list($expressions,$values) = $this->build_sql_from_hash($expressions,$glue);
  30. }
  31. if ($expressions != '')
  32. {
  33. if (!$values)
  34. $values = array_slice(func_get_args(),2);
  35. $this->values = $values;
  36. $this->expressions = $expressions;
  37. }
  38. }
  39. /**
  40. * Bind a value to the specific one based index. There must be a bind marker
  41. * for each value bound or to_s() will throw an exception.
  42. */
  43. public function bind($parameter_number, $value)
  44. {
  45. if ($parameter_number <= 0)
  46. throw new ExpressionsException("Invalid parameter index: $parameter_number");
  47. $this->values[$parameter_number-1] = $value;
  48. }
  49. public function bind_values($values)
  50. {
  51. $this->values = $values;
  52. }
  53. /**
  54. * Returns all the values currently bound.
  55. */
  56. public function values()
  57. {
  58. return $this->values;
  59. }
  60. /**
  61. * Returns the connection object.
  62. */
  63. public function get_connection()
  64. {
  65. return $this->connection;
  66. }
  67. /**
  68. * Sets the connection object. It is highly recommended to set this so we can
  69. * use the adapter's native escaping mechanism.
  70. *
  71. * @param string $connection a Connection instance
  72. */
  73. public function set_connection($connection)
  74. {
  75. $this->connection = $connection;
  76. }
  77. public function to_s($substitute=false, &$options=null)
  78. {
  79. if (!$options) $options = array();
  80. $values = array_key_exists('values',$options) ? $options['values'] : $this->values;
  81. $ret = "";
  82. $replace = array();
  83. $num_values = count($values);
  84. $len = strlen($this->expressions);
  85. $quotes = 0;
  86. for ($i=0,$n=strlen($this->expressions),$j=0; $i<$n; ++$i)
  87. {
  88. $ch = $this->expressions[$i];
  89. if ($ch == self::ParameterMarker)
  90. {
  91. if ($quotes % 2 == 0)
  92. {
  93. if ($j > $num_values-1)
  94. throw new ExpressionsException("No bound parameter for index $j");
  95. $ch = $this->substitute($values,$substitute,$i,$j++);
  96. }
  97. }
  98. elseif ($ch == '\'' && $i > 0 && $this->expressions[$i-1] != '\\')
  99. ++$quotes;
  100. $ret .= $ch;
  101. }
  102. return $ret;
  103. }
  104. private function build_sql_from_hash(&$hash, $glue)
  105. {
  106. $sql = $g = "";
  107. foreach ($hash as $name => $value)
  108. {
  109. if ($this->connection)
  110. $name = $this->connection->quote_name($name);
  111. if (is_array($value))
  112. $sql .= "$g$name IN(?)";
  113. elseif (is_null($value))
  114. $sql .= "$g$name IS ?";
  115. else
  116. $sql .= "$g$name=?";
  117. $g = $glue;
  118. }
  119. return array($sql,array_values($hash));
  120. }
  121. private function substitute(&$values, $substitute, $pos, $parameter_index)
  122. {
  123. $value = $values[$parameter_index];
  124. if (is_array($value))
  125. {
  126. if ($substitute)
  127. {
  128. $ret = '';
  129. for ($i=0,$n=count($value); $i<$n; ++$i)
  130. $ret .= ($i > 0 ? ',' : '') . $this->stringify_value($value[$i]);
  131. return $ret;
  132. }
  133. return join(',',array_fill(0,count($value),self::ParameterMarker));
  134. }
  135. if ($substitute)
  136. return $this->stringify_value($value);
  137. return $this->expressions[$pos];
  138. }
  139. private function stringify_value($value)
  140. {
  141. if (is_null($value))
  142. return "NULL";
  143. return is_string($value) ? $this->quote_string($value) : $value;
  144. }
  145. private function quote_string($value)
  146. {
  147. if ($this->connection)
  148. return $this->connection->escape($value);
  149. return "'" . str_replace("'","''",$value) . "'";
  150. }
  151. }
  152. ?>