# RFC: Catchable "call to a member function of a non-object"

- Status: Implemented (PHP 7.0)
- Authors: Timm Friebe thekid@php.net
- Date: 2014-04-26
- Version: 1.1
- Original: https://wiki.php.net/rfc/catchable-call-to-member-of-non-object
- Page: https://externals.io/rfc/catchable-call-to-member-of-non-object
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

## Votes

### Catchable Call to a member function bar() on a non-object (closed)

Yes: 32 · No: 0

Voters: ajf (Yes), brandon (Yes), brianlmoon (Yes), bwoebi (Yes), crodas (Yes), datibbaw (Yes), davey (Yes), dragoonis (Yes), fa (Yes), frozenfire (Yes), gooh (Yes), guilhermeblanco (Yes), gwynne (Yes), indeyets (Yes), jedibc (Yes), jpauli (Yes), jwage (Yes), kassner (Yes), kriscraig (Yes), levim (Yes), malukenho (Yes), mrook (Yes), nikic (Yes), oaass (Yes), philstu (Yes), rdlowrey (Yes), rdohms (Yes), reeze (Yes), seld (Yes), treffynnon (Yes), tyrael (Yes), zhangzhenyu (Yes)

## Discussions on the mailing list

- [RFC: Catchable "call to a member function of a non-object"](https://externals.io/message/73814.md) — started on 2014-04-27, 10 messages
- [\[VOTE\] RFC: Catchable "call to a member function of a non-object"](https://externals.io/message/75131.md) — started on 2014-06-29, 8 messages

---

**Note: This RFC has been superseded by the [engine exceptions](https://externals.io/rfc/engine_exceptions_for_php7) proposal.**

## Introduction

One of the most common fatal errors in PHP is the “call to a member function of a non-object” type. This occurs whenever a method is called on anything other than an object (usually null), e.g.:

```php
// ...when getAction() returns null:
$this->getAction()->invoke();
```

One situation in which fatal errors are problematic is if you want to run PHP as a webserver itself. For a long story on why you would want to do that in the first place, see <http://marcjschmidt.de/blog/2014/02/08/php-high-performance.html>.

Other situtations are described in the [Engine Exceptions RFC](https://externals.io/rfc/engine_exceptions#issues_with_fatal_errors).

## Proposal

This proposal's essence is to turns fatal errors generated from calls to methods on a non-object into `E_RECOVERABLE_ERROR`s.

```php
set_error_handler(function($code, $message) {
  var_dump($code, $message);
});
 
$x= null;
var_dump($x->method());
echo "Alive\n";
```

The above produces the following output:

```
int(4096)
string(50) "Call to a member function method() on a non-object"
NULL
Alive
```

### Consistency

This behavior is consistent with how type hints work. Framework authors can turn this into exceptions if they wish.

### Example: Exceptions

The following error handler could be embedded into frameworks:

```php
set_error_handler(function($code, $message) {
  if (0 === strncmp('Call', $message, 4)) {
    throw new BadMethodCallException($message);
  } else if (0 === strncmp('Argument', $message, 8)) {
    throw new InvalidArgumentException($message);
  } else {
    trigger_error($message, E_USER_ERROR);
  }
}, E_RECOVERABLE_ERROR);
 
$x= null;
try {
  $x->method();
} catch (BadMethodCallException $e) {
  echo "Caught expected ", $e->getMessage(), "!\n";
}
echo "Alive\n";
```

### Example: Without exceptions

This could be a way for people preferring not to use exceptions and instead to exit the script directly, but get a stacktrace instead of just the fatal error message:

```php
set_error_handler(function($code, $message) {
  echo "*** Error #$code: $message\n";
  debug_print_backtrace();
  exit(0xFF);
}, E_RECOVERABLE_ERROR);
 
$m= new some_db_model();
$row= $m->find(42); // null, deleted concurrently
$row->delete();
```

### Differences from Past RFCs

This proposal doesn't go as far as the controversial RFC [RFC: Engine exceptions](https://externals.io/rfc/engine_exceptions).

### Inner workings

Taken this code:

```php
function a($comparator) {
  $result= $comparator->compare(1, 2);
  // ...
}
```

You can unwind this to something like:

```
function a($comparator) {
  1: ZEND_INIT_METHOD_CALL $comparator 'compare'
  2: ZEND_SEND_VAL         1
  3: ZEND_SEND_VAL         2
  4: ZEND_DO_FCALL_BY_NAME
  5: ZEND_ASSIGN           $result
  // ...
}
```

The handling on checking whether the method is callable happends inside the opline #1 (`ZEND_INIT_METHOD_CALL`). The opcode handler for it checks its first argument (here: `$comparator`) whether it is an object or not. In case it's not, the following happens:

1. A recoverable error is triggered. Should the script exit here, there's nothing more to be done, this is just as it was before.
2. In case the script continues, all the oplines are skipped until we find the `FCALL_BY_NAME` opcode. This is comparable to jumping forward just as, e.g., the `ZEND_JMP` instruction would.
3. The return value is set to `ZVAL_NULL()`.
4. The control is handed back to the executor, which then continues with the `ASSIGN` opcode
5. The engine is again in full control; if an exception was raised by the handler, that leads to the known behavior.

## Other Impact

### On Backward Compatiblity

This RFC is backwards compatible with previous PHP releases.

### On SAPIs

There is no impact on any SAPI.

### On Existing Extensions

No impact.

### On Performance

No effect, before, the script terminated.

## Proposed PHP Version(s)

This RFC targets PHP 5.7 or PHP 6.0, whichever comes first.

## Proposed Voting Choices

This RFC modifies the PHP language behaviour and therefore requires a two-third majority of votes.

## Patches and Tests

There is a pull request available over at [GitHub](https://github.com/php/php-src/pull/647) which includes tests. Feedback welcome!

## Future Work

Ideas for future work include:

- Also allowing to catch and handle other fatal errors

## Vote

Voting started 2014-06-29 and ended 2014-07-30.

**Vote: Catchable Call to a member function bar() on a non-object** — Yes: 32 · No: 0 (closed)

## References

- PHP Bugs [46601](https://bugs.php.net/bug.php?id=46601), [51882](https://bugs.php.net/bug.php?id=51882) and [51848](https://bugs.php.net/bug.php?id=51848)- bugs which would be fixed by this
- PHP Bug [54195](https://bugs.php.net/bug.php?id=54195) - related, motivates necessity
- HHVM [throws a BadMethodCallException](https://github.com/facebook/hhvm/blob/master/hphp/test/quick/method-non-object.php.expectf) in these situations
- [Mailing list announcement](http://news.php.net/php.internals/73814)
- [Return type hinting RFC](https://wiki.php.net/rfc/returntypehinting) - related work: Some fatal errors can be prevented by setting return types.
