PageRenderTime 71ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/surosql/Surosql.class.php

https://bitbucket.org/SuRaMoN/surosql
PHP | 876 lines | 757 code | 117 blank | 2 comment | 162 complexity | f5136e260ce42b84c6e4d14e1764bc63 MD5 | raw file
Possible License(s): LGPL-3.0, AGPL-1.0, GPL-2.0, LGPL-2.1
  1. <?php
  2. // version: 3.3.0
  3. namespace ns;
  4. use \IteratorAggregate;
  5. use \ReflectionClass;
  6. use \ArrayIterator;
  7. use \Exception;
  8. class Surosql {
  9. /* table - global alias - (fieldname) - localalias */
  10. static $osql;
  11. public $aliases = array();
  12. public $class2info = array();
  13. public $connections = array();
  14. public $imported_query_functions = array();
  15. public $imported_osql_functions = array();
  16. function __construct() {
  17. $utils = array('connect', 'quote_rec', 'quote_lst', 'q', 'quote', 'logand', 'logor', 'count', 'select', 'selectoo', 'from', 'fromoo', 'update', 'delete', 'deleteoo');
  18. foreach($utils as $name) {
  19. $this->imported_osql_functions[$name] = array(__CLASS__, "${name}_util");
  20. }
  21. $bin_string_operators = array('in' => 'IN', 'nin' => 'NOT IN', 'eq' => '=', 'neq' => '!=', 'lt' => '<', 'gt' => '>', 'lte' => '<=', 'gte' => '>=', 'like' => 'LIKE', 'is' => '');
  22. foreach($bin_string_operators as $opname => $op) {
  23. $this->imported_osql_functions[$opname] = create_function('$osql, $key, $val = null', "return " . __CLASS__ . "::operator('$op', \$key, \$val);");
  24. $this->imported_query_functions["if{$opname}"] = create_function('$query, $key, $val = null', "return \$query->andwhere(" . __CLASS__ . "::operator('$op', \$key, \$val));");
  25. }
  26. }
  27. static function quote_rec_util($osql, $var, $maxdepth = PHP_INT_MAX, $depth = 0) {
  28. if(is_array($var) && $maxdepth > $depth) {
  29. foreach($var as $key => &$val) {
  30. $val = self::quote_rec_util($osql, $val, $maxdepth, $depth + 1);
  31. }
  32. return $var;
  33. } else if(is_object($var) && $maxdepth > $depth) {
  34. $obj = clone $var;
  35. foreach(get_object_vars($obj) as $field => $val) {
  36. $obj->$field = self::quote_rec_util($osql, $val, $maxdepth, $depth + 1);
  37. }
  38. return $obj;
  39. } else {
  40. return $osql->quote((string) $var);
  41. }
  42. }
  43. static function quote_lst_util($osql, $lst) {
  44. $lst = is_array($lst) ? self::array_flatten($lst) : explode(',', (string) $lst);
  45. return implode(',', self::quote_rec_util($osql, $lst, 1));
  46. }
  47. static function q_util($osql) {
  48. $args = func_get_args();
  49. return SurosqlBindable::q((array) array_slice($args, 1));
  50. }
  51. static function logand_util() {
  52. $args = func_get_args();
  53. return SurosqlBindable::merge('AND', (array) array_slice($args, 1));
  54. }
  55. static function logor_util() {
  56. $args = func_get_args();
  57. return SurosqlBindable::merge('OR', (array) array_slice($args, 1));
  58. }
  59. static function count_util($osql) {
  60. return SurosqlQuery::newinst($osql)->count();
  61. }
  62. static function select_util($osql, $args = '%*') {
  63. return SurosqlQuery::newinst($osql)->select($args);
  64. }
  65. static function selectoo_util($osql, $args = '%*') {
  66. return SurosqlQuery::newinst($osql)->selectoo($args);
  67. }
  68. static function from_util($osql, $from) {
  69. return $osql->select()->from($from);
  70. }
  71. function __invoke($from) {
  72. return $this->selectoo()->from($from);
  73. }
  74. static function fromoo_util($osql, $from) {
  75. return $osql->selectoo()->from($from);
  76. }
  77. static function update_util($osql, $from = null) {
  78. return SurosqlQuery::newinst($osql)->update($from);
  79. }
  80. static function delete_util($osql, $tables = '') {
  81. return SurosqlQuery::newinst($osql)->delete($tables);
  82. }
  83. static function deleteoo_util($osql, $tables = '') {
  84. return SurosqlQuery::newinst($osql)->deleteoo($tables);
  85. }
  86. function __call($method, $args) {
  87. return call_user_func_array($this->imported_osql_functions[$method], array_merge(array($this), $args));
  88. }
  89. static function __callStatic($method, $args) {
  90. return call_user_func_array(self::$osql->imported_osql_functions[$method], array_merge(array(self::$osql), $args));
  91. }
  92. static function operator($operator, $key, $val = null) {
  93. $conds = array();
  94. if(is_null($val)) {
  95. foreach($key as $col => $colval) {
  96. $conds[] = SurosqlBindable::q($col . ' ' . $operator, $colval);
  97. }
  98. } else {
  99. $keys = is_array($key) ? $key : array($key);
  100. foreach($keys as $col) {
  101. $conds[] = SurosqlBindable::q($col . ' ' . $operator, $val);
  102. }
  103. }
  104. return SurosqlBindable::merge('AND', $conds);
  105. }
  106. static function array_flatten($array) {
  107. if(!is_array($array)) {
  108. return $array;
  109. }
  110. $return_array = array();
  111. foreach($array as $key => $val) {
  112. if(is_array($val)) {
  113. $return_array = array_merge($return_array, self::array_flatten($val));
  114. } else if(is_int($key)) {
  115. $return_array[] = $val;
  116. } else {
  117. $return_array[$key] = $val;
  118. }
  119. }
  120. return $return_array;
  121. }
  122. static function field_serialization($field, $value, $info, $type = 'serialize') {
  123. $default_serialize = array('serialize', 'unserialize');
  124. $type = ($type == 'serialize' ? 0 : 1);
  125. if(!isset($info['serialization'])) {
  126. return $value;
  127. }
  128. foreach($info['serialization'] as $key => $val) {
  129. $field_name = is_array($val) ? $key : $val;
  130. if($field_name === $field) {
  131. $serialization_func = is_array($val) ? $val : $default_serialize;
  132. $value = call_user_func($serialization_func[$type], $value);
  133. }
  134. }
  135. return $value;
  136. }
  137. function glalias2table($glalias) {
  138. return !isset($this->aliases[$glalias]['table']) ? $glalias : $this->aliases[$glalias]['table'];
  139. }
  140. function connect_util($osql, $glalias, $connection, $extra = array()) {
  141. if(preg_match('/^(.*?)\s*-\s*(.*?)\s*(\(\s*(.*?)\s*\))? *$/', $glalias, $matches) != 1) {
  142. throw new Exception('Invalid connection string');
  143. }
  144. list($_, $base, $fieldname) = $matches;
  145. $basetable = $osql->glalias2table($base);
  146. $fieldglalias = (count($matches) == 5 ? $matches[4] : $fieldname);
  147. $fieldtable = $osql->glalias2table($fieldglalias);
  148. $connection = str_replace("%$base%", "%$basetable%", $connection);
  149. $extra = array_merge(array('jointype' => 'INNER'), $extra);
  150. $osql->connections["$base,$fieldname"] = array('sqljoin' => $connection, 'table' => $fieldtable, 'glalias' => $fieldglalias, 'extra' => $extra);
  151. return $osql;
  152. }
  153. function get_class_info($classname) {
  154. if(isset($this->class2info[$classname])) {
  155. return $this->class2info[$classname];
  156. }
  157. $refclass = new ReflectionClass($classname);
  158. $info = $refclass->getStaticPropertyValue('surosql', array());
  159. if(!is_null($refclass->getConstructor())) {
  160. $ooinit = array($refclass, 'newInstance');
  161. } else {
  162. $ooinit = create_function('$a', "return " . __CLASS__ . "::array_to_class(\$a, '$classname');");
  163. }
  164. $classvars = array_keys(array_diff_key(get_class_vars($classname), $refclass->getStaticProperties()));
  165. $info = array_merge(array('connections' => array(), 'table' => $classname, 'class' => $classname, 'ooinit' => $ooinit, 'classvars' => $classvars), $info);
  166. if(isset($info['alias'])) {
  167. $this->aliases[$info['alias']] = $info;
  168. foreach($info['connections'] as $connid => $connsql) {
  169. $this->connect("{$info['alias']} - $connid", $connsql);
  170. }
  171. }
  172. $this->class2info[$classname] = $info;
  173. return $info;
  174. }
  175. function addclass() {
  176. $func_args = func_get_args();
  177. foreach(self::array_flatten($func_args) as $classname) {
  178. $this->get_class_info($classname);
  179. }
  180. return $this;
  181. }
  182. function get_obj_info($obj) {
  183. $info = $this->get_class_info(get_class($obj));
  184. return isset($info['columns']) ? $info : array_merge($info, array('columns' => array_keys(get_object_vars($obj))));
  185. }
  186. static function array_to_class($arr, $classname) {
  187. $obj = new $classname();
  188. foreach($arr as $key => $value) {
  189. $obj->$key = $value;
  190. }
  191. return $obj;
  192. }
  193. function __get($from) {
  194. return $this->selectoo()->from($from);
  195. }
  196. }
  197. class SurosqlBindable {
  198. public $parts;
  199. function __construct($parts = array()) {
  200. $this->parts = array();
  201. $toadd = '';
  202. foreach($parts as $i => $part) {
  203. if(is_array($part)) {
  204. $this->parts[$i - 1] .= ' (';
  205. foreach($part as $i => $p) {
  206. $this->parts[] = $p;
  207. if($i != count($part) - 1) {
  208. $this->parts[] = ', ';
  209. }
  210. }
  211. $toadd = ') ';
  212. } else {
  213. if($toadd != '') {
  214. $this->parts[] = $toadd . $part;
  215. } else {
  216. $this->parts[] = $part;
  217. }
  218. $toadd = '';
  219. }
  220. }
  221. if($toadd != '') {
  222. $this->parts[] = $toadd;
  223. }
  224. }
  225. function andq() {
  226. $args = func_get_args();
  227. return self::merge('AND', $this, self::q($args));
  228. }
  229. function orq() {
  230. $args = func_get_args();
  231. return self::merge('OR', $this, self::q($args));
  232. }
  233. function logand() {
  234. $args = func_get_args();
  235. return self::merge('AND', $this, $args);
  236. }
  237. function logor() {
  238. $args = func_get_args();
  239. return self::merge('OR', $this, $args);
  240. }
  241. function append($boundable) {
  242. if(is_string($boundable)) {
  243. $boundable = new self(array($boundable));
  244. }
  245. if(count($boundable->parts) == 0) { return; }
  246. if($this->parts % 2 == 1) {
  247. $this->parts[count($this->parts) - 1] .= $boundable->parts[0];
  248. $this->parts = array_merge($this->parts, (array) array_slice($boundable->parts, 1));
  249. } else {
  250. $this->parts = array_merge($this->parts, $boundable->parts);
  251. }
  252. }
  253. function encapsulated($prefix = '(', $suffix = ')') {
  254. $ret = new self($this->parts);
  255. if(count($ret->parts) == 0) {
  256. return $ret;
  257. }
  258. $ret->parts[0] = $prefix . $ret->parts[0];
  259. if(count($ret->parts) % 2 == 1) {
  260. $ret->parts[count($ret->parts) - 1] = $ret->parts[count($ret->parts) - 1] . $suffix;
  261. } else {
  262. $ret->parts[] = $suffix;
  263. }
  264. return $ret;
  265. }
  266. static function merge($log_conn) {
  267. $args = func_get_args();
  268. return call_user_func_array(array(__CLASS__, 'merge_with_encapsulation_par'), array_merge(array('(', ')'), $args));
  269. }
  270. static function merge_no_encapsulation($log_conn) {
  271. $args = func_get_args();
  272. return call_user_func_array(array(__CLASS__, 'merge_with_encapsulation_par'), array_merge(array('', ''), $args));
  273. }
  274. static function merge_with_encapsulation_par($prefix, $suffix, $log_conn) {
  275. $args = func_get_args();
  276. $boundables = Surosql::array_flatten((array) array_slice($args, 3));
  277. if(count($boundables) == 1) {
  278. return is_string($boundables[0]) ? new self(array($boundables[0])) : $boundables[0];
  279. }
  280. $ret = new self(array());
  281. foreach($boundables as $boundable) {
  282. $toadd = is_string($boundable) ? new self(array($boundable)) : $boundable;
  283. $toadd = $toadd->encapsulated($prefix, $suffix);
  284. if(count($toadd->parts) > 0) {
  285. if(count($ret->parts) > 0) {
  286. $ret->append(" $log_conn ");
  287. }
  288. $ret->append($toadd);
  289. }
  290. }
  291. return $ret;
  292. }
  293. static function q() {
  294. $args = func_get_args();
  295. $args = count($args) == 1 && is_array($args[0]) ? $args[0] : $args;
  296. if(count($args) > 0 && is_array($args[0])) {
  297. return self::merge('AND', array_map(array(__CLASS__, 'q'), $args));
  298. } else {
  299. return new self($args);
  300. }
  301. }
  302. static function q4set() {
  303. $args = func_get_args();
  304. $args = count($args) == 1 && is_array($args[0]) ? $args[0] : $args;
  305. if(count($args) > 0 && is_array($args[0])) {
  306. return self::merge_no_encapsulation(',', array_map(array(__CLASS__, 'q4set'), $args));
  307. } else {
  308. return new self($args);
  309. }
  310. }
  311. function __toString() {
  312. return implode(' ', $this->parts);
  313. }
  314. }
  315. class SurosqlQuery implements IteratorAggregate {
  316. public $type, $set = '', $laliasinfo, $osql, $select, $select_unparsed, $from, $from_unparsed, $join, $where, $where_unparsed, $groupby, $orderby, $limit, $first_from_alias;
  317. public $table_extra_where = array();
  318. public $uid = 0;
  319. function __construct($osql) {
  320. $this->osql = $osql;
  321. $this->where = SurosqlBindable::q();
  322. }
  323. function __call($method, $args) {
  324. return call_user_func_array($this->osql->imported_query_functions[$method], array_merge(array($this), $args));
  325. }
  326. static function newinst($osql) {
  327. return new self($osql);
  328. }
  329. function array_to_oo($rows) {
  330. $r = array();
  331. foreach($rows as $row) {
  332. foreach($this->colmap as $lalias => $colmap) {
  333. foreach($colmap['maps'] as $from => $to) {
  334. $row[$lalias][$to] = $this->osql->field_serialization($to, $row[$from], $colmap['info'], 'unserialize');
  335. unset($row[$from]);
  336. }
  337. $row[$lalias] = call_user_func($colmap['ooinit'], $row[$lalias]);
  338. }
  339. $r[] = (count($row) == 1 && isset($lalias) ? current($row) : (object) $row);
  340. }
  341. return $r;
  342. }
  343. function is_oo() {
  344. return substr($this->type, -2) == 'oo';
  345. }
  346. function add_class_alias($glalias) {
  347. if(!isset($this->osql->aliases[$glalias])) {
  348. if($this->is_oo() && class_exists($glalias)) {
  349. $this->osql->aliases[$glalias] = array('class' => $glalias);
  350. } else {
  351. return;
  352. }
  353. }
  354. $curaliasinfo = $this->osql->aliases[$glalias];
  355. if($this->is_oo() && isset($curaliasinfo['class']) && count(array_diff(array('table', 'ooinit', 'classvars'), array_keys($curaliasinfo))) != 0) {
  356. $this->osql->aliases[$glalias] = array_merge($this->osql->get_class_info($curaliasinfo['class']), $this->osql->aliases[$glalias]);
  357. }
  358. }
  359. function getIterator() {
  360. return $this->getIteratorImpl();
  361. }
  362. function count() {
  363. return $this->select('COUNT(*)');
  364. }
  365. function select($select_unparsed = null) {
  366. if(is_null($select_unparsed)) {
  367. return $this->select_unparsed;
  368. }
  369. if($this->type === 'selectoo') {
  370. return $this->selectoo($select_unparsed);
  371. }
  372. $this->type = 'select';
  373. $this->select_unparsed = $select_unparsed;
  374. $this->select = null;
  375. return $this;
  376. }
  377. function selectoo($select_unparsed = null) {
  378. if(is_null($select_unparsed)) {
  379. return $this->select_unparsed;
  380. }
  381. if($this->type === 'select') {
  382. return $this->select($select_unparsed);
  383. }
  384. $this->type = 'selectoo';
  385. $this->select_unparsed = $select_unparsed;
  386. $this->select = null;
  387. return $this;
  388. }
  389. function parse_select_replace($matches) {
  390. if($matches[1] == '*') {
  391. $replacements = array();
  392. foreach($this->laliasinfo as $info) {
  393. $replacement = $this->parse_select_replace(array(false, $info['lalias'], ''));
  394. if($replacement === false && (!isset($replacements[0]) || $replacements[0] !== '*')) {
  395. $replacements = array_merge(array('*'), $replacements);
  396. } else if($replacement !== false) {
  397. $replacements[] = $replacement;
  398. }
  399. }
  400. return implode(', ', $replacements) . (count($replacements) > 0 ? $matches[2] : '');
  401. }
  402. $laliasinfo = $this->laliasinfo[$matches[1]];
  403. $classname = isset($laliasinfo['class']) ? $laliasinfo['class'] : null;
  404. if(isset($laliasinfo['columns'])) {
  405. $columns = $laliasinfo['columns'];
  406. } else if(!$this->is_oo() && isset($laliasinfo['classvars'])) {
  407. $columns = $laliasinfo['classvars'];
  408. } else if($this->is_oo() && !is_null($classname)) {
  409. if(!isset($this->osql->aliases[$classname]['classvars'])) {
  410. $this->add_class_alias($classname);
  411. }
  412. $columns = $this->osql->aliases[$classname]['classvars'];
  413. } else if($this->is_oo() && class_exists($laliasinfo['glalias'])) {
  414. $classname = $laliasinfo['glalias'];
  415. $this->add_class_alias($classname);
  416. $this->osql->aliases[$classname];
  417. $laliasinfo = $this->laliasinfo[$matches[1]] = array_merge($laliasinfo, $this->osql->aliases[$classname]);
  418. $columns = $this->osql->aliases[$classname]['classvars'];
  419. } else {
  420. return $matches[0];
  421. }
  422. $colmap = $newselect = array();
  423. foreach($columns as $defaultcol) {
  424. $newselect[] = "`{$laliasinfo['lalias']}`.`$defaultcol` AS `{$laliasinfo['lalias']}_$defaultcol`";
  425. $colmap["{$laliasinfo['lalias']}_$defaultcol"] = $defaultcol;
  426. }
  427. $ooinit = isset($laliasinfo['ooinit']) ? $laliasinfo['ooinit'] : create_function('$a', 'return (object) $a;');
  428. $this->colmap[$laliasinfo['lalias']] = array('maps' => $colmap, 'ooinit' => $ooinit, 'info' => $laliasinfo);
  429. return implode(', ', $newselect) . (count($newselect) > 0 ? $matches[2] : '');
  430. }
  431. function parse_select() {
  432. if(!is_null($this->select)) {
  433. return;
  434. }
  435. $this->colmap = array();
  436. $this->select = $this->select_unparsed;
  437. $strsplit = preg_split('/(["\'])(?:\\\\?.)*?\1/', $this->select_unparsed, -1, PREG_SPLIT_OFFSET_CAPTURE);
  438. $shift = 0;
  439. foreach($strsplit as $offset) {
  440. $repl = preg_replace_callback('/%([^ ,()]+)\s*(,|$)/', array($this, 'parse_select_replace'), substr($this->select, $offset[1] + $shift, strlen($offset[0])));
  441. $this->select = substr_replace($this->select, $repl, $offset[1] + $shift, strlen($offset[0]));
  442. $shift += strlen($repl) - strlen($offset[0]);
  443. }
  444. }
  445. function update($from = null) {
  446. if(is_null($from)) {
  447. return $this->from;
  448. }
  449. $this->type = 'update';
  450. $this->from = $from;
  451. return $this;
  452. }
  453. function delete($tables = '') {
  454. $this->type = 'delete';
  455. $this->select = $tables;
  456. return $this;
  457. }
  458. function deleteoo($tables = '') {
  459. $this->type = 'deleteoo';
  460. $this->select = $tables;
  461. return $this;
  462. }
  463. function from($from_unparsed = null) {
  464. if(is_null($from_unparsed)) {
  465. return $this->from_unparsed;
  466. }
  467. $this->from_unparsed = $from_unparsed;
  468. $this->from = null;
  469. return $this;
  470. }
  471. function parse_from() {
  472. if(!is_null($this->from)) {
  473. return;
  474. }
  475. $tablearr = preg_split('/(\s+|,|-|\(|\))/', $this->from_unparsed, -1, PREG_SPLIT_DELIM_CAPTURE);
  476. $tablearr = array_values(array_filter(array_map('trim', $tablearr), 'strlen'));
  477. $this->from = array();
  478. $this->laliasinfo = array();
  479. $parentstack = array();
  480. $this->first_from_alias = $from = null;
  481. while(($curstr = current($tablearr)) !== false) {
  482. $curstr = trim($curstr, '`');
  483. $connection = isset($this->osql->connections["{$from['glalias']},$curstr"]) ? $this->osql->connections["{$from['glalias']},$curstr"] : null;
  484. $connection = is_null($connection) && isset($this->osql->connections["{$from['table']},$curstr"]) ? $this->osql->connections["{$from['table']},$curstr"] : $connection;
  485. if(!is_null($from)) {
  486. $to = array('table' => $connection['table'], 'glalias' => $connection['glalias'], 'field' => $curstr, 'lalias' => $curstr);
  487. } else {
  488. $to = array('table' => $curstr, 'glalias' => $curstr, 'lalias' => $curstr);
  489. }
  490. if($connection['extra']['jointype'] == 'm2m') {
  491. $prev_index = key($tablearr);
  492. $m2m_repl = array();
  493. foreach(array_map('trim', explode('-', $connection['sqljoin'])) as $t) {
  494. $m2m_repl = array_merge($m2m_repl, array($t, $t . '_'. $this->uid++, '-'));
  495. }
  496. array_splice($tablearr, $prev_index, 1, array_slice($m2m_repl, 0, count($m2m_repl) - 2));
  497. while(key($tablearr) != $prev_index) { next($tablearr); }
  498. continue;
  499. }
  500. $this->add_class_alias($to['glalias']);
  501. $to = isset($this->osql->aliases[$to['glalias']]) ? array_merge($to, $this->osql->aliases[$to['glalias']]) : $to;
  502. $curstr = next($tablearr) == 'AS' ? next($tablearr) : current($tablearr);
  503. if($curstr !== false && strpos('(,-)', $curstr) === false) {
  504. $to['lalias'] = trim($curstr, '`');
  505. $curstr = next($tablearr);
  506. }
  507. if(isset($to['where'])) {
  508. $this->table_extra_where[] = str_replace("%{$to['glalias']}%", $to['lalias'], $to['where']);
  509. }
  510. $tablestr = ($to['table'] == $to['lalias'] ? "`{$to['table']}`" : "`{$to['table']}` AS `{$to['lalias']}`");
  511. $this->laliasinfo[$to['lalias']] = $to;
  512. if(is_null($from)) {
  513. $this->from[] = (count($this->from) == 0 ? '' : ',') . $tablestr;
  514. } else {
  515. $joinon = str_replace(array("%{$from['glalias']}%", "%{$from['table']}%", "%{$to['field']}%"), array($from['lalias'], $from['lalias'], $to['lalias']), $connection['sqljoin']);
  516. $this->from[] = "{$connection['extra']['jointype']} JOIN $tablestr ON $joinon";
  517. }
  518. if(is_null($this->first_from_alias)) {
  519. $this->first_from_alias = "`{$to['lalias']}`";
  520. }
  521. while(strpos('(,-)', $curstr) !== false) {
  522. if($curstr == '(') {
  523. $parentstack[] = $to;
  524. } else if($curstr == ')') {
  525. array_pop($parentstack);
  526. } else if($curstr == '-') {
  527. $from = $to;
  528. } else if($curstr == ',') {
  529. $from = count($parentstack) == 0 ? null : $parentstack[count($parentstack) - 1];
  530. }
  531. $curstr = next($tablearr);
  532. }
  533. }
  534. $this->from = implode(' ', $this->from);
  535. }
  536. function join($join = null) {
  537. if(is_null($join)) {
  538. return $this->join;
  539. }
  540. $this->join = $join;
  541. return $this;
  542. }
  543. function get_prepared_set() {
  544. $set = $this->set;
  545. if(is_array($set)) {
  546. $prepped_set = array();
  547. foreach($set as $col => $val) {
  548. $prepped_set = array_merge($prepped_set, array("$col = ", $val, ','));
  549. }
  550. $set = SurosqlBindable::q((array) array_slice($prepped_set, 0, -1));
  551. }
  552. if($set instanceof SurosqlBindable) {
  553. return $this->get_prepared_bindable($set, 'set');
  554. }
  555. return $set;
  556. }
  557. function set($set = null) {
  558. if(is_null($set)) {
  559. return $this->set;
  560. }
  561. $this->set = $set;
  562. return $this;
  563. }
  564. function setq() {
  565. $args = func_get_args();
  566. $this->set = SurosqlBindable::q4set($args);
  567. return $this;
  568. }
  569. function get_prepared_bindable($var, $type) {
  570. if(isset($this->osql->imported_query_functions['get_prepared_bindable'])) {
  571. return call_user_func($this->osql->imported_query_functions['get_prepared_bindable'], $this, $var, $type);
  572. }
  573. $prepped_bind = $var->parts;
  574. foreach($prepped_bind as $i => &$param) {
  575. if($i % 2 == 1) {
  576. $param = Surosql::quote($param);
  577. }
  578. }
  579. return implode(' ', $prepped_bind);
  580. }
  581. function where($where = null) {
  582. if(is_null($where)) {
  583. return $this->where_unparsed;
  584. }
  585. $this->where_unparsed = $where;
  586. $args = func_get_args();
  587. $this->where = SurosqlBindable::merge('AND', $args);
  588. return $this;
  589. }
  590. function andwhere($where) {
  591. $args = func_get_args();
  592. $this->where = $this->where_unparsed = SurosqlBindable::merge('AND', array_merge(array($this->where), $args));
  593. return $this;
  594. }
  595. function orwhere($where) {
  596. $func_args = func_get_args();
  597. $this->where = $this->where_unparsed = SurosqlBindable::merge('OR', array_merge(array($this->where), $args));
  598. return $this;
  599. }
  600. function whereq() {
  601. $args = func_get_args();
  602. return $this->where(count($args) == 0 ? null : SurosqlBindable::q($args));
  603. }
  604. function andwhereq() {
  605. $args = func_get_args();
  606. return $this->andwhere(SurosqlBindable::q($args));
  607. }
  608. function orwhereq() {
  609. $args = func_get_args();
  610. return $this->orwhere(SurosqlBindable::q($args));
  611. }
  612. function groupby($groupby = null) {
  613. if(is_null($groupby)) {
  614. return $this->groupby;
  615. }
  616. $this->groupby = $groupby;
  617. return $this;
  618. }
  619. function orderby($orderby = null) {
  620. if(is_null($orderby)) {
  621. return $this->orderby;
  622. }
  623. $this->orderby = $orderby;
  624. return $this;
  625. }
  626. function limit($start = null, $length = null) {
  627. if(is_null($start)) {
  628. return $this->limit;
  629. }
  630. $start = strpos($start, ',') !== false ? explode(',', $start) : $start;
  631. if(is_array($start)) {
  632. list($start, $length) = array_merge($start, array(null));
  633. }
  634. $this->limit = is_null($length) ? (string) $start : "$start,$length";
  635. return $this;
  636. }
  637. function sql() {
  638. if(in_array($this->type, array('select', 'selectoo'))) {
  639. $this->parse_from();
  640. $this->parse_select();
  641. $sql[] = "SELECT $this->select FROM $this->from";
  642. } else if(in_array($this->type, array('delete', 'deleteoo'))) {
  643. $this->parse_from();
  644. $select = strlen($this->select) == 0 ? $this->first_from_alias : $this->select;
  645. $sql[] = "DELETE FROM $select";
  646. } else if($this->type == 'update') {
  647. $from = $this->osql->glalias2table(trim(str_replace('`', '', $this->from)));
  648. $set = $this->get_prepared_set();
  649. $sql[] = "UPDATE `$from` SET $set";
  650. }
  651. if(!is_null($this->join)) {
  652. $sql[] = $this->join;
  653. }
  654. $where = $this->get_prepared_bindable(SurosqlBindable::merge('AND', $this->where, $this->table_extra_where), 'where');
  655. if($where != '') {
  656. $sql[] = "WHERE $where";
  657. }
  658. if(!is_null($this->groupby)) {
  659. $sql[] = "GROUP BY {$this->groupby}";
  660. }
  661. if(!is_null($this->orderby)) {
  662. $sql[] = "ORDER BY {$this->orderby}";
  663. }
  664. if(!is_null($this->limit)) {
  665. $sql[] = "LIMIT {$this->limit}";
  666. }
  667. return implode(' ', $sql);
  668. }
  669. function __toString() {
  670. return $this->sql();
  671. }
  672. }
  673. class SurosqlOOManager {
  674. static function init($osql) {
  675. $funcs = array(
  676. 'insertobj' => array(__CLASS__, 'insertobj'),
  677. 'replaceobj' => array(__CLASS__, 'replaceobj'),
  678. 'updateobj' => array(__CLASS__, 'updateobj'),
  679. 'deleteobj' => array(__CLASS__, 'deleteobj'),
  680. 'insertorupdateobj' => array(__CLASS__, 'insertorupdateobj'),
  681. );
  682. $osql->imported_osql_functions = array_merge($osql->imported_osql_functions, $funcs);
  683. }
  684. static function get_osql_inst($obj = null) {
  685. if(isset($obj->osql) && !is_null($obj->osql)) {
  686. return $obj->osql;
  687. }
  688. $cls = is_string($obj) ? $obj : get_class($obj);
  689. if(isset($cls::$surosql) && isset($cls::$surosql['osql']) && !is_null($cls::$surosql['osql'])) {
  690. return $cls::$surosql['osql'];
  691. }
  692. return Surosql::$osql;
  693. }
  694. static function update($obj, $fields = null, $values_unescaped = array()) {
  695. return self::updateobj(self::get_osql_inst($obj), $obj, $fields, $values_unescaped);
  696. }
  697. static function insert($obj, $fields = null, $values_unescaped = array()) {
  698. return self::insertobj(self::get_osql_inst($obj), $obj, $fields, $values_unescaped);
  699. }
  700. static function replace($obj, $fields = null, $values_unescaped = array()) {
  701. return self::replaceobj(self::get_osql_inst($obj), $obj, $fields, $values_unescaped);
  702. }
  703. static function insertorupdate($obj, $fields = null, $values_unescaped = array()) {
  704. return self::insertorupdateobj(self::get_osql_inst($obj), $obj, $fields, $values_unescaped);
  705. }
  706. static function delete($obj) {
  707. return self::deleteobj(self::get_osql_inst($obj), $obj);
  708. }
  709. static function updateobj($osql, $obj, $fields = null, $values_unescaped = array()) {
  710. $oo = $osql->get_obj_info($obj);
  711. $fields = is_null($fields) ? array_diff($oo['columns'], array($oo['primary'])) : $fields;
  712. $set = array();
  713. foreach($fields as $field) {
  714. $val = $osql->field_serialization($field, isset($obj->$field) ? $obj->$field : null, $oo);
  715. $set[] = array("`$field`=", $val);
  716. }
  717. foreach($values_unescaped as $field => $value) {
  718. $set[] = array("`$field` = $value");
  719. }
  720. $num_rows_updated = $osql->update($oo['table'])->setq($set)
  721. ->whereq("`{$oo['primary']}`=", $obj->{$oo['primary']})->exec();
  722. if($num_rows_updated !== 1) {
  723. throw new Exception('No rows found to update.');
  724. }
  725. return $num_rows_updated;
  726. }
  727. static function insertobj($osql, $obj, $fields = null, $values_unescaped = array()) {
  728. return self::insertreplaceobj('insert', $osql, $obj, $fields, $values_unescaped);
  729. }
  730. static function replaceobj($osql, $obj, $fields = null, $values_unescaped = array()) {
  731. return self::insertreplaceobj('replace', $osql, $obj, $fields, $values_unescaped);
  732. }
  733. static function insertreplaceobj($type, $osql, $obj, $fields = null, $values_unescaped = array()) {
  734. $oo = $osql->get_obj_info($obj);
  735. foreach($oo['columns'] as $field) {
  736. if(isset($obj->$field)) {
  737. $values_escaped[$field] = $osql->field_serialization($field, $obj->$field, $oo);
  738. }
  739. }
  740. if(isset($oo['primary']) && isset($values_escaped[$oo['primary']]) && is_null($values_escaped[$oo['primary']])) {
  741. unset($values_escaped[$oo['primary']]);
  742. }
  743. call_user_func(array($osql, $type), $oo['table'], $values_escaped, $values_unescaped);
  744. return isset($oo['primary']) ? ($obj->{$oo['primary']} = $osql->lastInsertId()) : null;
  745. }
  746. static function insertorupdateobj($osql, $obj, $fields = null, $values_unescaped = array()) {
  747. $oo = $osql->get_obj_info($obj);
  748. if(!isset($oo['primary']) || !isset($obj->{$oo['primary']}) || is_null($obj->{$oo['primary']})) {
  749. return self::insertobj($osql, $obj, $fields, $values_unescaped);
  750. } else {
  751. return self::updateobj($osql, $obj, $fields, $values_unescaped);
  752. }
  753. }
  754. static function deleteobj($osql, $obj) {
  755. $oo = $osql->get_obj_info($obj);
  756. return $osql->delete("`{$oo['table']}`")->whereq("`{$oo['primary']}`=", $obj->{$oo['primary']})->exec();
  757. }
  758. }
  759. ?>