Chaining Static Methods in PHP?

前端 未结 16 1949
情歌与酒
情歌与酒 2020-11-27 14:25

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)::subtrac         


        
相关标签:
16条回答
  • 2020-11-27 14:43

    The most easiest way i have ever found for method chaining from new Instance or Static method of class is as below. I have used Late Static Binding here and i really loved this solution.

    I have created a utility to send multiple User Notification on 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 below.

    Toaster::success("Success Message", "Success Title")
    
        ->setOption('showDuration', 5000)
    
        ->warning("Warning Message", "Warning Title")
    
        ->error("Error Message");
    
    0 讨论(0)
  • 2020-11-27 14:44

    This is more accurate, easier, and read-friendly (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

    0 讨论(0)
  • 2020-11-27 14:45

    Little crazy code on php5.3... 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();
    
    0 讨论(0)
  • 2020-11-27 14:48

    With php7 you will be able to use desired syntax because of new Uniform 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;
    

    Demo

    0 讨论(0)
  • 2020-11-27 14:48

    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.

    0 讨论(0)
  • 2020-11-27 14:52

    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.

    0 讨论(0)
提交回复
热议问题