Lexer.php
protected function read($callback, $length = 1)
    {

        if (!is_callable($callback))
            throw new \InvalidArgumentException(
                "Argument 1 passed to peekWhile needs to be callback"
            );

        $result = '';
        while (!$this->isAtEnd() && $callback($this->peek($length))) {

            //Keep $_line and $_offset updated
            $newLines = $this->substr_count($this->lastPeekResult, "\n");
            $this->line += $newLines;

            if ($newLines) {

                if (strlen($this->lastPeekResult) === 1)
                    $this->offset = 0;
                else {

                    $parts = explode("\n", $this->lastPeekResult);
                    $this->offset = strlen($parts[count($parts) - 1]) - 1;
                }
            }

            $this->consume();
            $result .= $this->lastPeekResult;
        }

        return $result;
    }
protected function read($callback, $length = 1)
    {

        if (!is_callable($callback))
            throw new \InvalidArgumentException(
                "Argument 1 passed to peekWhile needs to be callback"
            );

        $result = '';
        while (!$this->isAtEnd() && $callback($this->peek($length))) {

            //Keep $_line and $_offset updated
            $newLines = $this->substr_count($this->lastPeekResult, "\n");
            $this->line += $newLines;

            if ($newLines) {

                if (strlen($this->lastPeekResult) === 1)
                    $this->offset = 0;
                else {

                    $parts = explode("\n", $this->lastPeekResult);
                    $this->offset = strlen($parts[count($parts) - 1]) - 1;
                }
            }

            $this->consume();
            $result .= $this->lastPeekResult;
        }

        return $result;
    }
protected function read ( $ callback , $ length = 1 ) { if ( ! is_callable ( $ callback ) ) throw new \ InvalidArgumentException ( "Argument 1 passed to peekWhile needs to be callback" ) ; $ result = '' ; while ( ! $ this -> isAtEnd ( ) && $ callback ( $ this -> peek ( $ length ) ) ) { //Keep $_line and $_offset updated $ newLines = $ this -> substr_count ( $ this -> lastPeekResult , "\n" ) ; $ this -> line += $ newLines ; if ( $ newLines ) { if ( strlen ( $ this -> lastPeekResult ) === 1 ) $ this -> offset = 0 ; else { $ parts = explode ( "\n" , $ this -> lastPeekResult ) ; $ this -> offset = strlen ( $ parts [ count ( $ parts ) - 1 ] ) - 1 ; } } $ this -> consume ( ) ; $ result .= $ this -> lastPeekResult ; } return $ result ; }
Peeks and consumes chars until the passed callback returns false.

The callback takes the current character as the first argument.

This works great with ctype_*-functions

If the last character doesn't match, it also won't be consumed
You can always go on reading right after a call to ->read()

e.g.
$alNumString = $this->read('ctype_alnum')
$spaces = $this->read('ctype_space')

@param callable $callback the callback to check the current character against
@param int      $length   the length to peek. This will also increase the length of the characters passed to the callback

@return string the read string
@throws \Exception
Peeks and consumes chars until the passed callback returns false .