1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Database;
9:
10: use Nette;
11:
12:
13: 14: 15: 16: 17:
18: class SqlPreprocessor extends Nette\Object
19: {
20:
21: private $connection;
22:
23:
24: private $driver;
25:
26:
27: private $params;
28:
29:
30: private $remaining;
31:
32:
33: private $counter;
34:
35:
36: private $arrayMode;
37:
38:
39: private $arrayModes;
40:
41:
42: public function __construct(Connection $connection)
43: {
44: $this->connection = $connection;
45: $this->driver = $connection->getSupplementalDriver();
46: $this->arrayModes = array(
47: 'INSERT' => $this->driver->isSupported(ISupplementalDriver::SUPPORT_MULTI_INSERT_AS_SELECT) ? 'select' : 'values',
48: 'REPLACE' => 'values',
49: 'UPDATE' => 'assoc',
50: 'WHERE' => 'and',
51: 'HAVING' => 'and',
52: 'ORDER BY' => 'order',
53: 'GROUP BY' => 'order',
54: );
55: }
56:
57:
58: 59: 60: 61:
62: public function process($params)
63: {
64: $this->params = $params;
65: $this->counter = 0;
66: $this->remaining = array();
67: $this->arrayMode = 'assoc';
68: $res = array();
69:
70: while ($this->counter < count($params)) {
71: $param = $params[$this->counter++];
72:
73: if (($this->counter === 2 && count($params) === 2) || !is_scalar($param)) {
74: $res[] = $this->formatValue($param);
75: } else {
76: $res[] = Nette\Utils\Strings::replace(
77: $param,
78: '~\'.*?\'|".*?"|\?|\b(?:INSERT|REPLACE|UPDATE|WHERE|HAVING|ORDER BY|GROUP BY)\b|/\*.*?\*/|--[^\n]*~si',
79: array($this, 'callback')
80: );
81: }
82: }
83:
84: return array(implode(' ', $res), $this->remaining);
85: }
86:
87:
88:
89: public function callback($m)
90: {
91: $m = $m[0];
92: if ($m[0] === "'" || $m[0] === '"' || $m[0] === '/' || $m[0] === '-') {
93: return $m;
94:
95: } elseif ($m === '?') {
96: if ($this->counter >= count($this->params)) {
97: throw new Nette\InvalidArgumentException('There are more placeholders than passed parameters.');
98: }
99: return $this->formatValue($this->params[$this->counter++]);
100:
101: } else {
102: $this->arrayMode = $this->arrayModes[strtoupper($m)];
103: return $m;
104: }
105: }
106:
107:
108: private function formatValue($value)
109: {
110: if (is_string($value)) {
111: if (strlen($value) > 20) {
112: $this->remaining[] = $value;
113: return '?';
114:
115: } else {
116: return $this->connection->quote($value);
117: }
118:
119: } elseif (is_int($value)) {
120: return (string) $value;
121:
122: } elseif (is_float($value)) {
123: return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.');
124:
125: } elseif (is_bool($value)) {
126: return $this->driver->formatBool($value);
127:
128: } elseif ($value === NULL) {
129: return 'NULL';
130:
131: } elseif ($value instanceof Table\IRow) {
132: return $value->getPrimary();
133:
134: } elseif (is_array($value) || $value instanceof \Traversable) {
135: $vx = $kx = array();
136:
137: if ($value instanceof \Traversable) {
138: $value = iterator_to_array($value);
139: }
140:
141: if (array_key_exists(0, $value)) {
142: foreach ($value as $v) {
143: if (is_array($v) && isset($v[0])) {
144: $vx[] = '(' . $this->formatValue($v) . ')';
145: } else {
146: $vx[] = $this->formatValue($v);
147: }
148: }
149: if ($this->arrayMode === 'union') {
150: return implode(' ', $vx);
151: }
152: return implode(', ', $vx);
153:
154: } elseif ($this->arrayMode === 'values') {
155: $this->arrayMode = 'multi';
156: foreach ($value as $k => $v) {
157: $kx[] = $this->driver->delimite($k);
158: $vx[] = $this->formatValue($v);
159: }
160: return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')';
161:
162: } elseif ($this->arrayMode === 'select') {
163: $this->arrayMode = 'union';
164: foreach ($value as $k => $v) {
165: $kx[] = $this->driver->delimite($k);
166: $vx[] = $this->formatValue($v);
167: }
168: return '(' . implode(', ', $kx) . ') SELECT ' . implode(', ', $vx);
169:
170: } elseif ($this->arrayMode === 'assoc') {
171: foreach ($value as $k => $v) {
172: if (substr($k, -1) === '=') {
173: $k2 = $this->driver->delimite(substr($k, 0, -2));
174: $vx[] = $k2 . '=' . $k2 . ' ' . substr($k, -2, 1) . ' ' . $this->formatValue($v);
175: } else {
176: $vx[] = $this->driver->delimite($k) . '=' . $this->formatValue($v);
177: }
178: }
179: return implode(', ', $vx);
180:
181: } elseif ($this->arrayMode === 'multi') {
182: foreach ($value as $v) {
183: $vx[] = $this->formatValue($v);
184: }
185: return '(' . implode(', ', $vx) . ')';
186:
187: } elseif ($this->arrayMode === 'union') {
188: foreach ($value as $v) {
189: $vx[] = $this->formatValue($v);
190: }
191: return 'UNION ALL SELECT ' . implode(', ', $vx);
192:
193: } elseif ($this->arrayMode === 'and') {
194: foreach ($value as $k => $v) {
195: $k = $this->driver->delimite($k);
196: if (is_array($v)) {
197: $vx[] = $v ? ($k . ' IN (' . $this->formatValue(array_values($v)) . ')') : '1=0';
198: } else {
199: $v = $this->formatValue($v);
200: $vx[] = $k . ($v === 'NULL' ? ' IS ' : ' = ') . $v;
201: }
202: }
203: return $value ? '(' . implode(') AND (', $vx) . ')' : '1=1';
204:
205: } elseif ($this->arrayMode === 'order') {
206: foreach ($value as $k => $v) {
207: $vx[] = $this->driver->delimite($k) . ($v > 0 ? '' : ' DESC');
208: }
209: return implode(', ', $vx);
210: }
211:
212: } elseif ($value instanceof \DateTime || $value instanceof \DateTimeInterface) {
213: return $this->driver->formatDateTime($value);
214:
215: } elseif ($value instanceof SqlLiteral) {
216: $this->remaining = array_merge($this->remaining, $value->getParameters());
217: return $value->__toString();
218:
219: } else {
220: $this->remaining[] = $value;
221: return '?';
222: }
223: }
224:
225: }
226: