Parser.php 101 KB

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