Skip to content

Instantly share code, notes, and snippets.

@appkr
Last active December 18, 2018 04:49
Show Gist options
  • Select an option

  • Save appkr/cafd51ff2e7b29d7e0b970d7ab325989 to your computer and use it in GitHub Desktop.

Select an option

Save appkr/cafd51ff2e7b29d7e0b970d7ab325989 to your computer and use it in GitHub Desktop.
How to bind "this" to PHP Closure

Closure.call()

<?php

class Foo
{
    private $greeting;

    public function greet(callable $callback)
    {
        return $callback->call($this); // KEY POINT
    }

    public function getGreeting()
    {
        return $this->greeting;
    }

    public function setGreeting($greeting)
    {
        $this->greeting = $greeting;
    }
}

class Bar
{
    public function sayHello()
    {
        $foo = new Foo();
		
        return $foo->greet(function () {
            // `$this` is not object `Bar` any more
            // object `Foo` was bound to the closure scope 
            $this->setGreeting('Hello Foo');
            return $this->getGreeting();
        });
    }
}

$bar = new Bar();
echo $bar->sayHello(); // Hello Foo

Closure.bindTo()

<?php // http://php.net/manual/kr/closure.bindto.php

class A {
    function __construct($val) {
        $this->val = $val;
    }
    function getClosure() {
        //returns closure bound to this object and scope
        return function() { return $this->val; };
    }
}

$ob1 = new A(1);
$ob2 = new A(2);

$cl = $ob1->getClosure();
echo $cl(); // 1

$cl = $cl->bindTo($ob2); // KEY POINT
echo $cl(); // 2

Traditional Way: use

<?php // Laravel Example
  
class FooController
{
    private $finder;
    private $mutator;
    
    public function __construct(ModelFinder $finder, ModelMutator $mutator)
    {
        $this->finder = $finder;
        $this->mutator = $mutator;
    }
    
    public function updateFoo(Request $request, int $id)
    {
        $dto = $request->getDto();
        $foo = $this->finder->findById($id);
        $self = $this; // Cache `$this` to `$self`
        
        \DB::transaction(function () use ($dto, $foo, $self) { // KEY POINT
            $self->mutator->mutateFooByDto($dto);
            $foo->save();
        });

        return new JsonResponse(null, Response::HTTP_NO_CONTENT);
    }
}
@youngiggy

Copy link
Copy Markdown

\DB::transaction( 에 전달되는 callback안에서 $self === $this 입니다.
위에 두 예제는 명시적으로 바인딩한 반면, 여기서는 바인딩이 없는 이상 $this는 함수 외부의 scope을 따라가는 것 같아요.

http://php.net/manual/en/functions.anonymous.php
As of PHP 5.4.0, when declared in the context of a class, the current class is automatically bound to it, making $this available inside of the function's scope. If this automatic binding of the current class is not wanted, then static anonymous functions may be used instead.
5.3 까지는 $this 바인딩을 지원 안했던 것 같고요. 5.4부터는 자동 바인딩되는군요.

7.1에서는 동일한 변수명을 사용할 수 없는 제약 조건이 생기기도 했고요.
Anonymous functions may not close over superglobals, $this, or any variable with the same name as a parameter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment