Can't access $this on PHP Closure passed to an object method

I'm trying to pass a closure that uses object data without having to assign the object to a variable and passing it as a parameter to the closure, but I can't seem to figure out the right way to do it.

Here is my arbitrary class code:

class Person
{
    var $__data = [];

    function setData($key, $value)
    {
        $this->__data[$key] = $value;
        return $this;
    }

    function setAutoData($map)
    {
        $this->__data = array_merge($this->__data, $map());
        return $this;
    }

    function getData($key)
    {
        return $this->__data[$key];
    }

}

This piece of code here will work and add the first_name to the data array:

$p = (new Person())->setData('full_name', 'Valerie Maddison Bricks');

print_r($p->setAutoData(function () use($p) {
    return [
        'first_name' => array_shift(explode(' ', $p->getData('full_name')))
    ];
}));

/*
Output:

Person Object
(
    [__data] => Array
        (
            [full_name] => Valerie Maddison Bricks
            [first_name] => Valerie
        )

)
*/

This one doesn't work.

print_r((new Person())
            ->setData('full_name', 'Valerie Maddison Bricks')
            ->setAutoData(function () {
                return [
                    'first_name' => array_shift(explode(' ', $this->getData('full_name'))),
                ];
            }));

/*
Output:
Fatal error:  Uncaught Error: Using $this when not in object context in C:\Damian\xampp\web\dbo-dev\teste.php:44
Stack trace:
#0 C:\Damian\xampp\web\dbo-dev\teste.php(22): {closure}()
#1 C:\Damian\xampp\web\dbo-dev\teste.php(46): Person->setAutoData(Object(Closure))
#2 {main}
  thrown in C:\Damian\xampp\web\dbo-dev\teste.php on line 44
*/

Is there a way to achieve that in a similar way without relying on a variable?

Upvotes: 1

Views: 138

Answers (1)

Syscall
Syscall

Reputation: 19764

To avoid the use of $p, you can pass $this in the call of the closure. But you can't use $this in an anonymous function.

$this->__data = array_merge($this->__data, $map($this));

Then,

->setAutoData(function ($object) {
    $array = explode(' ', $object->getData('full_name'));
    return [
        'first_name' => array_shift($array),
    ];
})

Note that array_shift requires a reference. You should create a variable for that.

Code:

class Person
{
    private $__data = [];

    public function setData($key, $value)
    {
        $this->__data[$key] = $value;
        return $this;
    }

    public function setAutoData($map)
    {
        $this->__data = array_merge($this->__data, $map($this));
        return $this;
    }

    public function getData($key)
    {
        return $this->__data[$key];
    }

}

print_r(
    (new Person())
    ->setData('full_name', 'Valerie Maddison Bricks')
    ->setAutoData(function ($object) {
        $array = explode(' ', $object->getData('full_name'));
        return [
            'first_name' => array_shift($array),
        ];
    })
);

Output:

Person Object
(
    [__data:Person:private] => Array
        (
            [full_name] => Valerie Maddison Bricks
            [first_name] => Valerie
        )

)

Upvotes: 2

Related Questions