medoo.php 23 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115
  1. <?php
  2. /*!
  3. * Medoo database framework
  4. * http://medoo.in
  5. * Version 1.1.2
  6. *
  7. * Copyright 2016, Angel Lai
  8. * Released under the MIT license
  9. */
  10. class medoo
  11. {
  12. // General
  13. protected $database_type;
  14. protected $charset;
  15. protected $database_name;
  16. // For MySQL, MariaDB, MSSQL, Sybase, PostgreSQL, Oracle
  17. protected $server;
  18. protected $username;
  19. protected $password;
  20. // For SQLite
  21. protected $database_file;
  22. // For MySQL or MariaDB with unix_socket
  23. protected $socket;
  24. // Optional
  25. protected $port;
  26. protected $prefix;
  27. protected $option = array();
  28. // Variable
  29. protected $logs = array();
  30. protected $debug_mode = false;
  31. public function __construct($options = null)
  32. {
  33. try {
  34. $commands = array();
  35. $dsn = '';
  36. if (is_array($options))
  37. {
  38. foreach ($options as $option => $value)
  39. {
  40. $this->$option = $value;
  41. }
  42. }
  43. else
  44. {
  45. return false;
  46. }
  47. if (
  48. isset($this->port) &&
  49. is_int($this->port * 1)
  50. )
  51. {
  52. $port = $this->port;
  53. }
  54. $type = strtolower($this->database_type);
  55. $is_port = isset($port);
  56. if (isset($options[ 'prefix' ]))
  57. {
  58. $this->prefix = $options[ 'prefix' ];
  59. }
  60. switch ($type)
  61. {
  62. case 'mariadb':
  63. $type = 'mysql';
  64. case 'mysql':
  65. if ($this->socket)
  66. {
  67. $dsn = $type . ':unix_socket=' . $this->socket . ';dbname=' . $this->database_name;
  68. }
  69. else
  70. {
  71. $dsn = $type . ':host=' . $this->server . ($is_port ? ';port=' . $port : '') . ';dbname=' . $this->database_name;
  72. }
  73. // Make MySQL using standard quoted identifier
  74. $commands[] = 'SET SQL_MODE=ANSI_QUOTES';
  75. break;
  76. case 'pgsql':
  77. $dsn = $type . ':host=' . $this->server . ($is_port ? ';port=' . $port : '') . ';dbname=' . $this->database_name;
  78. break;
  79. case 'sybase':
  80. $dsn = 'dblib:host=' . $this->server . ($is_port ? ':' . $port : '') . ';dbname=' . $this->database_name;
  81. break;
  82. case 'oracle':
  83. $dbname = $this->server ?
  84. '//' . $this->server . ($is_port ? ':' . $port : ':1521') . '/' . $this->database_name :
  85. $this->database_name;
  86. $dsn = 'oci:dbname=' . $dbname . ($this->charset ? ';charset=' . $this->charset : '');
  87. break;
  88. case 'mssql':
  89. $dsn = strstr(PHP_OS, 'WIN') ?
  90. 'sqlsrv:server=' . $this->server . ($is_port ? ',' . $port : '') . ';database=' . $this->database_name :
  91. 'dblib:host=' . $this->server . ($is_port ? ':' . $port : '') . ';dbname=' . $this->database_name;
  92. // Keep MSSQL QUOTED_IDENTIFIER is ON for standard quoting
  93. $commands[] = 'SET QUOTED_IDENTIFIER ON';
  94. break;
  95. case 'sqlite':
  96. $dsn = $type . ':' . $this->database_file;
  97. $this->username = null;
  98. $this->password = null;
  99. break;
  100. }
  101. if (
  102. in_array($type, array('mariadb', 'mysql', 'pgsql', 'sybase', 'mssql')) &&
  103. $this->charset
  104. )
  105. {
  106. $commands[] = "SET NAMES '" . $this->charset . "'";
  107. }
  108. $this->pdo = new PDO(
  109. $dsn,
  110. $this->username,
  111. $this->password,
  112. $this->option
  113. );
  114. foreach ($commands as $value)
  115. {
  116. $this->pdo->exec($value);
  117. }
  118. }
  119. catch (PDOException $e) {
  120. throw new Exception($e->getMessage());
  121. }
  122. }
  123. public function query($query)
  124. {
  125. if ($this->debug_mode)
  126. {
  127. echo $query;
  128. $this->debug_mode = false;
  129. return false;
  130. }
  131. $this->logs[] = $query;
  132. return $this->pdo->query($query);
  133. }
  134. public function exec($query)
  135. {
  136. if ($this->debug_mode)
  137. {
  138. echo $query;
  139. $this->debug_mode = false;
  140. return false;
  141. }
  142. $this->logs[] = $query;
  143. return $this->pdo->exec($query);
  144. }
  145. public function quote($string)
  146. {
  147. return $this->pdo->quote($string);
  148. }
  149. protected function table_quote($table)
  150. {
  151. return '"' . $this->prefix . $table . '"';
  152. }
  153. protected function column_quote($string)
  154. {
  155. preg_match('/(\(JSON\)\s*|^#)?([a-zA-Z0-9_]*)\.([a-zA-Z0-9_]*)/', $string, $column_match);
  156. if (isset($column_match[ 2 ], $column_match[ 3 ]))
  157. {
  158. return '"' . $this->prefix . $column_match[ 2 ] . '"."' . $column_match[ 3 ] . '"';
  159. }
  160. return '"' . $string . '"';
  161. }
  162. protected function column_push(&$columns)
  163. {
  164. if ($columns == '*')
  165. {
  166. return $columns;
  167. }
  168. if (is_string($columns))
  169. {
  170. $columns = array($columns);
  171. }
  172. $stack = array();
  173. foreach ($columns as $key => $value)
  174. {
  175. if (is_array($value))
  176. {
  177. $stack[] = $this->column_push($value);
  178. }
  179. else
  180. {
  181. preg_match('/([a-zA-Z0-9_\-\.]*)\s*\(([a-zA-Z0-9_\-]*)\)/i', $value, $match);
  182. if (isset($match[ 1 ], $match[ 2 ]))
  183. {
  184. $stack[] = $this->column_quote( $match[ 1 ] ) . ' AS ' . $this->column_quote( $match[ 2 ] );
  185. $columns[ $key ] = $match[ 2 ];
  186. }
  187. else
  188. {
  189. $stack[] = $this->column_quote( $value );
  190. }
  191. }
  192. }
  193. return implode($stack, ',');
  194. }
  195. protected function array_quote($array)
  196. {
  197. $temp = array();
  198. foreach ($array as $value)
  199. {
  200. $temp[] = is_int($value) ? $value : $this->pdo->quote($value);
  201. }
  202. return implode($temp, ',');
  203. }
  204. protected function inner_conjunct($data, $conjunctor, $outer_conjunctor)
  205. {
  206. $haystack = array();
  207. foreach ($data as $value)
  208. {
  209. $haystack[] = '(' . $this->data_implode($value, $conjunctor) . ')';
  210. }
  211. return implode($outer_conjunctor . ' ', $haystack);
  212. }
  213. protected function fn_quote($column, $string)
  214. {
  215. return (strpos($column, '#') === 0 && preg_match('/^[A-Z0-9\_]*\([^)]*\)$/', $string)) ?
  216. $string :
  217. $this->quote($string);
  218. }
  219. protected function data_implode($data, $conjunctor, $outer_conjunctor = null)
  220. {
  221. $wheres = array();
  222. foreach ($data as $key => $value)
  223. {
  224. $type = gettype($value);
  225. if (
  226. preg_match("/^(AND|OR)(\s+#.*)?$/i", $key, $relation_match) &&
  227. $type == 'array'
  228. )
  229. {
  230. $wheres[] = 0 !== count(array_diff_key($value, array_keys(array_keys($value)))) ?
  231. '(' . $this->data_implode($value, ' ' . $relation_match[ 1 ]) . ')' :
  232. '(' . $this->inner_conjunct($value, ' ' . $relation_match[ 1 ], $conjunctor) . ')';
  233. }
  234. else
  235. {
  236. preg_match('/(#?)([\w\.\-]+)(\[(\>|\>\=|\<|\<\=|\!|\<\>|\>\<|\!?~)\])?/i', $key, $match);
  237. $column = $this->column_quote($match[ 2 ]);
  238. if (isset($match[ 4 ]))
  239. {
  240. $operator = $match[ 4 ];
  241. if ($operator == '!')
  242. {
  243. switch ($type)
  244. {
  245. case 'NULL':
  246. $wheres[] = $column . ' IS NOT NULL';
  247. break;
  248. case 'array':
  249. $wheres[] = $column . ' NOT IN (' . $this->array_quote($value) . ')';
  250. break;
  251. case 'integer':
  252. case 'double':
  253. $wheres[] = $column . ' != ' . $value;
  254. break;
  255. case 'boolean':
  256. $wheres[] = $column . ' != ' . ($value ? '1' : '0');
  257. break;
  258. case 'string':
  259. $wheres[] = $column . ' != ' . $this->fn_quote($key, $value);
  260. break;
  261. }
  262. }
  263. if ($operator == '<>' || $operator == '><')
  264. {
  265. if ($type == 'array')
  266. {
  267. if ($operator == '><')
  268. {
  269. $column .= ' NOT';
  270. }
  271. if (is_numeric($value[ 0 ]) && is_numeric($value[ 1 ]))
  272. {
  273. $wheres[] = '(' . $column . ' BETWEEN ' . $value[ 0 ] . ' AND ' . $value[ 1 ] . ')';
  274. }
  275. else
  276. {
  277. $wheres[] = '(' . $column . ' BETWEEN ' . $this->quote($value[ 0 ]) . ' AND ' . $this->quote($value[ 1 ]) . ')';
  278. }
  279. }
  280. }
  281. if ($operator == '~' || $operator == '!~')
  282. {
  283. if ($type != 'array')
  284. {
  285. $value = array($value);
  286. }
  287. $like_clauses = array();
  288. foreach ($value as $item)
  289. {
  290. $item = strval($item);
  291. $suffix = mb_substr($item, -1, 1);
  292. if (preg_match('/^(?!(%|\[|_])).+(?<!(%|\]|_))$/', $item))
  293. {
  294. $item = '%' . $item . '%';
  295. }
  296. $like_clauses[] = $column . ($operator === '!~' ? ' NOT' : '') . ' LIKE ' . $this->fn_quote($key, $item);
  297. }
  298. $wheres[] = implode(' OR ', $like_clauses);
  299. }
  300. if (in_array($operator, array('>', '>=', '<', '<=')))
  301. {
  302. if (is_numeric($value))
  303. {
  304. $wheres[] = $column . ' ' . $operator . ' ' . $value;
  305. }
  306. elseif (strpos($key, '#') === 0)
  307. {
  308. $wheres[] = $column . ' ' . $operator . ' ' . $this->fn_quote($key, $value);
  309. }
  310. else
  311. {
  312. $wheres[] = $column . ' ' . $operator . ' ' . $this->quote($value);
  313. }
  314. }
  315. }
  316. else
  317. {
  318. switch ($type)
  319. {
  320. case 'NULL':
  321. $wheres[] = $column . ' IS NULL';
  322. break;
  323. case 'array':
  324. $wheres[] = $column . ' IN (' . $this->array_quote($value) . ')';
  325. break;
  326. case 'integer':
  327. case 'double':
  328. $wheres[] = $column . ' = ' . $value;
  329. break;
  330. case 'boolean':
  331. $wheres[] = $column . ' = ' . ($value ? '1' : '0');
  332. break;
  333. case 'string':
  334. $wheres[] = $column . ' = ' . $this->fn_quote($key, $value);
  335. break;
  336. }
  337. }
  338. }
  339. }
  340. return implode($conjunctor . ' ', $wheres);
  341. }
  342. protected function where_clause($where)
  343. {
  344. $where_clause = '';
  345. if (is_array($where))
  346. {
  347. $where_keys = array_keys($where);
  348. $where_AND = preg_grep("/^AND\s*#?$/i", $where_keys);
  349. $where_OR = preg_grep("/^OR\s*#?$/i", $where_keys);
  350. $single_condition = array_diff_key($where, array_flip(
  351. array('AND', 'OR', 'GROUP', 'ORDER', 'HAVING', 'LIMIT', 'LIKE', 'MATCH')
  352. ));
  353. if ($single_condition != array())
  354. {
  355. $condition = $this->data_implode($single_condition, '');
  356. if ($condition != '')
  357. {
  358. $where_clause = ' WHERE ' . $condition;
  359. }
  360. }
  361. if (!empty($where_AND))
  362. {
  363. $value = array_values($where_AND);
  364. $where_clause = ' WHERE ' . $this->data_implode($where[ $value[ 0 ] ], ' AND');
  365. }
  366. if (!empty($where_OR))
  367. {
  368. $value = array_values($where_OR);
  369. $where_clause = ' WHERE ' . $this->data_implode($where[ $value[ 0 ] ], ' OR');
  370. }
  371. if (isset($where[ 'MATCH' ]))
  372. {
  373. $MATCH = $where[ 'MATCH' ];
  374. if (is_array($MATCH) && isset($MATCH[ 'columns' ], $MATCH[ 'keyword' ]))
  375. {
  376. $where_clause .= ($where_clause != '' ? ' AND ' : ' WHERE ') . ' MATCH ("' . str_replace('.', '"."', implode($MATCH[ 'columns' ], '", "')) . '") AGAINST (' . $this->quote($MATCH[ 'keyword' ]) . ')';
  377. }
  378. }
  379. if (isset($where[ 'GROUP' ]))
  380. {
  381. $where_clause .= ' GROUP BY ' . $this->column_quote($where[ 'GROUP' ]);
  382. if (isset($where[ 'HAVING' ]))
  383. {
  384. $where_clause .= ' HAVING ' . $this->data_implode($where[ 'HAVING' ], ' AND');
  385. }
  386. }
  387. if (isset($where[ 'ORDER' ]))
  388. {
  389. $ORDER = $where[ 'ORDER' ];
  390. if (is_array($ORDER))
  391. {
  392. $stack = array();
  393. foreach ($ORDER as $column => $value)
  394. {
  395. if (is_array($value))
  396. {
  397. $stack[] = 'FIELD(' . $this->column_quote($column) . ', ' . $this->array_quote($value) . ')';
  398. }
  399. else if ($value === 'ASC' || $value === 'DESC')
  400. {
  401. $stack[] = $this->column_quote($column) . ' ' . $value;
  402. }
  403. else if (is_int($column))
  404. {
  405. $stack[] = $this->column_quote($value);
  406. }
  407. }
  408. $where_clause .= ' ORDER BY ' . implode($stack, ',');
  409. }
  410. else
  411. {
  412. $where_clause .= ' ORDER BY ' . $this->column_quote($ORDER);
  413. }
  414. }
  415. if (isset($where[ 'LIMIT' ]))
  416. {
  417. $LIMIT = $where[ 'LIMIT' ];
  418. if (is_numeric($LIMIT))
  419. {
  420. $where_clause .= ' LIMIT ' . $LIMIT;
  421. }
  422. if (
  423. is_array($LIMIT) &&
  424. is_numeric($LIMIT[ 0 ]) &&
  425. is_numeric($LIMIT[ 1 ])
  426. )
  427. {
  428. if ($this->database_type === 'pgsql')
  429. {
  430. $where_clause .= ' OFFSET ' . $LIMIT[ 0 ] . ' LIMIT ' . $LIMIT[ 1 ];
  431. }
  432. else
  433. {
  434. $where_clause .= ' LIMIT ' . $LIMIT[ 0 ] . ',' . $LIMIT[ 1 ];
  435. }
  436. }
  437. }
  438. }
  439. else
  440. {
  441. if ($where != null)
  442. {
  443. $where_clause .= ' ' . $where;
  444. }
  445. }
  446. return $where_clause;
  447. }
  448. protected function select_context($table, $join, &$columns = null, $where = null, $column_fn = null)
  449. {
  450. preg_match('/([a-zA-Z0-9_\-]*)\s*\(([a-zA-Z0-9_\-]*)\)/i', $table, $table_match);
  451. if (isset($table_match[ 1 ], $table_match[ 2 ]))
  452. {
  453. $table = $this->table_quote($table_match[ 1 ]);
  454. $table_query = $this->table_quote($table_match[ 1 ]) . ' AS ' . $this->table_quote($table_match[ 2 ]);
  455. }
  456. else
  457. {
  458. $table = $this->table_quote($table);
  459. $table_query = $table;
  460. }
  461. $join_key = is_array($join) ? array_keys($join) : null;
  462. if (
  463. isset($join_key[ 0 ]) &&
  464. strpos($join_key[ 0 ], '[') === 0
  465. )
  466. {
  467. $table_join = array();
  468. $join_array = array(
  469. '>' => 'LEFT',
  470. '<' => 'RIGHT',
  471. '<>' => 'FULL',
  472. '><' => 'INNER'
  473. );
  474. foreach($join as $sub_table => $relation)
  475. {
  476. preg_match('/(\[(\<|\>|\>\<|\<\>)\])?([a-zA-Z0-9_\-]*)\s?(\(([a-zA-Z0-9_\-]*)\))?/', $sub_table, $match);
  477. if ($match[ 2 ] != '' && $match[ 3 ] != '')
  478. {
  479. if (is_string($relation))
  480. {
  481. $relation = 'USING ("' . $relation . '")';
  482. }
  483. if (is_array($relation))
  484. {
  485. // For ['column1', 'column2']
  486. if (isset($relation[ 0 ]))
  487. {
  488. $relation = 'USING ("' . implode($relation, '", "') . '")';
  489. }
  490. else
  491. {
  492. $joins = array();
  493. foreach ($relation as $key => $value)
  494. {
  495. $joins[] = (
  496. strpos($key, '.') > 0 ?
  497. // For ['tableB.column' => 'column']
  498. $this->column_quote($key) :
  499. // For ['column1' => 'column2']
  500. $table . '."' . $key . '"'
  501. ) .
  502. ' = ' .
  503. $this->table_quote(isset($match[ 5 ]) ? $match[ 5 ] : $match[ 3 ]) . '."' . $value . '"';
  504. }
  505. $relation = 'ON ' . implode($joins, ' AND ');
  506. }
  507. }
  508. $table_name = $this->table_quote($match[ 3 ]) . ' ';
  509. if (isset($match[ 5 ]))
  510. {
  511. $table_name .= 'AS ' . $this->table_quote($match[ 5 ]) . ' ';
  512. }
  513. $table_join[] = $join_array[ $match[ 2 ] ] . ' JOIN ' . $table_name . $relation;
  514. }
  515. }
  516. $table_query .= ' ' . implode($table_join, ' ');
  517. }
  518. else
  519. {
  520. if (is_null($columns))
  521. {
  522. if (is_null($where))
  523. {
  524. if (
  525. is_array($join) &&
  526. isset($column_fn)
  527. )
  528. {
  529. $where = $join;
  530. $columns = null;
  531. }
  532. else
  533. {
  534. $where = null;
  535. $columns = $join;
  536. }
  537. }
  538. else
  539. {
  540. $where = $join;
  541. $columns = null;
  542. }
  543. }
  544. else
  545. {
  546. $where = $columns;
  547. $columns = $join;
  548. }
  549. }
  550. if (isset($column_fn))
  551. {
  552. if ($column_fn == 1)
  553. {
  554. $column = '1';
  555. if (is_null($where))
  556. {
  557. $where = $columns;
  558. }
  559. }
  560. else
  561. {
  562. if (empty($columns))
  563. {
  564. $columns = '*';
  565. $where = $join;
  566. }
  567. $column = $column_fn . '(' . $this->column_push($columns) . ')';
  568. }
  569. }
  570. else
  571. {
  572. $column = $this->column_push($columns);
  573. }
  574. return 'SELECT ' . $column . ' FROM ' . $table_query . $this->where_clause($where);
  575. }
  576. protected function data_map($index, $key, $value, $data, &$stack)
  577. {
  578. if (is_array($value))
  579. {
  580. $sub_stack = array();
  581. foreach ($value as $sub_key => $sub_value)
  582. {
  583. if (is_array($sub_value))
  584. {
  585. $current_stack = $stack[ $index ][ $key ];
  586. $this->data_map(false, $sub_key, $sub_value, $data, $current_stack);
  587. $stack[ $index ][ $key ][ $sub_key ] = $current_stack[ 0 ][ $sub_key ];
  588. }
  589. else
  590. {
  591. $this->data_map(false, preg_replace('/^[\w]*\./i', "", $sub_value), $sub_key, $data, $sub_stack);
  592. $stack[ $index ][ $key ] = $sub_stack;
  593. }
  594. }
  595. }
  596. else
  597. {
  598. if ($index !== false)
  599. {
  600. $stack[ $index ][ $value ] = $data[ $value ];
  601. }
  602. else
  603. {
  604. $stack[ $key ] = $data[ $key ];
  605. }
  606. }
  607. }
  608. public function select($table, $join, $columns = null, $where = null)
  609. {
  610. $column = $where == null ? $join : $columns;
  611. $is_single_column = (is_string($column) && $column !== '*');
  612. $query = $this->query($this->select_context($table, $join, $columns, $where));
  613. $stack = array();
  614. $index = 0;
  615. if (!$query)
  616. {
  617. return false;
  618. }
  619. if ($columns === '*')
  620. {
  621. return $query->fetchAll(PDO::FETCH_ASSOC);
  622. }
  623. if ($is_single_column)
  624. {
  625. return $query->fetchAll(PDO::FETCH_COLUMN);
  626. }
  627. while ($row = $query->fetch(PDO::FETCH_ASSOC))
  628. {
  629. foreach ($columns as $key => $value)
  630. {
  631. if (is_array($value))
  632. {
  633. $this->data_map($index, $key, $value, $row, $stack);
  634. }
  635. else
  636. {
  637. $this->data_map($index, $key, preg_replace('/^[\w]*\./i', "", $value), $row, $stack);
  638. }
  639. }
  640. $index++;
  641. }
  642. return $stack;
  643. }
  644. public function insert($table, $datas)
  645. {
  646. $lastId = array();
  647. // Check indexed or associative array
  648. if (!isset($datas[ 0 ]))
  649. {
  650. $datas = array($datas);
  651. }
  652. foreach ($datas as $data)
  653. {
  654. $values = array();
  655. $columns = array();
  656. foreach ($data as $key => $value)
  657. {
  658. $columns[] = preg_replace("/^(\(JSON\)\s*|#)/i", "", $key);
  659. switch (gettype($value))
  660. {
  661. case 'NULL':
  662. $values[] = 'NULL';
  663. break;
  664. case 'array':
  665. preg_match("/\(JSON\)\s*([\w]+)/i", $key, $column_match);
  666. $values[] = isset($column_match[ 0 ]) ?
  667. $this->quote(json_encode($value)) :
  668. $this->quote(serialize($value));
  669. break;
  670. case 'boolean':
  671. $values[] = ($value ? '1' : '0');
  672. break;
  673. case 'integer':
  674. case 'double':
  675. case 'string':
  676. $values[] = $this->fn_quote($key, $value);
  677. break;
  678. }
  679. }
  680. $this->exec('INSERT INTO ' . $this->table_quote($table) . ' (' . implode(', ', $columns) . ') VALUES (' . implode($values, ', ') . ')');
  681. $lastId[] = $this->pdo->lastInsertId();
  682. }
  683. return count($lastId) > 1 ? $lastId : $lastId[ 0 ];
  684. }
  685. public function update($table, $data, $where = null)
  686. {
  687. $fields = array();
  688. foreach ($data as $key => $value)
  689. {
  690. preg_match('/([\w]+)(\[(\+|\-|\*|\/)\])?/i', $key, $match);
  691. if (isset($match[ 3 ]))
  692. {
  693. if (is_numeric($value))
  694. {
  695. $fields[] = $this->column_quote($match[ 1 ]) . ' = ' . $this->column_quote($match[ 1 ]) . ' ' . $match[ 3 ] . ' ' . $value;
  696. }
  697. }
  698. else
  699. {
  700. $column = $this->column_quote(preg_replace("/^(\(JSON\)\s*|#)/i", "", $key));
  701. switch (gettype($value))
  702. {
  703. case 'NULL':
  704. $fields[] = $column . ' = NULL';
  705. break;
  706. case 'array':
  707. preg_match("/\(JSON\)\s*([\w]+)/i", $key, $column_match);
  708. $fields[] = $column . ' = ' . $this->quote(
  709. isset($column_match[ 0 ]) ? json_encode($value) : serialize($value)
  710. );
  711. break;
  712. case 'boolean':
  713. $fields[] = $column . ' = ' . ($value ? '1' : '0');
  714. break;
  715. case 'integer':
  716. case 'double':
  717. case 'string':
  718. $fields[] = $column . ' = ' . $this->fn_quote($key, $value);
  719. break;
  720. }
  721. }
  722. }
  723. return $this->exec('UPDATE ' . $this->table_quote($table) . ' SET ' . implode(', ', $fields) . $this->where_clause($where));
  724. }
  725. public function delete($table, $where)
  726. {
  727. return $this->exec('DELETE FROM ' . $this->table_quote($table) . $this->where_clause($where));
  728. }
  729. public function replace($table, $columns, $search = null, $replace = null, $where = null)
  730. {
  731. if (is_array($columns))
  732. {
  733. $replace_query = array();
  734. foreach ($columns as $column => $replacements)
  735. {
  736. foreach ($replacements as $replace_search => $replace_replacement)
  737. {
  738. $replace_query[] = $column . ' = REPLACE(' . $this->column_quote($column) . ', ' . $this->quote($replace_search) . ', ' . $this->quote($replace_replacement) . ')';
  739. }
  740. }
  741. $replace_query = implode(', ', $replace_query);
  742. $where = $search;
  743. }
  744. else
  745. {
  746. if (is_array($search))
  747. {
  748. $replace_query = array();
  749. foreach ($search as $replace_search => $replace_replacement)
  750. {
  751. $replace_query[] = $columns . ' = REPLACE(' . $this->column_quote($columns) . ', ' . $this->quote($replace_search) . ', ' . $this->quote($replace_replacement) . ')';
  752. }
  753. $replace_query = implode(', ', $replace_query);
  754. $where = $replace;
  755. }
  756. else
  757. {
  758. $replace_query = $columns . ' = REPLACE(' . $this->column_quote($columns) . ', ' . $this->quote($search) . ', ' . $this->quote($replace) . ')';
  759. }
  760. }
  761. return $this->exec('UPDATE ' . $this->table_quote($table) . ' SET ' . $replace_query . $this->where_clause($where));
  762. }
  763. public function get($table, $join = null, $columns = null, $where = null)
  764. {
  765. $column = $where == null ? $join : $columns;
  766. $is_single_column = (is_string($column) && $column !== '*');
  767. $query = $this->query($this->select_context($table, $join, $columns, $where) . ' LIMIT 1');
  768. if ($query)
  769. {
  770. $data = $query->fetchAll(PDO::FETCH_ASSOC);
  771. if (isset($data[ 0 ]))
  772. {
  773. if ($is_single_column)
  774. {
  775. return $data[ 0 ][ preg_replace('/^[\w]*\./i', "", $column) ];
  776. }
  777. if ($column === '*')
  778. {
  779. return $data[ 0 ];
  780. }
  781. $stack = array();
  782. foreach ($columns as $key => $value)
  783. {
  784. if (is_array($value))
  785. {
  786. $this->data_map(0, $key, $value, $data[ 0 ], $stack);
  787. }
  788. else
  789. {
  790. $this->data_map(0, $key, preg_replace('/^[\w]*\./i', "", $value), $data[ 0 ], $stack);
  791. }
  792. }
  793. return $stack[ 0 ];
  794. }
  795. else
  796. {
  797. return false;
  798. }
  799. }
  800. else
  801. {
  802. return false;
  803. }
  804. }
  805. public function has($table, $join, $where = null)
  806. {
  807. $column = null;
  808. $query = $this->query('SELECT EXISTS(' . $this->select_context($table, $join, $column, $where, 1) . ')');
  809. if ($query)
  810. {
  811. return $query->fetchColumn() === '1';
  812. }
  813. else
  814. {
  815. return false;
  816. }
  817. }
  818. public function count($table, $join = null, $column = null, $where = null)
  819. {
  820. $query = $this->query($this->select_context($table, $join, $column, $where, 'COUNT'));
  821. return $query ? 0 + $query->fetchColumn() : false;
  822. }
  823. public function max($table, $join, $column = null, $where = null)
  824. {
  825. $query = $this->query($this->select_context($table, $join, $column, $where, 'MAX'));
  826. if ($query)
  827. {
  828. $max = $query->fetchColumn();
  829. return is_numeric($max) ? $max + 0 : $max;
  830. }
  831. else
  832. {
  833. return false;
  834. }
  835. }
  836. public function min($table, $join, $column = null, $where = null)
  837. {
  838. $query = $this->query($this->select_context($table, $join, $column, $where, 'MIN'));
  839. if ($query)
  840. {
  841. $min = $query->fetchColumn();
  842. return is_numeric($min) ? $min + 0 : $min;
  843. }
  844. else
  845. {
  846. return false;
  847. }
  848. }
  849. public function avg($table, $join, $column = null, $where = null)
  850. {
  851. $query = $this->query($this->select_context($table, $join, $column, $where, 'AVG'));
  852. return $query ? 0 + $query->fetchColumn() : false;
  853. }
  854. public function sum($table, $join, $column = null, $where = null)
  855. {
  856. $query = $this->query($this->select_context($table, $join, $column, $where, 'SUM'));
  857. return $query ? 0 + $query->fetchColumn() : false;
  858. }
  859. public function action($actions)
  860. {
  861. if (is_callable($actions))
  862. {
  863. $this->pdo->beginTransaction();
  864. $result = $actions($this);
  865. if ($result === false)
  866. {
  867. $this->pdo->rollBack();
  868. }
  869. else
  870. {
  871. $this->pdo->commit();
  872. }
  873. }
  874. else
  875. {
  876. return false;
  877. }
  878. }
  879. public function debug()
  880. {
  881. $this->debug_mode = true;
  882. return $this;
  883. }
  884. public function error()
  885. {
  886. return $this->pdo->errorInfo();
  887. }
  888. public function last_query()
  889. {
  890. return end($this->logs);
  891. }
  892. public function log()
  893. {
  894. return $this->logs;
  895. }
  896. public function info()
  897. {
  898. $output = array(
  899. 'server' => 'SERVER_INFO',
  900. 'driver' => 'DRIVER_NAME',
  901. 'client' => 'CLIENT_VERSION',
  902. 'version' => 'SERVER_VERSION',
  903. 'connection' => 'CONNECTION_STATUS'
  904. );
  905. foreach ($output as $key => $value)
  906. {
  907. $output[ $key ] = $this->pdo->getAttribute(constant('PDO::ATTR_' . $value));
  908. }
  909. return $output;
  910. }
  911. }
  912. ?>