Цепочка статических методов в PHP?

Можно ли объединить статические методы вместе, используя статический класс? Скажем, я хотел сделать что-то вроде этого:

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

, , , и, очевидно, я бы хотел, чтобы $ value было присвоено число 14. Возможно ли это?

Обновление : оно не работает (вы не можете вернуть «я» – это не экземпляр!), Но именно здесь мои мысли приняли меня:

 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; } } 

После того, как я это выяснил, я думаю, что просто нужно просто работать с экземпляром класса, а не пытаться связать статические вызовы функций (что не представляется возможным, если только вышеприведенный пример не может быть изменен каким-то образом).

Мне нравится решение, предоставленное Camilo выше, по сути, потому что все, что вы делаете, это изменение значения статического члена, и поскольку вы хотите цепочки (хотя это только синтаксический сахар), то, вероятно, лучший экземпляр TestClass ,

Я бы предложил шаблон Singleton, если вы хотите ограничить создание экземпляра класса:

 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(); 
 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(); 

Маленький сумасшедший код на php5.3 … просто для удовольствия.

 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(); 

Если toValue (x) возвращает объект, вы можете сделать следующее:

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

При условии, что toValue возвращает новый экземпляр объекта, и каждый следующий метод мутирует его, возвращая экземпляр $ this.

С помощью php7 вы сможете использовать желаемый синтаксис из-за нового синтаксиса Uniform Variable

 <?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; 

демонстрация

Вы всегда можете использовать первый метод как статический, а остальные – как методы экземпляра:

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

Или еще лучше:

  $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); } } 

Просто FYI .. Я написал это с моей головы (прямо здесь, на сайте). Таким образом, он может не работать, но это идея. Я мог бы также сделать рекурсивный вызов метода eval, но я подумал, что это может быть проще. Пожалуйста, дайте мне знать, если вы хотите, чтобы я разработал или предоставил любую другую помощь.

В двух словах … нет. 🙂 Оператор разрешения (: 🙂 будет работать для части TetsClass :: toValue (5), но все после этого просто даст синтаксическую ошибку.

После того, как пространства имен реализованы в 5.3, вы можете иметь «прикованные» :: операторы, но все, что будет сделано, – это развернуть дерево имен; невозможно будет иметь методы в середине таких вещей.

Лучшее, что можно сделать

 class S { public static function __callStatic($name,$args) { echo 'called S::'.$name . '( )<p>'; return '_t'; } } $_t='S'; ${${S::X()}::F()}::C(); 

Это более точная, простая и понятная для чтения (позволяет выполнить завершение кода)

 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; } } 

Пример:

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

результата: 0,63

Нет, это не сработает. Оператор TestClass::toValue(5) .

Поэтому, если toValue(5) вернул целое число 5, вы в основном будете вызывать int(5)::add(3) что, очевидно, является ошибкой.

Самый простой способ, который я когда-либо нашел для цепочки методов из нового экземпляра класса Instance или Static, приведен ниже. Я использовал Late Static Binding здесь, и мне действительно понравилось это решение.

Я создал утилиту для отправки нескольких уведомлений пользователей на следующей странице, используя tostr в 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); } } 

Это будет работать следующим образом.

 Toaster::success("Success Message", "Success Title") ->setOption('showDuration', 5000) ->warning("Warning Message", "Warning Title") ->error("Error Message");