5

PHPs token_get_all function (which allows converting a PHP source code into tokens) can throw two errors: One if an unterminated multiline comment is encountered, the other if an unexpected char is found.

I would like to catch those errors and throw them as Exceptions.

Problem is: As those errors are parse errors they cannot be handled with an error handling function you would normally specify using set_error_handler.

What I have currently implemented is the following:

// Reset the error message in error_get_last()
@$errorGetLastResetUndefinedVariable;

$this->tokens = @token_get_all($code);

$error = error_get_last();

if (preg_match(
        '~^(Unterminated comment) starting line ([0-9]+)$~',
        $error['message'],
        $matches
    )
) {
    throw new ParseErrorException($matches[1], $matches[2]);
}

if (preg_match(
        '~^(Unexpected character in input:\s+\'(.)\' \(ASCII=[0-9]+\))~s',
        $error['message'],
        $matches
    )
) {
    throw new ParseErrorException($matches[1]);
}

It should be obvious that I'm not really excited to use that solution. Especially the fact that I reset the error message in error_get_last by accessing an undefined variable seems pretty unsatisfactory.

So: Is there a better solution to this problem?

NikiC
  • 100,734
  • 37
  • 191
  • 225

1 Answers1

-1

Set a custom errorhandler using set_error_handler. Call token_get_all. Then unset the error handler by calling restore_error_handler.

This will allow you to catch warnings. Make sure you remove the @ suppressor. You can for instance register an error handler that is in a class that will just record any warnings for inspection later on.

Untested example code:

class CatchWarnings {

    private $warnings = array();

    public function handler($errno, $errstr, $errfile, $errline) {
        switch ($errno) {
        case E_USER_WARNING:
            $this->warnings[] = $errstr;
            return true;    // cancel error handling bubble
        }
        return false;   // error handling as usual
    }

    public function has_warnings() {
        return count($this->warnings) > 0;
    }
}

$cw = new CatchWarnings();
set_error_handler(array($cw, "handler"));
token_get_all();
restore_error_handler();

Usually validation and execution are two separate things, but it seems like there is no way to validate/lint a piece of PHP code (not since 5.x anyway).

Halcyon
  • 57,230
  • 10
  • 89
  • 128
  • Sadly, this does not work (as I already mentioned in the question): `token_get_all` throws an `E_COMPILE_WARNING` and this is one of the error types that cannot be catched with an error handler :( – NikiC Jun 10 '11 at 19:32
  • Looking at this http://php.net/manual/en/errorfunc.constants.php I would conclude that you can catch `E_COMPILE_WARNING`, are you sure you've set the right `error_reporting` level? – Halcyon Jun 28 '11 at 18:35
  • Unfortunately, by [`set_error_handler()`'s documentation](http://www.php.net/manual/en/function.set-error-handler.php) (albeit regarding an entirely different PHP version than when this question was asked), user-defined error handlers cannot handle `E_ERROR`, `E_PARSE`, `E_CORE_ERROR`, `E_CORE_WARNING`, `E_COMPILE_ERROR`, `E_COMPILE_WARNING`, or most of `E_STRICT`. – bosco Jun 06 '14 at 17:20