views:

232

answers:

5

Is it possible to reflect upon a chain of method calls to determine at what point you are in the chain of calls? At the very least, is it possible to discern whether a method is the last call in the chain?

$instance->method1()->method2()->method3()->method4()

Is it possible to do the same using properties that return instances of objects?

$instances->property1->property2->property3->property4
A: 
$instances->property1->property2->property3->property4->method();

OR

$instances->property1->property2->property3->property4=some_value

As for the first question: not without adding some code to track where you are in the chain.

Itay Moav
+1  A: 

Hello.

For chained methods, you could use PHP5's overloading methods (__call in this case).

I don't see any reason why you would want to track chained properties, but if you insist on doing this, you could use the __get overloading method on your classes to add the desired functionality.

Please let me know if you couldn't figure out how to use the suggestions above.

Lior Cohen
This seems to be the best approach so far, albeit I still don't see why someone would want to track the position of a chained method/property call.
André Hoffmann
+1  A: 

debug_backtrace() is not going to be correct regarding the use of "fluent interfaces" (the proper name for the "chaining" illustrated), because each method returns, before the next one is called.

grantwparks
You are correct. Fixed the answer. Thank you for pointing this out.
Lior Cohen
A: 

I don't think there's a feasible way for a class to know when it's last method call was made. I think you'd need some kind of ->execute(); function call at the end of the chain.

Besides, enabling such functionality in my opinion would probably make the code too magical and surprise users and/or have buggy symptoms.

Mario
That's somewhat my intention at this point.Writing an ORM, magic is definitely on the menu!
Omega
+1  A: 

If all the methods you're calling are returning the same object to create the fluent interface (as opposed to chaining different objects together), it should be fairly trivial to record the method calls in the object itself.

eg:

class Eg {
    protected $_callStack = array();

    public function f1()
    {
        $this->_callStack[] = __METHOD__;
        // other work
    }

    public function f2()
    {
        $this->_callStack[] = __METHOD__;
        // other work
    }

    public function getCallStack()
    {
        return $this->_callStack;
    }
}

Then chaining the calls like

$a = new Eg;
$a->f1()->f2()->f1();

would leave the call stack like: array('f1', 'f2', 'f1');

Brenton Alker
This is one approach I considered, though it moves some of the data away from the returned objects, making it not quite so cohesive.The main problem remains that I still cannot determine when I'm at the end of a chain. Thus I'm stuck having something like: "$a->f1()->f2()->f1()->go();" to trigger the end of a chain.
Omega
Right, I don't think there will be any way to automatically detect the "end of chain".If you're trying to build up an SQL query (which is my best guess, given the other comments) then implementing the __toString magic method in the class could look at the call stack and produce the final SQL string. This is the way many view helpers in Zend Framework are implemented.So doing echo $a->f1()->f2()->f1(); (or any other function causing a caste to string) would implicitly call the __toString() method - doing the work of your go() method.
Brenton Alker