Checks if the given character is a whitespace.
public static isWhitespace ( string $str ) : boolean | ||
$str | string | String to be checked. |
Результат | boolean |
public function testIsWhitespace() { $this->assertTrue(Context::isWhitespace(" ")); $this->assertTrue(Context::isWhitespace("\r")); $this->assertTrue(Context::isWhitespace("\n")); $this->assertTrue(Context::isWhitespace("\t")); $this->assertFalse(Context::isWhitespace("a")); $this->assertFalse(Context::isWhitespace("\\b")); $this->assertFalse(Context::isWhitespace("\\u1000")); }
/** * Extracts a statement from the buffer. * * @param bool $end Whether the end of the buffer was reached. * * @return string */ public function extract($end = false) { /** * The last parsed position. * * This is statically defined because it is not used outside anywhere * outside this method and there is probably a (minor) performance * improvement to it. * * @var int */ static $i = 0; if (empty($this->query)) { return false; } /** * The length of the buffer. * @var int $len */ $len = strlen($this->query); /** * The last index of the string that is going to be parsed. * * There must be a few characters left in the buffer so the parser can * avoid confusing some symbols that may have multiple meanings. * * For example, if the buffer ends in `-` that may be an operator or the * beginning of a comment. * * Another example if the buffer ends in `DELIMITE`. The parser is going * to require a few more characters because that may be a part of the * `DELIMITER` keyword or just a column named `DELIMITE`. * * Those extra characters are required only if there is more data * expected (the end of the buffer was not reached). * * @var int $loopLen */ $loopLen = $end ? $len : $len - 16; for (; $i < $loopLen; ++$i) { /* * Handling special parses statuses. */ if ($this->status === static::STATUS_STRING_SINGLE_QUOTES) { // Single-quoted strings like 'foo'. if ($this->query[$i - 1] != '\\' && $this->query[$i] === '\'') { $this->status = 0; } $this->current .= $this->query[$i]; continue; } elseif ($this->status === static::STATUS_STRING_DOUBLE_QUOTES) { // Double-quoted strings like "bar". if ($this->query[$i - 1] != '\\' && $this->query[$i] === '"') { $this->status = 0; } $this->current .= $this->query[$i]; continue; } elseif ($this->status === static::STATUS_STRING_BACKTICK) { if ($this->query[$i] === '`') { $this->status = 0; } $this->current .= $this->query[$i]; continue; } elseif ($this->status === static::STATUS_COMMENT_BASH || $this->status === static::STATUS_COMMENT_SQL) { // Bash-like (#) or SQL-like (-- ) comments end in new line. if ($this->query[$i] === "\n") { $this->status = 0; } continue; } elseif ($this->status === static::STATUS_COMMENT_C) { // C-like comments end in */. if ($this->query[$i - 1] === '*' && $this->query[$i] === '/') { $this->status = 0; } continue; } /* * Checking if a string started. */ if ($this->query[$i] === '\'') { $this->status = static::STATUS_STRING_SINGLE_QUOTES; $this->current .= $this->query[$i]; continue; } elseif ($this->query[$i] === '"') { $this->status = static::STATUS_STRING_DOUBLE_QUOTES; $this->current .= $this->query[$i]; continue; } elseif ($this->query[$i] === '`') { $this->status = static::STATUS_STRING_BACKTICK; $this->current .= $this->query[$i]; continue; } /* * Checking if a comment started. */ if ($this->query[$i] === '#') { $this->status = static::STATUS_COMMENT_BASH; continue; } elseif ($i + 2 < $len && $this->query[$i] === '-' && $this->query[$i + 1] === '-' && Context::isWhitespace($this->query[$i + 2])) { $this->status = static::STATUS_COMMENT_SQL; continue; } elseif ($i + 2 < $len && $this->query[$i] === '/' && $this->query[$i + 1] === '*' && $this->query[$i + 2] !== '!') { $this->status = static::STATUS_COMMENT_C; continue; } /* * Handling `DELIMITER` statement. * * The code below basically checks for * `strtoupper(substr($this->query, $i, 9)) === 'DELIMITER'` * * This optimization makes the code about 3 times faster. */ if ($i + 9 < $len && ($this->query[$i] === 'D' || $this->query[$i] === 'd') && ($this->query[$i + 1] === 'E' || $this->query[$i + 1] === 'e') && ($this->query[$i + 2] === 'L' || $this->query[$i + 2] === 'l') && ($this->query[$i + 3] === 'I' || $this->query[$i + 3] === 'i') && ($this->query[$i + 4] === 'M' || $this->query[$i + 4] === 'm') && ($this->query[$i + 5] === 'I' || $this->query[$i + 5] === 'i') && ($this->query[$i + 6] === 'T' || $this->query[$i + 6] === 't') && ($this->query[$i + 7] === 'E' || $this->query[$i + 7] === 'e') && ($this->query[$i + 8] === 'R' || $this->query[$i + 8] === 'r') && Context::isWhitespace($this->query[$i + 9])) { // Saving the current index to be able to revert any parsing // done in this block. $iBak = $i; $i += 9; // Skipping `DELIMITER`. // Skipping whitespaces. while ($i < $len && Context::isWhitespace($this->query[$i])) { ++$i; } // Parsing the delimiter. $delimiter = ''; while ($i < $len && !Context::isWhitespace($this->query[$i])) { $delimiter .= $this->query[$i++]; } // Checking if the delimiter definition ended. if ($delimiter != '' && ($i < $len && Context::isWhitespace($this->query[$i]) || $i === $len && $end)) { // Saving the delimiter. $this->setDelimiter($delimiter); // Whether this statement should be returned or not. $ret = ''; if (!empty($this->options['parse_delimiter'])) { // Appending the `DELIMITER` statement that was just // found to the current statement. $ret = trim($this->current . ' ' . substr($this->query, $iBak, $i - $iBak)); } // Removing the statement that was just extracted from the // query. $this->query = substr($this->query, $i); $i = 0; // Resetting the current statement. $this->current = ''; return $ret; } // Incomplete statement. Reverting $i = $iBak; return false; } /* * Checking if the current statement finished. * * The first letter of the delimiter is being checked as an * optimization. This code is almost as fast as the one above. * * There is no point in checking if two strings match if not even * the first letter matches. */ if ($this->query[$i] === $this->delimiter[0] && ($this->delimiterLen === 1 || substr($this->query, $i, $this->delimiterLen) === $this->delimiter)) { // Saving the statement that just ended. $ret = $this->current; // If needed, adds a delimiter at the end of the statement. if (!empty($this->options['add_delimiter'])) { $ret .= $this->delimiter; } // Removing the statement that was just extracted from the // query. $this->query = substr($this->query, $i + $this->delimiterLen); $i = 0; // Resetting the current statement. $this->current = ''; // Returning the statement. return trim($ret); } /* * Appending current character to current statement. */ $this->current .= $this->query[$i]; } if ($end && $i === $len) { // If the end of the buffer was reached, the buffer is emptied and // the current statement that was extracted is returned. $ret = $this->current; // Emptying the buffer. $this->query = ''; $i = 0; // Resetting the current statement. $this->current = ''; // Returning the statement. return trim($ret); } return ''; }
/** * Parses a whitespace. * * @return Token */ public function parseWhitespace() { $token = $this->str[$this->last]; if (!Context::isWhitespace($token)) { return null; } while (++$this->last < $this->len && Context::isWhitespace($this->str[$this->last])) { $token .= $this->str[$this->last]; } --$this->last; return new Token($token, Token::TYPE_WHITESPACE); }