Contact
Site: US UK AU |
Nexcess Blog

Reflection and Late Static Binding in PHP

June 20, 2012 1 Comment RSS Feed

Reflection and Late Static Binding in PHP

Late static binding (LSB) is new to PHP 5.3 (released 30 June 2009) but until recently there was a bug in how it worked with reflection. See the manual page for LSB with some examples and definition. For our example, you need to know that with class inheritance, LSB allows a static method call to be “bound to” the class from which the call originated as opposed to where the method is defined.

Consider the following:

class theFirstSelf {
  const THE_CONST = 'self 1st';
  static public function myConst() {
    return self::THE_CONST;
  }
}
class theSecondSelf extends theFirstSelf {
  const THE_CONST = 'self 2nd';
}

These static calls will result as shown:

echo theFirstSelf::myConst();  // self 1st
echo theSecondSelf::myConst(); // self 1st

We can invoke LSB functionality by using the keyword “static” in the place of “self”.

class theFirst {
  const THE_CONST = '1st';
  static public function myConst() {
    return static::THE_CONST;
  }
}
class theSecond extends theFirst {
  const THE_CONST = '2nd';
}

Now our static calls will have different results:

echo theFirst::myConst();  // 1st
echo theSecond::myConst(); // 2nd

Though fixed in PHP v3.5.9 (and further fixed for invokeArgs() in PHP v3.5.11), reflection in PHP was broken when it came to LSB.

// With ReflectionClass
$rc_first = new ReflectionClass( 'theFirst' );
$rc_second = new ReflectionClass( 'theSecond' );
echo $rc_first->getMethod( 'myConst' )->invoke( null );  // 1st
echo $rc_second->getMethod( 'myConst' )->invoke( null ); // 1st
// and also ReflectionMethod
$rm_first = new ReflectionMethod( 'theFirst', 'myConst' );
$rm_second = new ReflectionMethod( 'theSecond', 'myConst' );
echo $rm_first->invoke( null );  // 1st
echo $rm_second->invoke( null ); // 1st

Obviously reflection is not needed here but this is a simple example. At times when the class or method name is unknown and given as a variable, reflection can be handy. Here, then, is a work around for those unable to upgrade to 3.5.11. We’re simply using call_user_func() to handle the invoke() cases. For a reflection method invokeArgs() replacement, call_user_func_array() can be used.

echo call_user_func( array( 'theFirst', 'myConst' ) );  // 1st
echo call_user_func( array( 'theSecond', 'myConst' ) ); // 2nd
Posted in: General
  • Jānis Plaudis

    Thank you for this article! Until now I called static functions like this:
    $self = get_class($this);
    $result = call_user_func(array($self, ‘my_static_function’));
    Now it changes everything! :D