PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/code/classes/pinetd/SQL/SQLite3.class.php

https://github.com/blekkzor/pinetd2
PHP | 206 lines | 167 code | 29 blank | 10 comment | 28 complexity | 5791eea805fa05a9acb037adbe6720f0 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. namespace pinetd\SQL;
  3. use \Exception;
  4. class SQLite3 {
  5. private $settings;
  6. private $sqlite;
  7. private $unique;
  8. private $DAO = array();
  9. public function __construct(array $settings) {
  10. if (!extension_loaded('sqlite3')) throw new Exception('This class requires SQLite3');
  11. $this->settings = $settings;
  12. $this->sqlite = new \SQLite3($settings['File']);
  13. // TODO: handle errors
  14. $this->unique = sha1($settings['File']);
  15. $this->sqlite->busyTimeout(30000);
  16. $this->sqlite->exec('PRAGMA encoding = "UTF-8"');
  17. $this->sqlite->exec('PRAGMA legacy_file_format = 0');
  18. // support for missing SQL functions in sqlite
  19. $this->sqlite->createFunction('now', array($this, 'now'), 0);
  20. $this->sqlite->createFunction('unix_timestamp', array($this, 'unixTimestamp'), 1);
  21. }
  22. public function __call($func, $args) {
  23. return call_user_func_array(array($this->sqlite, $func), $args);
  24. }
  25. public function __get($var) {
  26. switch($var) {
  27. case 'error': return '['.$this->sqlite->lastErrorCode().'] '.$this->sqlite->lastErrorMsg();
  28. case 'insert_id': return $this->sqlite->lastInsertRowID();
  29. case 'affected_rows': return $this->sqlite->changes();
  30. }
  31. return $this->sqlite->$var;
  32. }
  33. public function unique() {
  34. // return an unique hash based on the connection
  35. return $this->unique;
  36. }
  37. public function now() {
  38. return date('Y-m-d H:i:s');
  39. }
  40. public function unixTimestamp($date) {
  41. return strtotime($date); // lazy
  42. }
  43. public function timeStamp($when) {
  44. return date('Y-m-d H:i:s', $when);
  45. }
  46. public function prepare($query) {
  47. $stmt = $this->sqlite->prepare($query);
  48. if (!$stmt) return $stmt;
  49. return new SQLite3\Stmt($stmt, $query);
  50. }
  51. public function insert($table, $data) {
  52. $fields = '';
  53. $values = '';
  54. foreach($data as $var => $val) {
  55. $fields .= ($fields == ''?'':',').'`'.$var.'`';
  56. $values .= ($values == ''?'':',').$this->quote_escape($val);
  57. }
  58. $req = 'INSERT INTO `'.$table.'` ('.$fields.') VALUES('.$values.')';
  59. return $this->sqlite->exec($req);
  60. }
  61. public function DAO($table, $key) {
  62. if (!isset($this->DAO[$table])) $this->DAO[$table] = new \DAO\SQLite3($this, $table, $key);
  63. return $this->DAO[$table];
  64. }
  65. public function query($query) {
  66. $res = @$this->sqlite->query($query);
  67. if (is_bool($res)) return $res;
  68. return new SQLite3\Result($res);
  69. }
  70. // when we fork, this is required
  71. public function reconnect() {
  72. // TODO
  73. }
  74. public function quote_escape($string) {
  75. if (is_null($string)) return 'NULL';
  76. if ($string instanceof \pinetd\SQL\Expr) return (string)$string;
  77. if (is_array($string)) {
  78. $res = '';
  79. foreach($string as $elem) $res .= ($res == ''?'':',') . $this->quote_escape($elem);
  80. return $res;
  81. }
  82. return '\''.$this->sqlite->escapeString($string).'\'';
  83. }
  84. public function escape_string($string) {
  85. return $this->sqlite->escapeString($string);
  86. }
  87. function col_gen_type($col) {
  88. $res = strtolower($col['type']);
  89. if ($col['auto_increment']) return 'INTEGER';
  90. switch($res) {
  91. case 'set': case 'enum': // not supported by sqlite
  92. $max_len = 0;
  93. foreach($col['values'] as $val) $max_len = max($max_len, strlen($val));
  94. $res='varchar('.$max_len.')';
  95. break;
  96. case 'text': case 'blob': case 'datetime': // no size!
  97. break;
  98. default:
  99. if (isset($col['size'])) $res.='('.$col['size'].')';
  100. break;
  101. }
  102. if ($col['unsigned']) $res='unsigned';
  103. return $res;
  104. }
  105. function gen_field_info($cname, $col) {
  106. $tmp = '`'.$cname.'` '.$this->col_gen_type($col);
  107. if ($col['key'] == 'PRIMARY') $tmp .=' PRIMARY KEY';
  108. if ($col['auto_increment']) $tmp.=' AUTOINCREMENT';
  109. if (!$col['null']) $tmp.=' NOT NULL';
  110. if (array_key_exists('default',$col)) $tmp.=' DEFAULT '.$this->quote_escape($col['default']);
  111. return $tmp;
  112. }
  113. function gen_create_query($name, $struct) {
  114. $req = '';
  115. $keys = array();
  116. foreach($struct as $cname=>$col) {
  117. $req.=($req==''?'':', ').$this->gen_field_info($cname, $col);
  118. if (isset($col['key'])) $keys[$col['key']][]=$cname;
  119. }
  120. $req = array('BEGIN TRANSACTION', 'CREATE TABLE `'.$name.'` ('.$req.')');
  121. foreach($keys as $kname=>$cols) {
  122. if ($kname == 'PRIMARY') continue;
  123. $tmp = '';
  124. foreach($cols as $c) $tmp.=($tmp==''?'':',').'`'.$c.'`';
  125. $tmp='('.$tmp.')';
  126. if (substr($kname, 0, 7)=='UNIQUE:') {
  127. $kname = substr($kname, 7);
  128. $tmp = 'CREATE UNIQUE INDEX `'.$name.'_'.$kname.'` ON `'.$name.'` '.$tmp;
  129. } else {
  130. $tmp = 'CREATE INDEX `'.$name.'_'.$kname.'` ON `'.$name.'` '.$tmp;
  131. }
  132. $req[]=$tmp;
  133. }
  134. $req[] = 'COMMIT';
  135. return $req;
  136. }
  137. public function validateStruct($table_name, $struct) {
  138. $f = array_flip(array_keys($struct)); // field list
  139. // check if table exists
  140. $res = $this->querySingle('SELECT 1 FROM `sqlite_master` WHERE `type`=\'table\' AND `name` = '.$this->quote_escape($table_name));
  141. if (is_null($res)) {
  142. // table does not exists
  143. $req = $this->gen_create_query($table_name, $struct);
  144. if (is_array($req)) {
  145. foreach($req as $query) {
  146. if (!$this->sqlite->exec($query)) {
  147. $this->sqlite->exec('ROLLBACK');
  148. return false;
  149. }
  150. }
  151. return true;
  152. }
  153. return $this->sqlite->exec($req);
  154. }
  155. // get structure for this table
  156. $res = $this->query('PRAGMA table_info('.$table_name.')');
  157. // TODO: decode "CREATE TABLE" returned by query (string)
  158. while($row = $res->fetch_assoc()) {
  159. if (!isset($f[$row['name']])) {
  160. // we got a field we don't know about
  161. $req = 'ALTER TABLE `'.$table_name.'` DROP `'.$row['name'].'`';
  162. @$this->sqlite->query($req);
  163. continue;
  164. }
  165. unset($f[$row['name']]);
  166. $col = $struct[$row['name']];
  167. if ($row['type']!=$this->col_gen_type($col)) {
  168. $req = 'ALTER TABLE `'.$table_name.'` CHANGE `'.$row['name'].'` '.$this->gen_field_info($row['name'], $col);
  169. @$this->sqlite->query($req);
  170. }
  171. }
  172. foreach($f as $k=>$ign) {
  173. $req = 'ALTER TABLE `'.$table_name.'` ADD '.$this->gen_field_info($k, $struct[$k]);
  174. @$this->sqlite->query($req);
  175. }
  176. }
  177. }