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