Parser.php 116 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621
  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\ORM\EntityManager;
  21. use Doctrine\ORM\Mapping\ClassMetadata;
  22. use Doctrine\ORM\Query;
  23. use Doctrine\ORM\Query\AST\AggregateExpression;
  24. use Doctrine\ORM\Query\AST\ArithmeticExpression;
  25. use Doctrine\ORM\Query\AST\ArithmeticFactor;
  26. use Doctrine\ORM\Query\AST\ArithmeticTerm;
  27. use Doctrine\ORM\Query\AST\BetweenExpression;
  28. use Doctrine\ORM\Query\AST\CoalesceExpression;
  29. use Doctrine\ORM\Query\AST\CollectionMemberExpression;
  30. use Doctrine\ORM\Query\AST\ComparisonExpression;
  31. use Doctrine\ORM\Query\AST\ConditionalPrimary;
  32. use Doctrine\ORM\Query\AST\DeleteClause;
  33. use Doctrine\ORM\Query\AST\DeleteStatement;
  34. use Doctrine\ORM\Query\AST\EmptyCollectionComparisonExpression;
  35. use Doctrine\ORM\Query\AST\ExistsExpression;
  36. use Doctrine\ORM\Query\AST\FromClause;
  37. use Doctrine\ORM\Query\AST\Functions;
  38. use Doctrine\ORM\Query\AST\Functions\FunctionNode;
  39. use Doctrine\ORM\Query\AST\GeneralCaseExpression;
  40. use Doctrine\ORM\Query\AST\GroupByClause;
  41. use Doctrine\ORM\Query\AST\HavingClause;
  42. use Doctrine\ORM\Query\AST\IdentificationVariableDeclaration;
  43. use Doctrine\ORM\Query\AST\IndexBy;
  44. use Doctrine\ORM\Query\AST\InExpression;
  45. use Doctrine\ORM\Query\AST\InputParameter;
  46. use Doctrine\ORM\Query\AST\InstanceOfExpression;
  47. use Doctrine\ORM\Query\AST\Join;
  48. use Doctrine\ORM\Query\AST\JoinAssociationPathExpression;
  49. use Doctrine\ORM\Query\AST\LikeExpression;
  50. use Doctrine\ORM\Query\AST\Literal;
  51. use Doctrine\ORM\Query\AST\NewObjectExpression;
  52. use Doctrine\ORM\Query\AST\Node;
  53. use Doctrine\ORM\Query\AST\NullComparisonExpression;
  54. use Doctrine\ORM\Query\AST\NullIfExpression;
  55. use Doctrine\ORM\Query\AST\OrderByClause;
  56. use Doctrine\ORM\Query\AST\OrderByItem;
  57. use Doctrine\ORM\Query\AST\PartialObjectExpression;
  58. use Doctrine\ORM\Query\AST\PathExpression;
  59. use Doctrine\ORM\Query\AST\QuantifiedExpression;
  60. use Doctrine\ORM\Query\AST\RangeVariableDeclaration;
  61. use Doctrine\ORM\Query\AST\SelectClause;
  62. use Doctrine\ORM\Query\AST\SelectExpression;
  63. use Doctrine\ORM\Query\AST\SelectStatement;
  64. use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
  65. use Doctrine\ORM\Query\AST\SimpleSelectClause;
  66. use Doctrine\ORM\Query\AST\SimpleSelectExpression;
  67. use Doctrine\ORM\Query\AST\SimpleWhenClause;
  68. use Doctrine\ORM\Query\AST\Subselect;
  69. use Doctrine\ORM\Query\AST\SubselectFromClause;
  70. use Doctrine\ORM\Query\AST\SubselectIdentificationVariableDeclaration;
  71. use Doctrine\ORM\Query\AST\UpdateClause;
  72. use Doctrine\ORM\Query\AST\UpdateItem;
  73. use Doctrine\ORM\Query\AST\UpdateStatement;
  74. use Doctrine\ORM\Query\AST\WhenClause;
  75. use Doctrine\ORM\Query\AST\WhereClause;
  76. use ReflectionClass;
  77. use function array_intersect;
  78. use function array_search;
  79. use function assert;
  80. use function call_user_func;
  81. use function class_exists;
  82. use function count;
  83. use function explode;
  84. use function implode;
  85. use function in_array;
  86. use function interface_exists;
  87. use function is_string;
  88. use function sprintf;
  89. use function strlen;
  90. use function strpos;
  91. use function strrpos;
  92. use function strtolower;
  93. use function substr;
  94. /**
  95. * An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language.
  96. * Parses a DQL query, reports any errors in it, and generates an AST.
  97. */
  98. class Parser
  99. {
  100. /**
  101. * READ-ONLY: Maps BUILT-IN string function names to AST class names.
  102. *
  103. * @psalm-var array<string, class-string<Functions\FunctionNode>>
  104. */
  105. private static $stringFunctions = [
  106. 'concat' => Functions\ConcatFunction::class,
  107. 'substring' => Functions\SubstringFunction::class,
  108. 'trim' => Functions\TrimFunction::class,
  109. 'lower' => Functions\LowerFunction::class,
  110. 'upper' => Functions\UpperFunction::class,
  111. 'identity' => Functions\IdentityFunction::class,
  112. ];
  113. /**
  114. * READ-ONLY: Maps BUILT-IN numeric function names to AST class names.
  115. *
  116. * @psalm-var array<string, class-string<Functions\FunctionNode>>
  117. */
  118. private static $numericFunctions = [
  119. 'length' => Functions\LengthFunction::class,
  120. 'locate' => Functions\LocateFunction::class,
  121. 'abs' => Functions\AbsFunction::class,
  122. 'sqrt' => Functions\SqrtFunction::class,
  123. 'mod' => Functions\ModFunction::class,
  124. 'size' => Functions\SizeFunction::class,
  125. 'date_diff' => Functions\DateDiffFunction::class,
  126. 'bit_and' => Functions\BitAndFunction::class,
  127. 'bit_or' => Functions\BitOrFunction::class,
  128. // Aggregate functions
  129. 'min' => Functions\MinFunction::class,
  130. 'max' => Functions\MaxFunction::class,
  131. 'avg' => Functions\AvgFunction::class,
  132. 'sum' => Functions\SumFunction::class,
  133. 'count' => Functions\CountFunction::class,
  134. ];
  135. /**
  136. * READ-ONLY: Maps BUILT-IN datetime function names to AST class names.
  137. *
  138. * @psalm-var array<string, class-string<Functions\FunctionNode>>
  139. */
  140. private static $datetimeFunctions = [
  141. 'current_date' => Functions\CurrentDateFunction::class,
  142. 'current_time' => Functions\CurrentTimeFunction::class,
  143. 'current_timestamp' => Functions\CurrentTimestampFunction::class,
  144. 'date_add' => Functions\DateAddFunction::class,
  145. 'date_sub' => Functions\DateSubFunction::class,
  146. ];
  147. /*
  148. * Expressions that were encountered during parsing of identifiers and expressions
  149. * and still need to be validated.
  150. */
  151. /** @psalm-var list<array{token: mixed, expression: mixed, nestingLevel: int}> */
  152. private $deferredIdentificationVariables = [];
  153. /** @psalm-var list<array{token: mixed, expression: mixed, nestingLevel: int}> */
  154. private $deferredPartialObjectExpressions = [];
  155. /** @psalm-var list<array{token: mixed, expression: mixed, nestingLevel: int}> */
  156. private $deferredPathExpressions = [];
  157. /** @psalm-var list<array{token: mixed, expression: mixed, nestingLevel: int}> */
  158. private $deferredResultVariables = [];
  159. /** @psalm-var list<array{token: mixed, expression: mixed, nestingLevel: int}> */
  160. private $deferredNewObjectExpressions = [];
  161. /**
  162. * The lexer.
  163. *
  164. * @var Lexer
  165. */
  166. private $lexer;
  167. /**
  168. * The parser result.
  169. *
  170. * @var ParserResult
  171. */
  172. private $parserResult;
  173. /**
  174. * The EntityManager.
  175. *
  176. * @var EntityManager
  177. */
  178. private $em;
  179. /**
  180. * The Query to parse.
  181. *
  182. * @var Query
  183. */
  184. private $query;
  185. /**
  186. * Map of declared query components in the parsed query.
  187. *
  188. * @psalm-var array<string, array<string, mixed>>
  189. */
  190. private $queryComponents = [];
  191. /**
  192. * Keeps the nesting level of defined ResultVariables.
  193. *
  194. * @var int
  195. */
  196. private $nestingLevel = 0;
  197. /**
  198. * Any additional custom tree walkers that modify the AST.
  199. *
  200. * @psalm-var list<class-string<TreeWalker>>
  201. */
  202. private $customTreeWalkers = [];
  203. /**
  204. * The custom last tree walker, if any, that is responsible for producing the output.
  205. *
  206. * @var class-string<TreeWalker>
  207. */
  208. private $customOutputWalker;
  209. /** @psalm-var list<AST\SelectExpression> */
  210. private $identVariableExpressions = [];
  211. /**
  212. * Creates a new query parser object.
  213. *
  214. * @param Query $query The Query to parse.
  215. */
  216. public function __construct(Query $query)
  217. {
  218. $this->query = $query;
  219. $this->em = $query->getEntityManager();
  220. $this->lexer = new Lexer((string) $query->getDQL());
  221. $this->parserResult = new ParserResult();
  222. }
  223. /**
  224. * Sets a custom tree walker that produces output.
  225. * This tree walker will be run last over the AST, after any other walkers.
  226. *
  227. * @param string $className
  228. *
  229. * @return void
  230. */
  231. public function setCustomOutputTreeWalker($className)
  232. {
  233. $this->customOutputWalker = $className;
  234. }
  235. /**
  236. * Adds a custom tree walker for modifying the AST.
  237. *
  238. * @return void
  239. *
  240. * @psalm-param class-string $className
  241. */
  242. public function addCustomTreeWalker($className)
  243. {
  244. $this->customTreeWalkers[] = $className;
  245. }
  246. /**
  247. * Gets the lexer used by the parser.
  248. *
  249. * @return Lexer
  250. */
  251. public function getLexer()
  252. {
  253. return $this->lexer;
  254. }
  255. /**
  256. * Gets the ParserResult that is being filled with information during parsing.
  257. *
  258. * @return ParserResult
  259. */
  260. public function getParserResult()
  261. {
  262. return $this->parserResult;
  263. }
  264. /**
  265. * Gets the EntityManager used by the parser.
  266. *
  267. * @return EntityManager
  268. */
  269. public function getEntityManager()
  270. {
  271. return $this->em;
  272. }
  273. /**
  274. * Parses and builds AST for the given Query.
  275. *
  276. * @return SelectStatement|UpdateStatement|DeleteStatement
  277. */
  278. public function getAST()
  279. {
  280. // Parse & build AST
  281. $AST = $this->QueryLanguage();
  282. // Process any deferred validations of some nodes in the AST.
  283. // This also allows post-processing of the AST for modification purposes.
  284. $this->processDeferredIdentificationVariables();
  285. if ($this->deferredPartialObjectExpressions) {
  286. $this->processDeferredPartialObjectExpressions();
  287. }
  288. if ($this->deferredPathExpressions) {
  289. $this->processDeferredPathExpressions();
  290. }
  291. if ($this->deferredResultVariables) {
  292. $this->processDeferredResultVariables();
  293. }
  294. if ($this->deferredNewObjectExpressions) {
  295. $this->processDeferredNewObjectExpressions($AST);
  296. }
  297. $this->processRootEntityAliasSelected();
  298. // TODO: Is there a way to remove this? It may impact the mixed hydration resultset a lot!
  299. $this->fixIdentificationVariableOrder($AST);
  300. return $AST;
  301. }
  302. /**
  303. * Attempts to match the given token with the current lookahead token.
  304. *
  305. * If they match, updates the lookahead token; otherwise raises a syntax
  306. * error.
  307. *
  308. * @param int $token The token type.
  309. *
  310. * @return void
  311. *
  312. * @throws QueryException If the tokens don't match.
  313. */
  314. public function match($token)
  315. {
  316. $lookaheadType = $this->lexer->lookahead['type'] ?? null;
  317. // Short-circuit on first condition, usually types match
  318. if ($lookaheadType === $token) {
  319. $this->lexer->moveNext();
  320. return;
  321. }
  322. // If parameter is not identifier (1-99) must be exact match
  323. if ($token < Lexer::T_IDENTIFIER) {
  324. $this->syntaxError($this->lexer->getLiteral($token));
  325. }
  326. // If parameter is keyword (200+) must be exact match
  327. if ($token > Lexer::T_IDENTIFIER) {
  328. $this->syntaxError($this->lexer->getLiteral($token));
  329. }
  330. // If parameter is T_IDENTIFIER, then matches T_IDENTIFIER (100) and keywords (200+)
  331. if ($token === Lexer::T_IDENTIFIER && $lookaheadType < Lexer::T_IDENTIFIER) {
  332. $this->syntaxError($this->lexer->getLiteral($token));
  333. }
  334. $this->lexer->moveNext();
  335. }
  336. /**
  337. * Frees this parser, enabling it to be reused.
  338. *
  339. * @param bool $deep Whether to clean peek and reset errors.
  340. * @param int $position Position to reset.
  341. *
  342. * @return void
  343. */
  344. public function free($deep = false, $position = 0)
  345. {
  346. // WARNING! Use this method with care. It resets the scanner!
  347. $this->lexer->resetPosition($position);
  348. // Deep = true cleans peek and also any previously defined errors
  349. if ($deep) {
  350. $this->lexer->resetPeek();
  351. }
  352. $this->lexer->token = null;
  353. $this->lexer->lookahead = null;
  354. }
  355. /**
  356. * Parses a query string.
  357. *
  358. * @return ParserResult
  359. */
  360. public function parse()
  361. {
  362. $AST = $this->getAST();
  363. $customWalkers = $this->query->getHint(Query::HINT_CUSTOM_TREE_WALKERS);
  364. if ($customWalkers !== false) {
  365. $this->customTreeWalkers = $customWalkers;
  366. }
  367. $customOutputWalker = $this->query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER);
  368. if ($customOutputWalker !== false) {
  369. $this->customOutputWalker = $customOutputWalker;
  370. }
  371. // Run any custom tree walkers over the AST
  372. if ($this->customTreeWalkers) {
  373. $treeWalkerChain = new TreeWalkerChain($this->query, $this->parserResult, $this->queryComponents);
  374. foreach ($this->customTreeWalkers as $walker) {
  375. $treeWalkerChain->addTreeWalker($walker);
  376. }
  377. switch (true) {
  378. case $AST instanceof AST\UpdateStatement:
  379. $treeWalkerChain->walkUpdateStatement($AST);
  380. break;
  381. case $AST instanceof AST\DeleteStatement:
  382. $treeWalkerChain->walkDeleteStatement($AST);
  383. break;
  384. case $AST instanceof AST\SelectStatement:
  385. default:
  386. $treeWalkerChain->walkSelectStatement($AST);
  387. }
  388. $this->queryComponents = $treeWalkerChain->getQueryComponents();
  389. }
  390. $outputWalkerClass = $this->customOutputWalker ?: SqlWalker::class;
  391. $outputWalker = new $outputWalkerClass($this->query, $this->parserResult, $this->queryComponents);
  392. // Assign an SQL executor to the parser result
  393. $this->parserResult->setSqlExecutor($outputWalker->getExecutor($AST));
  394. return $this->parserResult;
  395. }
  396. /**
  397. * Fixes order of identification variables.
  398. *
  399. * They have to appear in the select clause in the same order as the
  400. * declarations (from ... x join ... y join ... z ...) appear in the query
  401. * as the hydration process relies on that order for proper operation.
  402. *
  403. * @param AST\SelectStatement|AST\DeleteStatement|AST\UpdateStatement $AST
  404. *
  405. * @return void
  406. */
  407. private function fixIdentificationVariableOrder($AST)
  408. {
  409. if (count($this->identVariableExpressions) <= 1) {
  410. return;
  411. }
  412. assert($AST instanceof AST\SelectStatement);
  413. foreach ($this->queryComponents as $dqlAlias => $qComp) {
  414. if (! isset($this->identVariableExpressions[$dqlAlias])) {
  415. continue;
  416. }
  417. $expr = $this->identVariableExpressions[$dqlAlias];
  418. $key = array_search($expr, $AST->selectClause->selectExpressions);
  419. unset($AST->selectClause->selectExpressions[$key]);
  420. $AST->selectClause->selectExpressions[] = $expr;
  421. }
  422. }
  423. /**
  424. * Generates a new syntax error.
  425. *
  426. * @param string $expected Expected string.
  427. *
  428. * @return void
  429. *
  430. * @throws QueryException
  431. *
  432. * @psalm-param array<string, mixed>|null $token Got token.
  433. */
  434. public function syntaxError($expected = '', $token = null)
  435. {
  436. if ($token === null) {
  437. $token = $this->lexer->lookahead;
  438. }
  439. $tokenPos = $token['position'] ?? '-1';
  440. $message = sprintf('line 0, col %d: Error: ', $tokenPos);
  441. $message .= $expected !== '' ? sprintf('Expected %s, got ', $expected) : 'Unexpected ';
  442. $message .= $this->lexer->lookahead === null ? 'end of string.' : sprintf("'%s'", $token['value']);
  443. throw QueryException::syntaxError($message, QueryException::dqlError($this->query->getDQL()));
  444. }
  445. /**
  446. * Generates a new semantical error.
  447. *
  448. * @param string $message Optional message.
  449. *
  450. * @return void
  451. *
  452. * @throws QueryException
  453. *
  454. * @psalm-param array<string, mixed>|null $token Optional token.
  455. */
  456. public function semanticalError($message = '', $token = null)
  457. {
  458. if ($token === null) {
  459. $token = $this->lexer->lookahead ?? ['position' => null];
  460. }
  461. // Minimum exposed chars ahead of token
  462. $distance = 12;
  463. // Find a position of a final word to display in error string
  464. $dql = $this->query->getDQL();
  465. $length = strlen($dql);
  466. $pos = $token['position'] + $distance;
  467. $pos = strpos($dql, ' ', $length > $pos ? $pos : $length);
  468. $length = $pos !== false ? $pos - $token['position'] : $distance;
  469. $tokenPos = isset($token['position']) && $token['position'] > 0 ? $token['position'] : '-1';
  470. $tokenStr = substr($dql, $token['position'], $length);
  471. // Building informative message
  472. $message = 'line 0, col ' . $tokenPos . " near '" . $tokenStr . "': Error: " . $message;
  473. throw QueryException::semanticalError($message, QueryException::dqlError($this->query->getDQL()));
  474. }
  475. /**
  476. * Peeks beyond the matched closing parenthesis and returns the first token after that one.
  477. *
  478. * @param bool $resetPeek Reset peek after finding the closing parenthesis.
  479. *
  480. * @psalm-return array<string, mixed>| null
  481. */
  482. private function peekBeyondClosingParenthesis(bool $resetPeek = true)
  483. {
  484. $token = $this->lexer->peek();
  485. $numUnmatched = 1;
  486. while ($numUnmatched > 0 && $token !== null) {
  487. switch ($token['type']) {
  488. case Lexer::T_OPEN_PARENTHESIS:
  489. ++$numUnmatched;
  490. break;
  491. case Lexer::T_CLOSE_PARENTHESIS:
  492. --$numUnmatched;
  493. break;
  494. default:
  495. // Do nothing
  496. }
  497. $token = $this->lexer->peek();
  498. }
  499. if ($resetPeek) {
  500. $this->lexer->resetPeek();
  501. }
  502. return $token;
  503. }
  504. /**
  505. * Checks if the given token indicates a mathematical operator.
  506. *
  507. * @psalm-param array<string, mixed> $token
  508. */
  509. private function isMathOperator($token): bool
  510. {
  511. return $token !== null && in_array($token['type'], [Lexer::T_PLUS, Lexer::T_MINUS, Lexer::T_DIVIDE, Lexer::T_MULTIPLY]);
  512. }
  513. /**
  514. * Checks if the next-next (after lookahead) token starts a function.
  515. *
  516. * @return bool TRUE if the next-next tokens start a function, FALSE otherwise.
  517. */
  518. private function isFunction(): bool
  519. {
  520. $lookaheadType = $this->lexer->lookahead['type'];
  521. $peek = $this->lexer->peek();
  522. $this->lexer->resetPeek();
  523. return $lookaheadType >= Lexer::T_IDENTIFIER && $peek !== null && $peek['type'] === Lexer::T_OPEN_PARENTHESIS;
  524. }
  525. /**
  526. * Checks whether the given token type indicates an aggregate function.
  527. *
  528. * @return bool TRUE if the token type is an aggregate function, FALSE otherwise.
  529. *
  530. * @psalm-param Lexer::T_* $tokenType
  531. */
  532. private function isAggregateFunction(int $tokenType): bool
  533. {
  534. return in_array(
  535. $tokenType,
  536. [Lexer::T_AVG, Lexer::T_MIN, Lexer::T_MAX, Lexer::T_SUM, Lexer::T_COUNT]
  537. );
  538. }
  539. /**
  540. * Checks whether the current lookahead token of the lexer has the type T_ALL, T_ANY or T_SOME.
  541. */
  542. private function isNextAllAnySome(): bool
  543. {
  544. return in_array(
  545. $this->lexer->lookahead['type'],
  546. [Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME]
  547. );
  548. }
  549. /**
  550. * Validates that the given <tt>IdentificationVariable</tt> is semantically correct.
  551. * It must exist in query components list.
  552. */
  553. private function processDeferredIdentificationVariables(): void
  554. {
  555. foreach ($this->deferredIdentificationVariables as $deferredItem) {
  556. $identVariable = $deferredItem['expression'];
  557. // Check if IdentificationVariable exists in queryComponents
  558. if (! isset($this->queryComponents[$identVariable])) {
  559. $this->semanticalError(
  560. sprintf("'%s' is not defined.", $identVariable),
  561. $deferredItem['token']
  562. );
  563. }
  564. $qComp = $this->queryComponents[$identVariable];
  565. // Check if queryComponent points to an AbstractSchemaName or a ResultVariable
  566. if (! isset($qComp['metadata'])) {
  567. $this->semanticalError(
  568. sprintf("'%s' does not point to a Class.", $identVariable),
  569. $deferredItem['token']
  570. );
  571. }
  572. // Validate if identification variable nesting level is lower or equal than the current one
  573. if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) {
  574. $this->semanticalError(
  575. sprintf("'%s' is used outside the scope of its declaration.", $identVariable),
  576. $deferredItem['token']
  577. );
  578. }
  579. }
  580. }
  581. /**
  582. * Validates that the given <tt>NewObjectExpression</tt>.
  583. */
  584. private function processDeferredNewObjectExpressions(SelectStatement $AST): void
  585. {
  586. foreach ($this->deferredNewObjectExpressions as $deferredItem) {
  587. $expression = $deferredItem['expression'];
  588. $token = $deferredItem['token'];
  589. $className = $expression->className;
  590. $args = $expression->args;
  591. $fromClassName = $AST->fromClause->identificationVariableDeclarations[0]->rangeVariableDeclaration->abstractSchemaName ?? null;
  592. // If the namespace is not given then assumes the first FROM entity namespace
  593. if (strpos($className, '\\') === false && ! class_exists($className) && strpos($fromClassName, '\\') !== false) {
  594. $namespace = substr($fromClassName, 0, strrpos($fromClassName, '\\'));
  595. $fqcn = $namespace . '\\' . $className;
  596. if (class_exists($fqcn)) {
  597. $expression->className = $fqcn;
  598. $className = $fqcn;
  599. }
  600. }
  601. if (! class_exists($className)) {
  602. $this->semanticalError(sprintf('Class "%s" is not defined.', $className), $token);
  603. }
  604. $class = new ReflectionClass($className);
  605. if (! $class->isInstantiable()) {
  606. $this->semanticalError(sprintf('Class "%s" can not be instantiated.', $className), $token);
  607. }
  608. if ($class->getConstructor() === null) {
  609. $this->semanticalError(sprintf('Class "%s" has not a valid constructor.', $className), $token);
  610. }
  611. if ($class->getConstructor()->getNumberOfRequiredParameters() > count($args)) {
  612. $this->semanticalError(sprintf('Number of arguments does not match with "%s" constructor declaration.', $className), $token);
  613. }
  614. }
  615. }
  616. /**
  617. * Validates that the given <tt>PartialObjectExpression</tt> is semantically correct.
  618. * It must exist in query components list.
  619. */
  620. private function processDeferredPartialObjectExpressions(): void
  621. {
  622. foreach ($this->deferredPartialObjectExpressions as $deferredItem) {
  623. $expr = $deferredItem['expression'];
  624. $class = $this->queryComponents[$expr->identificationVariable]['metadata'];
  625. foreach ($expr->partialFieldSet as $field) {
  626. if (isset($class->fieldMappings[$field])) {
  627. continue;
  628. }
  629. if (
  630. isset($class->associationMappings[$field]) &&
  631. $class->associationMappings[$field]['isOwningSide'] &&
  632. $class->associationMappings[$field]['type'] & ClassMetadata::TO_ONE
  633. ) {
  634. continue;
  635. }
  636. $this->semanticalError(sprintf(
  637. "There is no mapped field named '%s' on class %s.",
  638. $field,
  639. $class->name
  640. ), $deferredItem['token']);
  641. }
  642. if (array_intersect($class->identifier, $expr->partialFieldSet) !== $class->identifier) {
  643. $this->semanticalError(
  644. 'The partial field selection of class ' . $class->name . ' must contain the identifier.',
  645. $deferredItem['token']
  646. );
  647. }
  648. }
  649. }
  650. /**
  651. * Validates that the given <tt>ResultVariable</tt> is semantically correct.
  652. * It must exist in query components list.
  653. */
  654. private function processDeferredResultVariables(): void
  655. {
  656. foreach ($this->deferredResultVariables as $deferredItem) {
  657. $resultVariable = $deferredItem['expression'];
  658. // Check if ResultVariable exists in queryComponents
  659. if (! isset($this->queryComponents[$resultVariable])) {
  660. $this->semanticalError(
  661. sprintf("'%s' is not defined.", $resultVariable),
  662. $deferredItem['token']
  663. );
  664. }
  665. $qComp = $this->queryComponents[$resultVariable];
  666. // Check if queryComponent points to an AbstractSchemaName or a ResultVariable
  667. if (! isset($qComp['resultVariable'])) {
  668. $this->semanticalError(
  669. sprintf("'%s' does not point to a ResultVariable.", $resultVariable),
  670. $deferredItem['token']
  671. );
  672. }
  673. // Validate if identification variable nesting level is lower or equal than the current one
  674. if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) {
  675. $this->semanticalError(
  676. sprintf("'%s' is used outside the scope of its declaration.", $resultVariable),
  677. $deferredItem['token']
  678. );
  679. }
  680. }
  681. }
  682. /**
  683. * Validates that the given <tt>PathExpression</tt> is semantically correct for grammar rules:
  684. *
  685. * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
  686. * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
  687. * StateFieldPathExpression ::= IdentificationVariable "." StateField
  688. * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
  689. * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField
  690. */
  691. private function processDeferredPathExpressions(): void
  692. {
  693. foreach ($this->deferredPathExpressions as $deferredItem) {
  694. $pathExpression = $deferredItem['expression'];
  695. $qComp = $this->queryComponents[$pathExpression->identificationVariable];
  696. $class = $qComp['metadata'];
  697. $field = $pathExpression->field;
  698. if ($field === null) {
  699. $field = $pathExpression->field = $class->identifier[0];
  700. }
  701. // Check if field or association exists
  702. if (! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) {
  703. $this->semanticalError(
  704. 'Class ' . $class->name . ' has no field or association named ' . $field,
  705. $deferredItem['token']
  706. );
  707. }
  708. $fieldType = AST\PathExpression::TYPE_STATE_FIELD;
  709. if (isset($class->associationMappings[$field])) {
  710. $assoc = $class->associationMappings[$field];
  711. $fieldType = $assoc['type'] & ClassMetadata::TO_ONE
  712. ? AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION
  713. : AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION;
  714. }
  715. // Validate if PathExpression is one of the expected types
  716. $expectedType = $pathExpression->expectedType;
  717. if (! ($expectedType & $fieldType)) {
  718. // We need to recognize which was expected type(s)
  719. $expectedStringTypes = [];
  720. // Validate state field type
  721. if ($expectedType & AST\PathExpression::TYPE_STATE_FIELD) {
  722. $expectedStringTypes[] = 'StateFieldPathExpression';
  723. }
  724. // Validate single valued association (*-to-one)
  725. if ($expectedType & AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION) {
  726. $expectedStringTypes[] = 'SingleValuedAssociationField';
  727. }
  728. // Validate single valued association (*-to-many)
  729. if ($expectedType & AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION) {
  730. $expectedStringTypes[] = 'CollectionValuedAssociationField';
  731. }
  732. // Build the error message
  733. $semanticalError = 'Invalid PathExpression. ';
  734. $semanticalError .= count($expectedStringTypes) === 1
  735. ? 'Must be a ' . $expectedStringTypes[0] . '.'
  736. : implode(' or ', $expectedStringTypes) . ' expected.';
  737. $this->semanticalError($semanticalError, $deferredItem['token']);
  738. }
  739. // We need to force the type in PathExpression
  740. $pathExpression->type = $fieldType;
  741. }
  742. }
  743. private function processRootEntityAliasSelected(): void
  744. {
  745. if (! count($this->identVariableExpressions)) {
  746. return;
  747. }
  748. foreach ($this->identVariableExpressions as $dqlAlias => $expr) {
  749. if (isset($this->queryComponents[$dqlAlias]) && $this->queryComponents[$dqlAlias]['parent'] === null) {
  750. return;
  751. }
  752. }
  753. $this->semanticalError('Cannot select entity through identification variables without choosing at least one root entity alias.');
  754. }
  755. /**
  756. * QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement
  757. *
  758. * @return SelectStatement|UpdateStatement|DeleteStatement
  759. */
  760. public function QueryLanguage()
  761. {
  762. $statement = null;
  763. $this->lexer->moveNext();
  764. switch ($this->lexer->lookahead['type'] ?? null) {
  765. case Lexer::T_SELECT:
  766. $statement = $this->SelectStatement();
  767. break;
  768. case Lexer::T_UPDATE:
  769. $statement = $this->UpdateStatement();
  770. break;
  771. case Lexer::T_DELETE:
  772. $statement = $this->DeleteStatement();
  773. break;
  774. default:
  775. $this->syntaxError('SELECT, UPDATE or DELETE');
  776. break;
  777. }
  778. // Check for end of string
  779. if ($this->lexer->lookahead !== null) {
  780. $this->syntaxError('end of string');
  781. }
  782. return $statement;
  783. }
  784. /**
  785. * SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
  786. *
  787. * @return SelectStatement
  788. */
  789. public function SelectStatement()
  790. {
  791. $selectStatement = new AST\SelectStatement($this->SelectClause(), $this->FromClause());
  792. $selectStatement->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
  793. $selectStatement->groupByClause = $this->lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null;
  794. $selectStatement->havingClause = $this->lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null;
  795. $selectStatement->orderByClause = $this->lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null;
  796. return $selectStatement;
  797. }
  798. /**
  799. * UpdateStatement ::= UpdateClause [WhereClause]
  800. *
  801. * @return UpdateStatement
  802. */
  803. public function UpdateStatement()
  804. {
  805. $updateStatement = new AST\UpdateStatement($this->UpdateClause());
  806. $updateStatement->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
  807. return $updateStatement;
  808. }
  809. /**
  810. * DeleteStatement ::= DeleteClause [WhereClause]
  811. *
  812. * @return DeleteStatement
  813. */
  814. public function DeleteStatement()
  815. {
  816. $deleteStatement = new AST\DeleteStatement($this->DeleteClause());
  817. $deleteStatement->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
  818. return $deleteStatement;
  819. }
  820. /**
  821. * IdentificationVariable ::= identifier
  822. *
  823. * @return string
  824. */
  825. public function IdentificationVariable()
  826. {
  827. $this->match(Lexer::T_IDENTIFIER);
  828. $identVariable = $this->lexer->token['value'];
  829. $this->deferredIdentificationVariables[] = [
  830. 'expression' => $identVariable,
  831. 'nestingLevel' => $this->nestingLevel,
  832. 'token' => $this->lexer->token,
  833. ];
  834. return $identVariable;
  835. }
  836. /**
  837. * AliasIdentificationVariable = identifier
  838. *
  839. * @return string
  840. */
  841. public function AliasIdentificationVariable()
  842. {
  843. $this->match(Lexer::T_IDENTIFIER);
  844. $aliasIdentVariable = $this->lexer->token['value'];
  845. $exists = isset($this->queryComponents[$aliasIdentVariable]);
  846. if ($exists) {
  847. $this->semanticalError(
  848. sprintf("'%s' is already defined.", $aliasIdentVariable),
  849. $this->lexer->token
  850. );
  851. }
  852. return $aliasIdentVariable;
  853. }
  854. /**
  855. * AbstractSchemaName ::= fully_qualified_name | aliased_name | identifier
  856. *
  857. * @return string
  858. */
  859. public function AbstractSchemaName()
  860. {
  861. if ($this->lexer->isNextToken(Lexer::T_FULLY_QUALIFIED_NAME)) {
  862. $this->match(Lexer::T_FULLY_QUALIFIED_NAME);
  863. return $this->lexer->token['value'];
  864. }
  865. if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) {
  866. $this->match(Lexer::T_IDENTIFIER);
  867. return $this->lexer->token['value'];
  868. }
  869. $this->match(Lexer::T_ALIASED_NAME);
  870. [$namespaceAlias, $simpleClassName] = explode(':', $this->lexer->token['value']);
  871. return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
  872. }
  873. /**
  874. * Validates an AbstractSchemaName, making sure the class exists.
  875. *
  876. * @param string $schemaName The name to validate.
  877. *
  878. * @throws QueryException if the name does not exist.
  879. */
  880. private function validateAbstractSchemaName($schemaName)
  881. {
  882. if (! (class_exists($schemaName, true) || interface_exists($schemaName, true))) {
  883. $this->semanticalError(
  884. sprintf("Class '%s' is not defined.", $schemaName),
  885. $this->lexer->token
  886. );
  887. }
  888. }
  889. /**
  890. * AliasResultVariable ::= identifier
  891. *
  892. * @return string
  893. */
  894. public function AliasResultVariable()
  895. {
  896. $this->match(Lexer::T_IDENTIFIER);
  897. $resultVariable = $this->lexer->token['value'];
  898. $exists = isset($this->queryComponents[$resultVariable]);
  899. if ($exists) {
  900. $this->semanticalError(
  901. sprintf("'%s' is already defined.", $resultVariable),
  902. $this->lexer->token
  903. );
  904. }
  905. return $resultVariable;
  906. }
  907. /**
  908. * ResultVariable ::= identifier
  909. *
  910. * @return string
  911. */
  912. public function ResultVariable()
  913. {
  914. $this->match(Lexer::T_IDENTIFIER);
  915. $resultVariable = $this->lexer->token['value'];
  916. // Defer ResultVariable validation
  917. $this->deferredResultVariables[] = [
  918. 'expression' => $resultVariable,
  919. 'nestingLevel' => $this->nestingLevel,
  920. 'token' => $this->lexer->token,
  921. ];
  922. return $resultVariable;
  923. }
  924. /**
  925. * JoinAssociationPathExpression ::= IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField)
  926. *
  927. * @return JoinAssociationPathExpression
  928. */
  929. public function JoinAssociationPathExpression()
  930. {
  931. $identVariable = $this->IdentificationVariable();
  932. if (! isset($this->queryComponents[$identVariable])) {
  933. $this->semanticalError(
  934. 'Identification Variable ' . $identVariable . ' used in join path expression but was not defined before.'
  935. );
  936. }
  937. $this->match(Lexer::T_DOT);
  938. $this->match(Lexer::T_IDENTIFIER);
  939. $field = $this->lexer->token['value'];
  940. // Validate association field
  941. $qComp = $this->queryComponents[$identVariable];
  942. $class = $qComp['metadata'];
  943. if (! $class->hasAssociation($field)) {
  944. $this->semanticalError('Class ' . $class->name . ' has no association named ' . $field);
  945. }
  946. return new AST\JoinAssociationPathExpression($identVariable, $field);
  947. }
  948. /**
  949. * Parses an arbitrary path expression and defers semantical validation
  950. * based on expected types.
  951. *
  952. * PathExpression ::= IdentificationVariable {"." identifier}*
  953. *
  954. * @param int $expectedTypes
  955. *
  956. * @return PathExpression
  957. */
  958. public function PathExpression($expectedTypes)
  959. {
  960. $identVariable = $this->IdentificationVariable();
  961. $field = null;
  962. if ($this->lexer->isNextToken(Lexer::T_DOT)) {
  963. $this->match(Lexer::T_DOT);
  964. $this->match(Lexer::T_IDENTIFIER);
  965. $field = $this->lexer->token['value'];
  966. while ($this->lexer->isNextToken(Lexer::T_DOT)) {
  967. $this->match(Lexer::T_DOT);
  968. $this->match(Lexer::T_IDENTIFIER);
  969. $field .= '.' . $this->lexer->token['value'];
  970. }
  971. }
  972. // Creating AST node
  973. $pathExpr = new AST\PathExpression($expectedTypes, $identVariable, $field);
  974. // Defer PathExpression validation if requested to be deferred
  975. $this->deferredPathExpressions[] = [
  976. 'expression' => $pathExpr,
  977. 'nestingLevel' => $this->nestingLevel,
  978. 'token' => $this->lexer->token,
  979. ];
  980. return $pathExpr;
  981. }
  982. /**
  983. * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
  984. *
  985. * @return PathExpression
  986. */
  987. public function AssociationPathExpression()
  988. {
  989. return $this->PathExpression(
  990. AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION |
  991. AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION
  992. );
  993. }
  994. /**
  995. * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
  996. *
  997. * @return PathExpression
  998. */
  999. public function SingleValuedPathExpression()
  1000. {
  1001. return $this->PathExpression(
  1002. AST\PathExpression::TYPE_STATE_FIELD |
  1003. AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION
  1004. );
  1005. }
  1006. /**
  1007. * StateFieldPathExpression ::= IdentificationVariable "." StateField
  1008. *
  1009. * @return PathExpression
  1010. */
  1011. public function StateFieldPathExpression()
  1012. {
  1013. return $this->PathExpression(AST\PathExpression::TYPE_STATE_FIELD);
  1014. }
  1015. /**
  1016. * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
  1017. *
  1018. * @return PathExpression
  1019. */
  1020. public function SingleValuedAssociationPathExpression()
  1021. {
  1022. return $this->PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION);
  1023. }
  1024. /**
  1025. * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField
  1026. *
  1027. * @return PathExpression
  1028. */
  1029. public function CollectionValuedPathExpression()
  1030. {
  1031. return $this->PathExpression(AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION);
  1032. }
  1033. /**
  1034. * SelectClause ::= "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression}
  1035. *
  1036. * @return SelectClause
  1037. */
  1038. public function SelectClause()
  1039. {
  1040. $isDistinct = false;
  1041. $this->match(Lexer::T_SELECT);
  1042. // Check for DISTINCT
  1043. if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) {
  1044. $this->match(Lexer::T_DISTINCT);
  1045. $isDistinct = true;
  1046. }
  1047. // Process SelectExpressions (1..N)
  1048. $selectExpressions = [];
  1049. $selectExpressions[] = $this->SelectExpression();
  1050. while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
  1051. $this->match(Lexer::T_COMMA);
  1052. $selectExpressions[] = $this->SelectExpression();
  1053. }
  1054. return new AST\SelectClause($selectExpressions, $isDistinct);
  1055. }
  1056. /**
  1057. * SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression
  1058. *
  1059. * @return SimpleSelectClause
  1060. */
  1061. public function SimpleSelectClause()
  1062. {
  1063. $isDistinct = false;
  1064. $this->match(Lexer::T_SELECT);
  1065. if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) {
  1066. $this->match(Lexer::T_DISTINCT);
  1067. $isDistinct = true;
  1068. }
  1069. return new AST\SimpleSelectClause($this->SimpleSelectExpression(), $isDistinct);
  1070. }
  1071. /**
  1072. * UpdateClause ::= "UPDATE" AbstractSchemaName ["AS"] AliasIdentificationVariable "SET" UpdateItem {"," UpdateItem}*
  1073. *
  1074. * @return UpdateClause
  1075. */
  1076. public function UpdateClause()
  1077. {
  1078. $this->match(Lexer::T_UPDATE);
  1079. $token = $this->lexer->lookahead;
  1080. $abstractSchemaName = $this->AbstractSchemaName();
  1081. $this->validateAbstractSchemaName($abstractSchemaName);
  1082. if ($this->lexer->isNextToken(Lexer::T_AS)) {
  1083. $this->match(Lexer::T_AS);
  1084. }
  1085. $aliasIdentificationVariable = $this->AliasIdentificationVariable();
  1086. $class = $this->em->getClassMetadata($abstractSchemaName);
  1087. // Building queryComponent
  1088. $queryComponent = [
  1089. 'metadata' => $class,
  1090. 'parent' => null,
  1091. 'relation' => null,
  1092. 'map' => null,
  1093. 'nestingLevel' => $this->nestingLevel,
  1094. 'token' => $token,
  1095. ];
  1096. $this->queryComponents[$aliasIdentificationVariable] = $queryComponent;
  1097. $this->match(Lexer::T_SET);
  1098. $updateItems = [];
  1099. $updateItems[] = $this->UpdateItem();
  1100. while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
  1101. $this->match(Lexer::T_COMMA);
  1102. $updateItems[] = $this->UpdateItem();
  1103. }
  1104. $updateClause = new AST\UpdateClause($abstractSchemaName, $updateItems);
  1105. $updateClause->aliasIdentificationVariable = $aliasIdentificationVariable;
  1106. return $updateClause;
  1107. }
  1108. /**
  1109. * DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName ["AS"] AliasIdentificationVariable
  1110. *
  1111. * @return DeleteClause
  1112. */
  1113. public function DeleteClause()
  1114. {
  1115. $this->match(Lexer::T_DELETE);
  1116. if ($this->lexer->isNextToken(Lexer::T_FROM)) {
  1117. $this->match(Lexer::T_FROM);
  1118. }
  1119. $token = $this->lexer->lookahead;
  1120. $abstractSchemaName = $this->AbstractSchemaName();
  1121. $this->validateAbstractSchemaName($abstractSchemaName);
  1122. $deleteClause = new AST\DeleteClause($abstractSchemaName);
  1123. if ($this->lexer->isNextToken(Lexer::T_AS)) {
  1124. $this->match(Lexer::T_AS);
  1125. }
  1126. $aliasIdentificationVariable = $this->lexer->isNextToken(Lexer::T_IDENTIFIER)
  1127. ? $this->AliasIdentificationVariable()
  1128. : 'alias_should_have_been_set';
  1129. $deleteClause->aliasIdentificationVariable = $aliasIdentificationVariable;
  1130. $class = $this->em->getClassMetadata($deleteClause->abstractSchemaName);
  1131. // Building queryComponent
  1132. $queryComponent = [
  1133. 'metadata' => $class,
  1134. 'parent' => null,
  1135. 'relation' => null,
  1136. 'map' => null,
  1137. 'nestingLevel' => $this->nestingLevel,
  1138. 'token' => $token,
  1139. ];
  1140. $this->queryComponents[$aliasIdentificationVariable] = $queryComponent;
  1141. return $deleteClause;
  1142. }
  1143. /**
  1144. * FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}*
  1145. *
  1146. * @return FromClause
  1147. */
  1148. public function FromClause()
  1149. {
  1150. $this->match(Lexer::T_FROM);
  1151. $identificationVariableDeclarations = [];
  1152. $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration();
  1153. while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
  1154. $this->match(Lexer::T_COMMA);
  1155. $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration();
  1156. }
  1157. return new AST\FromClause($identificationVariableDeclarations);
  1158. }
  1159. /**
  1160. * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}*
  1161. *
  1162. * @return SubselectFromClause
  1163. */
  1164. public function SubselectFromClause()
  1165. {
  1166. $this->match(Lexer::T_FROM);
  1167. $identificationVariables = [];
  1168. $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration();
  1169. while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
  1170. $this->match(Lexer::T_COMMA);
  1171. $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration();
  1172. }
  1173. return new AST\SubselectFromClause($identificationVariables);
  1174. }
  1175. /**
  1176. * WhereClause ::= "WHERE" ConditionalExpression
  1177. *
  1178. * @return WhereClause
  1179. */
  1180. public function WhereClause()
  1181. {
  1182. $this->match(Lexer::T_WHERE);
  1183. return new AST\WhereClause($this->ConditionalExpression());
  1184. }
  1185. /**
  1186. * HavingClause ::= "HAVING" ConditionalExpression
  1187. *
  1188. * @return HavingClause
  1189. */
  1190. public function HavingClause()
  1191. {
  1192. $this->match(Lexer::T_HAVING);
  1193. return new AST\HavingClause($this->ConditionalExpression());
  1194. }
  1195. /**
  1196. * GroupByClause ::= "GROUP" "BY" GroupByItem {"," GroupByItem}*
  1197. *
  1198. * @return GroupByClause
  1199. */
  1200. public function GroupByClause()
  1201. {
  1202. $this->match(Lexer::T_GROUP);
  1203. $this->match(Lexer::T_BY);
  1204. $groupByItems = [$this->GroupByItem()];
  1205. while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
  1206. $this->match(Lexer::T_COMMA);
  1207. $groupByItems[] = $this->GroupByItem();
  1208. }
  1209. return new AST\GroupByClause($groupByItems);
  1210. }
  1211. /**
  1212. * OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}*
  1213. *
  1214. * @return OrderByClause
  1215. */
  1216. public function OrderByClause()
  1217. {
  1218. $this->match(Lexer::T_ORDER);
  1219. $this->match(Lexer::T_BY);
  1220. $orderByItems = [];
  1221. $orderByItems[] = $this->OrderByItem();
  1222. while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
  1223. $this->match(Lexer::T_COMMA);
  1224. $orderByItems[] = $this->OrderByItem();
  1225. }
  1226. return new AST\OrderByClause($orderByItems);
  1227. }
  1228. /**
  1229. * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
  1230. *
  1231. * @return Subselect
  1232. */
  1233. public function Subselect()
  1234. {
  1235. // Increase query nesting level
  1236. $this->nestingLevel++;
  1237. $subselect = new AST\Subselect($this->SimpleSelectClause(), $this->SubselectFromClause());
  1238. $subselect->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
  1239. $subselect->groupByClause = $this->lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null;
  1240. $subselect->havingClause = $this->lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null;
  1241. $subselect->orderByClause = $this->lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null;
  1242. // Decrease query nesting level
  1243. $this->nestingLevel--;
  1244. return $subselect;
  1245. }
  1246. /**
  1247. * UpdateItem ::= SingleValuedPathExpression "=" NewValue
  1248. *
  1249. * @return UpdateItem
  1250. */
  1251. public function UpdateItem()
  1252. {
  1253. $pathExpr = $this->SingleValuedPathExpression();
  1254. $this->match(Lexer::T_EQUALS);
  1255. return new AST\UpdateItem($pathExpr, $this->NewValue());
  1256. }
  1257. /**
  1258. * GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression
  1259. *
  1260. * @return string|PathExpression
  1261. */
  1262. public function GroupByItem()
  1263. {
  1264. // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression
  1265. $glimpse = $this->lexer->glimpse();
  1266. if ($glimpse !== null && $glimpse['type'] === Lexer::T_DOT) {
  1267. return $this->SingleValuedPathExpression();
  1268. }
  1269. // Still need to decide between IdentificationVariable or ResultVariable
  1270. $lookaheadValue = $this->lexer->lookahead['value'];
  1271. if (! isset($this->queryComponents[$lookaheadValue])) {
  1272. $this->semanticalError('Cannot group by undefined identification or result variable.');
  1273. }
  1274. return isset($this->queryComponents[$lookaheadValue]['metadata'])
  1275. ? $this->IdentificationVariable()
  1276. : $this->ResultVariable();
  1277. }
  1278. /**
  1279. * OrderByItem ::= (
  1280. * SimpleArithmeticExpression | SingleValuedPathExpression | CaseExpression |
  1281. * ScalarExpression | ResultVariable | FunctionDeclaration
  1282. * ) ["ASC" | "DESC"]
  1283. *
  1284. * @return OrderByItem
  1285. */
  1286. public function OrderByItem()
  1287. {
  1288. $this->lexer->peek(); // lookahead => '.'
  1289. $this->lexer->peek(); // lookahead => token after '.'
  1290. $peek = $this->lexer->peek(); // lookahead => token after the token after the '.'
  1291. $this->lexer->resetPeek();
  1292. $glimpse = $this->lexer->glimpse();
  1293. switch (true) {
  1294. case $this->isMathOperator($peek):
  1295. $expr = $this->SimpleArithmeticExpression();
  1296. break;
  1297. case $glimpse !== null && $glimpse['type'] === Lexer::T_DOT:
  1298. $expr = $this->SingleValuedPathExpression();
  1299. break;
  1300. case $this->lexer->peek() && $this->isMathOperator($this->peekBeyondClosingParenthesis()):
  1301. $expr = $this->ScalarExpression();
  1302. break;
  1303. case $this->lexer->lookahead['type'] === Lexer::T_CASE:
  1304. $expr = $this->CaseExpression();
  1305. break;
  1306. case $this->isFunction():
  1307. $expr = $this->FunctionDeclaration();
  1308. break;
  1309. default:
  1310. $expr = $this->ResultVariable();
  1311. break;
  1312. }
  1313. $type = 'ASC';
  1314. $item = new AST\OrderByItem($expr);
  1315. switch (true) {
  1316. case $this->lexer->isNextToken(Lexer::T_DESC):
  1317. $this->match(Lexer::T_DESC);
  1318. $type = 'DESC';
  1319. break;
  1320. case $this->lexer->isNextToken(Lexer::T_ASC):
  1321. $this->match(Lexer::T_ASC);
  1322. break;
  1323. default:
  1324. // Do nothing
  1325. }
  1326. $item->type = $type;
  1327. return $item;
  1328. }
  1329. /**
  1330. * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary |
  1331. * EnumPrimary | SimpleEntityExpression | "NULL"
  1332. *
  1333. * NOTE: Since it is not possible to correctly recognize individual types, here is the full
  1334. * grammar that needs to be supported:
  1335. *
  1336. * NewValue ::= SimpleArithmeticExpression | "NULL"
  1337. *
  1338. * SimpleArithmeticExpression covers all *Primary grammar rules and also SimpleEntityExpression
  1339. *
  1340. * @return AST\ArithmeticExpression|AST\InputParameter|null
  1341. */
  1342. public function NewValue()
  1343. {
  1344. if ($this->lexer->isNextToken(Lexer::T_NULL)) {
  1345. $this->match(Lexer::T_NULL);
  1346. return null;
  1347. }
  1348. if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
  1349. $this->match(Lexer::T_INPUT_PARAMETER);
  1350. return new AST\InputParameter($this->lexer->token['value']);
  1351. }
  1352. return $this->ArithmeticExpression();
  1353. }
  1354. /**
  1355. * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}*
  1356. *
  1357. * @return IdentificationVariableDeclaration
  1358. */
  1359. public function IdentificationVariableDeclaration()
  1360. {
  1361. $joins = [];
  1362. $rangeVariableDeclaration = $this->RangeVariableDeclaration();
  1363. $indexBy = $this->lexer->isNextToken(Lexer::T_INDEX)
  1364. ? $this->IndexBy()
  1365. : null;
  1366. $rangeVariableDeclaration->isRoot = true;
  1367. while (
  1368. $this->lexer->isNextToken(Lexer::T_LEFT) ||
  1369. $this->lexer->isNextToken(Lexer::T_INNER) ||
  1370. $this->lexer->isNextToken(Lexer::T_JOIN)
  1371. ) {
  1372. $joins[] = $this->Join();
  1373. }
  1374. return new AST\IdentificationVariableDeclaration(
  1375. $rangeVariableDeclaration,
  1376. $indexBy,
  1377. $joins
  1378. );
  1379. }
  1380. /**
  1381. * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration
  1382. *
  1383. * {Internal note: WARNING: Solution is harder than a bare implementation.
  1384. * Desired EBNF support:
  1385. *
  1386. * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration | (AssociationPathExpression ["AS"] AliasIdentificationVariable)
  1387. *
  1388. * It demands that entire SQL generation to become programmatical. This is
  1389. * needed because association based subselect requires "WHERE" conditional
  1390. * expressions to be injected, but there is no scope to do that. Only scope
  1391. * accessible is "FROM", prohibiting an easy implementation without larger
  1392. * changes.}
  1393. *
  1394. * @return SubselectIdentificationVariableDeclaration|IdentificationVariableDeclaration
  1395. */
  1396. public function SubselectIdentificationVariableDeclaration()
  1397. {
  1398. /*
  1399. NOT YET IMPLEMENTED!
  1400. $glimpse = $this->lexer->glimpse();
  1401. if ($glimpse['type'] == Lexer::T_DOT) {
  1402. $associationPathExpression = $this->AssociationPathExpression();
  1403. if ($this->lexer->isNextToken(Lexer::T_AS)) {
  1404. $this->match(Lexer::T_AS);
  1405. }
  1406. $aliasIdentificationVariable = $this->AliasIdentificationVariable();
  1407. $identificationVariable = $associationPathExpression->identificationVariable;
  1408. $field = $associationPathExpression->associationField;
  1409. $class = $this->queryComponents[$identificationVariable]['metadata'];
  1410. $targetClass = $this->em->getClassMetadata($class->associationMappings[$field]['targetEntity']);
  1411. // Building queryComponent
  1412. $joinQueryComponent = array(
  1413. 'metadata' => $targetClass,
  1414. 'parent' => $identificationVariable,
  1415. 'relation' => $class->getAssociationMapping($field),
  1416. 'map' => null,
  1417. 'nestingLevel' => $this->nestingLevel,
  1418. 'token' => $this->lexer->lookahead
  1419. );
  1420. $this->queryComponents[$aliasIdentificationVariable] = $joinQueryComponent;
  1421. return new AST\SubselectIdentificationVariableDeclaration(
  1422. $associationPathExpression, $aliasIdentificationVariable
  1423. );
  1424. }
  1425. */
  1426. return $this->IdentificationVariableDeclaration();
  1427. }
  1428. /**
  1429. * Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN"
  1430. * (JoinAssociationDeclaration | RangeVariableDeclaration)
  1431. * ["WITH" ConditionalExpression]
  1432. *
  1433. * @return Join
  1434. */
  1435. public function Join()
  1436. {
  1437. // Check Join type
  1438. $joinType = AST\Join::JOIN_TYPE_INNER;
  1439. switch (true) {
  1440. case $this->lexer->isNextToken(Lexer::T_LEFT):
  1441. $this->match(Lexer::T_LEFT);
  1442. $joinType = AST\Join::JOIN_TYPE_LEFT;
  1443. // Possible LEFT OUTER join
  1444. if ($this->lexer->isNextToken(Lexer::T_OUTER)) {
  1445. $this->match(Lexer::T_OUTER);
  1446. $joinType = AST\Join::JOIN_TYPE_LEFTOUTER;
  1447. }
  1448. break;
  1449. case $this->lexer->isNextToken(Lexer::T_INNER):
  1450. $this->match(Lexer::T_INNER);
  1451. break;
  1452. default:
  1453. // Do nothing
  1454. }
  1455. $this->match(Lexer::T_JOIN);
  1456. $next = $this->lexer->glimpse();
  1457. $joinDeclaration = $next['type'] === Lexer::T_DOT ? $this->JoinAssociationDeclaration() : $this->RangeVariableDeclaration();
  1458. $adhocConditions = $this->lexer->isNextToken(Lexer::T_WITH);
  1459. $join = new AST\Join($joinType, $joinDeclaration);
  1460. // Describe non-root join declaration
  1461. if ($joinDeclaration instanceof AST\RangeVariableDeclaration) {
  1462. $joinDeclaration->isRoot = false;
  1463. }
  1464. // Check for ad-hoc Join conditions
  1465. if ($adhocConditions) {
  1466. $this->match(Lexer::T_WITH);
  1467. $join->conditionalExpression = $this->ConditionalExpression();
  1468. }
  1469. return $join;
  1470. }
  1471. /**
  1472. * RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
  1473. *
  1474. * @return RangeVariableDeclaration
  1475. *
  1476. * @throws QueryException
  1477. */
  1478. public function RangeVariableDeclaration()
  1479. {
  1480. if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS) && $this->lexer->glimpse()['type'] === Lexer::T_SELECT) {
  1481. $this->semanticalError('Subquery is not supported here', $this->lexer->token);
  1482. }
  1483. $abstractSchemaName = $this->AbstractSchemaName();
  1484. $this->validateAbstractSchemaName($abstractSchemaName);
  1485. if ($this->lexer->isNextToken(Lexer::T_AS)) {
  1486. $this->match(Lexer::T_AS);
  1487. }
  1488. $token = $this->lexer->lookahead;
  1489. $aliasIdentificationVariable = $this->AliasIdentificationVariable();
  1490. $classMetadata = $this->em->getClassMetadata($abstractSchemaName);
  1491. // Building queryComponent
  1492. $queryComponent = [
  1493. 'metadata' => $classMetadata,
  1494. 'parent' => null,
  1495. 'relation' => null,
  1496. 'map' => null,
  1497. 'nestingLevel' => $this->nestingLevel,
  1498. 'token' => $token,
  1499. ];
  1500. $this->queryComponents[$aliasIdentificationVariable] = $queryComponent;
  1501. return new AST\RangeVariableDeclaration($abstractSchemaName, $aliasIdentificationVariable);
  1502. }
  1503. /**
  1504. * JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy]
  1505. *
  1506. * @return AST\JoinAssociationDeclaration
  1507. */
  1508. public function JoinAssociationDeclaration()
  1509. {
  1510. $joinAssociationPathExpression = $this->JoinAssociationPathExpression();
  1511. if ($this->lexer->isNextToken(Lexer::T_AS)) {
  1512. $this->match(Lexer::T_AS);
  1513. }
  1514. $aliasIdentificationVariable = $this->AliasIdentificationVariable();
  1515. $indexBy = $this->lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null;
  1516. $identificationVariable = $joinAssociationPathExpression->identificationVariable;
  1517. $field = $joinAssociationPathExpression->associationField;
  1518. $class = $this->queryComponents[$identificationVariable]['metadata'];
  1519. $targetClass = $this->em->getClassMetadata($class->associationMappings[$field]['targetEntity']);
  1520. // Building queryComponent
  1521. $joinQueryComponent = [
  1522. 'metadata' => $targetClass,
  1523. 'parent' => $joinAssociationPathExpression->identificationVariable,
  1524. 'relation' => $class->getAssociationMapping($field),
  1525. 'map' => null,
  1526. 'nestingLevel' => $this->nestingLevel,
  1527. 'token' => $this->lexer->lookahead,
  1528. ];
  1529. $this->queryComponents[$aliasIdentificationVariable] = $joinQueryComponent;
  1530. return new AST\JoinAssociationDeclaration($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy);
  1531. }
  1532. /**
  1533. * PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet
  1534. * PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}"
  1535. *
  1536. * @return PartialObjectExpression
  1537. */
  1538. public function PartialObjectExpression()
  1539. {
  1540. $this->match(Lexer::T_PARTIAL);
  1541. $partialFieldSet = [];
  1542. $identificationVariable = $this->IdentificationVariable();
  1543. $this->match(Lexer::T_DOT);
  1544. $this->match(Lexer::T_OPEN_CURLY_BRACE);
  1545. $this->match(Lexer::T_IDENTIFIER);
  1546. $field = $this->lexer->token['value'];
  1547. // First field in partial expression might be embeddable property
  1548. while ($this->lexer->isNextToken(Lexer::T_DOT)) {
  1549. $this->match(Lexer::T_DOT);
  1550. $this->match(Lexer::T_IDENTIFIER);
  1551. $field .= '.' . $this->lexer->token['value'];
  1552. }
  1553. $partialFieldSet[] = $field;
  1554. while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
  1555. $this->match(Lexer::T_COMMA);
  1556. $this->match(Lexer::T_IDENTIFIER);
  1557. $field = $this->lexer->token['value'];
  1558. while ($this->lexer->isNextToken(Lexer::T_DOT)) {
  1559. $this->match(Lexer::T_DOT);
  1560. $this->match(Lexer::T_IDENTIFIER);
  1561. $field .= '.' . $this->lexer->token['value'];
  1562. }
  1563. $partialFieldSet[] = $field;
  1564. }
  1565. $this->match(Lexer::T_CLOSE_CURLY_BRACE);
  1566. $partialObjectExpression = new AST\PartialObjectExpression($identificationVariable, $partialFieldSet);
  1567. // Defer PartialObjectExpression validation
  1568. $this->deferredPartialObjectExpressions[] = [
  1569. 'expression' => $partialObjectExpression,
  1570. 'nestingLevel' => $this->nestingLevel,
  1571. 'token' => $this->lexer->token,
  1572. ];
  1573. return $partialObjectExpression;
  1574. }
  1575. /**
  1576. * NewObjectExpression ::= "NEW" AbstractSchemaName "(" NewObjectArg {"," NewObjectArg}* ")"
  1577. *
  1578. * @return NewObjectExpression
  1579. */
  1580. public function NewObjectExpression()
  1581. {
  1582. $this->match(Lexer::T_NEW);
  1583. $className = $this->AbstractSchemaName(); // note that this is not yet validated
  1584. $token = $this->lexer->token;
  1585. $this->match(Lexer::T_OPEN_PARENTHESIS);
  1586. $args[] = $this->NewObjectArg();
  1587. while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
  1588. $this->match(Lexer::T_COMMA);
  1589. $args[] = $this->NewObjectArg();
  1590. }
  1591. $this->match(Lexer::T_CLOSE_PARENTHESIS);
  1592. $expression = new AST\NewObjectExpression($className, $args);
  1593. // Defer NewObjectExpression validation
  1594. $this->deferredNewObjectExpressions[] = [
  1595. 'token' => $token,
  1596. 'expression' => $expression,
  1597. 'nestingLevel' => $this->nestingLevel,
  1598. ];
  1599. return $expression;
  1600. }
  1601. /**
  1602. * NewObjectArg ::= ScalarExpression | "(" Subselect ")"
  1603. *
  1604. * @return mixed
  1605. */
  1606. public function NewObjectArg()
  1607. {
  1608. $token = $this->lexer->lookahead;
  1609. $peek = $this->lexer->glimpse();
  1610. if ($token['type'] === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT) {
  1611. $this->match(Lexer::T_OPEN_PARENTHESIS);
  1612. $expression = $this->Subselect();
  1613. $this->match(Lexer::T_CLOSE_PARENTHESIS);
  1614. return $expression;
  1615. }
  1616. return $this->ScalarExpression();
  1617. }
  1618. /**
  1619. * IndexBy ::= "INDEX" "BY" StateFieldPathExpression
  1620. *
  1621. * @return IndexBy
  1622. */
  1623. public function IndexBy()
  1624. {
  1625. $this->match(Lexer::T_INDEX);
  1626. $this->match(Lexer::T_BY);
  1627. $pathExpr = $this->StateFieldPathExpression();
  1628. // Add the INDEX BY info to the query component
  1629. $this->queryComponents[$pathExpr->identificationVariable]['map'] = $pathExpr->field;
  1630. return new AST\IndexBy($pathExpr);
  1631. }
  1632. /**
  1633. * ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary |
  1634. * StateFieldPathExpression | BooleanPrimary | CaseExpression |
  1635. * InstanceOfExpression
  1636. *
  1637. * @return mixed One of the possible expressions or subexpressions.
  1638. */
  1639. public function ScalarExpression()
  1640. {
  1641. $lookahead = $this->lexer->lookahead['type'];
  1642. $peek = $this->lexer->glimpse();
  1643. switch (true) {
  1644. case $lookahead === Lexer::T_INTEGER:
  1645. case $lookahead === Lexer::T_FLOAT:
  1646. // SimpleArithmeticExpression : (- u.value ) or ( + u.value ) or ( - 1 ) or ( + 1 )
  1647. case $lookahead === Lexer::T_MINUS:
  1648. case $lookahead === Lexer::T_PLUS:
  1649. return $this->SimpleArithmeticExpression();
  1650. case $lookahead === Lexer::T_STRING:
  1651. return $this->StringPrimary();
  1652. case $lookahead === Lexer::T_TRUE:
  1653. case $lookahead === Lexer::T_FALSE:
  1654. $this->match($lookahead);
  1655. return new AST\Literal(AST\Literal::BOOLEAN, $this->lexer->token['value']);
  1656. case $lookahead === Lexer::T_INPUT_PARAMETER:
  1657. switch (true) {
  1658. case $this->isMathOperator($peek):
  1659. // :param + u.value
  1660. return $this->SimpleArithmeticExpression();
  1661. default:
  1662. return $this->InputParameter();
  1663. }
  1664. case $lookahead === Lexer::T_CASE:
  1665. case $lookahead === Lexer::T_COALESCE:
  1666. case $lookahead === Lexer::T_NULLIF:
  1667. // Since NULLIF and COALESCE can be identified as a function,
  1668. // we need to check these before checking for FunctionDeclaration
  1669. return $this->CaseExpression();
  1670. case $lookahead === Lexer::T_OPEN_PARENTHESIS:
  1671. return $this->SimpleArithmeticExpression();
  1672. // this check must be done before checking for a filed path expression
  1673. case $this->isFunction():
  1674. $this->lexer->peek(); // "("
  1675. switch (true) {
  1676. case $this->isMathOperator($this->peekBeyondClosingParenthesis()):
  1677. // SUM(u.id) + COUNT(u.id)
  1678. return $this->SimpleArithmeticExpression();
  1679. default:
  1680. // IDENTITY(u)
  1681. return $this->FunctionDeclaration();
  1682. }
  1683. break;
  1684. // it is no function, so it must be a field path
  1685. case $lookahead === Lexer::T_IDENTIFIER:
  1686. $this->lexer->peek(); // lookahead => '.'
  1687. $this->lexer->peek(); // lookahead => token after '.'
  1688. $peek = $this->lexer->peek(); // lookahead => token after the token after the '.'
  1689. $this->lexer->resetPeek();
  1690. if ($this->isMathOperator($peek)) {
  1691. return $this->SimpleArithmeticExpression();
  1692. }
  1693. return $this->StateFieldPathExpression();
  1694. default:
  1695. $this->syntaxError();
  1696. }
  1697. }
  1698. /**
  1699. * CaseExpression ::= GeneralCaseExpression | SimpleCaseExpression | CoalesceExpression | NullifExpression
  1700. * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END"
  1701. * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression
  1702. * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END"
  1703. * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator
  1704. * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression
  1705. * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
  1706. * NullifExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
  1707. *
  1708. * @return mixed One of the possible expressions or subexpressions.
  1709. */
  1710. public function CaseExpression()
  1711. {
  1712. $lookahead = $this->lexer->lookahead['type'];
  1713. switch ($lookahead) {
  1714. case Lexer::T_NULLIF:
  1715. return $this->NullIfExpression();
  1716. case Lexer::T_COALESCE:
  1717. return $this->CoalesceExpression();
  1718. case Lexer::T_CASE:
  1719. $this->lexer->resetPeek();
  1720. $peek = $this->lexer->peek();
  1721. if ($peek['type'] === Lexer::T_WHEN) {
  1722. return $this->GeneralCaseExpression();
  1723. }
  1724. return $this->SimpleCaseExpression();
  1725. default:
  1726. // Do nothing
  1727. break;
  1728. }
  1729. $this->syntaxError();
  1730. }
  1731. /**
  1732. * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
  1733. *
  1734. * @return CoalesceExpression
  1735. */
  1736. public function CoalesceExpression()
  1737. {
  1738. $this->match(Lexer::T_COALESCE);
  1739. $this->match(Lexer::T_OPEN_PARENTHESIS);
  1740. // Process ScalarExpressions (1..N)
  1741. $scalarExpressions = [];
  1742. $scalarExpressions[] = $this->ScalarExpression();
  1743. while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
  1744. $this->match(Lexer::T_COMMA);
  1745. $scalarExpressions[] = $this->ScalarExpression();
  1746. }
  1747. $this->match(Lexer::T_CLOSE_PARENTHESIS);
  1748. return new AST\CoalesceExpression($scalarExpressions);
  1749. }
  1750. /**
  1751. * NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
  1752. *
  1753. * @return NullIfExpression
  1754. */
  1755. public function NullIfExpression()
  1756. {
  1757. $this->match(Lexer::T_NULLIF);
  1758. $this->match(Lexer::T_OPEN_PARENTHESIS);
  1759. $firstExpression = $this->ScalarExpression();
  1760. $this->match(Lexer::T_COMMA);
  1761. $secondExpression = $this->ScalarExpression();
  1762. $this->match(Lexer::T_CLOSE_PARENTHESIS);
  1763. return new AST\NullIfExpression($firstExpression, $secondExpression);
  1764. }
  1765. /**
  1766. * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END"
  1767. *
  1768. * @return GeneralCaseExpression
  1769. */
  1770. public function GeneralCaseExpression()
  1771. {
  1772. $this->match(Lexer::T_CASE);
  1773. // Process WhenClause (1..N)
  1774. $whenClauses = [];
  1775. do {
  1776. $whenClauses[] = $this->WhenClause();
  1777. } while ($this->lexer->isNextToken(Lexer::T_WHEN));
  1778. $this->match(Lexer::T_ELSE);
  1779. $scalarExpression = $this->ScalarExpression();
  1780. $this->match(Lexer::T_END);
  1781. return new AST\GeneralCaseExpression($whenClauses, $scalarExpression);
  1782. }
  1783. /**
  1784. * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END"
  1785. * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator
  1786. *
  1787. * @return AST\SimpleCaseExpression
  1788. */
  1789. public function SimpleCaseExpression()
  1790. {
  1791. $this->match(Lexer::T_CASE);
  1792. $caseOperand = $this->StateFieldPathExpression();
  1793. // Process SimpleWhenClause (1..N)
  1794. $simpleWhenClauses = [];
  1795. do {
  1796. $simpleWhenClauses[] = $this->SimpleWhenClause();
  1797. } while ($this->lexer->isNextToken(Lexer::T_WHEN));
  1798. $this->match(Lexer::T_ELSE);
  1799. $scalarExpression = $this->ScalarExpression();
  1800. $this->match(Lexer::T_END);
  1801. return new AST\SimpleCaseExpression($caseOperand, $simpleWhenClauses, $scalarExpression);
  1802. }
  1803. /**
  1804. * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression
  1805. *
  1806. * @return WhenClause
  1807. */
  1808. public function WhenClause()
  1809. {
  1810. $this->match(Lexer::T_WHEN);
  1811. $conditionalExpression = $this->ConditionalExpression();
  1812. $this->match(Lexer::T_THEN);
  1813. return new AST\WhenClause($conditionalExpression, $this->ScalarExpression());
  1814. }
  1815. /**
  1816. * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression
  1817. *
  1818. * @return SimpleWhenClause
  1819. */
  1820. public function SimpleWhenClause()
  1821. {
  1822. $this->match(Lexer::T_WHEN);
  1823. $conditionalExpression = $this->ScalarExpression();
  1824. $this->match(Lexer::T_THEN);
  1825. return new AST\SimpleWhenClause($conditionalExpression, $this->ScalarExpression());
  1826. }
  1827. /**
  1828. * SelectExpression ::= (
  1829. * IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration |
  1830. * PartialObjectExpression | "(" Subselect ")" | CaseExpression | NewObjectExpression
  1831. * ) [["AS"] ["HIDDEN"] AliasResultVariable]
  1832. *
  1833. * @return SelectExpression
  1834. */
  1835. public function SelectExpression()
  1836. {
  1837. $expression = null;
  1838. $identVariable = null;
  1839. $peek = $this->lexer->glimpse();
  1840. $lookaheadType = $this->lexer->lookahead['type'];
  1841. switch (true) {
  1842. // ScalarExpression (u.name)
  1843. case $lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_DOT:
  1844. $expression = $this->ScalarExpression();
  1845. break;
  1846. // IdentificationVariable (u)
  1847. case $lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] !== Lexer::T_OPEN_PARENTHESIS:
  1848. $expression = $identVariable = $this->IdentificationVariable();
  1849. break;
  1850. // CaseExpression (CASE ... or NULLIF(...) or COALESCE(...))
  1851. case $lookaheadType === Lexer::T_CASE:
  1852. case $lookaheadType === Lexer::T_COALESCE:
  1853. case $lookaheadType === Lexer::T_NULLIF:
  1854. $expression = $this->CaseExpression();
  1855. break;
  1856. // DQL Function (SUM(u.value) or SUM(u.value) + 1)
  1857. case $this->isFunction():
  1858. $this->lexer->peek(); // "("
  1859. switch (true) {
  1860. case $this->isMathOperator($this->peekBeyondClosingParenthesis()):
  1861. // SUM(u.id) + COUNT(u.id)
  1862. $expression = $this->ScalarExpression();
  1863. break;
  1864. default:
  1865. // IDENTITY(u)
  1866. $expression = $this->FunctionDeclaration();
  1867. break;
  1868. }
  1869. break;
  1870. // PartialObjectExpression (PARTIAL u.{id, name})
  1871. case $lookaheadType === Lexer::T_PARTIAL:
  1872. $expression = $this->PartialObjectExpression();
  1873. $identVariable = $expression->identificationVariable;
  1874. break;
  1875. // Subselect
  1876. case $lookaheadType === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT:
  1877. $this->match(Lexer::T_OPEN_PARENTHESIS);
  1878. $expression = $this->Subselect();
  1879. $this->match(Lexer::T_CLOSE_PARENTHESIS);
  1880. break;
  1881. // Shortcut: ScalarExpression => SimpleArithmeticExpression
  1882. case $lookaheadType === Lexer::T_OPEN_PARENTHESIS:
  1883. case $lookaheadType === Lexer::T_INTEGER:
  1884. case $lookaheadType === Lexer::T_STRING:
  1885. case $lookaheadType === Lexer::T_FLOAT:
  1886. // SimpleArithmeticExpression : (- u.value ) or ( + u.value )
  1887. case $lookaheadType === Lexer::T_MINUS:
  1888. case $lookaheadType === Lexer::T_PLUS:
  1889. $expression = $this->SimpleArithmeticExpression();
  1890. break;
  1891. // NewObjectExpression (New ClassName(id, name))
  1892. case $lookaheadType === Lexer::T_NEW:
  1893. $expression = $this->NewObjectExpression();
  1894. break;
  1895. default:
  1896. $this->syntaxError(
  1897. 'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression',
  1898. $this->lexer->lookahead
  1899. );
  1900. }
  1901. // [["AS"] ["HIDDEN"] AliasResultVariable]
  1902. $mustHaveAliasResultVariable = false;
  1903. if ($this->lexer->isNextToken(Lexer::T_AS)) {
  1904. $this->match(Lexer::T_AS);
  1905. $mustHaveAliasResultVariable = true;
  1906. }
  1907. $hiddenAliasResultVariable = false;
  1908. if ($this->lexer->isNextToken(Lexer::T_HIDDEN)) {
  1909. $this->match(Lexer::T_HIDDEN);
  1910. $hiddenAliasResultVariable = true;
  1911. }
  1912. $aliasResultVariable = null;
  1913. if ($mustHaveAliasResultVariable || $this->lexer->isNextToken(Lexer::T_IDENTIFIER)) {
  1914. $token = $this->lexer->lookahead;
  1915. $aliasResultVariable = $this->AliasResultVariable();
  1916. // Include AliasResultVariable in query components.
  1917. $this->queryComponents[$aliasResultVariable] = [
  1918. 'resultVariable' => $expression,
  1919. 'nestingLevel' => $this->nestingLevel,
  1920. 'token' => $token,
  1921. ];
  1922. }
  1923. // AST
  1924. $expr = new AST\SelectExpression($expression, $aliasResultVariable, $hiddenAliasResultVariable);
  1925. if ($identVariable) {
  1926. $this->identVariableExpressions[$identVariable] = $expr;
  1927. }
  1928. return $expr;
  1929. }
  1930. /**
  1931. * SimpleSelectExpression ::= (
  1932. * StateFieldPathExpression | IdentificationVariable | FunctionDeclaration |
  1933. * AggregateExpression | "(" Subselect ")" | ScalarExpression
  1934. * ) [["AS"] AliasResultVariable]
  1935. *
  1936. * @return SimpleSelectExpression
  1937. */
  1938. public function SimpleSelectExpression()
  1939. {
  1940. $peek = $this->lexer->glimpse();
  1941. switch ($this->lexer->lookahead['type']) {
  1942. case Lexer::T_IDENTIFIER:
  1943. switch (true) {
  1944. case $peek['type'] === Lexer::T_DOT:
  1945. $expression = $this->StateFieldPathExpression();
  1946. return new AST\SimpleSelectExpression($expression);
  1947. case $peek['type'] !== Lexer::T_OPEN_PARENTHESIS:
  1948. $expression = $this->IdentificationVariable();
  1949. return new AST\SimpleSelectExpression($expression);
  1950. case $this->isFunction():
  1951. // SUM(u.id) + COUNT(u.id)
  1952. if ($this->isMathOperator($this->peekBeyondClosingParenthesis())) {
  1953. return new AST\SimpleSelectExpression($this->ScalarExpression());
  1954. }
  1955. // COUNT(u.id)
  1956. if ($this->isAggregateFunction($this->lexer->lookahead['type'])) {
  1957. return new AST\SimpleSelectExpression($this->AggregateExpression());
  1958. }
  1959. // IDENTITY(u)
  1960. return new AST\SimpleSelectExpression($this->FunctionDeclaration());
  1961. default:
  1962. // Do nothing
  1963. }
  1964. break;
  1965. case Lexer::T_OPEN_PARENTHESIS:
  1966. if ($peek['type'] !== Lexer::T_SELECT) {
  1967. // Shortcut: ScalarExpression => SimpleArithmeticExpression
  1968. $expression = $this->SimpleArithmeticExpression();
  1969. return new AST\SimpleSelectExpression($expression);
  1970. }
  1971. // Subselect
  1972. $this->match(Lexer::T_OPEN_PARENTHESIS);
  1973. $expression = $this->Subselect();
  1974. $this->match(Lexer::T_CLOSE_PARENTHESIS);
  1975. return new AST\SimpleSelectExpression($expression);
  1976. default:
  1977. // Do nothing
  1978. }
  1979. $this->lexer->peek();
  1980. $expression = $this->ScalarExpression();
  1981. $expr = new AST\SimpleSelectExpression($expression);
  1982. if ($this->lexer->isNextToken(Lexer::T_AS)) {
  1983. $this->match(Lexer::T_AS);
  1984. }
  1985. if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) {
  1986. $token = $this->lexer->lookahead;
  1987. $resultVariable = $this->AliasResultVariable();
  1988. $expr->fieldIdentificationVariable = $resultVariable;
  1989. // Include AliasResultVariable in query components.
  1990. $this->queryComponents[$resultVariable] = [
  1991. 'resultvariable' => $expr,
  1992. 'nestingLevel' => $this->nestingLevel,
  1993. 'token' => $token,
  1994. ];
  1995. }
  1996. return $expr;
  1997. }
  1998. /**
  1999. * ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}*
  2000. *
  2001. * @return AST\ConditionalExpression|AST\ConditionalFactor|AST\ConditionalPrimary|AST\ConditionalTerm
  2002. */
  2003. public function ConditionalExpression()
  2004. {
  2005. $conditionalTerms = [];
  2006. $conditionalTerms[] = $this->ConditionalTerm();
  2007. while ($this->lexer->isNextToken(Lexer::T_OR)) {
  2008. $this->match(Lexer::T_OR);
  2009. $conditionalTerms[] = $this->ConditionalTerm();
  2010. }
  2011. // Phase 1 AST optimization: Prevent AST\ConditionalExpression
  2012. // if only one AST\ConditionalTerm is defined
  2013. if (count($conditionalTerms) === 1) {
  2014. return $conditionalTerms[0];
  2015. }
  2016. return new AST\ConditionalExpression($conditionalTerms);
  2017. }
  2018. /**
  2019. * ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}*
  2020. *
  2021. * @return AST\ConditionalFactor|AST\ConditionalPrimary|AST\ConditionalTerm
  2022. */
  2023. public function ConditionalTerm()
  2024. {
  2025. $conditionalFactors = [];
  2026. $conditionalFactors[] = $this->ConditionalFactor();
  2027. while ($this->lexer->isNextToken(Lexer::T_AND)) {
  2028. $this->match(Lexer::T_AND);
  2029. $conditionalFactors[] = $this->ConditionalFactor();
  2030. }
  2031. // Phase 1 AST optimization: Prevent AST\ConditionalTerm
  2032. // if only one AST\ConditionalFactor is defined
  2033. if (count($conditionalFactors) === 1) {
  2034. return $conditionalFactors[0];
  2035. }
  2036. return new AST\ConditionalTerm($conditionalFactors);
  2037. }
  2038. /**
  2039. * ConditionalFactor ::= ["NOT"] ConditionalPrimary
  2040. *
  2041. * @return AST\ConditionalFactor|AST\ConditionalPrimary
  2042. */
  2043. public function ConditionalFactor()
  2044. {
  2045. $not = false;
  2046. if ($this->lexer->isNextToken(Lexer::T_NOT)) {
  2047. $this->match(Lexer::T_NOT);
  2048. $not = true;
  2049. }
  2050. $conditionalPrimary = $this->ConditionalPrimary();
  2051. // Phase 1 AST optimization: Prevent AST\ConditionalFactor
  2052. // if only one AST\ConditionalPrimary is defined
  2053. if (! $not) {
  2054. return $conditionalPrimary;
  2055. }
  2056. $conditionalFactor = new AST\ConditionalFactor($conditionalPrimary);
  2057. $conditionalFactor->not = $not;
  2058. return $conditionalFactor;
  2059. }
  2060. /**
  2061. * ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")"
  2062. *
  2063. * @return ConditionalPrimary
  2064. */
  2065. public function ConditionalPrimary()
  2066. {
  2067. $condPrimary = new AST\ConditionalPrimary();
  2068. if (! $this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
  2069. $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression();
  2070. return $condPrimary;
  2071. }
  2072. // Peek beyond the matching closing parenthesis ')'
  2073. $peek = $this->peekBeyondClosingParenthesis();
  2074. if (
  2075. $peek !== null && (
  2076. in_array($peek['value'], ['=', '<', '<=', '<>', '>', '>=', '!=']) ||
  2077. in_array($peek['type'], [Lexer::T_NOT, Lexer::T_BETWEEN, Lexer::T_LIKE, Lexer::T_IN, Lexer::T_IS, Lexer::T_EXISTS]) ||
  2078. $this->isMathOperator($peek)
  2079. )
  2080. ) {
  2081. $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression();
  2082. return $condPrimary;
  2083. }
  2084. $this->match(Lexer::T_OPEN_PARENTHESIS);
  2085. $condPrimary->conditionalExpression = $this->ConditionalExpression();
  2086. $this->match(Lexer::T_CLOSE_PARENTHESIS);
  2087. return $condPrimary;
  2088. }
  2089. /**
  2090. * SimpleConditionalExpression ::=
  2091. * ComparisonExpression | BetweenExpression | LikeExpression |
  2092. * InExpression | NullComparisonExpression | ExistsExpression |
  2093. * EmptyCollectionComparisonExpression | CollectionMemberExpression |
  2094. * InstanceOfExpression
  2095. *
  2096. * @return Node
  2097. */
  2098. public function SimpleConditionalExpression()
  2099. {
  2100. if ($this->lexer->isNextToken(Lexer::T_EXISTS)) {
  2101. return $this->ExistsExpression();
  2102. }
  2103. $token = $this->lexer->lookahead;
  2104. $peek = $this->lexer->glimpse();
  2105. $lookahead = $token;
  2106. if ($this->lexer->isNextToken(Lexer::T_NOT)) {
  2107. $token = $this->lexer->glimpse();
  2108. }
  2109. if ($token['type'] === Lexer::T_IDENTIFIER || $token['type'] === Lexer::T_INPUT_PARAMETER || $this->isFunction()) {
  2110. // Peek beyond the matching closing parenthesis.
  2111. $beyond = $this->lexer->peek();
  2112. switch ($peek['value']) {
  2113. case '(':
  2114. // Peeks beyond the matched closing parenthesis.
  2115. $token = $this->peekBeyondClosingParenthesis(false);
  2116. if ($token['type'] === Lexer::T_NOT) {
  2117. $token = $this->lexer->peek();
  2118. }
  2119. if ($token['type'] === Lexer::T_IS) {
  2120. $lookahead = $this->lexer->peek();
  2121. }
  2122. break;
  2123. default:
  2124. // Peek beyond the PathExpression or InputParameter.
  2125. $token = $beyond;
  2126. while ($token['value'] === '.') {
  2127. $this->lexer->peek();
  2128. $token = $this->lexer->peek();
  2129. }
  2130. // Also peek beyond a NOT if there is one.
  2131. if ($token['type'] === Lexer::T_NOT) {
  2132. $token = $this->lexer->peek();
  2133. }
  2134. // We need to go even further in case of IS (differentiate between NULL and EMPTY)
  2135. $lookahead = $this->lexer->peek();
  2136. }
  2137. // Also peek beyond a NOT if there is one.
  2138. if ($lookahead['type'] === Lexer::T_NOT) {
  2139. $lookahead = $this->lexer->peek();
  2140. }
  2141. $this->lexer->resetPeek();
  2142. }
  2143. if ($token['type'] === Lexer::T_BETWEEN) {
  2144. return $this->BetweenExpression();
  2145. }
  2146. if ($token['type'] === Lexer::T_LIKE) {
  2147. return $this->LikeExpression();
  2148. }
  2149. if ($token['type'] === Lexer::T_IN) {
  2150. return $this->InExpression();
  2151. }
  2152. if ($token['type'] === Lexer::T_INSTANCE) {
  2153. return $this->InstanceOfExpression();
  2154. }
  2155. if ($token['type'] === Lexer::T_MEMBER) {
  2156. return $this->CollectionMemberExpression();
  2157. }
  2158. if ($token['type'] === Lexer::T_IS && $lookahead['type'] === Lexer::T_NULL) {
  2159. return $this->NullComparisonExpression();
  2160. }
  2161. if ($token['type'] === Lexer::T_IS && $lookahead['type'] === Lexer::T_EMPTY) {
  2162. return $this->EmptyCollectionComparisonExpression();
  2163. }
  2164. return $this->ComparisonExpression();
  2165. }
  2166. /**
  2167. * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY"
  2168. *
  2169. * @return EmptyCollectionComparisonExpression
  2170. */
  2171. public function EmptyCollectionComparisonExpression()
  2172. {
  2173. $emptyCollectionCompExpr = new AST\EmptyCollectionComparisonExpression(
  2174. $this->CollectionValuedPathExpression()
  2175. );
  2176. $this->match(Lexer::T_IS);
  2177. if ($this->lexer->isNextToken(Lexer::T_NOT)) {
  2178. $this->match(Lexer::T_NOT);
  2179. $emptyCollectionCompExpr->not = true;
  2180. }
  2181. $this->match(Lexer::T_EMPTY);
  2182. return $emptyCollectionCompExpr;
  2183. }
  2184. /**
  2185. * CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression
  2186. *
  2187. * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression
  2188. * SimpleEntityExpression ::= IdentificationVariable | InputParameter
  2189. *
  2190. * @return CollectionMemberExpression
  2191. */
  2192. public function CollectionMemberExpression()
  2193. {
  2194. $not = false;
  2195. $entityExpr = $this->EntityExpression();
  2196. if ($this->lexer->isNextToken(Lexer::T_NOT)) {
  2197. $this->match(Lexer::T_NOT);
  2198. $not = true;
  2199. }
  2200. $this->match(Lexer::T_MEMBER);
  2201. if ($this->lexer->isNextToken(Lexer::T_OF)) {
  2202. $this->match(Lexer::T_OF);
  2203. }
  2204. $collMemberExpr = new AST\CollectionMemberExpression(
  2205. $entityExpr,
  2206. $this->CollectionValuedPathExpression()
  2207. );
  2208. $collMemberExpr->not = $not;
  2209. return $collMemberExpr;
  2210. }
  2211. /**
  2212. * Literal ::= string | char | integer | float | boolean
  2213. *
  2214. * @return Literal
  2215. */
  2216. public function Literal()
  2217. {
  2218. switch ($this->lexer->lookahead['type']) {
  2219. case Lexer::T_STRING:
  2220. $this->match(Lexer::T_STRING);
  2221. return new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']);
  2222. case Lexer::T_INTEGER:
  2223. case Lexer::T_FLOAT:
  2224. $this->match(
  2225. $this->lexer->isNextToken(Lexer::T_INTEGER) ? Lexer::T_INTEGER : Lexer::T_FLOAT
  2226. );
  2227. return new AST\Literal(AST\Literal::NUMERIC, $this->lexer->token['value']);
  2228. case Lexer::T_TRUE:
  2229. case Lexer::T_FALSE:
  2230. $this->match(
  2231. $this->lexer->isNextToken(Lexer::T_TRUE) ? Lexer::T_TRUE : Lexer::T_FALSE
  2232. );
  2233. return new AST\Literal(AST\Literal::BOOLEAN, $this->lexer->token['value']);
  2234. default:
  2235. $this->syntaxError('Literal');
  2236. }
  2237. }
  2238. /**
  2239. * InParameter ::= Literal | InputParameter
  2240. *
  2241. * @return AST\InputParameter|AST\Literal
  2242. */
  2243. public function InParameter()
  2244. {
  2245. if ($this->lexer->lookahead['type'] === Lexer::T_INPUT_PARAMETER) {
  2246. return $this->InputParameter();
  2247. }
  2248. return $this->Literal();
  2249. }
  2250. /**
  2251. * InputParameter ::= PositionalParameter | NamedParameter
  2252. *
  2253. * @return InputParameter
  2254. */
  2255. public function InputParameter()
  2256. {
  2257. $this->match(Lexer::T_INPUT_PARAMETER);
  2258. return new AST\InputParameter($this->lexer->token['value']);
  2259. }
  2260. /**
  2261. * ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")"
  2262. *
  2263. * @return ArithmeticExpression
  2264. */
  2265. public function ArithmeticExpression()
  2266. {
  2267. $expr = new AST\ArithmeticExpression();
  2268. if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
  2269. $peek = $this->lexer->glimpse();
  2270. if ($peek['type'] === Lexer::T_SELECT) {
  2271. $this->match(Lexer::T_OPEN_PARENTHESIS);
  2272. $expr->subselect = $this->Subselect();
  2273. $this->match(Lexer::T_CLOSE_PARENTHESIS);
  2274. return $expr;
  2275. }
  2276. }
  2277. $expr->simpleArithmeticExpression = $this->SimpleArithmeticExpression();
  2278. return $expr;
  2279. }
  2280. /**
  2281. * SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}*
  2282. *
  2283. * @return SimpleArithmeticExpression
  2284. */
  2285. public function SimpleArithmeticExpression()
  2286. {
  2287. $terms = [];
  2288. $terms[] = $this->ArithmeticTerm();
  2289. while (($isPlus = $this->lexer->isNextToken(Lexer::T_PLUS)) || $this->lexer->isNextToken(Lexer::T_MINUS)) {
  2290. $this->match($isPlus ? Lexer::T_PLUS : Lexer::T_MINUS);
  2291. $terms[] = $this->lexer->token['value'];
  2292. $terms[] = $this->ArithmeticTerm();
  2293. }
  2294. // Phase 1 AST optimization: Prevent AST\SimpleArithmeticExpression
  2295. // if only one AST\ArithmeticTerm is defined
  2296. if (count($terms) === 1) {
  2297. return $terms[0];
  2298. }
  2299. return new AST\SimpleArithmeticExpression($terms);
  2300. }
  2301. /**
  2302. * ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}*
  2303. *
  2304. * @return ArithmeticTerm
  2305. */
  2306. public function ArithmeticTerm()
  2307. {
  2308. $factors = [];
  2309. $factors[] = $this->ArithmeticFactor();
  2310. while (($isMult = $this->lexer->isNextToken(Lexer::T_MULTIPLY)) || $this->lexer->isNextToken(Lexer::T_DIVIDE)) {
  2311. $this->match($isMult ? Lexer::T_MULTIPLY : Lexer::T_DIVIDE);
  2312. $factors[] = $this->lexer->token['value'];
  2313. $factors[] = $this->ArithmeticFactor();
  2314. }
  2315. // Phase 1 AST optimization: Prevent AST\ArithmeticTerm
  2316. // if only one AST\ArithmeticFactor is defined
  2317. if (count($factors) === 1) {
  2318. return $factors[0];
  2319. }
  2320. return new AST\ArithmeticTerm($factors);
  2321. }
  2322. /**
  2323. * ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary
  2324. *
  2325. * @return ArithmeticFactor
  2326. */
  2327. public function ArithmeticFactor()
  2328. {
  2329. $sign = null;
  2330. $isPlus = $this->lexer->isNextToken(Lexer::T_PLUS);
  2331. if ($isPlus || $this->lexer->isNextToken(Lexer::T_MINUS)) {
  2332. $this->match($isPlus ? Lexer::T_PLUS : Lexer::T_MINUS);
  2333. $sign = $isPlus;
  2334. }
  2335. $primary = $this->ArithmeticPrimary();
  2336. // Phase 1 AST optimization: Prevent AST\ArithmeticFactor
  2337. // if only one AST\ArithmeticPrimary is defined
  2338. if ($sign === null) {
  2339. return $primary;
  2340. }
  2341. return new AST\ArithmeticFactor($primary, $sign);
  2342. }
  2343. /**
  2344. * ArithmeticPrimary ::= SingleValuedPathExpression | Literal | ParenthesisExpression
  2345. * | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings
  2346. * | FunctionsReturningDatetime | IdentificationVariable | ResultVariable
  2347. * | InputParameter | CaseExpression
  2348. *
  2349. * @return Node|string
  2350. */
  2351. public function ArithmeticPrimary()
  2352. {
  2353. if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
  2354. $this->match(Lexer::T_OPEN_PARENTHESIS);
  2355. $expr = $this->SimpleArithmeticExpression();
  2356. $this->match(Lexer::T_CLOSE_PARENTHESIS);
  2357. return new AST\ParenthesisExpression($expr);
  2358. }
  2359. switch ($this->lexer->lookahead['type']) {
  2360. case Lexer::T_COALESCE:
  2361. case Lexer::T_NULLIF:
  2362. case Lexer::T_CASE:
  2363. return $this->CaseExpression();
  2364. case Lexer::T_IDENTIFIER:
  2365. $peek = $this->lexer->glimpse();
  2366. if ($peek !== null && $peek['value'] === '(') {
  2367. return $this->FunctionDeclaration();
  2368. }
  2369. if ($peek !== null && $peek['value'] === '.') {
  2370. return $this->SingleValuedPathExpression();
  2371. }
  2372. if (isset($this->queryComponents[$this->lexer->lookahead['value']]['resultVariable'])) {
  2373. return $this->ResultVariable();
  2374. }
  2375. return $this->StateFieldPathExpression();
  2376. case Lexer::T_INPUT_PARAMETER:
  2377. return $this->InputParameter();
  2378. default:
  2379. $peek = $this->lexer->glimpse();
  2380. if ($peek !== null && $peek['value'] === '(') {
  2381. return $this->FunctionDeclaration();
  2382. }
  2383. return $this->Literal();
  2384. }
  2385. }
  2386. /**
  2387. * StringExpression ::= StringPrimary | ResultVariable | "(" Subselect ")"
  2388. *
  2389. * @return Subselect|string
  2390. */
  2391. public function StringExpression()
  2392. {
  2393. $peek = $this->lexer->glimpse();
  2394. // Subselect
  2395. if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS) && $peek['type'] === Lexer::T_SELECT) {
  2396. $this->match(Lexer::T_OPEN_PARENTHESIS);
  2397. $expr = $this->Subselect();
  2398. $this->match(Lexer::T_CLOSE_PARENTHESIS);
  2399. return $expr;
  2400. }
  2401. // ResultVariable (string)
  2402. if (
  2403. $this->lexer->isNextToken(Lexer::T_IDENTIFIER) &&
  2404. isset($this->queryComponents[$this->lexer->lookahead['value']]['resultVariable'])
  2405. ) {
  2406. return $this->ResultVariable();
  2407. }
  2408. return $this->StringPrimary();
  2409. }
  2410. /**
  2411. * StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression
  2412. *
  2413. * @return Node
  2414. */
  2415. public function StringPrimary()
  2416. {
  2417. $lookaheadType = $this->lexer->lookahead['type'];
  2418. switch ($lookaheadType) {
  2419. case Lexer::T_IDENTIFIER:
  2420. $peek = $this->lexer->glimpse();
  2421. if ($peek['value'] === '.') {
  2422. return $this->StateFieldPathExpression();
  2423. }
  2424. if ($peek['value'] === '(') {
  2425. // do NOT directly go to FunctionsReturningString() because it doesn't check for custom functions.
  2426. return $this->FunctionDeclaration();
  2427. }
  2428. $this->syntaxError("'.' or '('");
  2429. break;
  2430. case Lexer::T_STRING:
  2431. $this->match(Lexer::T_STRING);
  2432. return new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']);
  2433. case Lexer::T_INPUT_PARAMETER:
  2434. return $this->InputParameter();
  2435. case Lexer::T_CASE:
  2436. case Lexer::T_COALESCE:
  2437. case Lexer::T_NULLIF:
  2438. return $this->CaseExpression();
  2439. default:
  2440. if ($this->isAggregateFunction($lookaheadType)) {
  2441. return $this->AggregateExpression();
  2442. }
  2443. }
  2444. $this->syntaxError(
  2445. 'StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression'
  2446. );
  2447. }
  2448. /**
  2449. * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression
  2450. *
  2451. * @return AST\InputParameter|PathExpression
  2452. */
  2453. public function EntityExpression()
  2454. {
  2455. $glimpse = $this->lexer->glimpse();
  2456. if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER) && $glimpse['value'] === '.') {
  2457. return $this->SingleValuedAssociationPathExpression();
  2458. }
  2459. return $this->SimpleEntityExpression();
  2460. }
  2461. /**
  2462. * SimpleEntityExpression ::= IdentificationVariable | InputParameter
  2463. *
  2464. * @return AST\InputParameter|AST\PathExpression
  2465. */
  2466. public function SimpleEntityExpression()
  2467. {
  2468. if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
  2469. return $this->InputParameter();
  2470. }
  2471. return $this->StateFieldPathExpression();
  2472. }
  2473. /**
  2474. * AggregateExpression ::=
  2475. * ("AVG" | "MAX" | "MIN" | "SUM" | "COUNT") "(" ["DISTINCT"] SimpleArithmeticExpression ")"
  2476. *
  2477. * @return AggregateExpression
  2478. */
  2479. public function AggregateExpression()
  2480. {
  2481. $lookaheadType = $this->lexer->lookahead['type'];
  2482. $isDistinct = false;
  2483. if (! in_array($lookaheadType, [Lexer::T_COUNT, Lexer::T_AVG, Lexer::T_MAX, Lexer::T_MIN, Lexer::T_SUM])) {
  2484. $this->syntaxError('One of: MAX, MIN, AVG, SUM, COUNT');
  2485. }
  2486. $this->match($lookaheadType);
  2487. $functionName = $this->lexer->token['value'];
  2488. $this->match(Lexer::T_OPEN_PARENTHESIS);
  2489. if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) {
  2490. $this->match(Lexer::T_DISTINCT);
  2491. $isDistinct = true;
  2492. }
  2493. $pathExp = $this->SimpleArithmeticExpression();
  2494. $this->match(Lexer::T_CLOSE_PARENTHESIS);
  2495. return new AST\AggregateExpression($functionName, $pathExp, $isDistinct);
  2496. }
  2497. /**
  2498. * QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")"
  2499. *
  2500. * @return QuantifiedExpression
  2501. */
  2502. public function QuantifiedExpression()
  2503. {
  2504. $lookaheadType = $this->lexer->lookahead['type'];
  2505. $value = $this->lexer->lookahead['value'];
  2506. if (! in_array($lookaheadType, [Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME])) {
  2507. $this->syntaxError('ALL, ANY or SOME');
  2508. }
  2509. $this->match($lookaheadType);
  2510. $this->match(Lexer::T_OPEN_PARENTHESIS);
  2511. $qExpr = new AST\QuantifiedExpression($this->Subselect());
  2512. $qExpr->type = $value;
  2513. $this->match(Lexer::T_CLOSE_PARENTHESIS);
  2514. return $qExpr;
  2515. }
  2516. /**
  2517. * BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression
  2518. *
  2519. * @return BetweenExpression
  2520. */
  2521. public function BetweenExpression()
  2522. {
  2523. $not = false;
  2524. $arithExpr1 = $this->ArithmeticExpression();
  2525. if ($this->lexer->isNextToken(Lexer::T_NOT)) {
  2526. $this->match(Lexer::T_NOT);
  2527. $not = true;
  2528. }
  2529. $this->match(Lexer::T_BETWEEN);
  2530. $arithExpr2 = $this->ArithmeticExpression();
  2531. $this->match(Lexer::T_AND);
  2532. $arithExpr3 = $this->ArithmeticExpression();
  2533. $betweenExpr = new AST\BetweenExpression($arithExpr1, $arithExpr2, $arithExpr3);
  2534. $betweenExpr->not = $not;
  2535. return $betweenExpr;
  2536. }
  2537. /**
  2538. * ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression )
  2539. *
  2540. * @return ComparisonExpression
  2541. */
  2542. public function ComparisonExpression()
  2543. {
  2544. $this->lexer->glimpse();
  2545. $leftExpr = $this->ArithmeticExpression();
  2546. $operator = $this->ComparisonOperator();
  2547. $rightExpr = $this->isNextAllAnySome()
  2548. ? $this->QuantifiedExpression()
  2549. : $this->ArithmeticExpression();
  2550. return new AST\ComparisonExpression($leftExpr, $operator, $rightExpr);
  2551. }
  2552. /**
  2553. * InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
  2554. *
  2555. * @return InExpression
  2556. */
  2557. public function InExpression()
  2558. {
  2559. $inExpression = new AST\InExpression($this->ArithmeticExpression());
  2560. if ($this->lexer->isNextToken(Lexer::T_NOT)) {
  2561. $this->match(Lexer::T_NOT);
  2562. $inExpression->not = true;
  2563. }
  2564. $this->match(Lexer::T_IN);
  2565. $this->match(Lexer::T_OPEN_PARENTHESIS);
  2566. if ($this->lexer->isNextToken(Lexer::T_SELECT)) {
  2567. $inExpression->subselect = $this->Subselect();
  2568. } else {
  2569. $literals = [];
  2570. $literals[] = $this->InParameter();
  2571. while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
  2572. $this->match(Lexer::T_COMMA);
  2573. $literals[] = $this->InParameter();
  2574. }
  2575. $inExpression->literals = $literals;
  2576. }
  2577. $this->match(Lexer::T_CLOSE_PARENTHESIS);
  2578. return $inExpression;
  2579. }
  2580. /**
  2581. * InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")")
  2582. *
  2583. * @return InstanceOfExpression
  2584. */
  2585. public function InstanceOfExpression()
  2586. {
  2587. $instanceOfExpression = new AST\InstanceOfExpression($this->IdentificationVariable());
  2588. if ($this->lexer->isNextToken(Lexer::T_NOT)) {
  2589. $this->match(Lexer::T_NOT);
  2590. $instanceOfExpression->not = true;
  2591. }
  2592. $this->match(Lexer::T_INSTANCE);
  2593. $this->match(Lexer::T_OF);
  2594. $exprValues = [];
  2595. if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
  2596. $this->match(Lexer::T_OPEN_PARENTHESIS);
  2597. $exprValues[] = $this->InstanceOfParameter();
  2598. while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
  2599. $this->match(Lexer::T_COMMA);
  2600. $exprValues[] = $this->InstanceOfParameter();
  2601. }
  2602. $this->match(Lexer::T_CLOSE_PARENTHESIS);
  2603. $instanceOfExpression->value = $exprValues;
  2604. return $instanceOfExpression;
  2605. }
  2606. $exprValues[] = $this->InstanceOfParameter();
  2607. $instanceOfExpression->value = $exprValues;
  2608. return $instanceOfExpression;
  2609. }
  2610. /**
  2611. * InstanceOfParameter ::= AbstractSchemaName | InputParameter
  2612. *
  2613. * @return mixed
  2614. */
  2615. public function InstanceOfParameter()
  2616. {
  2617. if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
  2618. $this->match(Lexer::T_INPUT_PARAMETER);
  2619. return new AST\InputParameter($this->lexer->token['value']);
  2620. }
  2621. $abstractSchemaName = $this->AbstractSchemaName();
  2622. $this->validateAbstractSchemaName($abstractSchemaName);
  2623. return $abstractSchemaName;
  2624. }
  2625. /**
  2626. * LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char]
  2627. *
  2628. * @return LikeExpression
  2629. */
  2630. public function LikeExpression()
  2631. {
  2632. $stringExpr = $this->StringExpression();
  2633. $not = false;
  2634. if ($this->lexer->isNextToken(Lexer::T_NOT)) {
  2635. $this->match(Lexer::T_NOT);
  2636. $not = true;
  2637. }
  2638. $this->match(Lexer::T_LIKE);
  2639. if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
  2640. $this->match(Lexer::T_INPUT_PARAMETER);
  2641. $stringPattern = new AST\InputParameter($this->lexer->token['value']);
  2642. } else {
  2643. $stringPattern = $this->StringPrimary();
  2644. }
  2645. $escapeChar = null;
  2646. if ($this->lexer->lookahead !== null && $this->lexer->lookahead['type'] === Lexer::T_ESCAPE) {
  2647. $this->match(Lexer::T_ESCAPE);
  2648. $this->match(Lexer::T_STRING);
  2649. $escapeChar = new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']);
  2650. }
  2651. $likeExpr = new AST\LikeExpression($stringExpr, $stringPattern, $escapeChar);
  2652. $likeExpr->not = $not;
  2653. return $likeExpr;
  2654. }
  2655. /**
  2656. * NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | AggregateExpression | FunctionDeclaration | IdentificationVariable | SingleValuedPathExpression | ResultVariable) "IS" ["NOT"] "NULL"
  2657. *
  2658. * @return NullComparisonExpression
  2659. */
  2660. public function NullComparisonExpression()
  2661. {
  2662. switch (true) {
  2663. case $this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER):
  2664. $this->match(Lexer::T_INPUT_PARAMETER);
  2665. $expr = new AST\InputParameter($this->lexer->token['value']);
  2666. break;
  2667. case $this->lexer->isNextToken(Lexer::T_NULLIF):
  2668. $expr = $this->NullIfExpression();
  2669. break;
  2670. case $this->lexer->isNextToken(Lexer::T_COALESCE):
  2671. $expr = $this->CoalesceExpression();
  2672. break;
  2673. case $this->isFunction():
  2674. $expr = $this->FunctionDeclaration();
  2675. break;
  2676. default:
  2677. // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression
  2678. $glimpse = $this->lexer->glimpse();
  2679. if ($glimpse['type'] === Lexer::T_DOT) {
  2680. $expr = $this->SingleValuedPathExpression();
  2681. // Leave switch statement
  2682. break;
  2683. }
  2684. $lookaheadValue = $this->lexer->lookahead['value'];
  2685. // Validate existing component
  2686. if (! isset($this->queryComponents[$lookaheadValue])) {
  2687. $this->semanticalError('Cannot add having condition on undefined result variable.');
  2688. }
  2689. // Validate SingleValuedPathExpression (ie.: "product")
  2690. if (isset($this->queryComponents[$lookaheadValue]['metadata'])) {
  2691. $expr = $this->SingleValuedPathExpression();
  2692. break;
  2693. }
  2694. // Validating ResultVariable
  2695. if (! isset($this->queryComponents[$lookaheadValue]['resultVariable'])) {
  2696. $this->semanticalError('Cannot add having condition on a non result variable.');
  2697. }
  2698. $expr = $this->ResultVariable();
  2699. break;
  2700. }
  2701. $nullCompExpr = new AST\NullComparisonExpression($expr);
  2702. $this->match(Lexer::T_IS);
  2703. if ($this->lexer->isNextToken(Lexer::T_NOT)) {
  2704. $this->match(Lexer::T_NOT);
  2705. $nullCompExpr->not = true;
  2706. }
  2707. $this->match(Lexer::T_NULL);
  2708. return $nullCompExpr;
  2709. }
  2710. /**
  2711. * ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")"
  2712. *
  2713. * @return ExistsExpression
  2714. */
  2715. public function ExistsExpression()
  2716. {
  2717. $not = false;
  2718. if ($this->lexer->isNextToken(Lexer::T_NOT)) {
  2719. $this->match(Lexer::T_NOT);
  2720. $not = true;
  2721. }
  2722. $this->match(Lexer::T_EXISTS);
  2723. $this->match(Lexer::T_OPEN_PARENTHESIS);
  2724. $existsExpression = new AST\ExistsExpression($this->Subselect());
  2725. $existsExpression->not = $not;
  2726. $this->match(Lexer::T_CLOSE_PARENTHESIS);
  2727. return $existsExpression;
  2728. }
  2729. /**
  2730. * ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!="
  2731. *
  2732. * @return string
  2733. */
  2734. public function ComparisonOperator()
  2735. {
  2736. switch ($this->lexer->lookahead['value']) {
  2737. case '=':
  2738. $this->match(Lexer::T_EQUALS);
  2739. return '=';
  2740. case '<':
  2741. $this->match(Lexer::T_LOWER_THAN);
  2742. $operator = '<';
  2743. if ($this->lexer->isNextToken(Lexer::T_EQUALS)) {
  2744. $this->match(Lexer::T_EQUALS);
  2745. $operator .= '=';
  2746. } elseif ($this->lexer->isNextToken(Lexer::T_GREATER_THAN)) {
  2747. $this->match(Lexer::T_GREATER_THAN);
  2748. $operator .= '>';
  2749. }
  2750. return $operator;
  2751. case '>':
  2752. $this->match(Lexer::T_GREATER_THAN);
  2753. $operator = '>';
  2754. if ($this->lexer->isNextToken(Lexer::T_EQUALS)) {
  2755. $this->match(Lexer::T_EQUALS);
  2756. $operator .= '=';
  2757. }
  2758. return $operator;
  2759. case '!':
  2760. $this->match(Lexer::T_NEGATE);
  2761. $this->match(Lexer::T_EQUALS);
  2762. return '<>';
  2763. default:
  2764. $this->syntaxError('=, <, <=, <>, >, >=, !=');
  2765. }
  2766. }
  2767. /**
  2768. * FunctionDeclaration ::= FunctionsReturningStrings | FunctionsReturningNumerics | FunctionsReturningDatetime
  2769. *
  2770. * @return FunctionNode
  2771. */
  2772. public function FunctionDeclaration()
  2773. {
  2774. $token = $this->lexer->lookahead;
  2775. $funcName = strtolower($token['value']);
  2776. $customFunctionDeclaration = $this->CustomFunctionDeclaration();
  2777. // Check for custom functions functions first!
  2778. switch (true) {
  2779. case $customFunctionDeclaration !== null:
  2780. return $customFunctionDeclaration;
  2781. case isset(self::$stringFunctions[$funcName]):
  2782. return $this->FunctionsReturningStrings();
  2783. case isset(self::$numericFunctions[$funcName]):
  2784. return $this->FunctionsReturningNumerics();
  2785. case isset(self::$datetimeFunctions[$funcName]):
  2786. return $this->FunctionsReturningDatetime();
  2787. default:
  2788. $this->syntaxError('known function', $token);
  2789. }
  2790. }
  2791. /**
  2792. * Helper function for FunctionDeclaration grammar rule.
  2793. *
  2794. * @return FunctionNode
  2795. */
  2796. private function CustomFunctionDeclaration()
  2797. {
  2798. $token = $this->lexer->lookahead;
  2799. $funcName = strtolower($token['value']);
  2800. // Check for custom functions afterwards
  2801. $config = $this->em->getConfiguration();
  2802. switch (true) {
  2803. case $config->getCustomStringFunction($funcName) !== null:
  2804. return $this->CustomFunctionsReturningStrings();
  2805. case $config->getCustomNumericFunction($funcName) !== null:
  2806. return $this->CustomFunctionsReturningNumerics();
  2807. case $config->getCustomDatetimeFunction($funcName) !== null:
  2808. return $this->CustomFunctionsReturningDatetime();
  2809. default:
  2810. return null;
  2811. }
  2812. }
  2813. /**
  2814. * FunctionsReturningNumerics ::=
  2815. * "LENGTH" "(" StringPrimary ")" |
  2816. * "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" |
  2817. * "ABS" "(" SimpleArithmeticExpression ")" |
  2818. * "SQRT" "(" SimpleArithmeticExpression ")" |
  2819. * "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" |
  2820. * "SIZE" "(" CollectionValuedPathExpression ")" |
  2821. * "DATE_DIFF" "(" ArithmeticPrimary "," ArithmeticPrimary ")" |
  2822. * "BIT_AND" "(" ArithmeticPrimary "," ArithmeticPrimary ")" |
  2823. * "BIT_OR" "(" ArithmeticPrimary "," ArithmeticPrimary ")"
  2824. *
  2825. * @return FunctionNode
  2826. */
  2827. public function FunctionsReturningNumerics()
  2828. {
  2829. $funcNameLower = strtolower($this->lexer->lookahead['value']);
  2830. $funcClass = self::$numericFunctions[$funcNameLower];
  2831. $function = new $funcClass($funcNameLower);
  2832. $function->parse($this);
  2833. return $function;
  2834. }
  2835. /**
  2836. * @return FunctionNode
  2837. */
  2838. public function CustomFunctionsReturningNumerics()
  2839. {
  2840. // getCustomNumericFunction is case-insensitive
  2841. $functionName = strtolower($this->lexer->lookahead['value']);
  2842. $functionClass = $this->em->getConfiguration()->getCustomNumericFunction($functionName);
  2843. $function = is_string($functionClass)
  2844. ? new $functionClass($functionName)
  2845. : call_user_func($functionClass, $functionName);
  2846. $function->parse($this);
  2847. return $function;
  2848. }
  2849. /**
  2850. * FunctionsReturningDateTime ::=
  2851. * "CURRENT_DATE" |
  2852. * "CURRENT_TIME" |
  2853. * "CURRENT_TIMESTAMP" |
  2854. * "DATE_ADD" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" |
  2855. * "DATE_SUB" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")"
  2856. *
  2857. * @return FunctionNode
  2858. */
  2859. public function FunctionsReturningDatetime()
  2860. {
  2861. $funcNameLower = strtolower($this->lexer->lookahead['value']);
  2862. $funcClass = self::$datetimeFunctions[$funcNameLower];
  2863. $function = new $funcClass($funcNameLower);
  2864. $function->parse($this);
  2865. return $function;
  2866. }
  2867. /**
  2868. * @return FunctionNode
  2869. */
  2870. public function CustomFunctionsReturningDatetime()
  2871. {
  2872. // getCustomDatetimeFunction is case-insensitive
  2873. $functionName = $this->lexer->lookahead['value'];
  2874. $functionClass = $this->em->getConfiguration()->getCustomDatetimeFunction($functionName);
  2875. $function = is_string($functionClass)
  2876. ? new $functionClass($functionName)
  2877. : call_user_func($functionClass, $functionName);
  2878. $function->parse($this);
  2879. return $function;
  2880. }
  2881. /**
  2882. * FunctionsReturningStrings ::=
  2883. * "CONCAT" "(" StringPrimary "," StringPrimary {"," StringPrimary}* ")" |
  2884. * "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" |
  2885. * "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" |
  2886. * "LOWER" "(" StringPrimary ")" |
  2887. * "UPPER" "(" StringPrimary ")" |
  2888. * "IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")"
  2889. *
  2890. * @return FunctionNode
  2891. */
  2892. public function FunctionsReturningStrings()
  2893. {
  2894. $funcNameLower = strtolower($this->lexer->lookahead['value']);
  2895. $funcClass = self::$stringFunctions[$funcNameLower];
  2896. $function = new $funcClass($funcNameLower);
  2897. $function->parse($this);
  2898. return $function;
  2899. }
  2900. /**
  2901. * @return FunctionNode
  2902. */
  2903. public function CustomFunctionsReturningStrings()
  2904. {
  2905. // getCustomStringFunction is case-insensitive
  2906. $functionName = $this->lexer->lookahead['value'];
  2907. $functionClass = $this->em->getConfiguration()->getCustomStringFunction($functionName);
  2908. $function = is_string($functionClass)
  2909. ? new $functionClass($functionName)
  2910. : call_user_func($functionClass, $functionName);
  2911. $function->parse($this);
  2912. return $function;
  2913. }
  2914. }