This question already has answers here:
php call class function by string name
(6 answers)
Closed 8 years ago.
In PHP5, variables can be evaluated as functions1 such as:
function myFunc() {
echo "whatever";
}
$callableFunction = 'myFunc';
$callableFunction(); // executes myFunc()
Is there any syntax for assigning object member functions to a variable such as:
class MyClass {
function someCall() {
echo "yay";
}
}
$class = new MyClass();
// what I would like:
$assignedFunction = $class->someCall; // but I tried and it returns an error
$memberFunc = 'someCall';
$class->$memberFunc(); // I know this is valid, but I want a single variable to be able to be used to call different functions - I don't want to have to know whether it is part of a class or not.
// my current implementation because I don't know how to do it with anonymous functions:
$assignedFunction = function() { return $class->someCall(); } // <- seems lengthy; would be more efficient if I can just assign $class->someCall to the variable somehow?
$assignedFunction(); // I would like this to execute $class->someCall()
There is a way, but for php 5.4 and above...
class MyClass {
function someCall() {
echo "yay";
}
}
$obj = new Myclass();
$ref = array($obj, 'someCall');
$ref();
Hm.. actually it works for static too, just use the reference by name..
class MyClass {
static function someCall2() {
echo "yay2";
}
}
$ref = array('MyClass', 'someCall2');
$ref();
And for nonstatic this notation works as well. It creates a temporary instance of the class. So, this is what you need, only you need php 5.4 and above )
The PHP 5.4 solution above is good. If you need PHP 5.3, I don't think you can do much better than the anonymous function approach, but you could wrap that into a function that acts very similar to the PHP 5.4 method:
function buildCallable($obj, $function)
{
return function () use ($obj, $function) {
$args = func_get_args();
return call_user_func_array(array($obj, $function), $args);
};
}
//example
class MyClass
{
public function add($x, $y)
{
return $x + $y;
}
public static function multiply($x, $y)
{
return $x * $y;
}
}
//non-static methods
$callable = buildCallable(new MyClass(), 'add');
echo $callable(32, 10);
//static methods
$callable = buildCallable('MyClass', 'multiply');
echo $callable(21, 2);
This should work for any number of arguments to any (publicly visible) method.
Related
This question already has answers here:
How to call a closure that is a class variable?
(2 answers)
Closed 4 years ago.
$f = function($v) {
return $v + 1;
}
echo $f(4);
// output -> 5
The above works perfectly fine. However, I cannot reproduce this correctly when f is a property of a class.
class MyClass {
public $f;
public function __construct($f) {
$this->f = $f;
}
public function methodA($a) {
echo $this->f($a);
}
}
// When I try to call the property `f`, PHP gets confused
// and thinks I am trying to call a method of the class ...
$myObject = new myClass($f);
$myObject->methodA(4);
The above will result in an error:
Call to undefined method MyClass::f()
I think the problem is that it is trying to make sense of
echo $this->f($a);
And as you've found it wants to call a member function f in the class. If you change it to
echo ($this->f)($a);
It interprets it as you want it to.
PHP 5.6
Thanks to ADyson for the comment, think this works
$f = $this->f;
echo $f($a);
While Nigel Ren's answer (https://stackoverflow.com/a/50117174/5947043) will work in PHP 7, this slightly expanded syntax will work in PHP 5 as well:
class MyClass {
public $f;
public function __construct($f) {
$this->f = $f;
}
public function methodA($a) {
$func = $this->f;
echo $func($a);
}
}
$f = function($v) {
return $v + 1;
};
$myObject = new myClass($f);
$myObject->methodA(4);
See https://eval.in/997686 for a working demo.
I would like to be able to call a closure that I assign to an object's property directly without reassigning the closure to a variable and then calling it. Is this possible?
The code below doesn't work and causes Fatal error: Call to undefined method stdClass::callback().
$obj = new stdClass();
$obj->callback = function() {
print "HelloWorld!";
};
$obj->callback();
As of PHP7, you can do
$obj = new StdClass;
$obj->fn = function($arg) { return "Hello $arg"; };
echo ($obj->fn)('World');
or use Closure::call(), though that doesn't work on a StdClass.
Before PHP7, you'd have to implement the magic __call method to intercept the call and invoke the callback (which is not possible for StdClass of course, because you cannot add the __call method)
class Foo
{
public function __call($method, $args)
{
if(is_callable(array($this, $method))) {
return call_user_func_array($this->$method, $args);
}
// else throw exception
}
}
$foo = new Foo;
$foo->cb = function($who) { return "Hello $who"; };
echo $foo->cb('World');
Note that you cannot do
return call_user_func_array(array($this, $method), $args);
in the __call body, because this would trigger __call in an infinite loop.
You can do this by calling __invoke on the closure, since that's the magic method that objects use to behave like functions:
$obj = new stdClass();
$obj->callback = function() {
print "HelloWorld!";
};
$obj->callback->__invoke();
Of course that won't work if the callback is an array or a string (which can also be valid callbacks in PHP) - just for closures and other objects with __invoke behavior.
As of PHP 7 you can do the following:
($obj->callback)();
Since PHP 7 a closure can be called using the call() method:
$obj->callback->call($obj);
Since PHP 7 is possible to execute operations on arbitrary (...) expressions too (as explained by Korikulum):
($obj->callback)();
Other common PHP 5 approaches are:
using the magic method __invoke() (as explained by Brilliand)
$obj->callback->__invoke();
using the call_user_func() function
call_user_func($obj->callback);
using an intermediate variable in an expression
($_ = $obj->callback) && $_();
Each way has its own pros and cons, but the most radical and definitive solution still remains the one presented by Gordon.
class stdKlass
{
public function __call($method, $arguments)
{
// is_callable([$this, $method])
// returns always true when __call() is defined.
// is_callable($this->$method)
// triggers a "PHP Notice: Undefined property" in case of missing property.
if (isset($this->$method) && is_callable($this->$method)) {
return call_user_func($this->$method, ...$arguments);
}
// throw exception
}
}
$obj = new stdKlass();
$obj->callback = function() { print "HelloWorld!"; };
$obj->callback();
It seems to be possible using call_user_func().
call_user_func($obj->callback);
not elegant, though.... What #Gordon says is probably the only way to go.
Well, if you really insist. Another workaround would be:
$obj = new ArrayObject(array(),2);
$obj->callback = function() {
print "HelloWorld!";
};
$obj['callback']();
But that's not the nicest syntax.
However, the PHP parser always treats T_OBJECT_OPERATOR, IDENTIFIER, ( as method call. There seems to be no workaround for making -> bypass the method table and access the attributes instead.
I know this is old, but I think Traits nicely handle this problem if you are using PHP 5.4+
First, create a trait that makes properties callable:
trait CallableProperty {
public function __call($method, $args) {
if (property_exists($this, $method) && is_callable($this->$method)) {
return call_user_func_array($this->$method, $args);
}
}
}
Then, you can use that trait in your classes:
class CallableStdClass extends stdClass {
use CallableProperty;
}
Now, you can define properties via anonymous functions and call them directly:
$foo = new CallableStdClass();
$foo->add = function ($a, $b) { return $a + $b; };
$foo->add(2, 2); // 4
well, it should be emphisized that storing the closure in a variable, and call the varible is actually (wierdly) faster, depending on the call amount, it becomes quite a lot, with xdebug (so very precise measuring), we are talking about 1,5 (the factor, by using a varible, instead of directly calling the __invoke. so instead , just store the closure in a varible and call it.
Here's another alternative based on the accepted answer but extending stdClass directly:
class stdClassExt extends stdClass {
public function __call($method, $args)
{
if (isset($this->$method)) {
$func = $this->$method;
return call_user_func_array($func, $args);
}
}
}
Usage example:
$foo = new stdClassExt;
$foo->blub = 42;
$foo->whooho = function () { return 1; };
echo $foo->whooho();
You are probably better off using call_user_func or __invoke though.
Updated:
$obj = new stdClass();
$obj->callback = function() {
print "HelloWorld!";
};
PHP >= 7 :
($obj->callback)();
PHP >= 5.4 :
$callback = $obj->callback;
$callback();
If you're using PHP 5.4 or above you could bind a callable to the scope of your object to invoke custom behavior. So for example if you were to have the following set up..
function run_method($object, Closure $method)
{
$prop = uniqid();
$object->$prop = \Closure::bind($method, $object, $object);
$object->$prop->__invoke();
unset($object->$prop);
}
And you were operating on a class like so..
class Foo
{
private $value;
public function getValue()
{
return $this->value;
}
}
You could run your own logic as if you were operating from within the scope of your object
$foo = new Foo();
run_method($foo, function(){
$this->value = 'something else';
});
echo $foo->getValue(); // prints "something else"
I note that this works in PHP5.5
$a = array();
$a['callback'] = function() {
print "HelloWorld!";
};
$a['callback']();
Allows one to create a psuedo-object collection of closures.
PHP allows for variables to hold functions like so:
$f = function($a,$b) {
print "$a $b";
};
$f("Hello","World!"); //prints 'Hello World!'
This works just fine for me. I'm trying to pass a function into a class and set an instance variable to hold that function but with little luck:
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print $this->distanceFunc(1,7); //exceptions and errors abound
}
}
$func = function($a,$b) {
return abs($a-$b);
}
$c = new Clusterer($func);
Am I doing something wrong here? The error is that the function doesn't exist so my guess currently is that it looks for a class function with that name (which there isn't one) and then gives up rather than looking for variables as well... how can I make it view the $this->distanceFunc as a variable?
EDIT:
So after the advice from the answers below, I found a solution which was the make a function to wrap the invocation. For example my class is now:
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print $this->distanceFunc(1,7); //exceptions and errors abound
}
private function distanceFunc($a,$b) {
$holder = $this->distanceFunc;
return $holder($a,$b);
}
}
$func = function($a,$b) {
return abs($a-$b);
}
$c = new Clusterer($func);
and this works great. Php looks for functions first and can only tell if it is a variable by context I guess is the moral of this story.
Your code doesn't work because PHP interprets $this->distanceFunc(1,7) as a class method, but you can do the following:
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print call_user_func_array($this->distanceFunc, array(1, 7));
// print $this->distanceFunc(1,7); //exceptions and errors abound
}
}
$func = function($a,$b) {
return abs($a-$b);
};
$c = new Clusterer($func);
http://sandbox.onlinephpfunctions.com/code/cdc1bd6bd50f62d5c88631387ac9543368069310
In PHP, methods and properties of an object occupy separate namespaces. This is different from JavaScript, for example, where foo.bar = function() {} is a perfectly valid way of defining a method.
Consequently, $this->distanceFunc(1,7); looks for a method named distanceFunc on the current class, and the classes it inherits from, but never looks for the property which you happen to have given the same name.
One solution is to force PHP to look up a property, then execute it, e.g. $foo = $this->distanceFunc; $foo(1,7) or call_user_func($this->distanceFunc, 1, 7)
Another would be to define the magic method __call on your class, which gets run whenever a non-existent method is referenced. Something like this ought to work (I don't have an easy way to testright now):
function __call($func, $args) {
if ( property_exists($this, $func) && is_callable($this->$func) ) {
return call_user_func_array($this->$func, $args);
}
}
Note that this still isn't the same as a real method, for instance in terms of access to private properties.
It looks like you're going for a strategy pattern here. IE you want to be able to inject different methods for calculating distance? If so there is a more "sane" way to do it.
You can define an interface to the classes you will use to store the strategy method ensuring that the class will always have the method calculate() for example which would be your distance calculation function. Then in the constructor of your Clusterer class, type check against the interface in the parameter and call calculate() on the object passed in.
Looks like this:
interface Calculateable
{
public function calculate();
}
class MyDistanceCalculator implements Calculateable
{
public function calculate()
{
// Your function here
}
}
class Clusterer
{
protected $calc;
public function __construct(Calculateable $calc)
{
$this->calc = $calc;
$this->calc->calculate();
}
}
$myClusterer = new Clusterer(new MyDistanceCalculator());
Because you defined an interface, any object you pass in will have the calculate() function
In HHVM, you can do this:
<?php
class Foo
{
public function __construct()
{
$this->bar = function() { echo "Here\n"; };
($this->bar)();
}
}
new Foo();
But it's not yet supported in PHP. But, it will be in PHP 7 (there will be no release named PHP 6).
PHP doesn't have first class functions. In JavaScript if you returned a function you could do this: myFunctionThatReturnsAFunction()(1,2), but not in PHP.
<?php
class Clusterer {
private $distanceFunc;
public function __construct(Closure $f) {
$this->distanceFunc = $f;
}
public function getDistFunc()
{
return $this->distanceFunc;
}
}
$func = function($a,$b) {
return abs($a-$b);
};
$c = new Clusterer($func);
$a = $c->getDistFunc();
echo $a(1,2);
Take a look at call_user_func
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print call_user_func($this->distanceFunc, 1, 7); //works too ;)
}
}
$func = function($a,$b) {
return abs($a-$b);
};
$c = new Clusterer($func);
Don't ask me what is the difference, but it works the way you want (One of the reasons i hate this language)
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Use a variable to define a PHP function
Use Variable as Function Name in PHP
I want to perform a conditional function call but I don't necessarily know what what the function will be, so that would be a long switch.
For example;
$userSelection = "calculator"; /* or "stocks" or whatever widget */
$widget->get_widget($userSelection);
public function __construct($userSelection){
/* pseudo code */
call function $userSelection();
}
public function calculator(){
/* Get Calculator */
}
Sure there is. This feature is called variable functions:
$functionName = "strlen";
$length = $$functionName("Hello world!");
The $$var(...) syntax is convenient, but it will only work with free functions. If you want to call a class method this way, you will need to use call_user_func or call_user_func_array (these functions can also handle the "free function" case).
Look at the call-user-func function. This allows you to call another function, e.g.
call_user_func('calculator')
call_user_func($userSelection);
http://php.net/manual/en/function.call-user-func.php
Take a look at this php functions:
call_user_func(): http://php.net/manual/de/function.call-user-func.php
call_user_func_array(): http://www.php.net/manual/de/function.call-user-func-array.php
create_function(): http://www.php.net/manual/de/function.create-function.php
There is also a direct (though ugly) execution syntax:
function some_func(args) {...}
$function_name='some_func';
$$function_name(args2);
You can use call_user_func() for that, like this:
$userSelection = "calculator";
call_user_func($userSelection[, $param1, $param2, ...]);
call_user_func_array($userSelection, $params);
If it's just a function you're after then using this should solve your problems
$function = "echo";
$$function "fooBar";
If it's a class method that you want to keep flexible use magic method __call() which will allow you to use method names that are not pre-defined.
__call() is triggered when invoking inaccessible methods in an object context.
i.e.
class Foo {
public function __call($name, $arguments) {
echo $name;
}
}
$foo = new Foo();
$foo->bar(); // will echo "bar"
PHP built-in function 'eval' can do everything, but beware of injection.
$var = "somefunction";
eval("$var();");
http://php.net/manual/en/function.eval.php
It's pretty simple if that's what you mean.
function calculator() {
echo 'foo';
}
$userSelection = "calculator";
if (function_exists($userSelection)) {
$userSelection();
}
Or within a class like in your example:
class widget {
public function __construct($userSelection) {
echo 'constructed widget<br>';
if (function_exists($userSelection)) {
$this->$userSelection();
}
}
public function calculator() {
echo 'bar';
}
}
$userSelection = "calculator";
$widget = new widget($userSelection);
Or from outside a class when the function is part of the class.
class widget {
public function calculator() {
echo 'bar';
}
}
$widget = new widget();
$userSelection = "calculator";
$widget->$userSelection();
I would work with if/else statements though to determine the function to be called just to be sure that only valid functions are executed (do you sanitize the user selection or do you just get it from a $_POST? The latter would be a very bad idea).
You can do following :
$var = 'abc';
switch ($var) {
case 'abc':
$result = $var('test param');
echo $result;
break;
default :
echo 'default';
break;
}
function abc($data) {
return $data;
}
In PHP, I am able to use a normal function as a variable without problem, but I haven't figured out how to use a static method. Am I just missing the right syntax, or is this not possible?
(EDIT: the first suggested answer does not seem to work. I've extended my example to show the errors returned.)
function foo1($a,$b) { return $a/$b; }
class Bar
{
static function foo2($a,$b) { return $a/$b; }
public function UseReferences()
{
// WORKS FINE:
$fn = foo1;
print $fn(1,1);
// WORKS FINE:
print self::foo2(2,1);
print Bar::foo2(3,1);
// DOES NOT WORK ... error: Undefined class constant 'foo2'
//$fn = self::foo2;
//print $fn(4,1);
// DOES NOT WORK ... error: Call to undefined function self::foo2()
//$fn = 'self::foo2';
//print $fn(5,1);
// DOES NOT WORK ... error: Call to undefined function Bar::foo2()
//$fn = 'Bar::foo2';
//print $fn(5,1);
}
}
$x = new Bar();
$x->UseReferences();
(I am using PHP v5.2.6 -- does the answer change depending on version too?)
PHP handles callbacks as strings, not function pointers. The reason your first test works is because the PHP interpreter assumes foo1 as a string. If you have E_NOTICE level error enabled, you should see proof of that.
"Use of undefined constant foo1 - assumed 'foo1'"
You can't call static methods this way, unfortunately. The scope (class) is relevant so you need to use call_user_func instead.
<?php
function foo1($a,$b) { return $a/$b; }
class Bar
{
public static function foo2($a,$b) { return $a/$b; }
public function UseReferences()
{
$fn = 'foo1';
echo $fn(6,3);
$fn = array( 'self', 'foo2' );
print call_user_func( $fn, 6, 2 );
}
}
$b = new Bar;
$b->UseReferences();
In php 5.2, you can use a variable as the method name in a static call, but to use a variable as the class name, you'll have to use callbacks as described by BaileyP.
However, from php 5.3, you can use a variable as the class name in a static call. So:
class Bar
{
public static function foo2($a,$b) { return $a/$b; }
public function UseReferences()
{
$method = 'foo2';
print Bar::$method(6,2); // works in php 5.2.6
$class = 'Bar';
print $class::$method(6,2); // works in php 5.3
}
}
$b = new Bar;
$b->UseReferences();
?>
You could use the full name of static method, including the namespace.
<?php
function foo($method)
{
return $method('argument');
}
foo('YourClass::staticMethod');
foo('Namespace\YourClass::staticMethod');
The name array array('YourClass', 'staticMethod') is equal to it. But I think the string may be more clear for reading.
In PHP 5.3.0, you could also do the following:
<?php
class Foo {
static function Bar($a, $b) {
if ($a == $b)
return 0;
return ($a < $b) ? -1 : 1;
}
function RBar($a, $b) {
if ($a == $b)
return 0;
return ($a < $b) ? 1 : -1;
}
}
$vals = array(3,2,6,4,1);
$cmpFunc = array('Foo', 'Bar');
usort($vals, $cmpFunc);
// This would also work:
$fooInstance = new Foo();
$cmpFunc = array('fooInstance', 'RBar');
// Or
// $cmpFunc = array('fooInstance', 'Bar');
usort($vals, $cmpFunc);
?>
Coming from a javascript background and being spoiled by it, I just coded this:
function staticFunctionReference($name)
{
return function() use ($name)
{
$className = strstr($name, '::', true);
if (class_exists(__NAMESPACE__."\\$className")) $name = __NAMESPACE__."\\$name";
return call_user_func_array($name, func_get_args());
};
}
To use it:
$foo = staticFunctionReference('Foo::bar');
$foo('some', 'parameters');
It's a function that returns a function that calls the function you wanted to call. Sounds fancy but as you can see in practice it's piece of cake.
Works with namespaces and the returned function should work just like the static method - parameters work the same.
This seems to work for me:
<?php
class Foo{
static function Calc($x,$y){
return $x + $y;
}
public function Test(){
$z = self::Calc(3,4);
echo("z = ".$z);
}
}
$foo = new Foo();
$foo->Test();
?>
In addition to what was said you can also use PHP's reflection capabilities:
class Bar {
public static function foo($foo, $bar) {
return $foo . ' ' . $bar;
}
public function useReferences () {
$method = new ReflectionMethod($this, 'foo');
// Note NULL as the first argument for a static call
$result = $method->invoke(NULL, '123', 'xyz');
}
}
"A member or method declared with static can not be accessed with a variable that is an instance of the object and cannot be re-defined in an extending class"
(http://theserverpages.com/php/manual/en/language.oop5.static.php)