Search code examples
phpmagic-methodsmethod-chaining

Is it allowed to chain invoke method in PHP?


for example, I have following:

class A{
  __invoke(){
    // return an instance of class A itself
  }
}

so can I do the following?

new A()()()()... or (new A())()()()...

and what is the explanation here? suppose PHP version is newer than 5.4

Ok, I may give a little more explanation why I ask: I was using ganon.php which is an open source html dom parser. It is using syntax like $html_node('child_tag') to return another child $html_node where $html_node is an object instance of class HTML_NODE. So I was thinking if I could use chaining to do the selection in a nested html dom structure.


Solution

  • I don't think that the behaviour you describe actually works, even in PHP/7:

    class A{
        public function __invoke($arg){
            echo __METHOD__ . "($arg) called" . PHP_EOL;
        }
    }
    
    $a = new A();
    $a(0);
    $a(1)(2)(3);
    
    A::__invoke(0) called
    A::__invoke(1) called
    Fatal error: Function name must be a string
    

    (demo)

    You're probably confused by the variable functions feature. If foo() returns a 'bar' string, then foo()() equals bar():

    class A{
        public function __invoke(){
            return 'hello';
        }
    }
    
    function hello($name) {
        echo "Hello, $name!" . PHP_EOL;
    }
    
    $a = new A();
    $a()('Jim');
    
    Hello, Jim!
    

    (demo)

    You can chain that as long as your functions return more strings with valid function names, but neither __invoke nor classes play any relevant role:

    function one() {
        return 'two';
    }
    
    function two() {
        return 'three';
    }
    
    function three() {
        return 'four';
    }
    
    function four() {
        echo 'Done!';
    }
    
    $a = one()()()();
    
    Done!
    

    (demo)

    Note: all code snippets above require PHP/7 but they could be emulated with earlier versions just using the appropriate parenthesis and intermediate variables.


    Update based on UlrichEckhardt's comment: I overlooked the return an instance of class A itself comment. If you actually do so, code does work:

    class A{
        public function __invoke($arg){
            echo __METHOD__ . "($arg) called" . PHP_EOL;
            return $this;
        }
    }
    
    $a = new A();
    $a(0);
    $a(1)(2)(3);
    

    class A{
        public function __invoke($arg){
            echo __METHOD__ . "($arg) called" . PHP_EOL;
            return $this;
        }
    }
    
    $a = new A();
    $a(0);
    $a(1)(2)(3);
    

    (demo)

    Of course, this is PHP/7 syntax. For older versions, you need auxiliary variables that break the magic:

    $a = new A();
    $b = $a(1);
    $c = $b(2);
    $d = $c(3);
    $d(4);