/src/Parser/Util/ParseUtil.php

https://github.com/louislivi/SMProxy · PHP · 367 lines · 273 code · 32 blank · 62 comment · 86 complexity · cd5109501c801d23b239e1f9f66747e4 MD5 · raw file

  1. <?php
  2. namespace SMProxy\Parser\Util;
  3. /**
  4. * Author: Louis Livi <574747417@qq.com>
  5. * Date: 2018/11/3
  6. * Time: 上午9:30.
  7. */
  8. final class ParseUtil
  9. {
  10. public static function isEOF($c)
  11. {
  12. return ' ' == $c || "\t" == $c || "\n" == $c || "\r" == $c || ';' == $c;
  13. }
  14. public static function getSQLId(string $stmt)
  15. {
  16. $offset = strpos($stmt, '=');
  17. if (-1 != $offset && strlen($stmt) > ++$offset) {
  18. $id = trim(substr($stmt, $offset));
  19. return $id;
  20. }
  21. return 0;
  22. }
  23. /**
  24. * <code>'abc'</code>.
  25. *
  26. * @param string $stmt
  27. * @param int offset stmt.charAt(offset) == first <code>'</code>
  28. *
  29. * @return string
  30. */
  31. private static function parsestring(string $stmt, int $offset)
  32. {
  33. $sb = '';
  34. $stmtLen = strlen($stmt);
  35. for (++$offset; $offset < $stmtLen; ++$offset) {
  36. $c = $stmt[$offset];
  37. if ('\\' == $c) {
  38. switch ($c = $stmt[++$offset]) {
  39. case '0':
  40. $sb .= "\0";
  41. break;
  42. case 'b':
  43. $sb .= "\b";
  44. break;
  45. case 'n':
  46. $sb .= "\n";
  47. break;
  48. case 'r':
  49. $sb .= "\r";
  50. break;
  51. case 't':
  52. $sb .= "\t";
  53. break;
  54. case 'Z':
  55. $sb .= chr(26);
  56. break;
  57. default:
  58. $sb .= $c;
  59. }
  60. } elseif ('\'' == $c) {
  61. if ($offset + 1 < strlen($stmt) && '\'' == $stmt[$offset + 1]) {
  62. ++$offset;
  63. $sb .= '\'';
  64. } else {
  65. break;
  66. }
  67. } else {
  68. $sb .= $c;
  69. }
  70. }
  71. return $sb;
  72. }
  73. /**
  74. * <code>"abc"</code>.
  75. *
  76. * @param string $stmt
  77. * @param int offset stmt.charAt(offset) == first <code>"</code>
  78. *
  79. * @return string
  80. */
  81. private static function parsestring2(string $stmt, int $offset)
  82. {
  83. $sb = '';
  84. $stmtLen = strlen($stmt);
  85. for (++$offset; $offset < $stmtLen; ++$offset) {
  86. $c = $stmt = [$offset];
  87. if ('\\' == $c) {
  88. switch ($c = $stmt[++$offset]) {
  89. case '0':
  90. $sb .= "\0";
  91. break;
  92. case 'b':
  93. $sb .= "\b";
  94. break;
  95. case 'n':
  96. $sb .= "\n";
  97. break;
  98. case 'r':
  99. $sb .= "\r";
  100. break;
  101. case 't':
  102. $sb .= "\t";
  103. break;
  104. case 'Z':
  105. $sb .= chr(26);
  106. break;
  107. default:
  108. $sb .= $c;
  109. }
  110. } elseif ('"' == $c) {
  111. if ($offset + 1 < strlen($stmt) && '"' == $stmt[$offset + 1]) {
  112. ++$offset;
  113. $sb .= '"';
  114. } else {
  115. break;
  116. }
  117. } else {
  118. $sb .= $c;
  119. }
  120. }
  121. return $sb;
  122. }
  123. /**
  124. * <code>AS `abc`</code>.
  125. *
  126. * @param string $stmt
  127. * @param int $offset
  128. *
  129. * @return string
  130. */
  131. private static function parseIdentifierEscape(string $stmt, int $offset)
  132. {
  133. $sb = '';
  134. for (++$offset,$stemLen = strlen($stmt); $offset < $stemLen; ++$offset) {
  135. $c = $stmt[$offset];
  136. if ('`' == $c) {
  137. if ($offset + 1 < strlen($stmt) && '`' == $stmt[$offset + 1]) {
  138. ++$offset;
  139. $sb .= '`';
  140. } else {
  141. break;
  142. }
  143. } else {
  144. $sb .= $c;
  145. }
  146. }
  147. return $sb;
  148. }
  149. /**
  150. * @param string $stmt
  151. * @param int $aliasIndex
  152. * @return bool|null|string
  153. */
  154. public static function parseAlias(string $stmt, int $aliasIndex)
  155. {
  156. if ($aliasIndex < 0 || $aliasIndex >= strlen($stmt)) {
  157. return null;
  158. }
  159. switch ($stmt[$aliasIndex]) {
  160. case '\'':
  161. return self::parsestring($stmt, $aliasIndex);
  162. case '"':
  163. return self::parsestring2($stmt, $aliasIndex);
  164. case '`':
  165. return self::parseIdentifierEscape($stmt, $aliasIndex);
  166. default:
  167. $offset = $aliasIndex;
  168. $stmtLen = strlen($stmt);
  169. while ($offset < $stmtLen && CharTypes::isIdentifierChar($stmt[$offset])) {
  170. $offset++;
  171. }
  172. return substr($stmt, $aliasIndex, $offset);
  173. }
  174. }
  175. public static function comment(string $stmt, int $offset)
  176. {
  177. $len = strlen($stmt);
  178. $n = $offset;
  179. switch ($stmt[$n]) {
  180. case '/':
  181. if ($len > ++$n && '*' == $stmt[$n++] && $len > $n + 1 && '!' != $stmt[$n]) {
  182. for ($i = $n; $i < $len; ++$i) {
  183. if ('*' == $stmt[$i]) {
  184. $m = $i + 1;
  185. if ($len > $m && '/' == $stmt[$m]) {
  186. return $m;
  187. }
  188. }
  189. }
  190. }
  191. break;
  192. case '#':
  193. for ($i = $n + 1; $i < $len; ++$i) {
  194. if ("\n" == $stmt[$i]) {
  195. return $i;
  196. }
  197. }
  198. break;
  199. }
  200. return $offset;
  201. }
  202. public static function currentCharIsSep(string $stmt, int $offset)
  203. {
  204. if (strlen($stmt) > $offset) {
  205. switch ($stmt[$offset]) {
  206. case ' ':
  207. case "\t":
  208. case "\r":
  209. case "\n":
  210. return true;
  211. default:
  212. return false;
  213. }
  214. }
  215. return true;
  216. }
  217. /*****
  218. * 检查下一个字符是否为分隔符,并把偏移量加1
  219. * @param string $stmt
  220. * @param int $offset
  221. * @return bool
  222. */
  223. public static function nextCharIsSep(string $stmt, int $offset)
  224. {
  225. return self::currentCharIsSep($stmt, ++$offset);
  226. }
  227. /*****
  228. * 检查下一个字符串是否为期望的字符串,并把偏移量移到从offset开始计算,expectValue之后的位置
  229. *
  230. * @param string $stmt 被解析的sql
  231. * @param int $offset 被解析的sql的当前位置
  232. * @param string $nextExpectedstring 在stmt中准备查找的字符串
  233. * @param bool $checkSepChar 当找到expectValue值时,是否检查其后面字符为分隔符号
  234. *
  235. * @return int 如果包含指定的字符串,则移动相应的偏移量,否则返回值=offset
  236. */
  237. public static function nextstringIsExpectedWithIgnoreSepChar(
  238. string $stmt,
  239. int $offset,
  240. string $nextExpectedstring,
  241. bool $checkSepChar
  242. ) {
  243. if (null == $nextExpectedstring || strlen($nextExpectedstring) < 1) {
  244. return $offset;
  245. }
  246. $i = $offset;
  247. $index = 0;
  248. for ($stmtLen = strlen($stmt); $i < $stmtLen && $index < strlen($nextExpectedstring); ++$i) {
  249. if (0 == $index) {
  250. $isSep = self::currentCharIsSep($stmt, $i);
  251. if ($isSep) {
  252. continue;
  253. }
  254. }
  255. $actualChar = $stmt[$i];
  256. $expectedChar = $nextExpectedstring[$index++];
  257. if ($actualChar != $expectedChar) {
  258. return $offset;
  259. }
  260. }
  261. if ($index == strlen($nextExpectedstring)) {
  262. $ok = true;
  263. if ($checkSepChar) {
  264. $ok = self::nextCharIsSep($stmt, $i);
  265. }
  266. if ($ok) {
  267. return $i;
  268. }
  269. }
  270. return $offset;
  271. }
  272. const JSON = 'json';
  273. const EQ = '=';
  274. //private static final string WHERE = "where";
  275. //private static final string SET = "set";
  276. /**********
  277. * 检查下一个字符串是否json= *
  278. *
  279. * @param string $stmt 被解析的sql
  280. * @param int $offset
  281. * @return int 如果包含指定的字符串,则移动相应的偏移量,否则返回值=offset
  282. */
  283. public static function nextstringIsJsonEq(string $stmt, int $offset)
  284. {
  285. $i = $offset;
  286. // / drds 之后的符号
  287. if (!self::currentCharIsSep($stmt, ++$i)) {
  288. return $offset;
  289. }
  290. // json 串
  291. $k = self::nextstringIsExpectedWithIgnoreSepChar($stmt, $i, self::JSON, false);
  292. if ($k <= $i) {
  293. return $offset;
  294. }
  295. $i = $k;
  296. // 等于符号
  297. $k = self::nextstringIsExpectedWithIgnoreSepChar($stmt, $i, self::EQ, false);
  298. if ($k <= $i) {
  299. return $offset;
  300. }
  301. return $i;
  302. }
  303. public static function move(string $stmt, int $offset, int $length)
  304. {
  305. $stmtLen = strlen($stmt);
  306. for ($i = $offset; $i < $stmtLen; ++$i) {
  307. switch ($stmt[$i]) {
  308. case ' ':
  309. case "\t":
  310. case "\r":
  311. case "\n":
  312. continue 2;
  313. case '/':
  314. case '#':
  315. $i = self::comment($stmt, $i);
  316. continue 2;
  317. default:
  318. return $i + $length;
  319. }
  320. }
  321. return $i;
  322. }
  323. public static function compare(string $s, int $offset, $keyword)
  324. {
  325. if (strlen($s) >= $offset + count($keyword)) {
  326. for ($i = 0; $i < count($keyword); ++$i, ++$offset) {
  327. if (strtoupper($s[$offset]) != $keyword[$i]) {
  328. return false;
  329. }
  330. }
  331. return true;
  332. }
  333. return false;
  334. }
  335. }