4

I was trying to write js-like php, using closures. However, I don't understand why I can't assign a closure to a stdClass property.

The code explains itself:

$sum = function ($a, $b) {return $a + $b;};
echo $sum(11, 11);
// prints 22



$arr = [];
$arr['sum'] = function ($a, $b) {return $a + $b;};
echo $arr['sum'](22, 22);
// prints 44



$cl = new stdClass;
$cl->sum = function ($a, $b) {return $a + $b;};
echo $cl->sum(33, 33);
// Fatal error: Uncaught Error: Call to undefined method stdClass::sum()



# although I can't think of any use cases for this
class Custom {
    public $sum = NULL;

    function __construc() {
        $this->sum = function ($a, $b) {return $a + $b;};
    }
}

$custom = new Custom;
echo $custom->sum(44, 44);
// Fatal error: Uncaught Error: Call to undefined method Custom::sum()
DonJoe
  • 1,603
  • 1
  • 13
  • 24
  • 1
    @hanshenrik Idk, maybe there is a language implementation-related reason behind this... – DonJoe Jan 27 '19 at 22:37
  • as a workaround you can create this instead of StdObject: ```$cl = new class {public function __call($name,$args){return call_user_func_array($this->{$name},$args);}};``` - and now you can do ```$cl->sum = function ($a, $b) {return $a + $b;}; echo $cl->sum(33, 33);``` - but is it a good idea? i don't know – hanshenrik Jan 27 '19 at 22:46
  • I think this is what you are looking for : https://stackoverflow.com/questions/4535330/calling-closure-assigned-to-object-property-directly – Nouphal.M Jan 28 '19 at 08:06
  • @Nouphal.M not a single answer on 4535330 explains why these workarounds are necessary. lots of answers with lots of different workarounds, but no explanation of why plain old `$o=new StdClass;$o->f=function(){};$o->f();` doesn't work. – hanshenrik Jan 28 '19 at 09:00

2 Answers2

2

Simply wrap the property name in round brackets.

echo ($cl->sum)(33, 33);
echo ($custom->sum)(44, 44);

Example from 3v4l:

https://3v4l.org/3ZqNV

Edit based on comments:

An object in Javascript can have only properties. This properties can be, primitive values, other objects or functions, but the name of the property is unique despite its content.

In PHP an object can have both properties and method.

Let's assume we have the following code:

class MyClass 
{

    public $foo;

    public function __construct() 
    {
        $this->foo = function(int $a, int $b): int {
            return $a * $b;
        };
    }

    public function foo(int $a, int $b): int 
    {
        return $a + $b;
    }
}

$myClass = new MyClass();

echo $myClass->foo(2, 3);

Both property foo and method foo have the same signature, but what was the programmer's will?

This code will call the method foo and prints 5, so any line $var->name() is interpreted as method call.

If you want to call the closure inside the property you have to use a different syntax so that there is no ambiguity.

echo ($myClass->foo)(2, 3) //Prints 6

https://3v4l.org/PXiMQ

cn7r66
  • 164
  • 1
  • 5
0

You could use the closure methods bind(), bindTo() or call().

$cl = new stdClass;
$sum = function ($a, $b) {return $a + $b;};
Closure::bind($sum, $cl);

echo $sum(33, 33); // this function is now part of the class. 

If you wish to extend this functionality, you could create a register function in the class, that relys on magic methods such as __call() to check if that method exists, and if so call the registered closure internally.

Xorifelse
  • 7,878
  • 1
  • 27
  • 38
  • this provides a workaround, but it doesn't explain why `$cl->sum(33, 33);` doesn't work – hanshenrik Jan 28 '19 at 08:55
  • Well, that answer has already been given. However, that functionality is dependent on which PHP version you're running. Yes, this is a work around if you're already running PHP7 (I am not 100% sure with this statement, because parenthesis prioritization changed to a certain degree) but if not, this could do the trick. – Xorifelse Jan 29 '19 at 18:23