Accept function as parameter in PHP - php

I've been wondering whether it is possible or not to pass a function as a parameter in PHP. I want something similar to when you're programming the following code in JavaScript:
object.exampleMethod(function(){
// some stuff to execute
});
What I want is to execute that function somewhere in exampleMethod. Is that possible in PHP?

It's possible if you are using PHP 5.3.0 or higher.
See Anonymous Functions in the manual.
In your case, you would define exampleMethod like this:
function exampleMethod($anonFunc) {
//execute anonymous function
$anonFunc();
}

Just to add to the others, you can pass a function name:
function someFunc($a)
{
echo $a;
}
function callFunc($name)
{
$name('funky!');
}
callFunc('someFunc');
This will work in PHP4.

Valid: (PHP 4 >= 4.0.1, PHP 5, PHP 7)
You can also use create_function to create a function as a variable and pass it around. Though, I like the feeling of anonymous functions better. Go zombat.
Update 09 - Jan - 2022
Warning
This function has been DEPRECATED as of PHP 7.2.0, and REMOVED as of PHP 8.0.0. Relying on this function is highly discouraged.

Just code it like this:
function example($anon) {
$anon();
}
example(function(){
// some codes here
});
it would be great if you could invent something like this (inspired by Laravel Illuminate):
Object::method("param_1", function($param){
$param->something();
});

PHP VERSION >= 5.3.0
Example 1: basic
function test($test_param, $my_function) {
return $my_function($test_param);
}
test("param", function($param) {
echo $param;
}); //will echo "param"
Example 2: std object
$obj = new stdClass();
$obj->test = function ($test_param, $my_function) {
return $my_function($test_param);
};
$test = $obj->test;
$test("param", function($param) {
echo $param;
});
Example 3: non static class call
class obj{
public function test($test_param, $my_function) {
return $my_function($test_param);
}
}
$obj = new obj();
$obj->test("param", function($param) {
echo $param;
});
Example 4: static class call
class obj {
public static function test($test_param, $my_function) {
return $my_function($test_param);
}
}
obj::test("param", function($param) {
echo $param;
});

According to #zombat's answer, it's better to validate the Anonymous Functions first:
function exampleMethod($anonFunc) {
//execute anonymous function
if (is_callable($anonFunc)) {
$anonFunc();
}
}
Or validate argument type since PHP 5.4.0:
function exampleMethod(callable $anonFunc) {}

Tested for PHP 5.3
As i see here, Anonymous Function could help you:
http://php.net/manual/en/functions.anonymous.php
What you'll probably need and it's not said before it's how to pass a function without wrapping it inside a on-the-fly-created function.
As you'll see later, you'll need to pass the function's name written in a string as a parameter, check its "callability" and then call it.
The function to do check:
if( is_callable( $string_function_name ) ){
/*perform the call*/
}
Then, to call it, use this piece of code (if you need parameters also, put them on an array), seen at : http://php.net/manual/en/function.call-user-func.php
call_user_func_array( "string_holding_the_name_of_your_function", $arrayOfParameters );
as it follows (in a similar, parameterless, way):
function funToBeCalled(){
print("----------------------i'm here");
}
function wrapCaller($fun){
if( is_callable($fun)){
print("called");
call_user_func($fun);
}else{
print($fun." not called");
}
}
wrapCaller("funToBeCalled");
wrapCaller("cannot call me");
Here's a class explaining how to do something similar :
<?php
class HolderValuesOrFunctionsAsString{
private $functions = array();
private $vars = array();
function __set($name,$data){
if(is_callable($data))
$this->functions[$name] = $data;
else
$this->vars[$name] = $data;
}
function __get($name){
$t = $this->vars[$name];
if(isset($t))
return $t;
else{
$t = $this->$functions[$name];
if( isset($t))
return $t;
}
}
function __call($method,$args=null){
$fun = $this->functions[$method];
if(isset($fun)){
call_user_func_array($fun,$args);
} else {
// error out
print("ERROR: Funciton not found: ". $method);
}
}
}
?>
and an example of usage
<?php
/*create a sample function*/
function sayHello($some = "all"){
?>
<br>hello to <?=$some?><br>
<?php
}
$obj = new HolderValuesOrFunctionsAsString;
/*do the assignement*/
$obj->justPrintSomething = 'sayHello'; /*note that the given
"sayHello" it's a string ! */
/*now call it*/
$obj->justPrintSomething(); /*will print: "hello to all" and
a break-line, for html purpose*/
/*if the string assigned is not denoting a defined method
, it's treat as a simple value*/
$obj->justPrintSomething = 'thisFunctionJustNotExistsLOL';
echo $obj->justPrintSomething; /*what do you expect to print?
just that string*/
/*N.B.: "justPrintSomething" is treated as a variable now!
as the __set 's override specify"*/
/*after the assignement, the what is the function's destiny assigned before ? It still works, because it's held on a different array*/
$obj->justPrintSomething("Jack Sparrow");
/*You can use that "variable", ie "justPrintSomething", in both ways !! so you can call "justPrintSomething" passing itself as a parameter*/
$obj->justPrintSomething( $obj->justPrintSomething );
/*prints: "hello to thisFunctionJustNotExistsLOL" and a break-line*/
/*in fact, "justPrintSomething" it's a name used to identify both
a value (into the dictionary of values) or a function-name
(into the dictionary of functions)*/
?>

Simple example using a class :
class test {
public function works($other_parameter, $function_as_parameter)
{
return $function_as_parameter($other_parameter) ;
}
}
$obj = new test() ;
echo $obj->works('working well',function($other_parameter){
return $other_parameter;
});

Here is a simple procedural example of how you could implement validation of multiple data items using separate functions for each data item validation, passed as an array of functions argument to a master validations function, with the data to be validated (the arguments to the functions) passes as the other array argument to the master validation function. Useful for writing generic code to validate form data.
<?php
function valX($value) {
echo "<p>Validating $value == 5</p>";
if ($value == 5) {
return true;
} else {
return false;
}
}
function valY($value) {
echo "<p>Validating $value == 6</p>";
if ($value == 6) {
return true;
} else {
return false;
}
}
function validate($values, $functions) {
for ($i = 0; $i < count($values); $i++) {
if ($functions[$i]($values[$i])) {
echo "<p>$values[$i] passes validation</p>";
} else {
echo "<p>$values[$i] fails validation</p>";
}
}
}
$values = [5, 9];
$functions = ['valX', 'valY'];
validate($values, $functions);
?>

Related

Using function pointer with class or function in php

I am converting an old project that uses all functions to use a class. Not all installed instances will be updated so I have to have the code work with the function or the class but I can't get it to work. Here is an outline of the code I am using:
function GetStatus($id) {
return true;
}
class myclass (
public function GetStatus($id) {
return true;
}
}
if (class_exists('myclass')) {
$fcn = $myclass->GetStatus;
} else {
$fcn = GetStatus;
}
echo 'result = ' . $fcn($user_id);
So the code checks if the class exists and uses that method, else it uses the function. The code runs without errors but when the class method is used the return is not shown. That is, the results are
result = 1 //when function is used
result = //when method is used
Is this possible? If it matters, the minimum php version is 7.1.
To make a callable from an instance and method, you use an array containing the instance and the method name.
$fcn = [$myclass, 'GetStatus'];
Checking for a myclass existance when you already have the myclass instance stored in a $myclass variable makes no sense. Maybe you should check for a method existance?
if (method_exists($myclass, 'GetStatus')) {
$fcn = function($id) use ($myclass){
return $myclass->GetStatus($id);
};
} else {
$fcn = function($id){
return GetStatus($id);
};
}
echo 'result = ' . $fcn($user_id);

In PHP, how do you use a function as a parameter in another function?

I know that this question may have been asked before, but I don't quite understand the whole concept of anonymous functions (closures), and how they would apply specifically to my situation. NB: I know it is quite silly to have all these simple functions, but my task requirements say that I should have all these functions :/ .
I have several functions. In the function below, the parameters $action1 and $action2 will be both replaced by functions:
function dothis($num1, $num2, $action1, $action2)
{
$result = $num1 + $num2;
if ($result > 52){
//do $action1 which is a function
} else {
//do $action2 which is another function
}
return $result;
}
The function dothis will be called in another function called add. Here is the add function:
function add($action1,$action2)
{
$answer = dothis(42, 34, $action1, $action2);
echo $answer;
}
$action1 and $action2 in the add function are essentially the same as the $action1 and $action2 in the dothis function. Do they need to have different names now even though they are the same things?
Now, the add function will be called in the main function, where the parameters $action1 and $action2, will be replaced by the actual functions that they correspond to:
function main()
{
add($fun1,$fun2);
echo 'Arithmetic complete';
}
This is the code for $fun1 and $fun2:
$fun1 = function () {
echo 'Wow! The answer is greater than 52.';
};
$fun2 = function () {
echo 'Oh no! The answer is less than 52.';
};
What should I do, and what do I need to change? I really appreciate any help. Thanks in advance!
See the PHP documentation on Variable Functions
You call a function by putting () after it (with arguments inside the parentheses if necessary).
function dothis($num1,$num2,$action1,$action2){
$result = $num1 + $num2;
if ($result > 52){
$action1();
}
else{
$action2();
}
return $result;
}
For this syntax to work with anonymous functions, you have to turn off eAccelerator. See Anonymous functions does not work: Function name must be a string
If you can't use anonymous functions, you'll need to use named functions.
function fun1 () {
echo 'Wow! The answer is greater than 52.';
}
$fun1 = 'fun1';
function fun2() {
echo 'Oh no! The answer is less than 52.';
}
$fun2 = 'fun2';
I guess this is a rather bad approach. You could use a function like call_user_func() but not really for anonymous functions. Why not come up with a class defining both functions or (if the code is rather short) but it in the if/else statements directly?
Some dummy sample code for the class approach:
class doSomething {
function dothis($num1, $num2) {
$result = $num1 + $num2;
if ($result > 52) {
$this->action1($num1, $num2);
} else {
$this->action2($num1, $num2);
}
return $result;
}
function action1($numbers) {
// do sth. here
}
function action2($num1, $num2) {
// do sth. else here
}
}
// afterwards
$pointer = new doSomething();
$pointer->dothis(21,34); // action1
$pointer->dothis(1,1); // action2

Class variables holding a function in PHP

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)

PHP function, return by value or by reference?

When I use return statement in PHP, will the result be returned by value or by reference?
Thanks! Andree.
In PHP, everything is returned by value by default (I'm sure there are exceptions to this but I can't think of any atm). Except objects (PHP>5.0) which are passed by reference by default.
Apparently, it is returned by reference. This simple code proofs it.
<?php
class InsideObject
{
public $variable;
}
class OutsideObject
{
private $insideObject;
public function __construct()
{
$this->insideObject = new InsideObject();
$this->insideObject->variable = '1';
}
public function echoVar()
{
echo $this->insideObject->variable;
}
public function getInsideObject()
{
return $this->insideObject;
}
}
$object = new OutsideObject();
$object->echoVar(); // should be 1
$insideObject = $object->getInsideObject();
$insideObject->variable = '2';
$object->echoVar(); // should be 2

Reference to static method in PHP?

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)

Categories