PHP Exception Handling - ErrorException

PHP Exception Handling – ParseError

Next up in our in-depth PHP Exception Handling series we’re taking a closer look at the PHP ParseError. As you might suspect, a ParseError is thrown when PHP has trouble parsing a line of code. This could either be due to a typo or syntax error, or even while using the terrifying eval() function.

We’ll begin by looking at where the ParseError resides in the PHP Exception Hierarchy, then we’ll explore some basic code samples that illustrate how ParseErrors might occur, so let’s get going!

The Technical Rundown

  • All PHP errors implement the Throwable interface, or are extended from another inherited class therein.
  • Error implements the Throwable interface.
  • ParseError extends the Error class.

When Should You Use It?

At the most basic level, a ParseError can be thrown anytime code that is not syntactically correct is executed. This can occur in a variety of ways, but the most obvious is a typo. For example, perhaps you have two variables, $a and $b, and you want to assign the value of one to the other, so you’d write a statement like:

Super basic and common, but what happens if we accidentally forget the $ symbol for one of those variables?

Boom! Already we’re getting a ParseError because PHP doesn’t recognize a without a signifier (like $), or some indication that it’s another type of object (like a function):

Minor typos are all well and good, but the biggest potential for ParseError troubles is when using the eval() function. While many developers would suggest eval() be ignored and left by the wayside, the basic purpose of eval() is to evaluate and execute the passed string argument as PHP code. Any string passed into eval() will be executed as if it was normal PHP code. This has the power to be beneficial in some outlier cases, but, for the most part, using eval() is considered extremely dangerous. At the very least, it should never be used in combination with user-provided data, since that would open an avenue of attack similar to an SQL injection attack, except with direct access to your application’s codebase. Yikes!

Heh, of course, now that we’ve said all that, we’ll continue this ParseError examination with a bunch of eval() examples! (Do as I say, not as I do, yadda yadda yadda…) While all the following code is taken from (and found in) the provided code snippet, it’s always wise to understand any code that’s acquired elsewhere before executing it on your own system, particularly if it’s using dangerous capabilities like the eval() function. Therefore, while this code is completely safe, if you don’t understand something about it then it’s best to avoid running it.

Here’s the full, working example code we’ll be using for the rest of this article. We’ll break it down a bit more afterward:

To do something a little more interesting then just assigning the value of variable $b to variable $a, we’ve included a simple Book class to create book objects:

Now, before we get into using the frightening eval() function we’ll start with a normal code example. In this function we’re creating a new Book object and passing some initial property values as arguments, then outputting the content of the $book object variable to the log. Finally, we modify the properties of our object and output again to confirm the changes have taken place:

As expected, we see both outputs for our original and modified property set work just fine:

Heredoc and Nowdoc

Before we get into the code that makes use of eval() we should briefly discuss the special syntax we’ll be using to define the larger-than-normal strings that we’ll be passing to eval(). This syntax is called heredoc or nowdoc, depending on a slight variation. Both heredoc and nowdoc define strings with three less-than symbols (<<<). This operator is then followed by an identifier, then a new line, including however many lines of strings you wish to add, followed, at last, by another new line with a matching identifier symbol and a closing semi-colon (;).

For example, the following is a heredoc declaration of a single line of text, using the identifier of IDENT:

The identifier can be any set of alphanumeric characters that you desire, though many examples online will use EOF or EOD. It is also required that the closing identifier tag appear without indentation on the final line. Adding indentation beforehand causes the parser to think it’s a continuation of the actual string content.

nowdoc syntax is slightly different than heredoc (seen above), in that nowdoc requires the initial identifier to be surrounded by single-quotations: <<<'IDENT'.

The difference between heredoc and nowdoc is that heredoc will parse the string value for potential evaluation points, whereas nowdoc will ignore all potential evaluations. To illustrate, consider this example, where we’ve declared the $name variable and are using it inline with both a heredoc and a nowdoc string declaration:

And here’s the output of both print_r() calls:

As we can see, since heredoc evaluates any inner variables and the like before the string is generated, the parser converts $name to John Doe prior to output. On the other hand, nowdoc treats the entire string as a literal, thereby avoiding evaluation of $name.

Alright, now that we’re covered our string declaration method we can get back to the code example. In the evaluationTest() function we’re executing nearly identical code as we did above, except we’re using different books. Most importantly, we’re writing everything out as a string value, which we assign to the local $code variable and pass to the evaluateCode() function. evaluateCode() passes its $code string parameter to the eval() function, while attempting to catch any potential errors that might be thrown.

We’re also using nowdoc instead of heredoc to declare our string, because this forces the PHP parser to ignore any potential statements it might otherwise evaluate, so everything is treated as a literal string, which is a requirement if we want eval() to read our code as expected:

Executing the above passes our string to eval(), which then evaluates it as if it were normally written code. The result is a nearly-identical output as before, but with the appropriate book info changes:

That’s all well and good, and in a perfect world, we could use eval() all day without running into any trouble. However, it’s all too easy to pass a slightly incorrect string to eval() and cause the whole thing to break down (or worse). To illustrate, here we have the invalidEvaluationTest() function:

It may be difficult to spot, but there’s a small (yet, significant) typo in our declared code string that we want to pass to evaluateCode(): The initial quotation mark surrounding "The Two Towers" title is missing. As a result, executing this function throws a ParseError at us:

Check out the Airbrake-PHP library, designed to quickly and easily integrate into any PHP project, giving you and your team access to real-time error monitoring and reporting throughout your application’s entire life cycle. With automatic, instantaneous error and exception notifications at your fingertips, you’ll be constantly aware of your application’s health, including any issues that may arise. Best of all, with Airbrake’s robust web dashboard cataloging every error that occurs, you and your team can immediately dive into the exact details of what went wrong, making it easy to quickly recognize and resolve problems.