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