A chain of static methods in PHP?

Is it possible to bind static methods together using a static class? Let's say I wanted to do something like this:

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

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

Update : it does not work (you cannot return "I" - this is not an instance!), But it was here that my thoughts accepted 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 that, I think it would be more prudent to just work with an instance of the class, rather than try to bind calls to static functions (which is not possible, unless the above example can be changed in some way).

+46
oop php method-chaining
Sep 24 '08 at 3:32
source share
14 answers

I like the solution Camilo provided above, in fact, since all you do is change the value of the static member, and since you want chains (although it's just syntactic sugar), creating an instance of TestClass is probably the best way.

I would suggest a Singleton template if you want to limit the instantiation of a class:

 class TestClass { public static $currentValue; private static $_instance = null; private function __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 self::$currentValue; } } // Example Usage: $result = TestClass::getInstance () ->toValue(5) ->add(3) ->subtract(2) ->add(8) ->result(); 
+43
Sep 24 '08 at 4:13
source share
 class oop{ public static $val; public static function add($var){ static::$val+=$var; return new static; } public static function sub($var){ static::$val-=$var; return new static; } public static function out(){ return static::$val; } public static function init($var){ static::$val=$var; return new static; } } echo oop::init(5)->add(2)->out(); 
+38
Aug 10 '12 at 18:35
source share

Little crazy php5.3 code ... just for fun.

 namespace chaining; class chain { static public function one() {return get_called_class();} static public function two() {return get_called_class();} } ${${${${chain::one()} = chain::two()}::one()}::two()}::one(); 
+31
Jul 05 '12 at 5:50
source share

If toValue (x) returns an object, you can do the following:

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

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

+9
Sep 24 '08 at 3:47 a.m.
source share

With php7 you can use the desired syntax thanks to the new unified variable syntax

 <?php abstract class TestClass { public static $currentValue; public static function toValue($value) { self::$currentValue = $value; return __CLASS__; } public static function add($value) { self::$currentValue = self::$currentValue + $value; return __CLASS__; } public static function subtract($value) { self::$currentValue = self::$currentValue - $value; return __CLASS__; } public static function result() { return self::$currentValue; } } $value = TestClass::toValue(5)::add(3)::subtract(2)::add(8)::result(); echo $value; 

demonstration

+7
02 Sep '15 at 2:48
source share

You can always use the first method as static, and the rest 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 FYI .. I wrote it from my head (right here on the site). Thus, it may not work, but it is an idea. I could also make a recursive call to the eval method, but I thought it might be easier. Please let me know if you want me to develop or provide any other assistance.

+3
Sep 24 '08 at 4:35
source share

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

After namespaces are implemented in 5.3, you can have chained :: operators, but all that will do is expand the name tree; it will not be possible to have methods in the middle of such things.

+1
Sep 24 '08 at 3:39
source share

The best you can do

 class S { public static function __callStatic($name,$args) { echo 'called S::'.$name . '( )<p>'; return '_t'; } } $_t='S'; ${${S::X()}::F()}::C(); 
+1
Aug 20 '13 at 19:41
source share

It is more accurate, simple and easy to read (allows code completion)

 class Calculator { public static $value = 0; protected static $onlyInstance; protected function __construct () { // disable creation of public instances } protected static function getself() { if (static::$onlyInstance === null) { static::$onlyInstance = new Calculator; } return static::$onlyInstance; } /** * add to value * @param numeric $num * @return \Calculator */ public static function add($num) { static::$value += $num; return static::getself(); } /** * substruct * @param string $num * @return \Calculator */ public static function subtract($num) { static::$value -= $num; return static::getself(); } /** * multiple by * @param string $num * @return \Calculator */ public static function multiple($num) { static::$value *= $num; return static::getself(); } /** * devide by * @param string $num * @return \Calculator */ public static function devide($num) { static::$value /= $num; return static::getself(); } public static function result() { return static::$value; } } 

Example:

 echo Calculator::add(5) ->subtract(2) ->multiple(2.1) ->devide(10) ->result(); 

result: 0.63

+1
Sep 02 '14 at 12:23
source share

No, that will not work. The :: operator must evaluate the class back, so after evaluating TestClass::toValue(5) the ::add(3) method could only evaluate the answer to the latter.

So, if toValue(5) returns the integer 5, you basically call int(5)::add(3) , which is obviously an error.

0
Sep 24 '08 at 3:40
source share

The easiest way I've ever found for a chain of methods from a new instance of the Stance or Static class is given below. I used Late Static Binding here, and I really liked this solution.

I created a utility to send multiple user notifications on the next page using tostr in Laravel.

 <?php namespace App\Utils; use Session; use Illuminate\Support\HtmlString; class Toaster { private static $options = [ "closeButton" => false, "debug" => false, "newestOnTop" => false, "progressBar" => false, "positionClass" => "toast-top-right", "preventDuplicates" => false, "onclick" => null, "showDuration" => "3000", "hideDuration" => "1000", "timeOut" => "5000", "extendedTimeOut" => "1000", "showEasing" => "swing", "hideEasing" => "linear", "showMethod" => "fadeIn", "hideMethod" => "fadeOut" ]; private static $toastType = "success"; private static $instance; private static $title; private static $message; private static $toastTypes = ["success", "info", "warning", "error"]; public function __construct($options = []) { self::$options = array_merge(self::$options, $options); } public static function setOptions(array $options = []) { self::$options = array_merge(self::$options, $options); return self::getInstance(); } public static function setOption($option, $value) { self::$options[$option] = $value; return self::getInstance(); } private static function getInstance() { if(empty(self::$instance) || self::$instance === null) { self::setInstance(); } return self::$instance; } private static function setInstance() { self::$instance = new static(); } public static function __callStatic($method, $args) { if(in_array($method, self::$toastTypes)) { self::$toastType = $method; return self::getInstance()->initToast($method, $args); } throw new \Exception("Ohh my god. That toast doesn't exists."); } public function __call($method, $args) { return self::__callStatic($method, $args); } private function initToast($method, $params=[]) { if(count($params)==2) { self::$title = $params[0]; self::$message = $params[1]; } elseif(count($params)==1) { self::$title = ucfirst($method); self::$message = $params[0]; } $toasters = []; if(Session::has('toasters')) { $toasters = Session::get('toasters'); } $toast = [ "options" => self::$options, "type" => self::$toastType, "title" => self::$title, "message" => self::$message ]; $toasters[] = $toast; Session::forget('toasters'); Session::put('toasters', $toasters); return $this; } public static function renderToasters() { $toasters = Session::get('toasters'); $string = ''; if(!empty($toasters)) { $string .= '<script type="application/javascript">'; $string .= "$(function() {\n"; foreach ($toasters as $toast) { $string .= "\n toastr.options = " . json_encode($toast['options'], JSON_PRETTY_PRINT) . ";"; $string .= "\n toastr['{$toast['type']}']('{$toast['message']}', '{$toast['title']}');"; } $string .= "\n});"; $string .= '</script>'; } Session::forget('toasters'); return new HtmlString($string); } } 

This will work as shown below.

 Toaster::success("Success Message", "Success Title") ->setOption('showDuration', 5000) ->warning("Warning Message", "Warning Title") ->error("Error Message"); 
0
Jun 13 '17 at 13:43 on
source share

Technically, you can call a static method on an instance, such as $object::method() in PHP 7+, so returning a new instance should work as a replacement for return self . And really, it works.

 final class TestClass { public static $currentValue; public static function toValue($value) { self::$currentValue = $value; return new static(); } public static function add($value) { self::$currentValue = self::$currentValue + $value; return new static(); } public static function subtract($value) { self::$currentValue = self::$currentValue - $value; return new static(); } public static function result() { return self::$currentValue; } } $value = TestClass::toValue(5)::add(3)::subtract(2)::add(8)::result(); var_dump($value); 

Outputs int(14) .

This is about the same as returning __CLASS__ as used in another answer . I rather hope that no one ever decides to use these forms of the API, but you asked for it.

0
Oct 21 '18 at 4:42
source share

A fully functional example of a chain of methods with static attributes:

 <?php class Response { static protected $headers = []; static protected $http_code = 200; static protected $http_code_msg = ''; static protected $instance = NULL; protected function __construct() { } static function getInstance(){ if(static::$instance == NULL){ static::$instance = new static(); } return static::$instance; } public function addHeaders(array $headers) { static::$headers = $headers; return static::getInstance(); } public function addHeader(string $header) { static::$headers[] = $header; return static::getInstance(); } public function code(int $http_code, string $msg = NULL) { static::$http_code_msg = $msg; static::$http_code = $http_code; return static::getInstance(); } public function send($data, int $http_code = NULL){ $http_code = $http_code != NULL ? $http_code : static::$http_code; if ($http_code != NULL) header(trim("HTTP/1.0 ".$http_code.' '.static::$http_code_msg)); if (is_array($data) || is_object($data)) $data = json_encode($data); echo $data; exit(); } function sendError(string $msg_error, int $http_code = null){ $this->send(['error' => $msg_error], $http_code); } } 

Usage example:

 Response::getInstance()->code(400)->sendError("Lacks id in request"); 
0
Jan 11 '19 at 3:14
source share

Use PHP 7! If your web provider cannot → change provider! Do not lock yourself in the past.

 final class TestClass { public static $currentValue; public static function toValue($value) { self::$currentValue = $value; return __CLASS__; } public static function add($value) { self::$currentValue = self::$currentValue + $value; return __CLASS__; } public static function subtract($value) { self::$currentValue = self::$currentValue - $value; return __CLASS__; } public static function result() { return self::$currentValue; } } 

And a very simple use:

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

Return (or generate an error):

 int(14) 

completed contract.

There is only one rule: the most developed and serviced is always better.

-one
Mar 05 '18 at 15:12
source share



All Articles