class.db.php 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086
  1. <?php
  2. /*
  3. // create instance protocol://user:pass@host/db?charset=UTF8&persist=TRUE&timezone=Europe/Sofia
  4. $db = DB::get('mysqli://root@127.0.0.1/test');
  5. $db = DB::get('oracle://root:pass@VIRT/?charset=AL32UTF8');
  6. // execute a non-resulting query (returns boolean true)
  7. $db->query('UPDATE table SET value = 1 WHERE id = ?', array(1));
  8. // get all results as array
  9. $db->all('SELECT * FROM table WHERE id = ?', array(1), "array_key", bool_skip_key, "assoc"/"num");
  10. // get one result
  11. $db->one('SELECT * FROM table WHERE id = ?', array(1), "assoc"/"num");
  12. // get a traversable object to pass to foreach, or use count(), or use direct access: [INDEX]
  13. $db->get('SELECT * FROM table WHERE id = ?', array(1), "assoc"/"num")[1];
  14. */
  15. namespace
  16. {
  17. class db
  18. {
  19. private function __construct() {
  20. }
  21. public function __clone() {
  22. throw new \vakata\database\Exception('Cannot clone static DB');
  23. }
  24. public static function get($settings = null) {
  25. return new \vakata\database\DBC($settings);
  26. }
  27. public static function getc($settings = null, \vakata\cache\ICache $c = null) {
  28. if($c === null) { $c = \vakata\cache\cache::inst(); }
  29. return new \vakata\database\DBCCached($settings, $c);
  30. }
  31. }
  32. }
  33. namespace vakata\database
  34. {
  35. class Exception extends \Exception
  36. {
  37. }
  38. class Settings
  39. {
  40. public $type = null;
  41. public $username = 'root';
  42. public $password = null;
  43. public $database = null;
  44. public $servername = 'localhost';
  45. public $serverport = null;
  46. public $persist = false;
  47. public $timezone = null;
  48. public $charset = 'UTF8';
  49. public function __construct($settings) {
  50. $str = parse_url($settings);
  51. if(!$str) {
  52. throw new Exception('Malformed DB settings string: ' . $settings);
  53. }
  54. if(array_key_exists('scheme',$str)) {
  55. $this->type = rawurldecode($str['scheme']);
  56. }
  57. if(array_key_exists('user',$str)) {
  58. $this->username = rawurldecode($str['user']);
  59. }
  60. if(array_key_exists('pass',$str)) {
  61. $this->password = rawurldecode($str['pass']);
  62. }
  63. if(array_key_exists('path',$str)) {
  64. $this->database = trim(rawurldecode($str['path']),'/');
  65. }
  66. if(array_key_exists('host',$str)) {
  67. $this->servername = rawurldecode($str['host']);
  68. }
  69. if(array_key_exists('port',$str)) {
  70. $this->serverport = rawurldecode($str['port']);
  71. }
  72. if(array_key_exists('query',$str)) {
  73. parse_str($str['query'], $str);
  74. $this->persist = (array_key_exists('persist', $str) && $str['persist'] === 'TRUE');
  75. if(array_key_exists('charset', $str)) {
  76. $this->charset = $str['charset'];
  77. }
  78. if(array_key_exists('timezone', $str)) {
  79. $this->timezone = $str['timezone'];
  80. }
  81. }
  82. }
  83. }
  84. interface IDB
  85. {
  86. public function connect();
  87. public function query($sql, $vars);
  88. public function get($sql, $data, $key, $skip_key, $mode);
  89. public function all($sql, $data, $key, $skip_key, $mode);
  90. public function one($sql, $data, $mode);
  91. public function raw($sql);
  92. public function prepare($sql);
  93. public function execute($data);
  94. public function disconnect();
  95. }
  96. interface IDriver
  97. {
  98. public function prepare($sql);
  99. public function execute($data);
  100. public function query($sql, $data);
  101. public function nextr($result);
  102. public function seek($result, $row);
  103. public function nf($result);
  104. public function af();
  105. public function insert_id();
  106. public function real_query($sql);
  107. public function get_settings();
  108. }
  109. abstract class ADriver implements IDriver
  110. {
  111. protected $lnk = null;
  112. protected $settings = null;
  113. public function __construct(Settings $settings) {
  114. $this->settings = $settings;
  115. }
  116. public function __destruct() {
  117. if($this->is_connected()) {
  118. $this->disconnect();
  119. }
  120. }
  121. public function get_settings() {
  122. return $this->settings;
  123. }
  124. public function connect() {
  125. }
  126. public function is_connected() {
  127. return $this->lnk !== null;
  128. }
  129. public function disconnect() {
  130. }
  131. public function query($sql, $data = array()) {
  132. return $this->execute($this->prepare($sql), $data);
  133. }
  134. public function prepare($sql) {
  135. if(!$this->is_connected()) { $this->connect(); }
  136. return $sql;
  137. }
  138. public function execute($sql, $data = array()) {
  139. if(!$this->is_connected()) { $this->connect(); }
  140. if(!is_array($data)) { $data = array(); }
  141. $binder = '?';
  142. if(strpos($sql, $binder) !== false && is_array($data) && count($data)) {
  143. $tmp = explode($binder, $sql);
  144. if(!is_array($data)) { $data = array($data); }
  145. $data = array_values($data);
  146. if(count($data) >= count($tmp)) { $data = array_slice($data, 0, count($tmp)-1); }
  147. $sql = $tmp[0];
  148. foreach($data as $i => $v) {
  149. $sql .= $this->escape($v) . $tmp[($i + 1)];
  150. }
  151. }
  152. return $this->real_query($sql);
  153. }
  154. public function real_query($sql) {
  155. if(!$this->is_connected()) { $this->connect(); }
  156. }
  157. protected function escape($input) {
  158. if(is_array($input)) {
  159. foreach($input as $k => $v) {
  160. $input[$k] = $this->escape($v);
  161. }
  162. return implode(',',$input);
  163. }
  164. if(is_string($input)) {
  165. $input = addslashes($input);
  166. return "'".$input."'";
  167. }
  168. if(is_bool($input)) {
  169. return $input === false ? 0 : 1;
  170. }
  171. if(is_null($input)) {
  172. return 'NULL';
  173. }
  174. return $input;
  175. }
  176. public function nextr($result) {}
  177. public function nf($result) {}
  178. public function af() {}
  179. public function insert_id() {}
  180. public function seek($result, $row) {}
  181. }
  182. class Result implements \Iterator, \ArrayAccess, \Countable
  183. {
  184. protected $all = null;
  185. protected $rdy = false;
  186. protected $rslt = null;
  187. protected $mode = null;
  188. protected $fake = null;
  189. protected $skip = false;
  190. protected $fake_key = 0;
  191. protected $real_key = 0;
  192. public function __construct(Query $rslt, $key = null, $skip_key = false, $mode = 'assoc') {
  193. $this->rslt = $rslt;
  194. $this->mode = $mode;
  195. $this->fake = $key;
  196. $this->skip = $skip_key;
  197. }
  198. public function count() {
  199. return $this->rdy ? count($this->all) : $this->rslt->nf();
  200. }
  201. public function current() {
  202. if(!$this->count()) {
  203. return null;
  204. }
  205. if($this->rdy) {
  206. return current($this->all);
  207. }
  208. $tmp = $this->rslt->row();
  209. $row = array();
  210. switch($this->mode) {
  211. case 'num':
  212. foreach($tmp as $k => $v) {
  213. if(is_int($k)) {
  214. $row[$k] = $v;
  215. }
  216. }
  217. break;
  218. case 'both':
  219. $row = $tmp;
  220. break;
  221. case 'assoc':
  222. default:
  223. foreach($tmp as $k => $v) {
  224. if(!is_int($k)) {
  225. $row[$k] = $v;
  226. }
  227. }
  228. break;
  229. }
  230. if($this->fake) {
  231. $this->fake_key = $row[$this->fake];
  232. }
  233. if($this->skip) {
  234. unset($row[$this->fake]);
  235. }
  236. if(is_array($row) && count($row) === 1) {
  237. $row = current($row);
  238. }
  239. return $row;
  240. }
  241. public function key() {
  242. if($this->rdy) {
  243. return key($this->all);
  244. }
  245. return $this->fake ? $this->fake_key : $this->real_key;
  246. }
  247. public function next() {
  248. if($this->rdy) {
  249. return next($this->all);
  250. }
  251. $this->rslt->nextr();
  252. $this->real_key++;
  253. }
  254. public function rewind() {
  255. if($this->rdy) {
  256. return reset($this->all);
  257. }
  258. if($this->real_key !== null) {
  259. $this->rslt->seek(($this->real_key = 0));
  260. }
  261. $this->rslt->nextr();
  262. }
  263. public function valid() {
  264. if($this->rdy) {
  265. return current($this->all) !== false;
  266. }
  267. return $this->rslt->row() !== false && $this->rslt->row() !== null;
  268. }
  269. public function one() {
  270. $this->rewind();
  271. return $this->current();
  272. }
  273. public function get() {
  274. if(!$this->rdy) {
  275. $this->all = array();
  276. foreach($this as $k => $v) {
  277. $this->all[$k] = $v;
  278. }
  279. $this->rdy = true;
  280. }
  281. return $this->all;
  282. }
  283. public function offsetExists($offset) {
  284. if($this->rdy) {
  285. return isset($this->all[$offset]);
  286. }
  287. if($this->fake === null) {
  288. return $this->rslt->seek(($this->real_key = $offset));
  289. }
  290. $this->get();
  291. return isset($this->all[$offset]);
  292. }
  293. public function offsetGet($offset) {
  294. if($this->rdy) {
  295. return $this->all[$offset];
  296. }
  297. if($this->fake === null) {
  298. $this->rslt->seek(($this->real_key = $offset));
  299. $this->rslt->nextr();
  300. return $this->current();
  301. }
  302. $this->get();
  303. return $this->all[$offset];
  304. }
  305. public function offsetSet ($offset, $value ) {
  306. throw new Exception('Cannot set result');
  307. }
  308. public function offsetUnset ($offset) {
  309. throw new Exception('Cannot unset result');
  310. }
  311. public function __sleep() {
  312. $this->get();
  313. return array('all', 'rdy', 'mode', 'fake', 'skip');
  314. }
  315. public function __toString() {
  316. return print_r($this->get(), true);
  317. }
  318. }
  319. class Query
  320. {
  321. protected $drv = null;
  322. protected $sql = null;
  323. protected $prp = null;
  324. protected $rsl = null;
  325. protected $row = null;
  326. protected $num = null;
  327. protected $aff = null;
  328. protected $iid = null;
  329. public function __construct(IDriver $drv, $sql) {
  330. $this->drv = $drv;
  331. $this->sql = $sql;
  332. $this->prp = $this->drv->prepare($sql);
  333. }
  334. public function execute($vars = array()) {
  335. $this->rsl = $this->drv->execute($this->prp, $vars);
  336. $this->num = (is_object($this->rsl) || is_resource($this->rsl)) && is_callable(array($this->drv, 'nf')) ? (int)@$this->drv->nf($this->rsl) : 0;
  337. $this->aff = $this->drv->af();
  338. $this->iid = $this->drv->insert_id();
  339. return $this;
  340. }
  341. public function result($key = null, $skip_key = false, $mode = 'assoc') {
  342. return new Result($this, $key, $skip_key, $mode);
  343. }
  344. public function row() {
  345. return $this->row;
  346. }
  347. public function f($field) {
  348. return $this->row[$field];
  349. }
  350. public function nextr() {
  351. $this->row = $this->drv->nextr($this->rsl);
  352. return $this->row !== false && $this->row !== null;
  353. }
  354. public function seek($offset) {
  355. return @$this->drv->seek($this->rsl, $offset) ? true : false;
  356. }
  357. public function nf() {
  358. return $this->num;
  359. }
  360. public function af() {
  361. return $this->aff;
  362. }
  363. public function insert_id() {
  364. return $this->iid;
  365. }
  366. }
  367. class DBC implements IDB
  368. {
  369. protected $drv = null;
  370. protected $que = null;
  371. public function __construct($drv = null) {
  372. if(!$drv && defined('DATABASE')) {
  373. $drv = DATABASE;
  374. }
  375. if(!$drv) {
  376. $this->error('Could not create database (no settings)');
  377. }
  378. if(is_string($drv)) {
  379. $drv = new \vakata\database\Settings($drv);
  380. }
  381. if($drv instanceof Settings) {
  382. $tmp = '\\vakata\\database\\' . $drv->type . '_driver';
  383. if(!class_exists($tmp)) {
  384. $this->error('Could not create database (no driver: '.$drv->type.')');
  385. }
  386. $drv = new $tmp($drv);
  387. }
  388. if(!($drv instanceof IDriver)) {
  389. $this->error('Could not create database - wrong driver');
  390. }
  391. $this->drv = $drv;
  392. }
  393. public function connect() {
  394. if(!$this->drv->is_connected()) {
  395. try {
  396. $this->drv->connect();
  397. }
  398. catch (Exception $e) {
  399. $this->error($e->getMessage(), 1);
  400. }
  401. }
  402. return true;
  403. }
  404. public function disconnect() {
  405. if($this->drv->is_connected()) {
  406. $this->drv->disconnect();
  407. }
  408. }
  409. public function prepare($sql) {
  410. try {
  411. $this->que = new Query($this->drv, $sql);
  412. return $this->que;
  413. } catch (Exception $e) {
  414. $this->error($e->getMessage(), 2);
  415. }
  416. }
  417. public function execute($data = array()) {
  418. try {
  419. return $this->que->execute($data);
  420. } catch (Exception $e) {
  421. $this->error($e->getMessage(), 3);
  422. }
  423. }
  424. public function query($sql, $data = array()) {
  425. try {
  426. $this->que = new Query($this->drv, $sql);
  427. return $this->que->execute($data);
  428. }
  429. catch (Exception $e) {
  430. $this->error($e->getMessage(), 4);
  431. }
  432. }
  433. public function get($sql, $data = array(), $key = null, $skip_key = false, $mode = 'assoc') {
  434. return $this->query($sql, $data)->result($key, $skip_key, $mode);
  435. }
  436. public function all($sql, $data = array(), $key = null, $skip_key = false, $mode = 'assoc') {
  437. return $this->get($sql, $data, $key, $skip_key, $mode)->get();
  438. }
  439. public function one($sql, $data = array(), $mode = 'assoc') {
  440. return $this->query($sql, $data)->result(null, false, $mode)->one();
  441. }
  442. public function raw($sql) {
  443. return $this->drv->real_query($sql);
  444. }
  445. public function get_driver() {
  446. return $this->drv->get_settings()->type;
  447. }
  448. public function __call($method, $args) {
  449. if($this->que && is_callable(array($this->que, $method))) {
  450. try {
  451. return call_user_func_array(array($this->que, $method), $args);
  452. } catch (Exception $e) {
  453. $this->error($e->getMessage(), 5);
  454. }
  455. }
  456. }
  457. protected final function error($error = '') {
  458. $dirnm = defined('LOGROOT') ? LOGROOT : realpath(dirname(__FILE__));
  459. @file_put_contents(
  460. $dirnm . DIRECTORY_SEPARATOR . '_errors_sql.log',
  461. '[' . date('d-M-Y H:i:s') . '] ' . $this->settings->type . ' > ' . preg_replace("@[\s\r\n\t]+@", ' ', $error) . "\n",
  462. FILE_APPEND
  463. );
  464. throw new Exception($error);
  465. }
  466. }
  467. class DBCCached extends DBC
  468. {
  469. protected $cache_inst = null;
  470. protected $cache_nmsp = null;
  471. public function __construct($settings = null, \vakata\cache\ICache $c = null) {
  472. parent::__construct($settings);
  473. $this->cache_inst = $c;
  474. $this->cache_nmsp = 'DBCCached_' . md5(serialize($this->drv->get_settings()));
  475. }
  476. public function cache($expires, $sql, $data = array(), $key = null, $skip_key = false, $mode = 'assoc') {
  477. $arg = func_get_args();
  478. array_shift($arg);
  479. $key = md5(serialize($arg));
  480. if(!$this->cache_inst) {
  481. return call_user_func_array(array($this, 'all'), $arg);
  482. }
  483. $tmp = $this->cache_inst->get($key, $this->cache_nmsp);
  484. if(!$tmp) {
  485. $this->cache_inst->prep($key, $this->cache_nmsp);
  486. $tmp = call_user_func_array(array($this, 'all'), $arg);
  487. $this->cache_inst->set($key, $tmp, $this->cache_nmsp, $expires);
  488. }
  489. return $tmp;
  490. }
  491. public function clear() {
  492. if($this->cache_inst) {
  493. $this->cache_inst->clear($this->cache_nmsp);
  494. }
  495. }
  496. }
  497. class mysqli_driver extends ADriver
  498. {
  499. protected $iid = 0;
  500. protected $aff = 0;
  501. protected $mnd = false;
  502. public function __construct($settings) {
  503. parent::__construct($settings);
  504. if(!$this->settings->serverport) { $this->settings->serverport = 3306; }
  505. $this->mnd = function_exists('mysqli_fetch_all');
  506. }
  507. public function connect() {
  508. $this->lnk = new \mysqli(
  509. ($this->settings->persist ? 'p:' : '') . $this->settings->servername,
  510. $this->settings->username,
  511. $this->settings->password,
  512. $this->settings->database,
  513. $this->settings->serverport
  514. );
  515. if($this->lnk->connect_errno) {
  516. throw new Exception('Connect error: ' . $this->lnk->connect_errno);
  517. }
  518. if(!$this->lnk->set_charset($this->settings->charset)) {
  519. throw new Exception('Charset error: ' . $this->lnk->connect_errno);
  520. }
  521. if($this->settings->timezone) {
  522. @$this->lnk->query("SET time_zone = '" . addslashes($this->settings->timezone) . "'");
  523. }
  524. return true;
  525. }
  526. public function disconnect() {
  527. if($this->is_connected()) {
  528. @$this->lnk->close();
  529. }
  530. }
  531. public function real_query($sql) {
  532. if(!$this->is_connected()) { $this->connect(); }
  533. $temp = $this->lnk->query($sql);
  534. if(!$temp) {
  535. throw new Exception('Could not execute query : ' . $this->lnk->error . ' <'.$sql.'>');
  536. }
  537. $this->iid = $this->lnk->insert_id;
  538. $this->aff = $this->lnk->affected_rows;
  539. return $temp;
  540. }
  541. public function nextr($result) {
  542. if($this->mnd) {
  543. return $result->fetch_array(MYSQL_BOTH);
  544. }
  545. else {
  546. $ref = $result->result_metadata();
  547. if(!$ref) { return false; }
  548. $tmp = mysqli_fetch_fields($ref);
  549. if(!$tmp) { return false; }
  550. $ref = array();
  551. foreach($tmp as $col) { $ref[$col->name] = null; }
  552. $tmp = array();
  553. foreach($ref as $k => $v) { $tmp[] =& $ref[$k]; }
  554. if(!call_user_func_array(array($result, 'bind_result'), $tmp)) { return false; }
  555. if(!$result->fetch()) { return false; }
  556. $tmp = array();
  557. $i = 0;
  558. foreach($ref as $k => $v) { $tmp[$i++] = $v; $tmp[$k] = $v; }
  559. return $tmp;
  560. }
  561. }
  562. public function seek($result, $row) {
  563. $temp = $result->data_seek($row);
  564. return $temp;
  565. }
  566. public function nf($result) {
  567. return $result->num_rows;
  568. }
  569. public function af() {
  570. return $this->aff;
  571. }
  572. public function insert_id() {
  573. return $this->iid;
  574. }
  575. public function prepare($sql) {
  576. if(!$this->is_connected()) { $this->connect(); }
  577. $temp = $this->lnk->prepare($sql);
  578. if(!$temp) {
  579. throw new Exception('Could not prepare : ' . $this->lnk->error . ' <'.$sql.'>');
  580. }
  581. return $temp;
  582. }
  583. public function execute($sql, $data = array()) {
  584. if(!$this->is_connected()) { $this->connect(); }
  585. if(!is_array($data)) { $data = array(); }
  586. if(is_string($sql)) {
  587. return parent::execute($sql, $data);
  588. }
  589. $data = array_values($data);
  590. if($sql->param_count) {
  591. if(count($data) < $sql->param_count) {
  592. throw new Exception('Prepared execute - not enough parameters.');
  593. }
  594. $ref = array('');
  595. foreach($data as $i => $v) {
  596. switch(gettype($v)) {
  597. case "boolean":
  598. case "integer":
  599. $data[$i] = (int)$v;
  600. $ref[0] .= 'i';
  601. $ref[$i+1] =& $data[$i];
  602. break;
  603. case "double":
  604. $ref[0] .= 'd';
  605. $ref[$i+1] =& $data[$i];
  606. break;
  607. case "array":
  608. // TODO: work around IN statements?
  609. $data[$i] = implode(',',$v);
  610. $ref[0] .= 's';
  611. $ref[$i+1] =& $data[$i];
  612. break;
  613. case "object":
  614. case "resource":
  615. $data[$i] = serialize($data[$i]);
  616. $ref[0] .= 's';
  617. $ref[$i+1] =& $data[$i];
  618. break;
  619. default:
  620. $ref[0] .= 's';
  621. $ref[$i+1] =& $data[$i];
  622. break;
  623. }
  624. }
  625. call_user_func_array(array($sql, 'bind_param'), $ref);
  626. }
  627. $rtrn = $sql->execute();
  628. if(!$this->mnd) {
  629. $sql->store_result();
  630. }
  631. if(!$rtrn) {
  632. throw new Exception('Prepared execute error : ' . $this->lnk->error);
  633. }
  634. $this->iid = $this->lnk->insert_id;
  635. $this->aff = $this->lnk->affected_rows;
  636. if(!$this->mnd) {
  637. return $sql->field_count ? $sql : $rtrn;
  638. }
  639. else {
  640. return $sql->field_count ? $sql->get_result() : $rtrn;
  641. }
  642. }
  643. protected function escape($input) {
  644. if(is_array($input)) {
  645. foreach($input as $k => $v) {
  646. $input[$k] = $this->escape($v);
  647. }
  648. return implode(',',$input);
  649. }
  650. if(is_string($input)) {
  651. $input = $this->lnk->real_escape_string($input);
  652. return "'".$input."'";
  653. }
  654. if(is_bool($input)) {
  655. return $input === false ? 0 : 1;
  656. }
  657. if(is_null($input)) {
  658. return 'NULL';
  659. }
  660. return $input;
  661. }
  662. }
  663. class mysql_driver extends ADriver
  664. {
  665. protected $iid = 0;
  666. protected $aff = 0;
  667. public function __construct($settings) {
  668. parent::__construct($settings);
  669. if(!$this->settings->serverport) { $this->settings->serverport = 3306; }
  670. }
  671. public function connect() {
  672. $this->lnk = ($this->settings->persist) ?
  673. @mysql_pconnect(
  674. $this->settings->servername.':'.$this->settings->serverport,
  675. $this->settings->username,
  676. $this->settings->password
  677. ) :
  678. @mysql_connect(
  679. $this->settings->servername.':'.$this->settings->serverport,
  680. $this->settings->username,
  681. $this->settings->password
  682. );
  683. if($this->lnk === false || !mysql_select_db($this->settings->database, $this->lnk) || !mysql_query("SET NAMES '".$this->settings->charset."'", $this->lnk)) {
  684. throw new Exception('Connect error: ' . mysql_error());
  685. }
  686. if($this->settings->timezone) {
  687. @mysql_query("SET time_zone = '" . addslashes($this->settings->timezone) . "'", $this->lnk);
  688. }
  689. return true;
  690. }
  691. public function disconnect() {
  692. if(is_resource($this->lnk)) {
  693. mysql_close($this->lnk);
  694. }
  695. }
  696. public function real_query($sql) {
  697. if(!$this->is_connected()) { $this->connect(); }
  698. $temp = mysql_query($sql, $this->lnk);
  699. if(!$temp) {
  700. throw new Exception('Could not execute query : ' . mysql_error($this->lnk) . ' <'.$sql.'>');
  701. }
  702. $this->iid = mysql_insert_id($this->lnk);
  703. $this->aff = mysql_affected_rows($this->lnk);
  704. return $temp;
  705. }
  706. public function nextr($result) {
  707. return mysql_fetch_array($result, MYSQL_BOTH);
  708. }
  709. public function seek($result, $row) {
  710. $temp = @mysql_data_seek($result, $row);
  711. if(!$temp) {
  712. //throw new Exception('Could not seek : ' . mysql_error($this->lnk));
  713. }
  714. return $temp;
  715. }
  716. public function nf($result) {
  717. return mysql_num_rows($result);
  718. }
  719. public function af() {
  720. return $this->aff;
  721. }
  722. public function insert_id() {
  723. return $this->iid;
  724. }
  725. protected function escape($input) {
  726. if(is_array($input)) {
  727. foreach($input as $k => $v) {
  728. $input[$k] = $this->escape($v);
  729. }
  730. return implode(',',$input);
  731. }
  732. if(is_string($input)) {
  733. $input = mysql_real_escape_string($input, $this->lnk);
  734. return "'".$input."'";
  735. }
  736. if(is_bool($input)) {
  737. return $input === false ? 0 : 1;
  738. }
  739. if(is_null($input)) {
  740. return 'NULL';
  741. }
  742. return $input;
  743. }
  744. }
  745. class postgre_driver extends ADriver
  746. {
  747. protected $iid = 0;
  748. protected $aff = 0;
  749. public function __construct($settings) {
  750. parent::__construct($settings);
  751. if(!$this->settings->serverport) { $this->settings->serverport = 5432; }
  752. }
  753. public function connect() {
  754. $this->lnk = ($this->settings->persist) ?
  755. @pg_pconnect(
  756. "host=" . $this->settings->servername . " " .
  757. "port=" . $this->settings->serverport . " " .
  758. "user=" . $this->settings->username . " " .
  759. "password=" . $this->settings->password . " " .
  760. "dbname=" . $this->settings->database . " " .
  761. "options='--client_encoding=".strtoupper($this->settings->charset)."' "
  762. ) :
  763. @pg_connect(
  764. "host=" . $this->settings->servername . " " .
  765. "port=" . $this->settings->serverport . " " .
  766. "user=" . $this->settings->username . " " .
  767. "password=" . $this->settings->password . " " .
  768. "dbname=" . $this->settings->database . " " .
  769. "options='--client_encoding=".strtoupper($this->settings->charset)."' "
  770. );
  771. if($this->lnk === false) {
  772. throw new Exception('Connect error');
  773. }
  774. if($this->settings->timezone) {
  775. @pg_query($this->lnk, "SET TIME ZONE '".addslashes($this->settings->timezone)."'");
  776. }
  777. return true;
  778. }
  779. public function disconnect() {
  780. if(is_resource($this->lnk)) {
  781. pg_close($this->lnk);
  782. }
  783. }
  784. public function real_query($sql) {
  785. return $this->query($sql);
  786. }
  787. public function prepare($sql) {
  788. if(!$this->is_connected()) { $this->connect(); }
  789. $binder = '?';
  790. if(strpos($sql, $binder) !== false) {
  791. $tmp = explode($binder, $sql);
  792. $sql = $tmp[0];
  793. foreach($tmp as $i => $v) {
  794. $sql .= '$' . ($i + 1);
  795. if(isset($tmp[($i + 1)])) {
  796. $sql .= $tmp[($i + 1)];
  797. }
  798. }
  799. }
  800. return $sql;
  801. }
  802. public function execute($sql, $data = array()) {
  803. if(!$this->is_connected()) { $this->connect(); }
  804. if(!is_array($data)) { $data = array(); }
  805. $temp = (is_array($data) && count($data)) ? pg_query_params($this->lnk, $sql, $data) : pg_query_params($this->lnk, $sql, array());
  806. if(!$temp) {
  807. throw new Exception('Could not execute query : ' . pg_last_error($this->lnk) . ' <'.$sql.'>');
  808. }
  809. if(preg_match('@^\s*(INSERT|REPLACE)\s+INTO@i', $sql)) {
  810. $this->iid = pg_query($this->lnk, 'SELECT lastval()');
  811. $this->aff = pg_affected_rows($temp);
  812. }
  813. return $temp;
  814. }
  815. public function nextr($result) {
  816. return pg_fetch_array($result, NULL, PGSQL_BOTH);
  817. }
  818. public function seek($result, $row) {
  819. $temp = @pg_result_seek($result, $row);
  820. if(!$temp) {
  821. //throw new Exception('Could not seek : ' . pg_last_error($this->lnk));
  822. }
  823. return $temp;
  824. }
  825. public function nf($result) {
  826. return pg_num_rows($result);
  827. }
  828. public function af() {
  829. return $this->aff;
  830. }
  831. public function insert_id() {
  832. return $this->iid;
  833. }
  834. // Функция mysql_query?
  835. // - http://okbob.blogspot.com/2009/08/mysql-functions-for-postgresql.html
  836. // - http://www.xach.com/aolserver/mysql-to-postgresql.html
  837. // - REPLACE unixtimestamp / limit / curdate
  838. }
  839. class oracle_driver extends ADriver
  840. {
  841. protected $iid = 0;
  842. protected $aff = 0;
  843. public function connect() {
  844. $this->lnk = ($this->settings->persist) ?
  845. @oci_pconnect($this->settings->username, $this->settings->password, $this->settings->servername, $this->settings->charset) :
  846. @oci_connect ($this->settings->username, $this->settings->password, $this->settings->servername, $this->settings->charset);
  847. if($this->lnk === false) {
  848. throw new Exception('Connect error : ' . oci_error());
  849. }
  850. if($this->settings->timezone) {
  851. $this->real_query("ALTER session SET time_zone = '" . addslashes($this->settings->timezone) . "'");
  852. }
  853. return true;
  854. }
  855. public function disconnect() {
  856. if(is_resource($this->lnk)) {
  857. oci_close($this->lnk);
  858. }
  859. }
  860. public function real_query($sql) {
  861. if(!$this->is_connected()) { $this->connect(); }
  862. $temp = oci_parse($this->lnk, $sql);
  863. if(!$temp || !oci_execute($temp)) {
  864. throw new Exception('Could not execute real query : ' . oci_error($temp));
  865. }
  866. $this->aff = oci_num_rows($temp);
  867. return $temp;
  868. }
  869. public function prepare($sql) {
  870. if(!$this->is_connected()) { $this->connect(); }
  871. $binder = '?';
  872. if(strpos($sql, $binder) !== false && $vars !== false) {
  873. $tmp = explode($this->binder, $sql);
  874. $sql = $tmp[0];
  875. foreach($tmp as $i => $v) {
  876. $sql .= ':f' . $i;
  877. if(isset($tmp[($i + 1)])) {
  878. $sql .= $tmp[($i + 1)];
  879. }
  880. }
  881. }
  882. return oci_parse($this->lnk, $sql);
  883. }
  884. public function execute($sql, $data = array()) {
  885. if(!$this->is_connected()) { $this->connect(); }
  886. if(!is_array($data)) { $data = array(); }
  887. $data = array_values($data);
  888. foreach($data as $i => $v) {
  889. switch(gettype($v)) {
  890. case "boolean":
  891. case "integer":
  892. $data[$i] = (int)$v;
  893. oci_bind_by_name($sql, 'f'.$i, $data[$i], SQLT_INT);
  894. break;
  895. case "array":
  896. // TODO: work around IN statements?
  897. $data[$i] = implode(',',$v);
  898. oci_bind_by_name($sql, 'f'.$i, $data[$i]);
  899. break;
  900. case "object":
  901. case "resource":
  902. $data[$i] = serialize($data[$i]);
  903. oci_bind_by_name($sql, 'f'.$i, $data[$i]);
  904. break;
  905. default:
  906. oci_bind_by_name($sql, 'f'.$i, $data[$i]);
  907. break;
  908. }
  909. }
  910. $temp = oci_execute($sql);
  911. if(!$temp) {
  912. throw new Exception('Could not execute query : ' . oci_error($sql));
  913. }
  914. $this->aff = oci_num_rows($sql);
  915. /* TODO: get iid
  916. if(!$seqname) { return $this->error('INSERT_ID not supported with no sequence.'); }
  917. $stm = oci_parse($this->link, 'SELECT '.strtoupper(str_replace("'",'',$seqname)).'.CURRVAL FROM DUAL');
  918. oci_execute($stm, $sql);
  919. $tmp = oci_fetch_array($stm);
  920. $tmp = $tmp[0];
  921. oci_free_statement($stm);
  922. */
  923. return $sql;
  924. }
  925. public function nextr($result) {
  926. return oci_fetch_array($result, OCI_BOTH);
  927. }
  928. public function seek($result, $row) {
  929. $cnt = 0;
  930. while($cnt < $row) {
  931. if(oci_fetch_array($result, OCI_BOTH) === false) {
  932. return false;
  933. }
  934. $cnt++;
  935. }
  936. return true;
  937. }
  938. public function nf($result) {
  939. return oci_num_rows($result);
  940. }
  941. public function af() {
  942. return $this->aff;
  943. }
  944. public function insert_id() {
  945. return $this->iid;
  946. }
  947. }
  948. class ibase_driver extends ADriver
  949. {
  950. protected $iid = 0;
  951. protected $aff = 0;
  952. public function __construct($settings) {
  953. parent::__construct($settings);
  954. if(!is_file($this->settings->database) && is_file('/'.$this->settings->database)) {
  955. $this->settings->database = '/'.$this->settings->database;
  956. }
  957. $this->settings->servername = ($this->settings->servername === 'localhost' || $this->settings->servername === '127.0.0.1' || $this->settings->servername === '') ?
  958. '' :
  959. $this->settings->servername . ':';
  960. }
  961. public function connect() {
  962. $this->lnk = ($this->settings->persist) ?
  963. @ibase_pconnect(
  964. $this->settings->servername . $this->settings->database,
  965. $this->settings->username,
  966. $this->settings->password,
  967. strtoupper($this->settings->charset)
  968. ) :
  969. @ibase_connect(
  970. $this->settings->servername . $this->settings->database,
  971. $this->settings->username,
  972. $this->settings->password,
  973. strtoupper($this->settings->charset)
  974. );
  975. if($this->lnk === false) {
  976. throw new Exception('Connect error: ' . ibase_errmsg());
  977. }
  978. return true;
  979. }
  980. public function disconnect() {
  981. if(is_resource($this->lnk)) {
  982. ibase_close($this->lnk);
  983. }
  984. }
  985. public function real_query($sql) {
  986. if(!$this->is_connected()) { $this->connect(); }
  987. $temp = ibase_query($sql, $this->lnk);
  988. if(!$temp) {
  989. throw new Exception('Could not execute query : ' . ibase_errmsg() . ' <'.$sql.'>');
  990. }
  991. //$this->iid = mysql_insert_id($this->lnk);
  992. $this->aff = ibase_affected_rows($this->lnk);
  993. return $temp;
  994. }
  995. public function prepare($sql) {
  996. if(!$this->is_connected()) { $this->connect(); }
  997. return ibase_prepare($this->lnk, $sql);
  998. }
  999. public function execute($sql, $data = array()) {
  1000. if(!$this->is_connected()) { $this->connect(); }
  1001. if(!is_array($data)) { $data = array(); }
  1002. $data = array_values($data);
  1003. foreach($data as $i => $v) {
  1004. switch(gettype($v)) {
  1005. case "boolean":
  1006. case "integer":
  1007. $data[$i] = (int)$v;
  1008. break;
  1009. case "array":
  1010. // TODO: work around IN statements?
  1011. $data[$i] = implode(',',$v);
  1012. break;
  1013. case "object":
  1014. case "resource":
  1015. $data[$i] = serialize($data[$i]);
  1016. break;
  1017. }
  1018. }
  1019. array_unshift($data, $sql);
  1020. $temp = call_user_func_array("ibase_execute", $data);
  1021. if(!$temp) {
  1022. throw new Exception('Could not execute query : ' . ibase_errmsg() . ' <'.$sql.'>');
  1023. }
  1024. $this->aff = ibase_affected_rows($this->lnk);
  1025. return $temp;
  1026. }
  1027. public function nextr($result) {
  1028. return ibase_fetch_assoc($result, IBASE_TEXT);
  1029. }
  1030. public function seek($result, $row) {
  1031. return false;
  1032. }
  1033. public function nf($result) {
  1034. return false;
  1035. }
  1036. public function af() {
  1037. return $this->aff;
  1038. }
  1039. public function insert_id() {
  1040. return $this->iid;
  1041. }
  1042. }
  1043. }