0

I have a class A and class B which inherits from class A, and I want to run some checks before I run the function.

class A {
  public class __call($name, $params) {
     if (method_exists?($this, $name)) {
       $result = call_user_func_array([$this, $name], $params);
       return $result;
     }
  }
}

class B {
  private function hello() {
    echo "Hello"
  }
}

I was expecting that when I call:

$b = new B();
$b->hello();

It will call __call and then execute private function hello, but it starts infinite loop, it looks that call_user_func_array triggers __call again. But the code works if I create hello function in class A

Is this expected behavior? Is there anything I can do to prevent this?

sad_jane
  • 23
  • 2

2 Answers2

0

After playing around for a while, it appears that you can make the hello function protected instead of private.

Your code has several small issues. See comments.

class A {
  public function __call($name, $params) {
    if (method_exists($this, "{$name}")) {
      $this->before();
      $result = call_user_func_array([$this, "{$name}"], $params);
      $this->after();
      return $result;
    }
  }
  private function before() {
    echo "before\n";
  }
  private function after() {
    echo "after\n";
  }
}

class B extends A {
  protected function hello() {
    echo "Hello\n";
  }
}

$b = new B();
$b->hello();

When I ran it, this is the result I got.

before
Hello
after

I ran it on PHP 7.0.8.

Short answer: Public methods in a parent class cannot call private methods in a child class.

You can also use a Trait.

trait Wrappable
{
  public function __call($name, $params) {
    if (method_exists($this, $name)) {
      $this->before();
      $result = call_user_func_array([$this, $name], $params);
      $this->after();
      return $result;
    }
  }
  private function before() {
    echo "before\n";
  }
  private function after() {
    echo "after\n";
  }

}

class A {

  use Wrappable;

  public function pub()
  {
    echo __METHOD__ . "\n";
  }

}

class B {
  use Wrappable;
  protected function hello() {
    echo "Hello\n";
  }

  protected function protHello()
  {
    echo __METHOD__ . "\n";
    $this->privHello();
  }
  protected function visibilityBridge($f, $a)
  {

  }
  private function privHello()
  {
    echo __METHOD__ . "\n";
  }
}
$a = new A();
$a->pub();
$b = new B();
$b->privHello();
ryantxr
  • 4,119
  • 1
  • 11
  • 25
  • the idea was to implement `before_action`, so it should be private, then __call can catch it, it is based on https://stackoverflow.com/questions/20826311/php-before-action-and-after-action?rq=1 – sad_jane May 18 '19 at 23:42
  • Make the hello function protected instead of private. – ryantxr May 19 '19 at 00:38
  • changing to `protected` worked, could elaborate more why it didn't work for `private` function – sad_jane May 19 '19 at 01:14
  • I don't know exactly. It looks as though `call_user_func_array` was re-triggering __call which called call_user_func_array and so on. __call is triggered when a function does not exist from the current scope. I conclude that when `call_user_func_array` called private `hello()`, that function was not visible and therefore it triggered __call again. – ryantxr May 19 '19 at 01:20
0

You need PHP 7+ for the array deconstruction.

class A {
  public function __call($name, $params) { //this is a function
     if (method_exists($this, $name)) { // Remove ?
       $result = $this->$name(...$params); //Really calls the function from the context
       return $result;
     }
     // As a suggestion you should throw an exception here for maintainability
  }
}

class B extends A { // You need 'extends A'
  private function hello() {
    echo "Hello"; // ;
  }
}

$b = new B();
$b->hello(); // Hello
Stefmachine
  • 382
  • 4
  • 11