skip to Main Content

I am trying to store a callable/closure as a class field/property.

My initial attempt was this:

class MyClass
{
    protected callable $modifier;

    public function __construct(callable $modifier) {
        $this->modifier = $modifier;
    }
}

class SomeOtherClass
{
    public static function func()
    {
        return ['a' => 'b'];
    }
}

$test = new MyClass('SomeOtherClass::func');

This returned an error of:

$modifier cannot have type callable

Then, after reading this online, I’ve tried this with no success:

class MyClass
{
    protected Closure $modifier;

    public function __construct(callable $modifier) {
        $this->modifier = Closure::fromCallable($modifier);
    }
}

class SomeOtherClass
{
    public static function func()
    {
        return ['a' => 'b'];
    }
}

$test = new MyClass('SomeOtherClass::func');

This last one returns this error:

Argument #1 ($modifier) must be of type callable, string given

Then I’ve tried this:

class MyClass
{
    protected Closure $modifier;

    public function __construct(callable $modifier) {
        $this->modifier = Closure::fromCallable($modifier);
    }
}

class SomeOtherClass
{
    public static function func()
    {
        return ['a' => 'b'];
    }
}

$test = new MyClass(['SomeOtherClass', 'func']); // <----- changed string to an array

Which returns this error:

Argument #1 ($modifier) must be of type callable, array given

Is this even possible to do in PHP?

2

Answers


  1. Assuming you’re using PHP 8.1 or above (which at the time of writing are the only supported versions of PHP anyway), then you can use first-class callable syntax, as introduced in 8.1.

    To do this, you use the name of a function and then (...) as the argument to pass in.

    In your case, you could therefore do this:

    class MyClass
    {
        protected Closure $modifier;
    
        public function __construct(callable $modifier) {
            $this->modifier = Closure::fromCallable($modifier);
        }
        
        public function runClosure()
        {
            var_dump(($this->modifier)());
        }
    }
    
    class SomeOtherClass
    {
        public static function func()
        {
            return ['a' => 'b'];
        }
    }
    
    $test = new MyClass(SomeOtherClass::func(...));
    $test->runClosure();
    

    Live demo: https://3v4l.org/nMmvq


    N.B. The code above assumes that you want to use func() as a static method (as defined in your sample code). If anyone is reading and wants to do something similar but with an instance method, you’d need a small adjustment as follows:

    class MyClass
    {
        protected Closure $modifier;
    
        public function __construct(callable $modifier) {
            $this->modifier = Closure::fromCallable($modifier);
        }
    
        public function runClosure()
        {
            var_dump(($this->modifier)());
        }
    }
    
    class SomeOtherClass
    {
        public function func()
        {
            return ['a' => 'b'];
        }
    }
    
    $soc = new SomeOtherClass();
    $test = new MyClass($soc->func(...));
    $test->runClosure();
    

    Live demo: https://3v4l.org/iEAiM

    Login or Signup to reply.
  2. Just for the sake of completeness. The problem in OP’s code was most likely caused by namespaces.

    You have to use fully qualified name in array/string callable. So, you have to use [SomeOtherClass::class, 'func'] or ['MyNamespaceSomeOtherClass', 'func'].

    See live demo: https://3v4l.org/pnWYC

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search