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