Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP turn "Call to a member function on a non-object" into exception

When I am running "Behat" steps, the Behat error handler turns "Trying to get property of non-object" errors into exceptions.

This is very helpful, as it causes the step to be marked as failed, and allows the test run to continue at the next scenario.

However, "Call to a member function on a non-object" errors are fatal, and immediately halt the test execution (including aborting writing the results to xml). This is unhelpful.

My questions are:

  1. What is the difference between these two errors? Are they different "error levels"? Where is that documented? I have searched the PHP site and Google, and cannot find the canonical reference, just lots of questions about debugging specific instances of each error.

  2. Is there any way to convert the latter error into an exception, instead of halting the script entirely? It doesn't seem to me that dereferencing "null" with "->" would be an error that "can not be recovered from, such as a memory allocation problem".

Update:

It looks like this is just a known issue with PHP. See:

  • #51882 Call To Member Function on Non-Object Should Throw An Exception
  • #46601 E_RECOVERABLE_ERROR for "Call to a member function on a non-object"
  • #51848 Non-object method call errors should be catchable with set_error_handler()
  • #63538 "Call to undefined function" should be catchable

Some people say it's "by design", but I think it's just an artefact of the error levels being defined before objects were added to PHP. In a non-OO language calling a non-existent function is a serious error, and I can see how it might be described as "fatal" or "not recoverable" (although, in a non-OO language where functions can be defined on-the-fly, even that seems overly pessimistic). These days, now that you can do "$a->f()" on any old $a, it is far more likely that "f" may not exist, and it seems like it should not be a fatal error (cf. Java where this would be a NullPointerException).

I suppose that leads me to a new question:

_ 3. How could you patch PHP to make "Call to a member function on a non-object" errors non-fatal, without massively breaking backwards compatibility, and what steps could you take to maximise the likelihood of that patch being accepted into PHP?

Update 2 re patching PHP:

There is some limited support on the PHP internals mailing list for making this fix. Now I just need to write a patch to fix this and create an RFC.

like image 776
Rich Avatar asked Mar 19 '13 14:03

Rich


2 Answers

The problem is that you can declare properties of an object dynamic, but you cannot declare a method dynamic. So if you try to call a method on a non object you are getting a fatal error because PHP could not determine that the function exists.

Check this codepad for some simple output with error numbers.

Now look closely at the part about "Set and acces unset property". You can see that PHP gives you a message saying "Creating default object from empty value". So if a class does not exist, PHP will create it for you. The same happens when you use typecasting, see object typecasting

So you can access a propery of a non-object because it will check for that propety in a dynamic created default object. But if you access a method, that default object still doesnt have that method.

Thats why a property returns a level 8 error and a method a level 1 error (see error levels). And since it returns a level 1 fatal error, you cannot continue after this process.

like image 135
Hugo Delsing Avatar answered Sep 20 '22 23:09

Hugo Delsing


This has been fixed in PHP 7:

  • http://php.net/manual/en/language.errors.php7.php
  • https://wiki.php.net/rfc/engine_exceptions

In PHP 7+, this code will now throw, instead of causing an irrecoverable fatal error.

like image 30
Rich Avatar answered Sep 18 '22 23:09

Rich