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: public function __construct(Connection $connection)
40: {
41: $this->connection = $connection;
42: $this->driver = $connection->getSupplementalDriver();
43: }
44:
45:
46: 47: 48: 49: 50:
51: public function process($sql, $params)
52: {
53: $this->params = $params;
54: $this->counter = 0;
55: $this->remaining = array();
56: $this->arrayMode = 'assoc';
57:
58: $sql = Nette\Utils\Strings::replace($sql, '~\'.*?\'|".*?"|\?|\b(?:INSERT|REPLACE|UPDATE)\b|/\*.*?\*/|--[^\n]*~si', array($this, 'callback'));
59:
60: while ($this->counter < count($params)) {
61: $sql .= ' ' . $this->formatValue($params[$this->counter++]);
62: }
63:
64: return array($sql, $this->remaining);
65: }
66:
67:
68:
69: public function callback($m)
70: {
71: $m = $m[0];
72: if ($m[0] === "'" || $m[0] === '"' || $m[0] === '/' || $m[0] === '-') {
73: return $m;
74:
75: } elseif ($m === '?') {
76: if ($this->counter >= count($this->params)) {
77: throw new Nette\InvalidArgumentException('There are more placeholders than passed parameters.');
78: }
79: return $this->formatValue($this->params[$this->counter++]);
80:
81: } else {
82: $this->arrayMode = strtoupper($m) === 'UPDATE' ? 'assoc' : 'values';
83: return $m;
84: }
85: }
86:
87:
88: private function formatValue($value)
89: {
90: if (is_string($value)) {
91: if (strlen($value) > 20) {
92: $this->remaining[] = $value;
93: return '?';
94:
95: } else {
96: return $this->connection->quote($value);
97: }
98:
99: } elseif (is_int($value)) {
100: return (string) $value;
101:
102: } elseif (is_float($value)) {
103: return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.');
104:
105: } elseif (is_bool($value)) {
106: return $this->driver->formatBool($value);
107:
108: } elseif ($value === NULL) {
109: return 'NULL';
110:
111: } elseif ($value instanceof Table\ActiveRow) {
112: return $value->getPrimary();
113:
114: } elseif (is_array($value) || $value instanceof \Traversable) {
115: $vx = $kx = array();
116:
117: if ($value instanceof \Traversable) {
118: $value = iterator_to_array($value);
119: }
120:
121: if (isset($value[0])) {
122: foreach ($value as $v) {
123: $vx[] = $this->formatValue($v);
124: }
125: return implode(', ', $vx);
126:
127: } elseif ($this->arrayMode === 'values') {
128: $this->arrayMode = 'multi';
129: foreach ($value as $k => $v) {
130: $kx[] = $this->driver->delimite($k);
131: $vx[] = $this->formatValue($v);
132: }
133: return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')';
134:
135: } elseif ($this->arrayMode === 'assoc') {
136: foreach ($value as $k => $v) {
137: $vx[] = $this->driver->delimite($k) . '=' . $this->formatValue($v);
138: }
139: return implode(', ', $vx);
140:
141: } elseif ($this->arrayMode === 'multi') {
142: foreach ($value as $v) {
143: $vx[] = $this->formatValue($v);
144: }
145: return '(' . implode(', ', $vx) . ')';
146: }
147:
148: } elseif ($value instanceof \DateTime || $value instanceof \DateTimeInterface) {
149: return $this->driver->formatDateTime($value);
150:
151: } elseif ($value instanceof SqlLiteral) {
152: return $value->__toString();
153:
154: } else {
155: $this->remaining[] = $value;
156: return '?';
157: }
158: }
159:
160: }
161: