rap-2-h
rap-2-h

Reputation: 31948

call_user_func() : cannot access private method

This code does not work:

class A {
    public function b() {
        (new B)->d([$this, 'c']); // Error is here
    }
    private function c() {
        echo 'test';
    }
}

class B {
    public function d($e) {
        call_user_func($e);
    }
}

(new A)->b();

Running this code gives:

call_user_func() [...] cannot access private method A::c()

I can understand why it does not work. But I don't know the real reason. In A::b(), I'm in A context, that's why I thought c() can be called ($this is well known and do refers to A, so I'm in A context).

What is really weird for me is that it works if I replace:

(new B)->d([$this, 'c']);

By:

(new B)->d(function() { $this->c(); });

So it's not a problem to call a private function now. I can't understand why. And I did not found anything in doc about this.

Why (new B)->d(function() { $this->c(); }); works but not (new B)->d([$this, 'c']); when I call (new A)->b();?

Upvotes: 4

Views: 4054

Answers (2)

Zeus
Zeus

Reputation: 1275

When you use [$this, 'c'] an array is passed with index [0] as reference of object of class A and index [1] as a string c, so it tries to call the function from method d using string stored in array index [1].

When you pass the function directly a closure is passed which is binded to class A so it can be used to call function c as it is binded to class A.

However you can use the first method too and not get any error by a little trick like this :

class A {
    public function b() {
        (new B)->d([$this, 'c']);// NO ERROR NOW!
    }
    private function c() {
        echo 'test';
    }
}

class B {
    public function d($e) {     
        // Here is the trick :          
        $f = function() { $this->c(); };
        $f = Closure::bind($f, $e[0], 'A');                 
        $f();
    }
}

(new A)->b();

What I did was create a closure and then bind using ::bind it to class A using the object reference received in $e[0].

Upvotes: 1

realmuster
realmuster

Reputation: 156

I'd say if you use:

$this->c();

it gets executed right there, so from the class A, which has this function. if you do it like:

[$this, 'c']

you pass the class and the function that should be executed by class B, and it will also be executed by class B, but class B cannot access the private function of class A.

Regards

Upvotes: 0

Related Questions