PageRenderTime 57ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/surosql/wordpress_plugin/surosql/src/Surosql.class.php

https://bitbucket.org/SuRaMoN/surosql
PHP | 467 lines | 409 code | 55 blank | 3 comment | 94 complexity | df208cf599c030d1f4aa8d13bc1ff494 MD5 | raw file
Possible License(s): LGPL-3.0, AGPL-1.0, GPL-2.0, LGPL-2.1
  1. <?php
  2. // version 0.4
  3. class Surosql {
  4. /* table - global alias - (fieldname) - localalias */
  5. public $aliases = array();
  6. public $class2info = array();
  7. public $connections = array();
  8. public $imported_query_functions = array();
  9. public $imported_osql_functions = array();
  10. function __call($method, $args) {
  11. return call_user_func_array($this->imported_osql_functions[$method], array_merge(array($this), $args));
  12. }
  13. static function field_serialization($values, $info, $type = 'serialize') {
  14. $default_serialize = array('serialize', 'unserialize');
  15. $type = $type == 'serialize' ? 0 : 1;
  16. if(!isset($info['serialization'])) {
  17. return $values;
  18. }
  19. foreach($info['serialization'] as $key => $val) {
  20. $field_name = is_array($val) ? $key : $val;
  21. if(isset($values[$field_name])) {
  22. $serialization_func = is_array($val) ? $val : $default_serialize;
  23. $values[$field_name] = call_user_func($serialization_func[$type], $values[$field_name]);
  24. }
  25. }
  26. return $values;
  27. }
  28. static function array_flatten($array) {
  29. if(!is_array($array)) {
  30. return $array;
  31. }
  32. $return_array = array();
  33. foreach($array as $key => $val) {
  34. if(is_array($val)) {
  35. $return_array = array_merge($return_array, self::array_flatten($val));
  36. } else if(is_int($val)) {
  37. $return_array[] = $val;
  38. } else {
  39. $return_array[$key] = $val;
  40. }
  41. }
  42. return $return_array;
  43. }
  44. function logand() {
  45. $args = func_get_args();
  46. $args = self::array_flatten($args);
  47. return count($args) === 0 ? '1' : '(' . implode(') AND (', $args) . ')';
  48. }
  49. function logor() {
  50. $args = func_get_args();
  51. $args = self::array_flatten($args);
  52. return count($args) === 0 ? '0' : '(' . implode(') OR (', $args) . ')';
  53. }
  54. function glalias2table($glalias) {
  55. return !isset($this->aliases[$glalias]['table']) ? $glalias : $this->aliases[$glalias]['table'];
  56. }
  57. function connect($glalias, $connection, $extra = array()) {
  58. preg_match('/^(.*?)\s*-\s*(.*?)\s*(\(\s*(.*?)\s*\))? *$/', $glalias, $matches);
  59. list($_, $base, $fieldname) = $matches;
  60. $basetable = $this->glalias2table($base);
  61. $fieldglalias = (count($matches) == 5 ? $matches[4] : $fieldname);
  62. $fieldtable = $this->glalias2table($fieldglalias);
  63. $connection = str_replace("%$base%", "%$basetable%", $connection);
  64. $extra = array_merge(array('jointype' => 'INNER'), $extra);
  65. $this->connections["$base,$fieldname"] = array('sqljoin' => $connection, 'table' => $fieldtable, 'glalias' => $fieldglalias, 'extra' => $extra);
  66. return $this;
  67. }
  68. function get_class_info($classname) {
  69. if(isset($this->class2info[$classname])) {
  70. return $this->class2info[$classname];
  71. }
  72. $refclass = new ReflectionClass($classname);
  73. $info = $refclass->getStaticPropertyValue('surosql', array());
  74. if(!is_null($refclass->getConstructor())) {
  75. $ooinit = array($refclass, 'newInstance');
  76. } else {
  77. $ooinit = create_function('$a', "return Surosql::array_to_class(\$a, '$classname');");
  78. }
  79. $classvars = array_keys(array_diff_key(get_class_vars($classname), $refclass->getStaticProperties()));
  80. $info = array_merge(array('connections' => array(), 'table' => $classname, 'class' => $classname, 'ooinit' => $ooinit, 'classvars' => $classvars), $info);
  81. if(isset($info['alias'])) {
  82. $this->aliases[$info['alias']] = $info;
  83. foreach($info['connections'] as $connid => $connsql) {
  84. $this->connect("{$info['alias']} - $connid", $connsql);
  85. }
  86. }
  87. $this->class2info[$classname] = $info;
  88. return $info;
  89. }
  90. function addclass() {
  91. $func_args = func_get_args();
  92. foreach(self::array_flatten($func_args) as $classname) {
  93. $this->get_class_info($classname);
  94. }
  95. return $this;
  96. }
  97. function get_obj_info($obj) {
  98. $info = $this->get_class_info(get_class($obj));
  99. return isset($info['columns']) ? $info : array_merge($info, array('columns' => array_keys(get_object_vars($obj))));
  100. }
  101. static function array_to_class($arr, $classname) {
  102. $obj = new $classname();
  103. foreach($arr as $key => $value) {
  104. $obj->$key = $value;
  105. }
  106. return $obj;
  107. }
  108. function count() {
  109. $query = new SurosqlQuery($this);
  110. return $query->count();
  111. }
  112. function select($args = '%*') {
  113. $query = new SurosqlQuery($this);
  114. return $query->select($args);
  115. }
  116. function selectoo($args = '%*') {
  117. $query = new SurosqlQuery($this);
  118. return $query->selectoo($args);
  119. }
  120. function from($from) {
  121. return $this->select()->from($from);
  122. }
  123. function fromoo($from) {
  124. return $this->selectoo()->from($from);
  125. }
  126. function __get($from) {
  127. return $this->selectoo()->from($from);
  128. }
  129. function update($from = null) {
  130. $query = new SurosqlQuery($this);
  131. return $query->update($from);
  132. }
  133. }
  134. class SurosqlQuery implements IteratorAggregate {
  135. public $type, $set, $laliasinfo, $osql, $select, $select_unparsed, $from, $from_unparsed, $join, $groupby, $orderby, $limit;
  136. public $where = array();
  137. public $table_extra_where = array();
  138. function __construct($osql) {
  139. $this->osql = $osql;
  140. }
  141. function __call($method, $args) {
  142. return call_user_func_array($this->osql->imported_query_functions[$method], array_merge(array($this), $args));
  143. }
  144. function array_to_oo($rows) {
  145. $r = array();
  146. foreach($rows as $row) {
  147. foreach($this->colmap as $lalias => $colmap) {
  148. foreach($colmap['maps'] as $from => $to) {
  149. $row[$lalias][$to] = $row[$from];
  150. unset($row[$from]);
  151. }
  152. $row[$lalias] = call_user_func($colmap['ooinit'], $this->osql->field_serialization($row[$lalias], $colmap['info'], 'unserialize'));
  153. }
  154. $r[] = (count($row) == 1 && isset($lalias) ? current($row) : (object)$row);
  155. }
  156. return $r;
  157. }
  158. private function add_class_alias($glalias) {
  159. if(!isset($this->osql->aliases[$glalias])) {
  160. if($this->type == 'selectoo' && class_exists($glalias)) {
  161. $this->osql->aliases[$glalias] = array('class' => $glalias);
  162. } else {
  163. return;
  164. }
  165. }
  166. $curaliasinfo = $this->osql->aliases[$glalias];
  167. if($this->type == 'selectoo' && isset($curaliasinfo['class']) && count(array_diff(array('table', 'ooinit', 'classvars'), array_keys($curaliasinfo))) != 0) {
  168. $this->osql->aliases[$glalias] = array_merge($this->osql->get_class_info($curaliasinfo['class']), $this->osql->aliases[$glalias]);
  169. }
  170. }
  171. function getIterator() {
  172. return $this->getIteratorImpl();
  173. }
  174. function count() {
  175. return $this->select('COUNT(*)');
  176. }
  177. function select($select_unparsed = null) {
  178. if(is_null($select_unparsed)) {
  179. return $this->select_unparsed;
  180. }
  181. if($this->type === 'selectoo') {
  182. return $this->selectoo($select_unparsed);
  183. }
  184. $this->type = 'select';
  185. $this->select_unparsed = $select_unparsed;
  186. $this->select = null;
  187. return $this;
  188. }
  189. function selectoo($select_unparsed = null) {
  190. if(is_null($select_unparsed)) {
  191. return $this->select_unparsed;
  192. }
  193. if($this->type === 'select') {
  194. return $this->select($select_unparsed);
  195. }
  196. $this->type = 'selectoo';
  197. $this->select_unparsed = $select_unparsed;
  198. $this->select = null;
  199. return $this;
  200. }
  201. function parse_select_replace($matches) {
  202. if($matches[1] == '*') {
  203. $replacements = array();
  204. foreach($this->laliasinfo as $info) {
  205. $replacement = $this->parse_select_replace(array(false, $info['lalias'], ''));
  206. if($replacement === false && (!isset($replacements[0]) || $replacements[0] !== '*')) {
  207. $replacements = array_merge(array('*'), $replacements);
  208. } else if($replacement !== false) {
  209. $replacements[] = $replacement;
  210. }
  211. }
  212. return implode(', ', $replacements) . (count($replacements) > 0 ? $matches[2] : '');
  213. }
  214. $laliasinfo = $this->laliasinfo[$matches[1]];
  215. $classname = isset($laliasinfo['class']) ? $laliasinfo['class'] : null;
  216. if(isset($laliasinfo['columns'])) {
  217. $columns = $laliasinfo['columns'];
  218. } else if($this->type == 'selectoo' && !is_null($classname)) {
  219. if(!isset($this->osql->aliases[$classname]['classvars'])) {
  220. $this->add_class_alias($classname);
  221. }
  222. $columns = $this->osql->aliases[$classname]['classvars'];
  223. } else if($this->type == 'selectoo' && class_exists($laliasinfo['glalias'])) {
  224. $classname = $laliasinfo['glalias'];
  225. $this->add_class_alias($classname);
  226. $this->osql->aliases[$classname];
  227. $laliasinfo = $this->laliasinfo[$matches[1]] = array_merge($laliasinfo, $this->osql->aliases[$classname]);
  228. $columns = $this->osql->aliases[$classname]['classvars'];
  229. // TODO: make test 4 this case
  230. } else {
  231. return $matches[0];
  232. }
  233. $colmap = $newselect = array();
  234. foreach($columns as $defaultcol) {
  235. $newselect[] = "`{$laliasinfo['lalias']}`.`$defaultcol` AS `{$laliasinfo['lalias']}_$defaultcol`";
  236. $colmap["{$laliasinfo['lalias']}_$defaultcol"] = $defaultcol;
  237. }
  238. $ooinit = isset($laliasinfo['ooinit']) ? $laliasinfo['ooinit'] : create_function('$a', 'return (object)$a;');
  239. $this->colmap[$laliasinfo['lalias']] = array('maps' => $colmap, 'ooinit' => $ooinit, 'info' => $laliasinfo);
  240. return implode(', ', $newselect) . (count($newselect) > 0 ? $matches[2] : '');
  241. }
  242. function parse_select() {
  243. if(!is_null($this->select)) {
  244. return;
  245. }
  246. $this->colmap = array();
  247. $this->select = $this->select_unparsed;
  248. $strsplit = preg_split('/(["\'])(?:\\\\?.)*?\1/', $this->select_unparsed, -1, PREG_SPLIT_OFFSET_CAPTURE);
  249. $shift = 0;
  250. foreach($strsplit as $offset) {
  251. $repl = preg_replace_callback('/%([^ ,()]+)\s*(,|$)/', array($this, 'parse_select_replace'), substr($this->select, $offset[1] + $shift, strlen($offset[0])));
  252. $this->select = substr_replace($this->select, $repl, $offset[1] + $shift, strlen($offset[0]));
  253. $shift += strlen($repl) - strlen($offset[0]);
  254. }
  255. }
  256. function update($from = null) {
  257. if(is_null($from)) {
  258. return $this->from;
  259. }
  260. $this->type = 'update';
  261. $this->from = $from;
  262. return $this;
  263. }
  264. function from($from_unparsed = null) {
  265. if(is_null($from_unparsed)) {
  266. return $this->from_unparsed;
  267. }
  268. $this->from_unparsed = $from_unparsed;
  269. $this->from = null;
  270. return $this;
  271. }
  272. function parse_from() {
  273. if(!is_null($this->from)) {
  274. return;
  275. }
  276. $tablearr = preg_split('/(\s+|,|-|\(|\))/', $this->from_unparsed, -1, PREG_SPLIT_DELIM_CAPTURE);
  277. $tablearr = array_values(array_filter(array_map('trim', $tablearr), 'strlen'));
  278. $this->from = array();
  279. $this->laliasinfo = array();
  280. $parentstack = array();
  281. $from = null;
  282. while(($curstr = current($tablearr)) !== false) {
  283. $curstr = trim($curstr, '`');
  284. $connection = isset($this->osql->connections["{$from['glalias']},$curstr"]) ? $this->osql->connections["{$from['glalias']},$curstr"] : null;
  285. $connection = is_null($connection) && isset($this->osql->connections["{$from['table']},$curstr"]) ? $this->osql->connections["{$from['table']},$curstr"] : $connection;
  286. if(!is_null($from)) {
  287. $to = array('table' => $connection['table'], 'glalias' => $connection['glalias'], 'field' => $curstr, 'lalias' => $curstr);
  288. } else {
  289. $to = array('table' => $curstr, 'glalias' => $curstr, 'lalias' => $curstr);
  290. }
  291. $this->add_class_alias($to['glalias']);
  292. $to = isset($this->osql->aliases[$to['glalias']]) ? array_merge($to, $this->osql->aliases[$to['glalias']]) : $to;
  293. $curstr = next($tablearr) == 'AS' ? next($tablearr) : current($tablearr);
  294. if($curstr !== false && strpos('(,-)', $curstr) === false) {
  295. $to['lalias'] = trim($curstr, '`');
  296. $curstr = next($tablearr);
  297. }
  298. if(isset($to['where'])) {
  299. $this->table_extra_where[] = str_replace("%{$to['glalias']}%", $to['lalias'], $to['where']);
  300. }
  301. $tablestr = ($to['table'] == $to['lalias'] ? "`{$to['table']}`" : "`{$to['table']}` AS `{$to['lalias']}`");
  302. $this->laliasinfo[$to['lalias']] = $to;
  303. if(is_null($from)) {
  304. $this->from[] = (count($this->from) == 0 ? '' : ',') . $tablestr;
  305. } else {
  306. $joinon = str_replace(array("%{$from['glalias']}%", "%{$from['table']}%", "%{$to['field']}%"), array($from['lalias'], $from['lalias'], $to['lalias']), $connection['sqljoin']);
  307. $this->from[] = "{$connection['extra']['jointype']} JOIN $tablestr ON $joinon";
  308. }
  309. while(strpos('(,-)', $curstr) !== false) {
  310. if($curstr == '(') {
  311. $parentstack[] = $to;
  312. } else if($curstr == ')') {
  313. array_pop($parentstack);
  314. } else if($curstr == '-') {
  315. $from = $to;
  316. } else if($curstr == ',') {
  317. $from = count($parentstack) == 0 ? null : $parentstack[count($parentstack) - 1];
  318. }
  319. $curstr = next($tablearr);
  320. }
  321. }
  322. $this->from = implode(' ', $this->from);
  323. }
  324. function join($join = null) {
  325. if(is_null($join)) {
  326. return $this->join;
  327. }
  328. $this->join = $join;
  329. return $this;
  330. }
  331. function set($set = null) {
  332. if(is_null($set)) {
  333. return $this->set;
  334. }
  335. $this->set = $set;
  336. return $this;
  337. }
  338. function where($where = null) {
  339. if(is_null($where)) {
  340. return '(' . implode(') AND (', $this->where) . ')';
  341. }
  342. $this->where = func_get_args();
  343. $this->where = $this->osql->array_flatten($this->where);
  344. return $this;
  345. }
  346. function andwhere($where) {
  347. $func_args = func_get_args();
  348. $this->where = array_merge($this->osql->array_flatten($func_args), $this->where);
  349. return $this;
  350. }
  351. function orwhere($where) {
  352. $func_args = func_get_args();
  353. $this->where = array('((' . implode(') AND (', $this->where) . ')) OR (' . implode(') OR (', $this->osql->array_flatten($func_args)) . ')');
  354. return $this;
  355. }
  356. function groupby($groupby = null) {
  357. if(is_null($groupby)) {
  358. return $this->groupby;
  359. }
  360. $this->groupby = $groupby;
  361. return $this;
  362. }
  363. function orderby($orderby = null) {
  364. if(is_null($orderby)) {
  365. return $this->orderby;
  366. }
  367. $this->orderby = $orderby;
  368. return $this;
  369. }
  370. function limit($start = null, $length = null) {
  371. if(is_null($start)) {
  372. return $this->limit;
  373. }
  374. $start = strpos($start, ',') !== false ? explode(',', $start) : $start;
  375. if(is_array($start)) {
  376. list($start, $length) = array_merge($start, array(null));
  377. }
  378. $this->limit = is_null($length) ? (string)$start : "$start,$length";
  379. return $this;
  380. }
  381. function sql() {
  382. if($this->type == 'select' || $this->type == 'selectoo') {
  383. $this->parse_from();
  384. $this->parse_select();
  385. $sql[] = "SELECT $this->select FROM $this->from";
  386. } else if($this->type == 'update') {
  387. $from = $this->osql->glalias2table(trim(str_replace('`', '', $this->from)));
  388. $sql[] = "UPDATE `$from` SET $this->set";
  389. }
  390. if(!is_null($this->join)) {
  391. $sql[] = $this->join;
  392. }
  393. $where = array_merge($this->table_extra_where, $this->where);
  394. if(count($where) != 0) {
  395. $sql[] = 'WHERE (' . implode(') AND (', $where) . ')';
  396. }
  397. if(!is_null($this->groupby)) {
  398. $sql[] = "GROUP BY {$this->groupby}";
  399. }
  400. if(!is_null($this->orderby)) {
  401. $sql[] = "ORDER BY {$this->orderby}";
  402. }
  403. if(!is_null($this->limit)) {
  404. $sql[] = "LIMIT {$this->limit}";
  405. }
  406. return implode(' ', $sql);
  407. }
  408. function __toString() {
  409. return $this->sql();
  410. }
  411. }
  412. ?>