File tree Expand file tree Collapse file tree 3 files changed +114
-0
lines changed
Expand file tree Collapse file tree 3 files changed +114
-0
lines changed Original file line number Diff line number Diff line change @@ -531,6 +531,9 @@ public function parse()
531531 $ lastStatement ->last = $ statement ->last ;
532532
533533 $ unionType = false ;
534+
535+ // Validate clause order
536+ $ statement ->validateClauseOrder ($ this , $ list );
534537 continue ;
535538 }
536539
@@ -556,9 +559,15 @@ public function parse()
556559 }
557560 $ lastTransaction = null ;
558561 }
562+
563+ // Validate clause order
564+ $ statement ->validateClauseOrder ($ this , $ list );
559565 continue ;
560566 }
561567
568+ // Validate clause order
569+ $ statement ->validateClauseOrder ($ this , $ list );
570+
562571 // Finally, storing the statement.
563572 if ($ lastTransaction !== null ) {
564573 $ lastTransaction ->statements [] = $ statement ;
Original file line number Diff line number Diff line change @@ -422,4 +422,47 @@ public function __toString()
422422 {
423423 return $ this ->build ();
424424 }
425+
426+ /**
427+ * Validates the order of the clauses in parsed statement
428+ * Ideally this should be called after successfully
429+ * completing the parsing of each statement
430+ *
431+ * @param Parser $parser The instance that requests parsing.
432+ * @param TokensList $list The list of tokens to be parsed.
433+ *
434+ * @return boolean
435+ */
436+ public function validateClauseOrder ($ parser , $ list )
437+ {
438+ $ clauses = array_flip (array_keys ($ this ->getClauses ()));
439+
440+ if (empty ($ clauses )
441+ || count ($ clauses ) == 0
442+ ) {
443+ return true ;
444+ }
445+
446+ $ minIdx = -1 ;
447+ foreach ($ clauses as $ clauseType => $ index ) {
448+ $ clauseStartIdx = Utils \Query::getClauseStartOffset (
449+ $ this ,
450+ $ list ,
451+ $ clauseType
452+ );
453+
454+ if ($ clauseStartIdx != -1 && $ clauseStartIdx < $ minIdx ) {
455+ $ token = $ list ->tokens [$ clauseStartIdx ];
456+ $ parser ->error (
457+ __ ('Unexpected ordering of clauses. ' ),
458+ $ token
459+ );
460+ return false ;
461+ } elseif ($ clauseStartIdx != -1 ) {
462+ $ minIdx = $ clauseStartIdx ;
463+ }
464+ }
465+
466+ return true ;
467+ }
425468}
Original file line number Diff line number Diff line change @@ -783,4 +783,66 @@ public static function getFirstStatement($query, $delimiter = null)
783783
784784 return array (trim ($ statement ), $ query , $ delimiter );
785785 }
786+
787+ /**
788+ * Gets a starting offset of a specific clause.
789+ *
790+ * @param Statement $statement The parsed query that has to be modified.
791+ * @param TokensList $list The list of tokens.
792+ * @param string $clause The clause to be returned.
793+ *
794+ * @return int
795+ */
796+ public static function getClauseStartOffset ($ statement , $ list , $ clause )
797+ {
798+
799+ /**
800+ * The index of the current clause.
801+ *
802+ * @var int $currIdx
803+ */
804+ $ currIdx = 0 ;
805+
806+ /**
807+ * The count of brackets.
808+ * We keep track of them so we won't insert the clause in a subquery.
809+ *
810+ * @var int $brackets
811+ */
812+ $ brackets = 0 ;
813+
814+ /**
815+ * The clauses of this type of statement and their index.
816+ *
817+ * @var array $clauses
818+ */
819+ $ clauses = array_flip (array_keys ($ statement ->getClauses ()));
820+
821+ for ($ i = $ statement ->first ; $ i <= $ statement ->last ; ++$ i ) {
822+ $ token = $ list ->tokens [$ i ];
823+
824+ if ($ token ->type === Token::TYPE_COMMENT ) {
825+ continue ;
826+ }
827+
828+ if ($ token ->type === Token::TYPE_OPERATOR ) {
829+ if ($ token ->value === '( ' ) {
830+ ++$ brackets ;
831+ } elseif ($ token ->value === ') ' ) {
832+ --$ brackets ;
833+ }
834+ }
835+
836+ if ($ brackets == 0 ) {
837+ if (($ token ->type === Token::TYPE_KEYWORD )
838+ && (isset ($ clauses [$ token ->value ]))
839+ && ($ clause === $ token ->value )
840+ ) {
841+ return $ i ;
842+ }
843+ }
844+ }
845+
846+ return -1 ;
847+ }
786848}
You can’t perform that action at this time.
0 commit comments