Indirect Invocation Considered Harmful

There is a bug in PHP that prevents scalar type declarations to be interpreted strictly when a function or method is invoked using the Reflection API.

Consider a class C that has a method m which expects a parameter $x of type string:

1 2 3 4 5 6 7 8
< ? php
class C
{
public function m ( string $x )
{
var_dump ( $x ) ;
}
}

Let us try to pass an integer instead of a string, which, when we enable strict interpretation of scalar type declarations, should yield a type error.

When we invoke that method directly, we get the error we expect:

1 2 3 4 5
< ? php
declare ( strict_types = 1 ) ;

$o = new C ;
$o -> m ( 1 ) ;

Executing the code shown above will print the output shown below:

Fatal error: Uncaught TypeError: Argument 1 passed to C::m()
must be of the type string, integer given, called in ...

If we, however, invoke the method indirectly then PHP will not generate the expected type error.

1 2 3 4 5 6 7 8
< ? php
declare ( strict_types = 1 ) ;

$object = new C ;
$arguments = [ 1 ] ;

$method = new ReflectionMethod ( $object , 'm' ) ;
$method -> invokeArgs ( $object , $arguments ) ;

Instead, executing the code shown above will print the output shown below:

string(1) "1"

While the discussion of that bug mentions that other forms of indirect invocation might also be affected, we were only able to reproduce the bug for the Reflection API. We tried callbacks with functions such as array_map(), the call_user_func() function, and the $object->$method() syntax. For all these forms of indirect invocation a type error was raised, as is expected and correct.

In the example above, the Reflection API is used to invoke a method with arguments coming from an array. The same effect can be achieved using argument unpacking:

1 2 3 4 5 6 7
< ? php
declare ( strict_types = 1 ) ;

$object = new C ;
$arguments = [ 1 ] ;

$object -> m ( ... $arguments ) ;

Executing the code shown above will once again print:

Fatal error: Uncaught TypeError: Argument 1 passed to C::m()
must be of the type string, integer given, called in ...

We hope that developers do not use this bug in PHP to bypass type safety in code that they call – and, of course, that this bug will be fixed soon.


This article is an excerpt from our eBook PHP 7 Explained, which we have updated for PHP 7.2 recently.

For more insights into PHP 7, get your copy now, it includes life-long free updates.

About the authors

Sebastian Bergmann
Sebastian Bergmann
Twitter LinkedIn Xing

Sebastian Bergmann, creator of PHPUnit, is an internationally sought-after expert who has played a vital role in professionalizing the PHP community.

Arne Blankerts
Arne Blankerts
Twitter LinkedIn Xing

Arne Blankerts has created solutions far ahead of the times already years ago, and finds security issues with magic intuition.

Stefan Priebsch
Stefan Priebsch
Twitter LinkedIn Xing

For over 20 years, Stefan Priebsch has been finding sustainable solutions using a unique blend of new ideas and proven approaches.

Share this article
Why PHP 7.2 Is Important The Future of Zend