Pwnna
Pwnna

Reputation: 9538

Pass a function by reference in PHP

Is it possible to pass functions by reference?

Something like this:

function call($func){
    $func();
}

function test(){
    echo "hello world!";
}

call(test);

I know that you could do 'test', but I don't really want that, as I need to pass the function by reference.

Is the only way to do so via anonymous functions?

Clarification: If you recall from C++, you could pass a function via pointers:

void call(void (*func)(void)){
    func();
}

Or in Python:

def call(func):
    func()

That's what i'm trying to accomplish.

Upvotes: 39

Views: 54081

Answers (11)

MAChitgarha
MAChitgarha

Reputation: 4298

As of PHP 8.1, you can use First-class callables. For example, to reference a function named test, write the function name followed by (...), like this:

$testFn = test(...);
// Then you can call it like a normal function:
// $testFn();

For your example, you can run:

call(test(...));

You can even reference methods:

call($obj->test(...));

As simple as it is.

Upvotes: 5

deceze
deceze

Reputation: 522625

No, functions are not first class values in PHP, they cannot be passed by their name literal (which is what you're asking for). Even anonymous functions or functions created via create_function are passed by an object or string reference. Edit: PHP 8.1 now has first-class callables, so this is no longer true for newer versions of PHP.

You can pass a name of a function as string, the name of an object method as (object, string) array or an anonymous function as object. None of these pass pointers or references, they just pass on the name of the function. All of these methods are known as the callback pseudo-type: http://php.net/callback

Upvotes: 5

Mikko Rantalainen
Mikko Rantalainen

Reputation: 16055

It appears a bit unclear why do you want to pass functions by reference? Usually things are passed by reference only when the referenced data needs to be (potentially) modified by the function.

As PHP uses arrays or strings to refer functions, you could just pass an array or a string by reference and that would allow the function reference to be modified.

For example, you could do something like

<?php
$mysort = function($a, b) { return ($a < $b) ? 1 : -1; };
adjust_sort_from_config($mysort); // modifies $mysort
do_something_with_data($mysort);

where

<?php
function load_my_configuration(&$fun)
{
  $sort_memory = new ...;
  ...
  $fun = [$sort_memory, "customSort"];
  // or simply
  $fun = function($a, b) { return (rand(1,10) < 4 ? 1 : -1; };
}

This works because there are three ways to refer to function in PHP via a variable:

  • $name – the string $name contains the name of the function in global namespace that should be called
  • array($object, $name) – refers to method called string $name of object $object.
  • array($class, $name) – refers to static function string $name of class $class.

If I remember correctly, the methods and static functions pointed by these constructs must be public. The "First-class callable syntax" should improve this restriction given recent enough PHP version but it seems to be just some syntactic sugar around Closure::fromCallable().

Anonymous functions work the same behind the scenes. You just don't see the literal random names of those functions anywhere but the reference to an anonymous function is just a value of a variable, too.

Upvotes: 0

zeroimpl
zeroimpl

Reputation: 2846

You can create a reference by assigning the function to a local variable when you declare it:

$test = function() {
    echo "hello world!";
};

function call($func){
    $func();
}

call($test);

Upvotes: 2

Pristine Kallio
Pristine Kallio

Reputation: 515

A similar pattern of this Javascript first class function:

function add(first, second, callback){
    console.log(first+second);
    if (callback) callback();
}

function logDone(){
    console.log('done');
}

function logDoneAgain(){
    console.log('done Again');
}
add(2,3, logDone);
add(3,5, logDoneAgain);

Can be done in PHP (Tested with 5.5.9-1ubuntu on C9 IDE) in the following way:

// first class function
$add = function($first, $second, $callback) {
  echo "\n\n". $first+$second . "\n\n";
  if ($callback) $callback();
};

function logDone(){
  echo "\n\n done \n\n";
}
call_user_func_array($add, array(2, 3, logDone));
call_user_func_array($add, array(3, 6, function(){
  echo "\n\n done executing an anonymous function!";
}));

Result: 5 done 9 done executing an anonymous function!

Reference: https://github.com/zenithtekla/unitycloud/commit/873659c46c10c1fe5312f5cde55490490191e168

Upvotes: 1

Rob Evans
Rob Evans

Reputation: 6978

In PHP 5.4.4 (haven't tested lower or other versions), you can do exactly as you suggested.

Take this as an example:

function test ($func) {
    $func('moo');
}

function aFunctionToPass ($str) {
    echo $str;
}

test('aFunctionToPass');

The script will echo "moo" as if you called "aFunctionToPass" directly.

Upvotes: 2

maček
maček

Reputation: 77826

The problem with call_user_func() is that you're passing the return value of the function called, not the function itself.

I've run into this problem before too and here's the solution I came up with.

function funcRef($func){
  return create_function('', "return call_user_func_array('{$func}', func_get_args());");
}

function foo($a, $b, $c){
    return sprintf("A:%s B:%s C:%s", $a, $b, $c);
}

$b = funcRef("foo");

echo $b("hello", "world", 123);

//=> A:hello B:world C:123

ideone.com demo

Upvotes: 18

dhbmarcos
dhbmarcos

Reputation: 39

function func1(){
    echo 'echo1 ';
    return 'return1';
}

function func2($func){
    echo 'echo2 ' . $func();
}

func2('func1');

Result:

echo1 echo2 return1

Upvotes: 3

user166390
user166390

Reputation:

For what it's worth, how about giving something like this a shot? (Yes, I know it's an anonymous function which was mentioned in the post, but I was disgruntled at the abundance of replies that did not mention closures/function-objects at all so this is mostly a note for people running across this post.)

I don't use PHP, but using a closure appears to work in PHP 5.3 (but not PHP 5.2) as demonstrated here. I am not sure what the limitations, if any, there are. (For all I know the closure will eat your children. You have been warned.)

function doIt ($fn) {
  echo "doIt\n";
  return $fn();
}

function doMe () {
  echo "doMe\n";
}

// I am using a closure here.
// There may be a more clever way to "get the function-object" representing a given
// named function, but I do not know what it is. Again, I *don't use PHP* :-)
echo doIt(function () { doMe(); });

Happy coding.

Upvotes: 35

Brian
Brian

Reputation: 13527

Instead of call(test);, use call_user_func('test');.

Upvotes: 0

Scott C Wilson
Scott C Wilson

Reputation: 20046

You can say

$fun = 'test'; 
call($fun); 

Upvotes: 0

Related Questions