1: <?php /* vim: set expandtab : */
2: /**
3: * Class for making custom expressions for AMysql value binding.
4: * This is what you should use to be able to add specific kinds
5: * of purposely unquoted, non-numeric values into prepared
6: * statements, such as being able to call mysql functions to
7: * set values.
8: *
9: * This class can be extended and is recommended to do so if you want
10: * custom expressions; in which case you would have to manually
11: * instatiate that class each time, rather than using AMysql::expr()
12: *
13: * Expression codes 0-999 should be considered reserved to this class;
14: * if you extend this class, please use at least 1000 as expression code ints.
15: *
16: * Visit https://github.com/amcsi/amysql
17: * @author Szerémi Attila
18: * @created 2011.06.10. 13:26:56
19: * @license MIT License; http://www.opensource.org/licenses/mit-license.php
20: **/
21: class AMysql_Expr
22: {
23:
24: public $prepared;
25:
26: public $amysql;
27:
28: /**
29: * Literal string
30: *
31: * e.g. $currentTimestampBind = new AMysql_Expr(
32: * AMysql_Expr::LITERAL, 'CURRENT_TIMESPAMP'
33: * );
34: * // or
35: * $currentTimestampBind = new AMysql_Expr('CURRENT_TIMESTAMP');
36: **/
37: const LITERAL = 0;
38: const EXPR_LITERAL = 0;
39:
40: /**
41: * IN() function. In this case, the 2nd parameter is the table name, the
42: * third is the array of values
43: *
44: * e.g.
45: * $idIn = new AMysql_Expr(AMysql_Expr::COLUMN_IN, 'id', array (
46: * '3', '4', '6'
47: * ));
48: **/
49: const COLUMN_IN = 1;
50: const EXPR_COLUMN_IN = 1;
51:
52: /**
53: * Escapes wildcards for a LIKE statement.
54: * The second parameter has to be the string to escape, and
55: * the third is optional, and is the sprintf format of the string,
56: * where literal wildcards should appear. The default is %%%s%%,
57: * where the input of "something" will result in:
58: * "'%something%' ESCAPE '='"
59: **/
60: const ESCAPE_LIKE = 2;
61:
62: /**
63: * Escapes a table name and encloses it in quotes. The second parameter is the table name.
64: */
65: const ESCAPE_TABLE = 3;
66: const EXPR_TABLE = 3;
67:
68: /**
69: * Escapes a column name and encloses it in quotes. The second parameter is the column name.
70: */
71: const ESCAPE_COLUMN = 4;
72: const EXPR_COLUMN = 4;
73:
74: /**
75: * @constructor
76: * This constructor accepts different parameters in different cases.
77: * Before everything, if the first parameter is an AMysql instance, it
78: * is saved, and is shifted from the arguments array, and the following
79: * applies in either case:
80: * The first parameter is mandatory: the one that gives the type of
81: * expression. The types of expressions can be found as constants on this
82: * class, and their documentation can be found above each constant type
83: * declaration.
84: *
85: * @params ...$variadic
86: *
87: * In case of a literal string, you can just pass the literal string as
88: * the only parameter.
89: **/
90: public function __construct(/* args */)
91: {
92: $args = func_get_args();
93: if ($args[0] instanceof AMysql_Abstract) {
94: $this->amysql = array_shift($args);
95: }
96: if ($args) {
97: call_user_func_array(array($this, 'set'), $args);
98: }
99: }
100:
101: public function set()
102: {
103: $args = func_get_args();
104: // literal
105: if (is_string($args[0])) {
106: $prepared = $args[0];
107: } else {
108: $prepared = $this->setByArray($args);
109: }
110: $this->prepared = $prepared;
111: }
112:
113: /**
114: * This method performs the logic of determining what the
115: * expression should result in.
116: * When extending this class, it is recommended that you
117: * override this method, determine whether the expression code
118: * is something that you want to handle, otherwise call and return
119: * the parent's setByArray() method
120: *
121: * @param array $args The first index should contain the type
122: * of expression to use, the rest depends on
123: * the type of expression. See the class
124: * constants for available types.
125: * @access protected
126: * @return string The literal string to use
127: */
128: protected function setByArray(array $args)
129: {
130: switch ($args[0]) {
131: case self::EXPR_LITERAL:
132: $prepared = $args[1];
133: break;
134: case self::EXPR_COLUMN_IN:
135: $prepared = '';
136: if ($args[2]) {
137: foreach ($args[2] as &$val) {
138: $val = $this->amysql->escape($val);
139: }
140: $prepared = ' ' . $this->escapeColumn($args[1]) . ' IN ' .
141: '(' . join(', ', $args[2]) . ') ';
142: } else {
143: // If the array is empty, don't break the WHERE syntax
144: $prepared = 0;
145: }
146: break;
147: case self::ESCAPE_LIKE:
148: $format = '%%%s%%';
149: if (!empty($args[2])) {
150: $format = $args[2];
151: }
152: $likeEscaped = AMysql_Abstract::escapeLike($args[1]);
153: $formatted = sprintf($format, $likeEscaped);
154: if ($this->amysql) {
155: if ('mysqli' == $this->amysql->isMysqli) {
156: $escaped = $this->amysql->link->real_escape_string(
157: $formatted
158: );
159: } else {
160: $escaped = mysql_real_escape_string(
161: $formatted,
162: $this->amysql->link
163: );
164: }
165: } else {
166: $escaped = mysql_real_escape_string($formatted);
167: }
168: $prepared = "'$escaped'";
169: $prepared .= " ESCAPE '='";
170: break;
171: case self::ESCAPE_TABLE:
172: $prepared = $this->amysql->escapeTable($args[1]);
173: break;
174: case self::ESCAPE_COLUMN:
175: $prepared = $this->amysql->escapeColumn($args[1]);
176: break;
177: default:
178: throw new Exception("No such expression type: `$args[0]`.");
179: break;
180: }
181: return $prepared;
182: }
183:
184: public function escapeTable($table)
185: {
186: if ($this->amysql instanceof AMysql_Abstract) {
187: return $this->amysql->escapeTable($table);
188: }
189: return AMysql_Abstract::escapeIdentifier($table);
190: }
191:
192: public function escapeColumn($column)
193: {
194: if ($this->amysql instanceof AMysql_Abstract) {
195: return $this->amysql->escapeColumn($column);
196: }
197: return AMysql_Abstract::escapeIdentifier($column);
198: }
199:
200: public function toString()
201: {
202: if (!isset($this->prepared)) {
203: throw new Exception("No prepared string for mysql expression.");
204: }
205: return (string) $this->prepared;
206: }
207:
208: /**
209: * Returns the literal string this expression should resolve to.
210: *
211: * @access public
212: * @return string
213: */
214: public function __toString()
215: {
216: return (string) $this->toString();
217: }
218: }
219: