Search code examples
phpclassfunction-pointersinstance-variables

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.


Solution

  • 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);