According to http://php.net/manual/en/functions.anonymous.php, in PHP 5.3 $this is not accessible from inside an implicit function, even if the function is defined in a context where $this exists. Is there any way to work around this limitation? (by the way, upgrading the PHP installation on the webserver is not possible)
The way I would like to use the implicit function is to define a callback which is a member function of some object. More precisely, I would like to do something like
$callback = function() { return $this->my_callback(); }
Actually, an event better syntax would be
$callback = $this->my_callback
but I cannot make it work (PHP dies with "Fatal error: Function name must be a string" when I try to execute the callback).
Should do the work:
$object = $this ;
$callback = function() use ($object) { return $object->my_callback(); } ;
use will bring an accessible variable (in our case the reference of the object) upon its declaration to the function scope, so you will not have to send it as a parameter.
Sometimes it is even better to use such a varname as $self or $that so to be more clear.
$function = array($this, 'my_callback');
(perhaps combined with call_user_func() )
It looks like you can pass variables to the callback function. I haven't worked with closures in PHP, but I think this would work for you:
$callback = function($instance) { return $instance->my_callback(); }
$callback($this);
Or if the callback is triggered outside the current class.
$callback($myClassInstance);
Related
When I have to write a reference to a callable function I use the standard syntax of PHP defined as:
A PHP function is passed by its name as a string. Any built-in or user-defined function can be used [... omitted...].
A method of an instantiated object is passed as an array containing an object at index 0 and the method name (aka string)
at index 1.
Static class methods can also be passed without instantiating an object of that class by passing the class name (still a string)
instead of an object at index 0.
As of PHP 5.2.3, it is also possible to pass (the string) 'ClassName::methodName'.
Apart from common user-defined function, anonymous functions can also be passed to a callback parameter.
All of these ways are not "IDE friendly" for operations like function name refactor or find usage of.
In my answer I propose a solution, but there are other approaches that can be applied, even totally different, that allow to IDE to "find" the invocation of the methods?
You already are next to the shortest thing you can do
You can perfectly call your anonymous function directly in your function call without using a variable
For instance, you can replace:
$callable=function($param) use ($object){
return $object->myMethod($param);
}
call_user_func($callable, $param);
by:
call_user_func(function($param) use ($object){
return $object->myMethod($param);
}, $param);
You will have to wait for arrow functions in future PHP versions, and you should be able to use something like:
call_user_func(fn($a) => $object->myMethod($a), $param);
I became to a solution, always based on the anonymous-functions that solve the problem but leave me not full satisfied:
Static method of a class
$callable = function($param){
return \my\namespace\myClass::myMethod($param);
}
method of a object
$callable = function($param) use ($object){
return $object->myMethod($param);
}
method of $this object
$callable = function($param){
return self::myMethod($param);
}
Alternatives for oldest php versions
Inside the all classess you gonna call (or in their parents) define the function classname() as follow:
public static function className()
{
return get_called_class();
}
or for very old PHP:
public static function className()
{
return "MyClass";
}
Then
call_user_func(array(MyClass::className(), 'myCallbackMethod'));
I am interested in something google couldn't really help me with...
I know that its possible to use anonymous functions and also store functions in a variable in PHP like that
$function = function myFoo() { echo "bar"; }
and call it using the variable: $function();
So far so good but what if I have a function or method declared somewhere but not saved on intializing?
I have a function that shall expect a callable, of course call_user_func() can help me here but then I need to pass the method name in my callback handler which I think is pretty unsafe because in the moment I add the callback I cant say if it really is a function and exists when I store it.
Thatswhy I would like to realize the following szenario:
This function:
function anyFunction() {
echo "doWhatever...!";
}
should be saved in a variable at a later point in time:
$myOtherFunction = get_registered_func("anyFunction");
I know get_registered_func() doesnt exist but I want to know if this is possible somehow!
With this I could now have another function
function setCallback(callable $theCallbackFunction) { }
And use it like this:
setCallback($myOtherFunction);
This would have a great advantage that an exception / a fatal is thrown when the parameter is no function or does not exist.
So in short, is there a way to store a previously defined, already existing function or method in a variable?
PHP's callable meta type can be:
a string
an array
an anonymous function
Only the latter one is somewhat "type safe", i.e. if you get an anonymous function you know it's something you can call. The other two options are merely formalised informal standards (if that makes sense) which are supported by a few functions that accept callbacks; they're not actually a type in PHP's type system. Therefore there's basically no guarantee you can make about them.
You can only work around this by checking whether the callable you got is, well, callable using is_callable before you execute them. You could wrap this into your own class, for example, to actually create a real callable type.
I see no reason why this shouldn't be possible, other than there not being a PHP function to do it. The anonymous function syntax is newly introduced in PHP, I wouldn'be surprised if it was still a little rough around the edges.
You can always wrap it:
function wrap_function ($callback) {
if (is_callable($callback) === false) {
throw new Exception("nope");
}
return function () {
call_user_func($callback, func_get_args());
}
}
$foo = new Foo();
$ref = wrap_function(array($foo, "bar"));
Not a good way, but maybe this helps you:
function foo($a) {
echo $a;
}
$callFoo = 'foo';
$callFoo('Hello World!'); // prints Hello
I have a PHP library function expecting a callback with no arguments.
I know I can pass an object's method with array($this , 'my_function_name') but how can I give parameters to the my_function_name?
I have found a solution using create_function but from PHP manual I see it has security issues.
$that = $this;
$wrapper = function() use($that) {
return $that->my_function_name('arg1', 'arg2');
};
There is a function called call_user_func_array.
call_user_func_array([$this, 'method'], $args);
This will depend on your library functions initiation of the callback method. You mentioned that you're making a wordpress theme. WP uses the call_user_func method typically. In your library function, use this function to invoke a call back with args, wrap a methods argument when calling it:
Callback Definition:
$callbackArg = "Got here";
defined_lib_func(array($this , 'my_function_name'), $callbackArg);
Library Script:
function defined_lib_function($callback, $args) {
call_user_func($callback, $args);
}
for multiple arguments, just pass them as an array and define your callback method to accept an array as an argument.
Approach 1 would work, but Approach 2 would not - it whould result in a fatal error (Function name must be a string in...)
# Approach 1
$function = self::$function and $function();
# Approach 2
self::$function and self::$function();
Isn't this a bit strange? I hope someone could explain.
Edit: To give some context, here's a whole class:
class Example
{
static $function;
static function method()
{
self::$function();
}
}
function callback() {}
Example::$function = 'callback';
Example::method();
If you don't want to get into technical details of precedence and ambiguity, the short explanation is that self::$function() works according to the same logic as $obj->$method(); it tries to call the variable function $function() of self.
The syntax seems ambigous to me. Probably it is for PHP too. It would be more clean, and functional to either save the function name in a local variable:
static function method()
$method = self::$function;
$method();
}
Or use call_user_func():
static function method()
call_user_func(self::$function);
}
Why approach #1 works
It's an artifact of PHP's operator precedence.
$function = self::$function and $function();
is equivalent to
($function = self::$function) and $function();
which is of course equivalent to
$function = self::$function;
if ($function) $function();
The last line works fine because that's the syntax for variable functions.
Why approach #2 does not work
Because there's ambiguity, which the compiler resolves "the wrong way".
Consider this:
self::$function();
Does this mean that PHP should invoke a callback stored in the static property $function, or that it should invoke a static method whose name is the value of the variable $function?
PHP goes with the latter but there's no variable $function in scope, resulting first in a notice and then in an error ($function evaluates to null, which is not a string).
I'm getting the error Using $this when not in object context when using the code below. Is this because the function is on the stack and has no access to the object?
array_walk($pins, function (&$array) {
$array->timestamp = $this->convertTime(strtotime($array->timestamp));
});
What's the best way to get around this? I was thinking of using a foreach, but wanted to learn more of PHP's lesser used functions that suit the purpose.
Solved it with
foreach($pins as $pin) {
$pin->timestamp = $this->convertTime(strtotime($pin->timestamp));
}
But I would still like to know how to get around the issue with array_walk.
try this:
$that =& $this;
array_walk($pins, function (&$array) use ($that) {
$array->timestamp = $that->convertTime(strtotime($array->timestamp));
});
Also, if you are actually walking an array, the correct syntax will probably be:
$that =& $this;
array_walk($pins, function (&$array) use ($that) {
$array['timestamp'] = $that->convertTime(strtotime($array['timestamp']));
});
What's the best way to get around this? I was thinking of using a foreach, but wanted to learn more of PHP's lesser used functions that suit the purpose.
I would say for your case, foreach is a very good solution because it's clear what it does. You might want to mimic the aliasing array_walk offers with your example:
foreach($pins as &$pin) {
$pin->timestamp = $this->convertTime(strtotime($pin->timestamp));
}
unset($pin);
This should come more closely to array_walk. Probably you want to reference the array itself as well (probably):
$pinsRef = &$pins;
foreach($pinsRef as &$pin) {
$pin->timestamp = $this->convertTime(strtotime($pin->timestamp));
}
unset($pin);
If you want to encapsulate the object itself ($this) inside an anonymous function with private access, this is not possible. Because there does not exists any $this in the context of the anonymous function (yet, a later PHP version might have that).
However one workaround is to pass the object instance into the local scope of the anonymous function by making use of the use clause.
$_this = $this;
$callback = function (&$array) use ($_this) {
$array->timestamp = $_this->convertTime(strtotime($array->timestamp));
});
array_walk($pins, $callback);
But this workaround is limited, as private members of $this are not accessible. Instead you can create the callback function as part of your object and specify it, private access is possible then and you can invoke it like any other object callback:
array_walk($pins, array($this, 'callback'));
Which brings me back to the beginning saying that foreach is probably the better weapon of choice than array_walk in your case.
I hope this is helpful to show you the different alternatives.
The reason is that you're passing a plain function which is not part of an object. As there is no object, there's nothing $thiscould point to. If convertTime is in global scope, simply omit $this->. Othwewise, consider passing an object as a callback to array_walk.
As of PHP 5.4.0, you can access $this from inside of anonymous functions.