PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/php-activerecord/php-activerecord/lib/SQLBuilder.php

https://gitlab.com/sulistiana/api-mrbn
PHP | 423 lines | 285 code | 72 blank | 66 comment | 39 complexity | d73184cd3c9f23a556c364ac404e6777 MD5 | raw file
  1. <?php
  2. /**
  3. * @package ActiveRecord
  4. */
  5. namespace ActiveRecord;
  6. /**
  7. * Helper class for building sql statements progmatically.
  8. *
  9. * @package ActiveRecord
  10. */
  11. class SQLBuilder
  12. {
  13. private $connection;
  14. private $operation = 'SELECT';
  15. private $table;
  16. private $select = '*';
  17. private $joins;
  18. private $order;
  19. private $limit;
  20. private $offset;
  21. private $group;
  22. private $having;
  23. private $update;
  24. // for where
  25. private $where;
  26. private $where_values = array();
  27. // for insert/update
  28. private $data;
  29. private $sequence;
  30. /**
  31. * Constructor.
  32. *
  33. * @param Connection $connection A database connection object
  34. * @param string $table Name of a table
  35. * @return SQLBuilder
  36. * @throws ActiveRecordException if connection was invalid
  37. */
  38. public function __construct($connection, $table)
  39. {
  40. if (!$connection)
  41. throw new ActiveRecordException('A valid database connection is required.');
  42. $this->connection = $connection;
  43. $this->table = $table;
  44. }
  45. /**
  46. * Returns the SQL string.
  47. *
  48. * @return string
  49. */
  50. public function __toString()
  51. {
  52. return $this->to_s();
  53. }
  54. /**
  55. * Returns the SQL string.
  56. *
  57. * @see __toString
  58. * @return string
  59. */
  60. public function to_s()
  61. {
  62. $func = 'build_' . strtolower($this->operation);
  63. return $this->$func();
  64. }
  65. /**
  66. * Returns the bind values.
  67. *
  68. * @return array
  69. */
  70. public function bind_values()
  71. {
  72. $ret = array();
  73. if ($this->data)
  74. $ret = array_values($this->data);
  75. if ($this->get_where_values())
  76. $ret = array_merge($ret,$this->get_where_values());
  77. return array_flatten($ret);
  78. }
  79. public function get_where_values()
  80. {
  81. return $this->where_values;
  82. }
  83. public function where(/* (conditions, values) || (hash) */)
  84. {
  85. $this->apply_where_conditions(func_get_args());
  86. return $this;
  87. }
  88. public function order($order)
  89. {
  90. $this->order = $order;
  91. return $this;
  92. }
  93. public function group($group)
  94. {
  95. $this->group = $group;
  96. return $this;
  97. }
  98. public function having($having)
  99. {
  100. $this->having = $having;
  101. return $this;
  102. }
  103. public function limit($limit)
  104. {
  105. $this->limit = intval($limit);
  106. return $this;
  107. }
  108. public function offset($offset)
  109. {
  110. $this->offset = intval($offset);
  111. return $this;
  112. }
  113. public function select($select)
  114. {
  115. $this->operation = 'SELECT';
  116. $this->select = $select;
  117. return $this;
  118. }
  119. public function joins($joins)
  120. {
  121. $this->joins = $joins;
  122. return $this;
  123. }
  124. public function insert($hash, $pk=null, $sequence_name=null)
  125. {
  126. if (!is_hash($hash))
  127. throw new ActiveRecordException('Inserting requires a hash.');
  128. $this->operation = 'INSERT';
  129. $this->data = $hash;
  130. if ($pk && $sequence_name)
  131. $this->sequence = array($pk,$sequence_name);
  132. return $this;
  133. }
  134. public function update($mixed)
  135. {
  136. $this->operation = 'UPDATE';
  137. if (is_hash($mixed))
  138. $this->data = $mixed;
  139. elseif (is_string($mixed))
  140. $this->update = $mixed;
  141. else
  142. throw new ActiveRecordException('Updating requires a hash or string.');
  143. return $this;
  144. }
  145. public function delete()
  146. {
  147. $this->operation = 'DELETE';
  148. $this->apply_where_conditions(func_get_args());
  149. return $this;
  150. }
  151. /**
  152. * Reverses an order clause.
  153. */
  154. public static function reverse_order($order)
  155. {
  156. if (!trim($order))
  157. return $order;
  158. $parts = explode(',',$order);
  159. for ($i=0,$n=count($parts); $i<$n; ++$i)
  160. {
  161. $v = strtolower($parts[$i]);
  162. if (strpos($v,' asc') !== false)
  163. $parts[$i] = preg_replace('/asc/i','DESC',$parts[$i]);
  164. elseif (strpos($v,' desc') !== false)
  165. $parts[$i] = preg_replace('/desc/i','ASC',$parts[$i]);
  166. else
  167. $parts[$i] .= ' DESC';
  168. }
  169. return join(',',$parts);
  170. }
  171. /**
  172. * Converts a string like "id_and_name_or_z" into a conditions value like array("id=? AND name=? OR z=?", values, ...).
  173. *
  174. * @param Connection $connection
  175. * @param $name Underscored string
  176. * @param $values Array of values for the field names. This is used
  177. * to determine what kind of bind marker to use: =?, IN(?), IS NULL
  178. * @param $map A hash of "mapped_column_name" => "real_column_name"
  179. * @return A conditions array in the form array(sql_string, value1, value2,...)
  180. */
  181. public static function create_conditions_from_underscored_string(Connection $connection, $name, &$values=array(), &$map=null)
  182. {
  183. if (!$name)
  184. return null;
  185. $parts = preg_split('/(_and_|_or_)/i',$name,-1,PREG_SPLIT_DELIM_CAPTURE);
  186. $num_values = count($values);
  187. $conditions = array('');
  188. for ($i=0,$j=0,$n=count($parts); $i<$n; $i+=2,++$j)
  189. {
  190. if ($i >= 2)
  191. $conditions[0] .= preg_replace(array('/_and_/i','/_or_/i'),array(' AND ',' OR '),$parts[$i-1]);
  192. if ($j < $num_values)
  193. {
  194. if (!is_null($values[$j]))
  195. {
  196. $bind = is_array($values[$j]) ? ' IN(?)' : '=?';
  197. $conditions[] = $values[$j];
  198. }
  199. else
  200. $bind = ' IS NULL';
  201. }
  202. else
  203. $bind = ' IS NULL';
  204. // map to correct name if $map was supplied
  205. $name = $map && isset($map[$parts[$i]]) ? $map[$parts[$i]] : $parts[$i];
  206. $conditions[0] .= $connection->quote_name($name) . $bind;
  207. }
  208. return $conditions;
  209. }
  210. /**
  211. * Like create_conditions_from_underscored_string but returns a hash of name => value array instead.
  212. *
  213. * @param string $name A string containing attribute names connected with _and_ or _or_
  214. * @param $args Array of values for each attribute in $name
  215. * @param $map A hash of "mapped_column_name" => "real_column_name"
  216. * @return array A hash of array(name => value, ...)
  217. */
  218. public static function create_hash_from_underscored_string($name, &$values=array(), &$map=null)
  219. {
  220. $parts = preg_split('/(_and_|_or_)/i',$name);
  221. $hash = array();
  222. for ($i=0,$n=count($parts); $i<$n; ++$i)
  223. {
  224. // map to correct name if $map was supplied
  225. $name = $map && isset($map[$parts[$i]]) ? $map[$parts[$i]] : $parts[$i];
  226. $hash[$name] = $values[$i];
  227. }
  228. return $hash;
  229. }
  230. /**
  231. * prepends table name to hash of field names to get around ambiguous fields when SQL builder
  232. * has joins
  233. *
  234. * @param array $hash
  235. * @return array $new
  236. */
  237. private function prepend_table_name_to_fields($hash=array())
  238. {
  239. $new = array();
  240. $table = $this->connection->quote_name($this->table);
  241. foreach ($hash as $key => $value)
  242. {
  243. $k = $this->connection->quote_name($key);
  244. $new[$table.'.'.$k] = $value;
  245. }
  246. return $new;
  247. }
  248. private function apply_where_conditions($args)
  249. {
  250. require_once 'Expressions.php';
  251. $num_args = count($args);
  252. if ($num_args == 1 && is_hash($args[0]))
  253. {
  254. $hash = is_null($this->joins) ? $args[0] : $this->prepend_table_name_to_fields($args[0]);
  255. $e = new Expressions($this->connection,$hash);
  256. $this->where = $e->to_s();
  257. $this->where_values = array_flatten($e->values());
  258. }
  259. elseif ($num_args > 0)
  260. {
  261. // if the values has a nested array then we'll need to use Expressions to expand the bind marker for us
  262. $values = array_slice($args,1);
  263. foreach ($values as $name => &$value)
  264. {
  265. if (is_array($value))
  266. {
  267. $e = new Expressions($this->connection,$args[0]);
  268. $e->bind_values($values);
  269. $this->where = $e->to_s();
  270. $this->where_values = array_flatten($e->values());
  271. return;
  272. }
  273. }
  274. // no nested array so nothing special to do
  275. $this->where = $args[0];
  276. $this->where_values = &$values;
  277. }
  278. }
  279. private function build_delete()
  280. {
  281. $sql = "DELETE FROM $this->table";
  282. if ($this->where)
  283. $sql .= " WHERE $this->where";
  284. if ($this->connection->accepts_limit_and_order_for_update_and_delete())
  285. {
  286. if ($this->order)
  287. $sql .= " ORDER BY $this->order";
  288. if ($this->limit)
  289. $sql = $this->connection->limit($sql,null,$this->limit);
  290. }
  291. return $sql;
  292. }
  293. private function build_insert()
  294. {
  295. require_once 'Expressions.php';
  296. $keys = join(',',$this->quoted_key_names());
  297. if ($this->sequence)
  298. {
  299. $sql =
  300. "INSERT INTO $this->table($keys," . $this->connection->quote_name($this->sequence[0]) .
  301. ") VALUES(?," . $this->connection->next_sequence_value($this->sequence[1]) . ")";
  302. }
  303. else
  304. $sql = "INSERT INTO $this->table($keys) VALUES(?)";
  305. $e = new Expressions($this->connection,$sql,array_values($this->data));
  306. return $e->to_s();
  307. }
  308. private function build_select()
  309. {
  310. $sql = "SELECT $this->select FROM $this->table";
  311. if ($this->joins)
  312. $sql .= ' ' . $this->joins;
  313. if ($this->where)
  314. $sql .= " WHERE $this->where";
  315. if ($this->group)
  316. $sql .= " GROUP BY $this->group";
  317. if ($this->having)
  318. $sql .= " HAVING $this->having";
  319. if ($this->order)
  320. $sql .= " ORDER BY $this->order";
  321. if ($this->limit || $this->offset)
  322. $sql = $this->connection->limit($sql,$this->offset,$this->limit);
  323. return $sql;
  324. }
  325. private function build_update()
  326. {
  327. if (strlen($this->update) > 0)
  328. $set = $this->update;
  329. else
  330. $set = join('=?, ', $this->quoted_key_names()) . '=?';
  331. $sql = "UPDATE $this->table SET $set";
  332. if ($this->where)
  333. $sql .= " WHERE $this->where";
  334. if ($this->connection->accepts_limit_and_order_for_update_and_delete())
  335. {
  336. if ($this->order)
  337. $sql .= " ORDER BY $this->order";
  338. if ($this->limit)
  339. $sql = $this->connection->limit($sql,null,$this->limit);
  340. }
  341. return $sql;
  342. }
  343. private function quoted_key_names()
  344. {
  345. $keys = array();
  346. foreach ($this->data as $key => $value)
  347. $keys[] = $this->connection->quote_name($key);
  348. return $keys;
  349. }
  350. }
  351. ?>