1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14: require_once dirname(__FILE__) . '/sqlite.reflector.php';
15:
16:
17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33:
34: class DibiSqliteDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
35: {
36:
37: private $connection;
38:
39:
40: private $resultSet;
41:
42:
43: private $buffered;
44:
45:
46: private $fmtDate, $fmtDateTime;
47:
48:
49: private $dbcharset, $charset;
50:
51:
52:
53: 54: 55:
56: public function __construct()
57: {
58: if (!extension_loaded('sqlite')) {
59: throw new DibiDriverException("PHP extension 'sqlite' is not loaded.");
60: }
61: }
62:
63:
64:
65: 66: 67: 68: 69:
70: public function connect(array &$config)
71: {
72: DibiConnection::alias($config, 'database', 'file');
73: $this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
74: $this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U';
75:
76: $errorMsg = '';
77: if (isset($config['resource'])) {
78: $this->connection = $config['resource'];
79: } elseif (empty($config['persistent'])) {
80: $this->connection = @sqlite_open($config['database'], 0666, $errorMsg); 81: } else {
82: $this->connection = @sqlite_popen($config['database'], 0666, $errorMsg); 83: }
84:
85: if (!$this->connection) {
86: throw new DibiDriverException($errorMsg);
87: }
88:
89: $this->buffered = empty($config['unbuffered']);
90:
91: $this->dbcharset = empty($config['dbcharset']) ? 'UTF-8' : $config['dbcharset'];
92: $this->charset = empty($config['charset']) ? 'UTF-8' : $config['charset'];
93: if (strcasecmp($this->dbcharset, $this->charset) === 0) {
94: $this->dbcharset = $this->charset = NULL;
95: }
96: }
97:
98:
99:
100: 101: 102: 103:
104: public function disconnect()
105: {
106: sqlite_close($this->connection);
107: }
108:
109:
110:
111: 112: 113: 114: 115: 116:
117: public function query($sql)
118: {
119: if ($this->dbcharset !== NULL) {
120: $sql = iconv($this->charset, $this->dbcharset . '//IGNORE', $sql);
121: }
122:
123: DibiDriverException::tryError();
124: if ($this->buffered) {
125: $this->resultSet = sqlite_query($this->connection, $sql);
126: } else {
127: $this->resultSet = sqlite_unbuffered_query($this->connection, $sql);
128: }
129: if (DibiDriverException::catchError($msg)) {
130: throw new DibiDriverException($msg, sqlite_last_error($this->connection), $sql);
131: }
132:
133: return is_resource($this->resultSet) ? clone $this : NULL;
134: }
135:
136:
137:
138: 139: 140: 141:
142: public function getAffectedRows()
143: {
144: return sqlite_changes($this->connection);
145: }
146:
147:
148:
149: 150: 151: 152:
153: public function getInsertId($sequence)
154: {
155: return sqlite_last_insert_rowid($this->connection);
156: }
157:
158:
159:
160: 161: 162: 163: 164: 165:
166: public function begin($savepoint = NULL)
167: {
168: $this->query('BEGIN');
169: }
170:
171:
172:
173: 174: 175: 176: 177: 178:
179: public function commit($savepoint = NULL)
180: {
181: $this->query('COMMIT');
182: }
183:
184:
185:
186: 187: 188: 189: 190: 191:
192: public function rollback($savepoint = NULL)
193: {
194: $this->query('ROLLBACK');
195: }
196:
197:
198:
199: 200: 201: 202:
203: public function getResource()
204: {
205: return $this->connection;
206: }
207:
208:
209:
210: 211: 212: 213:
214: public function getReflector()
215: {
216: return new DibiSqliteReflector($this);
217: }
218:
219:
220:
221:
222:
223:
224:
225: 226: 227: 228: 229: 230: 231:
232: public function escape($value, $type)
233: {
234: switch ($type) {
235: case dibi::TEXT:
236: case dibi::BINARY:
237: return "'" . sqlite_escape_string($value) . "'";
238:
239: case dibi::IDENTIFIER:
240: return '[' . strtr($value, '[]', ' ') . ']';
241:
242: case dibi::BOOL:
243: return $value ? 1 : 0;
244:
245: case dibi::DATE:
246: return $value instanceof DateTime ? $value->format($this->fmtDate) : date($this->fmtDate, $value);
247:
248: case dibi::DATETIME:
249: return $value instanceof DateTime ? $value->format($this->fmtDateTime) : date($this->fmtDateTime, $value);
250:
251: default:
252: throw new InvalidArgumentException('Unsupported type.');
253: }
254: }
255:
256:
257:
258: 259: 260: 261: 262: 263:
264: public function escapeLike($value, $pos)
265: {
266: throw new NotSupportedException;
267: }
268:
269:
270:
271: 272: 273: 274: 275: 276: 277:
278: public function unescape($value, $type)
279: {
280: if ($type === dibi::BINARY) {
281: return $value;
282: }
283: throw new InvalidArgumentException('Unsupported type.');
284: }
285:
286:
287:
288: 289: 290: 291: 292: 293: 294:
295: public function applyLimit(&$sql, $limit, $offset)
296: {
297: if ($limit < 0 && $offset < 1) return;
298: $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
299: }
300:
301:
302:
303:
304:
305:
306:
307: 308: 309: 310:
311: public function getRowCount()
312: {
313: if (!$this->buffered) {
314: throw new DibiDriverException('Row count is not available for unbuffered queries.');
315: }
316: return sqlite_num_rows($this->resultSet);
317: }
318:
319:
320:
321: 322: 323: 324: 325:
326: public function fetch($assoc)
327: {
328: $row = sqlite_fetch_array($this->resultSet, $assoc ? SQLITE_ASSOC : SQLITE_NUM);
329: $charset = $this->charset === NULL ? NULL : $this->charset . '//TRANSLIT';
330: if ($row && ($assoc || $charset)) {
331: $tmp = array();
332: foreach ($row as $k => $v) {
333: if ($charset !== NULL && is_string($v)) {
334: $v = iconv($this->dbcharset, $charset, $v);
335: }
336: $tmp[str_replace(array('[', ']'), '', $k)] = $v;
337: }
338: return $tmp;
339: }
340: return $row;
341: }
342:
343:
344:
345: 346: 347: 348: 349: 350:
351: public function seek($row)
352: {
353: if (!$this->buffered) {
354: throw new DibiDriverException('Cannot seek an unbuffered result set.');
355: }
356: return sqlite_seek($this->resultSet, $row);
357: }
358:
359:
360:
361: 362: 363: 364:
365: public function free()
366: {
367: $this->resultSet = NULL;
368: }
369:
370:
371:
372: 373: 374: 375:
376: public function getResultColumns()
377: {
378: $count = sqlite_num_fields($this->resultSet);
379: $columns = array();
380: for ($i = 0; $i < $count; $i++) {
381: $name = str_replace(array('[', ']'), '', sqlite_field_name($this->resultSet, $i));
382: $pair = explode('.', $name);
383: $columns[] = array(
384: 'name' => isset($pair[1]) ? $pair[1] : $pair[0],
385: 'table' => isset($pair[1]) ? $pair[0] : NULL,
386: 'fullname' => $name,
387: 'nativetype' => NULL,
388: );
389: }
390: return $columns;
391: }
392:
393:
394:
395: 396: 397: 398:
399: public function getResultResource()
400: {
401: return $this->resultSet;
402: }
403:
404:
405:
406:
407:
408:
409:
410: 411: 412: 413: 414: 415: 416:
417: public function registerFunction($name, $callback, $numArgs = -1)
418: {
419: sqlite_create_function($this->connection, $name, $callback, $numArgs);
420: }
421:
422:
423:
424: 425: 426: 427: 428: 429: 430: 431:
432: public function registerAggregateFunction($name, $rowCallback, $agrCallback, $numArgs = -1)
433: {
434: sqlite_create_aggregate($this->connection, $name, $rowCallback, $agrCallback, $numArgs);
435: }
436:
437: }
438: