/** * Gets a specific clause. * * @param Statement $statement The parsed query that has to be modified. * @param TokensList $list The list of tokens. * @param string $clause The clause to be returned. * @param int|string $type The type of the search. * If int, * -1 for everything that was before * 0 only for the clause * 1 for everything after * If string, the name of the first clause that * should not be included. * @param bool $skipFirst Whether to skip the first keyword in clause. * * @return string */ public static function getClause($statement, $list, $clause, $type = 0, $skipFirst = true) { /** * The index of the current clause. * * @var int $currIdx */ $currIdx = 0; /** * The count of brackets. * We keep track of them so we won't insert the clause in a subquery. * * @var int $brackets */ $brackets = 0; /** * The string to be returned. * * @var string $ret */ $ret = ''; /** * The clauses of this type of statement and their index. * * @var array $clauses */ $clauses = array_flip(array_keys($statement->getClauses())); /** * Lexer used for lexing the clause. * * @var Lexer $lexer */ $lexer = new Lexer($clause); /** * The type of this clause. * * @var string $clauseType */ $clauseType = $lexer->list->getNextOfType(Token::TYPE_KEYWORD)->value; /** * The index of this clause. * * @var int $clauseIdx */ $clauseIdx = $clauses[$clauseType]; $firstClauseIdx = $clauseIdx; $lastClauseIdx = $clauseIdx + 1; // Determining the behavior of this function. if ($type === -1) { $firstClauseIdx = -1; // Something small enough. $lastClauseIdx = $clauseIdx - 1; } elseif ($type === 1) { $firstClauseIdx = $clauseIdx + 1; $lastClauseIdx = 10000; // Something big enough. } elseif (is_string($type)) { if ($clauses[$type] > $clauseIdx) { $firstClauseIdx = $clauseIdx + 1; $lastClauseIdx = $clauses[$type] - 1; } else { $firstClauseIdx = $clauses[$type] + 1; $lastClauseIdx = $clauseIdx - 1; } } // This option is unavailable for multiple clauses. if ($type !== 0) { $skipFirst = false; } for ($i = $statement->first; $i <= $statement->last; ++$i) { $token = $list->tokens[$i]; if ($token->type === Token::TYPE_COMMENT) { continue; } if ($token->type === Token::TYPE_OPERATOR) { if ($token->value === '(') { ++$brackets; } elseif ($token->value === ')') { --$brackets; } } if ($brackets == 0) { // Checking if the section was changed. if ($token->type === Token::TYPE_KEYWORD && isset($clauses[$token->value]) && $clauses[$token->value] >= $currIdx) { $currIdx = $clauses[$token->value]; if ($skipFirst && $currIdx == $clauseIdx) { // This token is skipped (not added to the old // clause) because it will be replaced. continue; } } } if ($firstClauseIdx <= $currIdx && $currIdx <= $lastClauseIdx) { $ret .= $token->token; } } return trim($ret); }
/** * Gets a starting offset of a specific clause. * * @param Statement $statement The parsed query that has to be modified. * @param TokensList $list The list of tokens. * @param string $clause The clause to be returned. * * @return int */ public static function getClauseStartOffset($statement, $list, $clause) { /** * The count of brackets. * We keep track of them so we won't insert the clause in a subquery. * * @var int $brackets */ $brackets = 0; /** * The clauses of this type of statement and their index. * * @var array $clauses */ $clauses = array_flip(array_keys($statement->getClauses())); for ($i = $statement->first; $i <= $statement->last; ++$i) { $token = $list->tokens[$i]; if ($token->type === Token::TYPE_COMMENT) { continue; } if ($token->type === Token::TYPE_OPERATOR) { if ($token->value === '(') { ++$brackets; } elseif ($token->value === ')') { --$brackets; } } if ($brackets == 0) { if ($token->type === Token::TYPE_KEYWORD && isset($clauses[$token->value]) && $clause === $token->value) { return $i; } } } return -1; }