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:
- "self::methodname" – use the current class.
- "parent::methodname" – use the parent class.
- "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
