Search code examples
phpmethodschaining

Global PHP functions chaining through a Class


Is it possible to chain all PHP functions through an object/class?

I have this on my mind and I imagine it something like this:

$c = new Chainer();

$c->strtolower('StackOverFlow')->ucwords(/* the value from the first function argument */)->str_replace('St', 'B', /* the value from the first function argument */);

this should produce:

Backoverflow

Thanks.


Solution

  • Do you mean to do str_replace('St', 'B', ucwords(strtolower('StackOverFlow'))) ?

    The methods you are calling above are functions, not methods tied to any class. Chainer would have to implement these methods. If this is what you want to do (perhaps for a different purpose and this is just an example) your implementation of Chainer might look like this:

    class Chainer {
       private $string;
       public function strtolower($string) {
          $this->string = strtolower($string);
          return $this;
       }
       public function ucwords() {
          $this->string = ucwords($this->string);
          return $this;
       }
       public function str_replace($from, $to) {
          $this->string = str_replace($from, $to, $this->string);
          return $this;
       }
       public function __toString() {
          return $this->string;
       }
    }
    

    This would work in your above example somewhat, but you would call it like this:

    $c = new Chainer;
    echo $c->strtolower('StackOverFlow')
       ->ucwords()
       ->str_replace('St', 'B')
    ; //Backoverflow
    

    Note that you never get the value of /* the value from the first function argument */ back out from the chain as this wouldn't make sense. Maybe you could do it with a global variable, but that would be quite hideous.

    The point is, you can chain methods by returning $this each time. The next method is called on the returned value which is the same object because you returned it (returned $this). It is important to know which methods start and stop the chain.

    I think that this implementation makes the most sense:

    class Chainer {
       private $string;
       public function __construct($string = '') {
          $this->string = $string;
          if (!strlen($string)) {
             throw new Chainer_empty_string_exception;
          }
       }
       public function strtolower() {
          $this->string = strtolower($this->string);
          return $this;
       }
       public function ucwords() {
          $this->string = ucwords($this->string);
          return $this;
       }
       public function str_replace($from, $to) {
          $this->string = str_replace($from, $to, $this->string);
          return $this;
       }
       public function __toString() {
          return $this->string;
       }
    }
    class Chainer_empty_string_exception extends Exception {
       public function __construct() {
          parent::__construct("Cannot create chainer with an empty string");
       }
    }
    
    try {
       $c = new Chainer;
       echo $c->strtolower('StackOverFlow')
          ->ucwords()
          ->str_replace('St', 'B')
       ; //Backoverflow
    }
    catch (Chainer_empty_string_exception $cese) {
       echo $cese->getMessage();
    }