views:

732

answers:

5

Is it possible to chain static methods together using a static class? Say I wanted to do something like this:

$value = TestClass::toValue(5)::add(3)::subtract(2)::add(8)::result();

. . . and obviously I would want $value to be assigned the number 14. Is this possible?

Update: It doesn't work (you can't return "self" - it's not an instance!), but this is where my thoughts have taken me:

class TestClass {
    public static $currentValue;

    public static function toValue($value) {
     self::$currentValue = $value;
    }

    public static function add($value) {
     self::$currentValue = self::$currentValue + $value;
     return self;
    }

    public static function subtract($value) {
     self::$currentValue = self::$currentValue - $value;
     return self;
    }

    public static function result() {
     return self::$value;
    }
}

After working that out, I think it would just make more sense to simply work with a class instance rather than trying to chain static function calls (which doesn't look possible, unless the above example could be tweaked somehow).

A: 

In a nutshell... no. :) The resolution operator (::) would work for the TetsClass::toValue(5) part, but everything after that will just give a syntax error.

Once namespaces are implemented in 5.3, you can have "chained" :: operators, but all that'll do is drill down through the namespace tree; it won't be possible to have methods in the middle of things like this.

dirtside
A: 

No, this won't work. The :: operator needs to evaluate back to a class, so after the TestClass::toValue(5) evaluates, the ::add(3) method would only be able to evaluate on the answer of the last one. So if toValue(5) returned the integer 5, you would basically be calling int(5)::add(3) which obviously is an error.

jW
+5  A: 

If toValue(x) returns an object, you could do like this:

$value = TestClass::toValue(5)->add(3)->substract(2)->add(8);

Providing that toValue returns a new instance of the object, and each next method mutates it, returning an instance of $this.

Camilo Díaz
+6  A: 

I like the solution provided by Camilo above, essentially since all you're doing is altering the value of a static member, and since you do want chaining (even though it's only syntatic sugar), then instantiating TestClass is probably the best way to go.

I'd suggest a Singleton pattern if you want to restrict instantiation of the class:

class TestClass
{   
    public static $currentValue;

    private static $_instance = null;

    private __construct () { }

    public static function getInstance ()
    {
        if (self::$_instance === null) {
            self::$_instance = new self;
        }

        return self::$_instance;
    }

    public function toValue($value) {
        self::$currentValue = $value;
        return $this;
    }

    public function add($value) {
        self::$currentValue = self::$currentValue + $value;
        return $this;
    }

    public function subtract($value) {
        self::$currentValue = self::$currentValue - $value;
        return $this;
    }

    public function result() {
        return $this::$currentValue;
    }
}

// Example Usage:
$result = TestClass::getInstance ()
    ->toValue (5)
    ->add (3)
    ->subtract (2)
    ->add (8)
    ->result ();
Mathew Byrne
Just finished implementing this method and it works perfectly!
Wilco
`public function result() { return $this::$value; }` is this line meant to be `public function result() { return $this::$currentValue; }` ????
Val
Thanks, updated.
Mathew Byrne
+1  A: 

You could always use the First method as a static and the remaining as instance methods:

$value = Math::toValue(5)->add(3)->subtract(2)->add(8)->result();

Or better yet:

 $value = Math::eval(Math::value(5)->add(3)->subtract(2)->add(8));

class Math {
     public $operation;
     public $operationValue;
     public $args;
     public $allOperations = array();

     public function __construct($aOperation, $aValue, $theArgs)
     {
       $this->operation = $aOperation;
       $this->operationValue = $aValue;
       $this->args = $theArgs;
     }

     public static function eval($math) {
       if(strcasecmp(get_class($math), "Math") == 0){
            $newValue = $math->operationValue;
            foreach ($math->allOperations as $operationKey=>$currentOperation) {
                switch($currentOperation->operation){
                    case "add":
                         $newvalue = $currentOperation->operationValue + $currentOperation->args;
                         break;
                    case "subtract":
                         $newvalue = $currentOperation->operationValue - $currentOperation->args;
                         break;
                }
            }
            return $newValue;
       }
       return null;
     }

     public function add($number){
         $math = new Math("add", null, $number);
         $this->allOperations[count($this->allOperations)] &= $math;
         return $this;
     }

     public function subtract($number){
         $math = new Math("subtract", null, $number);
         $this->allOperations[count($this->allOperations)] &= $math;
         return $this;
     }

     public static function value($number){
         return new Math("value", $number, null);
     }
 }

Just an FYI.. I wrote this off the top of my head (right here on the site). So, it may not run, but that is the idea. I could have also did a recursive method call to eval, but I thought this may be simpler. Please let me know if you would like me to elaborate or provide any other help.

Phobis