PHP7 method_exists Uncaught Error: Function name must be a string

backups picture backups · Dec 29, 2015 · Viewed 13.7k times · Source

I am getting this error:

Fatal error: Uncaught Error: Function name must be a string in

For this code:

if (function_exists($item['function'])) {
    $item['function']($item, $default);
} elseif (method_exists($this, $item['function'])) {
    $this->$item['function']($item, $default);
}

I know that changing the code to

if (function_exists($item['function'])) {
    $item['function']($item, $default);
} elseif (method_exists($this,$item['function'])) {
    $this->{$item['function']}($item, $default);
}

Solved that error, but my question is, should this line

 $item['function']($item, $default);

also be converted to

{$item['function']}($item, $default);

or can it be left as is?

Answer

Rizier123 picture Rizier123 · Dec 29, 2015

This is due to incompatible changes in the order-of-evaluation for processing indirect variables and methods:

Changes to the handling of indirect variables, properties, and methods

Indirect access to variables, properties, and methods will now be evaluated strictly in left-to-right order, as opposed to the previous mix of special cases. The table below shows how the order of evaluaiton has changed.

No, you don't have to change this line:

$item['function']($item, $default);

Because there is no special evaluation going on here, it will just use the array element as function name and call the function. You could change it, and the code will still work properly, but it isn't necessary.

But as you already did correctly you have to change:

$this->$item['function']($item, $default);

to:

$this->{$item['function']}($item, $default);
       ↑                 ↑

Since as you can see in this table:

                    Old and new evaluation of indirect expressions
      Expression            PHP 5 interpretation         PHP 7 interpretation
-------------------------------------------------------------------------------
  $$foo['bar']['baz'] |     ${$foo['bar']['baz']}  |    ($$foo)['bar']['baz']
  $foo->$bar['baz']   |     $foo->{$bar['baz']}    |    ($foo->$bar)['baz']
  $foo->$bar['baz']() |     $foo->{$bar['baz']}()  |    ($foo->$bar)['baz']()
  Foo::$bar['baz']()  |     Foo::{$bar['baz']}()   |    (Foo::$bar)['baz']()

PHP 7 will assume you first want to access an object property, and then you want to access an index from that property, and use its value as method name to call a method (left-to-right order).

To use the variable and index as property name, you have to use curly braces to indicate that.