How to use class methods as callbacks
96,616
Solution 1
Check the callable
manual to see all the different ways to pass a function as a callback. I copied that manual here and added some examples of each approach based on your scenario.
Callable
- A PHP function is passed by its name as a string. Any built-in or user-defined function can be used, except language constructs such as: array(), echo, empty(), eval(), exit(), isset(), list(), print or unset().
// Not applicable in your scenario
$this->processSomething('some_global_php_function');
- A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1.
// Only from inside the same class
$this->processSomething([$this, 'myCallback']);
$this->processSomething([$this, 'myStaticCallback']);
// From either inside or outside the same class
$myObject->processSomething([new MyClass(), 'myCallback']);
$myObject->processSomething([new MyClass(), 'myStaticCallback']);
- Static class methods can also be passed without instantiating an object of that class by passing the class name instead of an object at index 0.
// Only from inside the same class
$this->processSomething([__CLASS__, 'myStaticCallback']);
// From either inside or outside the same class
$myObject->processSomething(['\Namespace\MyClass', 'myStaticCallback']);
$myObject->processSomething(['\Namespace\MyClass::myStaticCallback']); // PHP 5.2.3+
$myObject->processSomething([MyClass::class, 'myStaticCallback']); // PHP 5.5.0+
- Apart from common user-defined function, anonymous functions can also be passed to a callback parameter.
// Not applicable in your scenario unless you modify the structure
$this->processSomething(function() {
// process something directly here...
});
Solution 2
Since 5.3 there is a more elegant way you can write it, I'm still trying to find out if it can be reduced more
$this->processSomething(function() {
$this->myCallback();
});
Solution 3
You can also to use call_user_func() to specify a callback:
public function myMethod() {
call_user_func(array($this, 'myCallback'));
}
private function myCallback() {
// do something...
}
Solution 4
As of PHP 8.1, we now have first-class callables. They use the syntax $callable = functionName(...)
.
You can use the new syntax to create callable class methods.
Class MyClass {
public function myMethod() {
// first-class callables
$this->processSomething($this->myCallback(...));
$this->processSomething(self::myStaticCallback(...));
}
private function processSomething(callable $callback) {
// Process something...
$callback();
}
private function myCallback() {
// Do something...
}
private static function myStaticCallback() {
// Do something...
}
}
Related videos on Youtube
Author by
SmxCde
Updated on March 05, 2022Comments
-
SmxCde over 2 years
I have a class with methods that I want to use as callbacks.
How can I pass them as arguments?Class MyClass { public function myMethod() { // How should these be called? $this->processSomething(this->myCallback); $this->processSomething(self::myStaticCallback); } private function processSomething(callable $callback) { // Process something... $callback(); } private function myCallback() { // Do something... } private static function myStaticCallback() { // Do something... } }
-
MikO over 9 yearsThis may be more elegant, but you're adding overhead by adding an unnecesary call to the anonymous function, which just calls the actual callback...And what if
myCallback
needs 5 arguments? You have to write those arguments 4 times in that class! I agree anonymous functions are nice as callbacks, but in the case you write the code inside the anonymous function, not just including a call to another function... -
Bankzilla over 9 yearsYou're correct, I didn't think about passing the arguments across. But unless I'm reading it wrong, the way you've specified you also can't pass arguements.
-
SmxCde over 9 yearsMikO, thanks for your response! I know that this wasn't in original question, but how to call for static method with static method as argument from static method (where there is no $this)?
-
SmxCde over 9 yearsSo far, the best solution I could come up with is
self::processSomething([__CLASS__, 'callback']);
-
MikO over 9 years@SmxCde I updated the answer with all the options you have - If you are passing you
myStaticCallback
from a static method, you need to pass__CLASS__
. If you are passing yourmyCallBack
from a static method, you need to create a new instance of the class withnew MyClass()
, as within the static method there's no instance of$this
you can pass... Hope it's all clear :) -
Christian about 8 yearsYou forgot to mention
__invoke
magic method. -
Kolyunya over 7 yearsPassing around method names as strings is a refactoring nightmare.
-
Kolyunya over 7 yearsUpvoting this. And yes, it can be even more elegant:
$this->processSomething($this->myCallback());
whilemyCallback()
should return an anonymous function. This is WAY more maintainable than passing around method names as strings. -
Radek Pech about 6 yearsSince PHP 5.6 you can use variadic function and argument unpacking:
$this->processSomething(function(...$args) { $this->myCallback(...$args); });
-
HartleySan about 6 years
__CLASS__
for the static class methods section was what I was looking for. Thanks a million! -
MannikJ almost 6 yearsIs there not a better way of passing an existing function? Just another example why I don't like to do it like this: Assume processSomething validates the given parameter(s) and throws an Exception if the first paramater is not a valid callback. If you wrap the call in a new Closure like above, it will first go through and then blow up later when the callback function is actually executed. That makes it very hard to debug, because you're skipping the validation which was implemented for a reason.
-
naneri over 4 yearsHow do I pass params to that callback?
-
luukvhoudt about 4 yearsWell, I guess I'm the only one who gets a
Fatal error: Uncaught TypeError: Argument 1 passed to ::processSomething must be callable, array given
-
Electric Coffee almost 4 yearssince
self
is an alias for the class itself, can[self::class, 'myCallback']
be used? -
Ricardo almost 4 years@Fleuv, that’s because you are passing an out of scope method (a protected or private method to an external function or class). Not an obvious error message at all.