SqlQueryWhereBuilder.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. <?php
  2. class SqlQueryWhereBuilder {
  3. private $_log = array();
  4. private $_usedFields = array();
  5. private $_raw = "";
  6. public function __construct() {
  7. }
  8. public function openBlock($blockType) {
  9. if (!$this->isAllowedBlockType($blockType)) throw new Exception("unsupported block type");
  10. $this->_log[] = "open_block_{$blockType}";
  11. }
  12. public function closeBlock($blockType) {
  13. if (!$this->isAllowedBlockType($blockType)) throw new Exception("unsupported block type");
  14. $this->_log[] = "close_block_{$blockType}";
  15. }
  16. public function addComparisonFieldToValue($fieldName, $comparisonSign, $value) {
  17. $sqlCompSign = "";
  18. switch ($comparisonSign) {
  19. case '=': $sqlCompSign = '='; break;
  20. case 'like': $sqlCompSign = 'like'; break;
  21. case 'GreaterThen': $sqlCompSign = '>'; break;
  22. case 'LessThen': $sqlCompSign = '<'; break;
  23. case 'GreaterThenOrEqualTo': $sqlCompSign = '>='; break;
  24. case 'LessThenOrEqualTo': $sqlCompSign = '<='; break;
  25. default: throw new Exception("Unsupported comparison sign");
  26. }
  27. $this->_log[] = array('comparisonFieldToValue', $fieldName, $sqlCompSign, $value);
  28. }
  29. public function addComparisonFieldFunToValue($fieldFun, $fieldName, $comparisonSign, $value) {
  30. $sqlCompSign = "";
  31. switch ($comparisonSign) {
  32. case '=': $sqlCompSign = '='; break;
  33. case 'like': $sqlCompSign = 'like'; break;
  34. case 'GreaterThen': $sqlCompSign = '>'; break;
  35. case 'LessThen': $sqlCompSign = '<'; break;
  36. case 'GreaterThenOrEqualTo': $sqlCompSign = '>='; break;
  37. case 'LessThenOrEqualTo': $sqlCompSign = '<='; break;
  38. default: throw new Exception("Unsupported comparison sign");
  39. }
  40. $this->_log[] = array('comparisonFieldFunToValue', $fieldFun, $fieldName, $sqlCompSign, $value);
  41. }
  42. public function sql_filter_comparisonFieldIsNull($fieldName) {
  43. $this->_log[] = array('comparisonFieldIsNull', $fieldName);
  44. }
  45. public function getQueryWhere($tablePrefix = '') {
  46. $sqlWhereRaw = $this->parseQueryWhere();
  47. $sqlTablePrefix = ($tablePrefix)? "`{$tablePrefix}`." : '';
  48. return str_replace('{tablePrefix}', $sqlTablePrefix, $sqlWhereRaw);
  49. }
  50. public function filterRawArray($item) {
  51. $lowerItem = array(); foreach ((array)$item as $fieldName => $value) $lowerItem[strtolower($fieldName)] = $value;
  52. DBG::_('DBG_DS_OGC', '>2', "\$lowerItem", $lowerItem, __CLASS__, __FUNCTION__, __LINE__);
  53. $sqlBlocksStack = array();
  54. $sqlValuesStack = array();
  55. $arr = array(); array_push($sqlValuesStack, $arr);// empty array to start
  56. foreach ($this->_log as $idxLog => $log) {
  57. switch ($log) {
  58. case 'open_block_and':
  59. case 'open_block_or':
  60. case 'open_block_not': {
  61. $blockType = substr($log, 11);// 'and', 'or', 'not'
  62. array_push($sqlBlocksStack, $blockType);
  63. $arr = array(); array_push($sqlValuesStack, $arr);
  64. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$log}' \$sqlBlocksStack", $sqlBlocksStack, __CLASS__, __FUNCTION__, __LINE__);
  65. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$log}' \$sqlValuesStack", $sqlValuesStack, __CLASS__, __FUNCTION__, __LINE__);
  66. }
  67. break;
  68. case 'close_block_and': {
  69. $blockType = substr($log, 12);// 'and', 'or'
  70. if (empty($sqlBlocksStack)) throw new Exception("parse sql query failed - blocks stack is empty");
  71. $stackBlockType = array_pop($sqlBlocksStack);
  72. if ($blockType != $stackBlockType) throw new Exception("parse sql query failed - expected stop '{$blockType}', given '{$stackBlockType}'");
  73. // parse to string and add to parent value stack
  74. $stackValue = array_pop($sqlValuesStack);
  75. if (!is_array($stackValue)) throw new Exception("parse sql query failed - stack value is not array");
  76. if (empty($stackValue)) throw new Exception("parse sql query failed - stack value is empty");
  77. $sqlFromStack = true;
  78. foreach ($stackValue as $bool) {
  79. if (!is_scalar($bool) && ($bool == true or $bool == false)) throw new Exception("parse query failed - expected bool values in block or");
  80. if (!$bool) $sqlFromStack = false;
  81. }
  82. $parentStackValue = array_pop($sqlValuesStack);
  83. if (!is_array($parentStackValue)) throw new Exception("parse sql query failed - parent stack value is not array");
  84. array_push($parentStackValue, $sqlFromStack);
  85. array_push($sqlValuesStack, $parentStackValue);
  86. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$log}' \$sqlBlocksStack", $sqlBlocksStack, __CLASS__, __FUNCTION__, __LINE__);
  87. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$log}' \$sqlValuesStack", $sqlValuesStack, __CLASS__, __FUNCTION__, __LINE__);
  88. }
  89. break;
  90. case 'close_block_or': {
  91. $blockType = substr($log, 12);// 'and', 'or'
  92. if (empty($sqlBlocksStack)) throw new Exception("parse sql query failed - blocks stack is empty");
  93. $stackBlockType = array_pop($sqlBlocksStack);
  94. if ($blockType != $stackBlockType) throw new Exception("parse sql query failed - expected stop '{$blockType}', given '{$stackBlockType}'");
  95. // parse to string and add to parent value stack
  96. $stackValue = array_pop($sqlValuesStack);
  97. if (!is_array($stackValue)) throw new Exception("parse sql query failed - stack value is not array");
  98. if (empty($stackValue)) throw new Exception("parse sql query failed - stack value is empty");
  99. $sqlFromStack = false;
  100. foreach ($stackValue as $bool) {
  101. if (!is_scalar($bool) && ($bool == true or $bool == false)) throw new Exception("parse query failed - expected bool values in block or");
  102. if ($bool) $sqlFromStack = true;
  103. }
  104. $parentStackValue = array_pop($sqlValuesStack);
  105. if (!is_array($parentStackValue)) throw new Exception("parse sql query failed - parent stack value is not array");
  106. array_push($parentStackValue, $sqlFromStack);
  107. array_push($sqlValuesStack, $parentStackValue);
  108. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$log}' \$sqlBlocksStack", $sqlBlocksStack, __CLASS__, __FUNCTION__, __LINE__);
  109. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$log}' \$sqlValuesStack", $sqlValuesStack, __CLASS__, __FUNCTION__, __LINE__);
  110. }
  111. break;
  112. case 'close_block_not': {
  113. $blockType = substr($log, 12);// 'not'
  114. if (empty($sqlBlocksStack)) throw new Exception("parse sql query failed - blocks stack is empty");
  115. $stackBlockType = array_pop($sqlBlocksStack);
  116. if ($blockType != $stackBlockType) throw new Exception("parse sql query failed - expected stop '{$blockType}', given '{$stackBlockType}'");
  117. // parse to string and add to parent value stack
  118. $stackValue = array_pop($sqlValuesStack);
  119. if (!is_array($stackValue)) throw new Exception("parse sql query failed - stack value is not array");
  120. if (empty($stackValue)) throw new Exception("parse sql query failed - stack value is empty");
  121. if (1 != count($stackValue)) throw new Exception("parse sql query failed - stack value count is not equal to 1");
  122. $stackValue = reset($stackValue);
  123. $sqlFromStack = " ! ({$stackValue}) ";
  124. $parentStackValue = array_pop($sqlValuesStack);
  125. if (!is_array($parentStackValue)) throw new Exception("parse sql query failed - parent stack value is not array");
  126. array_push($parentStackValue, $sqlFromStack);
  127. array_push($sqlValuesStack, $parentStackValue);
  128. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$log}' \$sqlBlocksStack", $sqlBlocksStack, __CLASS__, __FUNCTION__, __LINE__);
  129. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$log}' \$sqlValuesStack", $sqlValuesStack, __CLASS__, __FUNCTION__, __LINE__);
  130. }
  131. break;
  132. default: {
  133. if (is_array($log) && 4 == count($log) && 'comparisonFieldToValue' == $log[0]) {
  134. list($logType, $fieldName, $compSign, $compValue) = $log;
  135. $fieldName = strtolower($fieldName);
  136. if (!array_key_exists($fieldName, $lowerItem)) throw new Exception("field '{$fieldName}' not defined");
  137. $fieldValue = $lowerItem[$fieldName];
  138. switch ($compSign) {
  139. case '=': {
  140. $sqlFromStack = ($fieldValue == $compValue);
  141. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$logType}' comparison({$fieldValue} == {$compValue}) \$sqlFromStack(".($sqlFromStack ? 'true' : 'false').")", null, __CLASS__, __FUNCTION__, __LINE__);
  142. break;
  143. }
  144. case 'like': {
  145. $pattern = str_replace('%', '(.*)', $compValue);
  146. $sqlFromStack = (bool)preg_match("/^{$pattern}\$/i", $fieldValue);
  147. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$logType}' comparison(preg_match('/^{$pattern}\$/i', '{$fieldValue}')) \$sqlFromStack(".($sqlFromStack ? 'true' : 'false').")", null, __CLASS__, __FUNCTION__, __LINE__);
  148. break;
  149. }
  150. case '>=': {
  151. $sqlFromStack = (bool)($fieldValue >= $compValue);
  152. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$logType}' comparison({$compValue} {$compSign} '{$fieldValue}')) \$sqlFromStack(".($sqlFromStack ? 'true' : 'false').")", null, __CLASS__, __FUNCTION__, __LINE__);
  153. break;
  154. }
  155. case '<=': {
  156. $sqlFromStack = (bool)($fieldValue <= $compValue);
  157. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$logType}' comparison({$compValue} {$compSign} '{$fieldValue}')) \$sqlFromStack(".($sqlFromStack ? 'true' : 'false').")", null, __CLASS__, __FUNCTION__, __LINE__);
  158. break;
  159. }
  160. case '>': {
  161. $sqlFromStack = (bool)($fieldValue > $compValue);
  162. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$logType}' comparison({$compValue} {$compSign} '{$fieldValue}')) \$sqlFromStack(".($sqlFromStack ? 'true' : 'false').")", null, __CLASS__, __FUNCTION__, __LINE__);
  163. break;
  164. }
  165. case '<': {
  166. $sqlFromStack = (bool)($fieldValue < $compValue);
  167. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$logType}' comparison({$compValue} {$compSign} '{$fieldValue}')) \$sqlFromStack(".($sqlFromStack ? 'true' : 'false').")", null, __CLASS__, __FUNCTION__, __LINE__);
  168. break;
  169. }
  170. default: throw new Exception("compaison sign '{$compSign}' not defined");
  171. }
  172. // $this->_usedFields[$sqlFieldName] = true;
  173. // $sqlFromStack = "{tablePrefix}`{$sqlFieldName}` {$compSign} '{$compValue}'";
  174. $stackValue = array_pop($sqlValuesStack);
  175. if (!is_array($stackValue)) throw new Exception("parse sql query failed - stack value is not array");
  176. array_push($stackValue, $sqlFromStack);
  177. array_push($sqlValuesStack, $stackValue);
  178. } else if (is_array($log) && 5 == count($log) && 'comparisonFieldFunToValue' == $log[0]) {
  179. $sqlFieldFunName = $log[1];
  180. if (strtolower($sqlFieldFunName) != 'geometrytype') throw new Exception("Unsupported db function {$sqlFieldName}");
  181. $sqlFieldName = $log[2];
  182. // $this->_usedFields[$sqlFieldName] = true;
  183. $sqlFromStack = "{$sqlFieldFunName}({tablePrefix}`{$sqlFieldName}`) {$log[3]} '{$log[4]}'";
  184. $stackValue = array_pop($sqlValuesStack);
  185. if (!is_array($stackValue)) throw new Exception("parse sql query failed - stack value is not array");
  186. array_push($stackValue, $sqlFromStack);
  187. array_push($sqlValuesStack, $stackValue);
  188. } else if (is_array($log) && 2 == count($log) && 'comparisonFieldIsNull' == $log[0]) {
  189. $sqlFieldName = $log[1];
  190. $sqlFromStack = "{tablePrefix}`{$sqlFieldName}` is null";
  191. $stackValue = array_pop($sqlValuesStack);
  192. if (!is_array($stackValue)) throw new Exception("parse sql query failed - stack value is not array");
  193. array_push($stackValue, $sqlFromStack);
  194. array_push($sqlValuesStack, $stackValue);
  195. } else {
  196. throw new Exception("parse sql query failed - unknown '" . json_encode($log) . "'");
  197. }
  198. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$log}' \$sqlBlocksStack", $sqlBlocksStack, __CLASS__, __FUNCTION__, __LINE__);
  199. DBG::_('DBG_DS_OGC', '>2', "\nlog loop({$idxLog}) '{$log}' \$sqlValuesStack", $sqlValuesStack, __CLASS__, __FUNCTION__, __LINE__);
  200. }
  201. }
  202. }
  203. if (!empty($sqlBlocksStack)) throw new Exception("parse sql query failed - blocks stack is not empty");
  204. if (1 !== count($sqlValuesStack)) throw new Exception("parse sql query failed - values stack is empty");
  205. if (1 !== count($sqlValuesStack[0])) throw new Exception("parse sql query failed - values stack is empty");
  206. $bool = $sqlValuesStack[0][0];
  207. if (!is_scalar($bool) && ($bool == true or $bool == false)) throw new Exception("parse query failed - expected bool in stack value");
  208. DBG::_('DBG_DS_OGC', '>2', "\nOK return ".($bool ? 'true' : 'false')." for item:", $item, __CLASS__, __FUNCTION__, __LINE__);
  209. return $bool;
  210. }
  211. public function parseQueryWhere() {
  212. $this->_raw = "";
  213. $this->_usedFields = array();
  214. $sqlBlocksStack = array();
  215. $sqlValuesStack = array();
  216. $arr = array(); array_push($sqlValuesStack, $arr);// empty array to start
  217. foreach ($this->_log as $log) {
  218. switch ($log) {
  219. case 'open_block_and':
  220. case 'open_block_or':
  221. case 'open_block_not': {
  222. $blockType = substr($log, 11);
  223. array_push($sqlBlocksStack, $blockType);
  224. $arr = array(); array_push($sqlValuesStack, $arr);
  225. //echo "L.".__LINE__.":sqlBlocksStack:" . json_encode($sqlBlocksStack) . "\n";
  226. //echo "L.".__LINE__.":sqlValuesStack:" . json_encode($sqlValuesStack) . "\n\n";
  227. }
  228. break;
  229. case 'close_block_and':
  230. case 'close_block_or': {
  231. $blockType = substr($log, 12);
  232. if (empty($sqlBlocksStack)) throw new Exception("parse sql query failed - blocks stack is empty");
  233. $stackBlockType = array_pop($sqlBlocksStack);
  234. if ($blockType != $stackBlockType) throw new Exception("parse sql query failed - expected stop '{$blockType}', given '{$stackBlockType}'");
  235. // parse to string and add to parent value stack
  236. $stackValue = array_pop($sqlValuesStack);
  237. if (!is_array($stackValue)) throw new Exception("parse sql query failed - stack value is not array");
  238. if (empty($stackValue)) throw new Exception("parse sql query failed - stack value is empty");
  239. $sqlFromStack = " ( " . implode(" {$blockType} ", $stackValue) . " ) ";
  240. $parentStackValue = array_pop($sqlValuesStack);
  241. if (!is_array($parentStackValue)) throw new Exception("parse sql query failed - parent stack value is not array");
  242. array_push($parentStackValue, $sqlFromStack);
  243. array_push($sqlValuesStack, $parentStackValue);
  244. }
  245. break;
  246. case 'close_block_not': {
  247. $blockType = substr($log, 12);
  248. if (empty($sqlBlocksStack)) throw new Exception("parse sql query failed - blocks stack is empty");
  249. $stackBlockType = array_pop($sqlBlocksStack);
  250. if ($blockType != $stackBlockType) throw new Exception("parse sql query failed - expected stop '{$blockType}', given '{$stackBlockType}'");
  251. // parse to string and add to parent value stack
  252. $stackValue = array_pop($sqlValuesStack);
  253. if (!is_array($stackValue)) throw new Exception("parse sql query failed - stack value is not array");
  254. if (empty($stackValue)) throw new Exception("parse sql query failed - stack value is empty");
  255. if (1 != count($stackValue)) throw new Exception("parse sql query failed - stack value count is not equal to 1");
  256. $stackValue = reset($stackValue);
  257. $sqlFromStack = " ! ({$stackValue}) ";
  258. $parentStackValue = array_pop($sqlValuesStack);
  259. if (!is_array($parentStackValue)) throw new Exception("parse sql query failed - parent stack value is not array");
  260. array_push($parentStackValue, $sqlFromStack);
  261. array_push($sqlValuesStack, $parentStackValue);
  262. }
  263. break;
  264. default: {
  265. if (is_array($log) && 4 == count($log) && 'comparisonFieldToValue' == $log[0]) {
  266. $sqlFieldName = $log[1];
  267. $this->_usedFields[$sqlFieldName] = true;
  268. $sqlFromStack = "{tablePrefix}`{$sqlFieldName}` {$log[2]} '{$log[3]}'";
  269. $stackValue = array_pop($sqlValuesStack);
  270. if (!is_array($stackValue)) throw new Exception("parse sql query failed - stack value is not array");
  271. array_push($stackValue, $sqlFromStack);
  272. array_push($sqlValuesStack, $stackValue);
  273. //echo "L.".__LINE__.":sqlBlocksStack:" . json_encode($sqlBlocksStack) . "\n";
  274. //echo "L.".__LINE__.":sqlValuesStack:" . json_encode($sqlValuesStack) . "\n\n";
  275. } else if (is_array($log) && 5 == count($log) && 'comparisonFieldFunToValue' == $log[0]) {
  276. $sqlFieldFunName = $log[1];
  277. if (strtolower($sqlFieldFunName) != 'geometrytype') throw new Exception("Unsupported db function {$sqlFieldName}");
  278. $sqlFieldName = $log[2];
  279. $this->_usedFields[$sqlFieldName] = true;
  280. $sqlFromStack = "{$sqlFieldFunName}({tablePrefix}`{$sqlFieldName}`) {$log[3]} '{$log[4]}'";
  281. $stackValue = array_pop($sqlValuesStack);
  282. if (!is_array($stackValue)) throw new Exception("parse sql query failed - stack value is not array");
  283. array_push($stackValue, $sqlFromStack);
  284. array_push($sqlValuesStack, $stackValue);
  285. } else if (is_array($log) && 2 == count($log) && 'comparisonFieldIsNull' == $log[0]) {
  286. $sqlFieldName = $log[1];
  287. $sqlFromStack = "{tablePrefix}`{$sqlFieldName}` is null";
  288. $stackValue = array_pop($sqlValuesStack);
  289. if (!is_array($stackValue)) throw new Exception("parse sql query failed - stack value is not array");
  290. array_push($stackValue, $sqlFromStack);
  291. array_push($sqlValuesStack, $stackValue);
  292. } else {
  293. throw new Exception("parse sql query failed - unknown '" . json_encode($log) . "'");
  294. }
  295. }
  296. }
  297. }
  298. // TODO: parse log
  299. //echo "L.".__LINE__.":sqlBlocksStack:" . json_encode($sqlBlocksStack) . "\n";
  300. //echo "L.".__LINE__.":sqlValuesStack:" . json_encode($sqlValuesStack) . "\n--------\n";
  301. if (!empty($sqlBlocksStack)) throw new Exception("parse sql query failed - blocks stack is not empty");
  302. if (1 !== count($sqlValuesStack)) throw new Exception("parse sql query failed - values stack is empty");
  303. $this->_raw = implode("\n", $sqlValuesStack[0]);
  304. return $this->_raw;
  305. }
  306. public function getUsedFields() {
  307. return array_keys($this->_usedFields);
  308. }
  309. public function isAllowedBlockType($blockType) {
  310. if ('and' == $blockType || 'or' == $blockType) {
  311. return true;
  312. } else if ('not' == $blockType) {
  313. return true;
  314. }
  315. return false;
  316. }
  317. public function splitQueryToWords($q) {
  318. $searchWords = explode(' ', $q);
  319. $sqlSearchWords = array();
  320. if (!empty($searchWords)) {
  321. foreach ($searchWords as $word) {
  322. if (!empty($word) || '0' === $word) {
  323. $sqlSearchWords[] = $word;
  324. }
  325. }
  326. }
  327. return $sqlSearchWords;
  328. }
  329. }