Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Equivalent of Function.prototype.apply in PHP?

Tags:

function

php

this

In Javascript, you can control what object the this keyword referes to by using apply. In the following example, foo will be called with this equal to bar:

function foo() { console.log(this); }

let bar = {};
foo.apply(bar);

Is there any way to achieve the same effect in PHP? Just creating a function referencing $this outside of a class seems possible, but it is (unsurprisingly) just treated like any undefined variable.

I am aware of call_user_func_array, but it does not let me assign to $this. I am thinking reflection might be useful here, but I am not sure how.

like image 663
Anders Avatar asked Oct 17 '22 11:10

Anders


2 Answers

As you could read in the comments, in PHP $this refers to an object context and that could differs from JavaScript context.

However, talking about scope, you can work with closures that carriers an object scope. That scope, which is inside $this variable, can be changed using Closure::bindTo or Closure::bind.

Both of them have the intention to change the closure scope. It means to change the object inside $this variable.

Here a simple example:

class Foo
{
    private $number;

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

    public function getClosure()
    {
        return function() {
            echo $this->number;
        };
    }        
}

And its use:

$foo = new Foo(10);
$closure = $foo->getClosure();
$closure();

Output:

10

The number 10 is the value from $number private variable inside Foo class from $foo instance.

Changing the scope, we can obtain a different result while we access the same variable from other instance:

$foo = new Foo(10);
$foo2 = new Foo(50);
$closureFoo = $foo->getClosure();
$closureFoo();//First execution

echo ' and ';

$closureFoo2 = $closureFoo->bindTo($foo2);
$closureFoo2();//Second execution

Output:

10 and 50

If you want to use Closure::bind, that's the code:

$closureFoo2 = Closure::bind($closureFoo , $foo2);

you can find more examples here: https://stackoverflow.com/a/40772588/1628790

like image 62
Gabriel Heming Avatar answered Oct 31 '22 00:10

Gabriel Heming


Using the insights from other answers and comments, I came up with this implementation of apply in PHP:

function apply($func, $new_this, ...$args) {
    Closure::fromCallable($func)->bindTo($new_this)(...$args);
}

Not sure if this is useful in any way. However, it can be done. Note that this only works if $new_this is an object, and not e.g. a string or number.

like image 29
Anders Avatar answered Oct 31 '22 00:10

Anders