PageRenderTime 62ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/class_generator/libs/Surosql.class.php

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