Lexer.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the MIT license. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\ORM\Query;
  20. use Doctrine\Common\Lexer\AbstractLexer;
  21. use function constant;
  22. use function ctype_alpha;
  23. use function defined;
  24. use function is_numeric;
  25. use function str_replace;
  26. use function stripos;
  27. use function strlen;
  28. use function strpos;
  29. use function strtoupper;
  30. use function substr;
  31. /**
  32. * Scans a DQL query for tokens.
  33. */
  34. class Lexer extends AbstractLexer
  35. {
  36. // All tokens that are not valid identifiers must be < 100
  37. public const T_NONE = 1;
  38. public const T_INTEGER = 2;
  39. public const T_STRING = 3;
  40. public const T_INPUT_PARAMETER = 4;
  41. public const T_FLOAT = 5;
  42. public const T_CLOSE_PARENTHESIS = 6;
  43. public const T_OPEN_PARENTHESIS = 7;
  44. public const T_COMMA = 8;
  45. public const T_DIVIDE = 9;
  46. public const T_DOT = 10;
  47. public const T_EQUALS = 11;
  48. public const T_GREATER_THAN = 12;
  49. public const T_LOWER_THAN = 13;
  50. public const T_MINUS = 14;
  51. public const T_MULTIPLY = 15;
  52. public const T_NEGATE = 16;
  53. public const T_PLUS = 17;
  54. public const T_OPEN_CURLY_BRACE = 18;
  55. public const T_CLOSE_CURLY_BRACE = 19;
  56. // All tokens that are identifiers or keywords that could be considered as identifiers should be >= 100
  57. public const T_ALIASED_NAME = 100;
  58. public const T_FULLY_QUALIFIED_NAME = 101;
  59. public const T_IDENTIFIER = 102;
  60. // All keyword tokens should be >= 200
  61. public const T_ALL = 200;
  62. public const T_AND = 201;
  63. public const T_ANY = 202;
  64. public const T_AS = 203;
  65. public const T_ASC = 204;
  66. public const T_AVG = 205;
  67. public const T_BETWEEN = 206;
  68. public const T_BOTH = 207;
  69. public const T_BY = 208;
  70. public const T_CASE = 209;
  71. public const T_COALESCE = 210;
  72. public const T_COUNT = 211;
  73. public const T_DELETE = 212;
  74. public const T_DESC = 213;
  75. public const T_DISTINCT = 214;
  76. public const T_ELSE = 215;
  77. public const T_EMPTY = 216;
  78. public const T_END = 217;
  79. public const T_ESCAPE = 218;
  80. public const T_EXISTS = 219;
  81. public const T_FALSE = 220;
  82. public const T_FROM = 221;
  83. public const T_GROUP = 222;
  84. public const T_HAVING = 223;
  85. public const T_HIDDEN = 224;
  86. public const T_IN = 225;
  87. public const T_INDEX = 226;
  88. public const T_INNER = 227;
  89. public const T_INSTANCE = 228;
  90. public const T_IS = 229;
  91. public const T_JOIN = 230;
  92. public const T_LEADING = 231;
  93. public const T_LEFT = 232;
  94. public const T_LIKE = 233;
  95. public const T_MAX = 234;
  96. public const T_MEMBER = 235;
  97. public const T_MIN = 236;
  98. public const T_NEW = 237;
  99. public const T_NOT = 238;
  100. public const T_NULL = 239;
  101. public const T_NULLIF = 240;
  102. public const T_OF = 241;
  103. public const T_OR = 242;
  104. public const T_ORDER = 243;
  105. public const T_OUTER = 244;
  106. public const T_PARTIAL = 245;
  107. public const T_SELECT = 246;
  108. public const T_SET = 247;
  109. public const T_SOME = 248;
  110. public const T_SUM = 249;
  111. public const T_THEN = 250;
  112. public const T_TRAILING = 251;
  113. public const T_TRUE = 252;
  114. public const T_UPDATE = 253;
  115. public const T_WHEN = 254;
  116. public const T_WHERE = 255;
  117. public const T_WITH = 256;
  118. /**
  119. * Creates a new query scanner object.
  120. *
  121. * @param string $input A query string.
  122. */
  123. public function __construct($input)
  124. {
  125. $this->setInput($input);
  126. }
  127. /**
  128. * {@inheritdoc}
  129. */
  130. protected function getCatchablePatterns()
  131. {
  132. return [
  133. '[a-z_][a-z0-9_]*\:[a-z_][a-z0-9_]*(?:\\\[a-z_][a-z0-9_]*)*', // aliased name
  134. '[a-z_\\\][a-z0-9_]*(?:\\\[a-z_][a-z0-9_]*)*', // identifier or qualified name
  135. '(?:[0-9]+(?:[\.][0-9]+)*)(?:e[+-]?[0-9]+)?', // numbers
  136. "'(?:[^']|'')*'", // quoted strings
  137. '\?[0-9]*|:[a-z_][a-z0-9_]*', // parameters
  138. ];
  139. }
  140. /**
  141. * {@inheritdoc}
  142. */
  143. protected function getNonCatchablePatterns()
  144. {
  145. return ['\s+', '--.*', '(.)'];
  146. }
  147. /**
  148. * {@inheritdoc}
  149. */
  150. protected function getType(&$value)
  151. {
  152. $type = self::T_NONE;
  153. switch (true) {
  154. // Recognize numeric values
  155. case is_numeric($value):
  156. if (strpos($value, '.') !== false || stripos($value, 'e') !== false) {
  157. return self::T_FLOAT;
  158. }
  159. return self::T_INTEGER;
  160. // Recognize quoted strings
  161. case $value[0] === "'":
  162. $value = str_replace("''", "'", substr($value, 1, strlen($value) - 2));
  163. return self::T_STRING;
  164. // Recognize identifiers, aliased or qualified names
  165. case ctype_alpha($value[0]) || $value[0] === '_' || $value[0] === '\\':
  166. $name = 'Doctrine\ORM\Query\Lexer::T_' . strtoupper($value);
  167. if (defined($name)) {
  168. $type = constant($name);
  169. if ($type > 100) {
  170. return $type;
  171. }
  172. }
  173. if (strpos($value, ':') !== false) {
  174. return self::T_ALIASED_NAME;
  175. }
  176. if (strpos($value, '\\') !== false) {
  177. return self::T_FULLY_QUALIFIED_NAME;
  178. }
  179. return self::T_IDENTIFIER;
  180. // Recognize input parameters
  181. case $value[0] === '?' || $value[0] === ':':
  182. return self::T_INPUT_PARAMETER;
  183. // Recognize symbols
  184. case $value === '.':
  185. return self::T_DOT;
  186. case $value === ',':
  187. return self::T_COMMA;
  188. case $value === '(':
  189. return self::T_OPEN_PARENTHESIS;
  190. case $value === ')':
  191. return self::T_CLOSE_PARENTHESIS;
  192. case $value === '=':
  193. return self::T_EQUALS;
  194. case $value === '>':
  195. return self::T_GREATER_THAN;
  196. case $value === '<':
  197. return self::T_LOWER_THAN;
  198. case $value === '+':
  199. return self::T_PLUS;
  200. case $value === '-':
  201. return self::T_MINUS;
  202. case $value === '*':
  203. return self::T_MULTIPLY;
  204. case $value === '/':
  205. return self::T_DIVIDE;
  206. case $value === '!':
  207. return self::T_NEGATE;
  208. case $value === '{':
  209. return self::T_OPEN_CURLY_BRACE;
  210. case $value === '}':
  211. return self::T_CLOSE_CURLY_BRACE;
  212. // Default
  213. default:
  214. // Do nothing
  215. }
  216. return $type;
  217. }
  218. }