Quantcast
Channel: Pressed – hakre on wordpress
Viewing all articles
Browse latest Browse all 53

Late Static Bindings in PHP Callbacks

$
0
0

No idea if this is ever useful, but just found this not documented in the PHP manual so far (and the Callbacks entry looks already chaotic so I don’t edit it right now):

You can write callbacks of static class methods as a string in the form of "classname::methodname". This should be commonly known, an example of that is:

// http://3v4l.org/vdhJq

echo Module::work('hello world'), "\n";              # HELLO WORLD
echo call_user_func('Module::work', 'hello world');  # HELLO WORLD

abstract class Module
{
    public static function work($it) {
        return strtoupper($it);
    }
}

What I stumbled over and which I didn’t know is that since PHP 5.3 it is also possible to reference the current class with three additional strings for the classname placeholder above:

  1. "self::methodname" – use the current class.
  2. "parent::methodname" – use the parent class.
  3. "static::methodname" – use the class that was initially called at runtime.

Here an example that uses a callback function inside the module class to do some string replacements with the "static" string:

// http://3v4l.org/SsaZg

echo Module::work('hello world.'), "\n";  # [[[hello]]] [[[world]]].
echo MyModule::work('hello world.');      # {{{hello}}} {{{world}}}.

abstract class Module
{
    public static function work($it) {
		return preg_replace_callback('~\w+~', 'static::replace', $it);
    }

    protected static function replace($matches) {
        return sprintf('[[[%s]]]', $matches[0]);
    }
}

abstract class MyModule extends Module
{
    protected static function replace($matches) {
        return sprintf('{{{%s}}}', $matches[0]);
    }

}

Sidenote: The array callback notation works for that as well:

    preg_replace_callback('~\w+~', array('static', 'replace'), $it);

This came a bit unexpected to me because in another feature introduced in PHP 5.3: A call of static class methods with a variable for the classname PHP:

// http://3v4l.org/IWQtr

echo Module::work('hello world'), "\n";  # HELLO WORLD

abstract class Module
{
    public static function work($it) {
        $module = 'Module';
        return $module::strtoupper($it);
    }

    private static function strtoupper($it) {
		return strtoupper($it);
    }

}

Unexpected because it does not allow to use the strings "self", "parent" or "static" inside that variable:

// http://3v4l.org/p0RQv

echo Module::work('hello world'), "\n";  # Fatal error: Class 'self' not found

abstract class Module
{
    public static function work($it) {
        $module = 'self';
        return $module::strtoupper($it);
    }

    private static function strtoupper($it) {
		return strtoupper($it);
    }

}

Last but not least, there is this very special callback, I don’t have any name for. What it does is working exactly like the "self::methodname" callback:

    
    preg_replace_callback('~\w+~', array('self',   'self::replace'),   $it);
                                         // like self - http://3v4l.org/B6Lbe

    preg_replace_callback('~\w+~', array('static', 'static::replace'), $it);
                                       // like static - http://3v4l.org/adTgD

    preg_replace_callback('~\w+~', array('static', 'self::replace'),   $it);
                                       // like static - http://3v4l.org/GaCVT

    preg_replace_callback('~\w+~', array('self',   'static::replace'), $it);
    // like "final" (invalid callback in LSB context) - http://3v4l.org/oBeNN

So much for the magic static and self-self life in callbacks and class method calls. PHP is not well known for language consistency and I put that one as well into the book. One should not exploit this static feature anyway and a module class should be abstract final probably but sadly, PHP does not know that.

This post is just another write-up for the know-your-language department and not a recommendation at all.


Edit: Parts of my first conclusion on self-self were wrong, it does not do any LSB, what I thought was LSB was in the first static::replace example was wrong. I also made that example more distinct.


Tagged: Callback, call_user_func_array, Class, Late Static Bindings, LSB, Parent Keyword, PHP, PHP 5.3, Self Keyword, Static, Static Keyword, Static Method

Viewing all articles
Browse latest Browse all 53

Trending Articles