length() : strlen($str); $lines = [0]; for ($i = 0; $i < $len; ++$i) { if ($str[$i] !== "\n") { continue; } $lines[] = $i + 1; } return $lines; } /** * Computes the number of the line and column given an absolute position. * * @param array $lines The starting position of each line. * @param int $pos The absolute position * * @return array */ public static function findLineNumberAndColumn(array $lines, $pos) { $line = 0; foreach ($lines as $lineNo => $lineStart) { if ($lineStart > $pos) { break; } $line = $lineNo; } return [ $line, $pos - $lines[$line], ]; } /** * Runs the linting process. * * @param string $query The query to be checked. * * @return array */ public static function lint($query) { // Disabling lint for huge queries to save some resources. if (mb_strlen($query) > 10000) { return [ [ 'message' => __('Linting is disabled for this query because it exceeds the maximum length.'), 'fromLine' => 0, 'fromColumn' => 0, 'toLine' => 0, 'toColumn' => 0, 'severity' => 'warning', ], ]; } /** * Lexer used for tokenizing the query. */ $lexer = new Lexer($query); /** * Parsed used for analysing the query. */ $parser = new Parser($lexer->list); /** * Array containing all errors. */ $errors = ParserError::get([$lexer, $parser]); /** * The response containing of all errors. * * @var array */ $response = []; /** * The starting position for each line. * * CodeMirror requires relative position to line, but the parser stores * only the absolute position of the character in string. */ $lines = static::getLines($query); // Building the response. foreach ($errors as $error) { // Starting position of the string that caused the error. [$fromLine, $fromColumn] = static::findLineNumberAndColumn($lines, $error[3]); // Ending position of the string that caused the error. [$toLine, $toColumn] = static::findLineNumberAndColumn( $lines, $error[3] + mb_strlen((string) $error[2]) ); // Building the response. $response[] = [ 'message' => sprintf( __('%1$s (near %2$s)'), htmlspecialchars((string) $error[0]), htmlspecialchars((string) $error[2]) ), 'fromLine' => $fromLine, 'fromColumn' => $fromColumn, 'toLine' => $toLine, 'toColumn' => $toColumn, 'severity' => 'error', ]; } // Sending back the answer. return $response; } }