Цепочка статических методов в 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;
}
}
После этого я думаю, что было бы просто разумнее просто работать с экземпляром класса, а не пытаться связать вызовы статических функций (что не представляется возможным, если только приведенный выше пример не может быть изменен каким-то образом).
Ответы
Ответ 1
Мне нравится решение, предоставленное 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();
Ответ 2
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();
Ответ 3
Маленький сумасшедший код на 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();
Ответ 4
Если toValue (x) возвращает объект, вы можете сделать следующее:
$value = TestClass::toValue(5)->add(3)->substract(2)->add(8);
Обеспечение того, что toValue возвращает новый экземпляр объекта, и каждый следующий метод его мутирует, возвращая экземпляр $this.
Ответ 5
С php7 вы сможете использовать желаемый синтаксис благодаря новому унифицированному синтаксису переменной
<?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;
демонстрация
Ответ 6
Вы всегда можете использовать первый метод как статический, а остальные - как методы экземпляра:
$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, но я подумал, что это может быть проще. Пожалуйста, дайте мне знать, если вы хотите, чтобы я разработал или предоставил любую другую помощь.
Ответ 7
В двух словах... нет.:) Оператор разрешения (::) будет работать для части TetsClass:: toValue (5), но все после этого просто даст синтаксическую ошибку.
После того, как пространства имен реализованы в 5.3, вы можете иметь "прикованные":: операторы, но все, что будет делать, - это развернуть дерево имен; не будет возможности иметь методы в середине таких вещей.
Ответ 8
Лучшее, что можно сделать
class S
{
public static function __callStatic($name,$args)
{
echo 'called S::'.$name . '( )<p>';
return '_t';
}
}
$_t='S';
${${S::X()}::F()}::C();
Ответ 9
Это более точный, простой и удобный для чтения (позволяет выполнить завершение кода)
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
Ответ 10
Нет, это не сработает. Оператор ::
должен оценивать обратно класс, поэтому после оценки TestClass::toValue(5)
метод ::add(3)
мог бы только оценить ответ на последний.
Итак, если toValue(5)
возвращает целое число 5, вы в основном вызываете int(5)::add(3)
, который, очевидно, является ошибкой.
Ответ 11
Самый простой способ, который я когда-либо нашел для цепочки методов из нового экземпляра класса Stance или 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");
Ответ 12
Технически вы можете вызвать статический метод для экземпляра, такого как $object::method()
в PHP 7+, поэтому возврат нового экземпляра должен работать как замена для return self
. И действительно, это работает.
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);
Выходы int(14)
.
Это примерно так же, как возвращение __CLASS__
как используется в другом ответе. Я скорее надеюсь, что никто никогда не решит использовать эти формы API, но вы просили об этом.
Ответ 13
Полнофункциональный пример цепочки методов со статическими атрибутами:
<?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);
}
}
Пример использования:
Response::getInstance()->code(400)->sendError("Lacks id in request");
Ответ 14
Используйте PHP 7! Если ваш веб-провайдер не может → сменить провайдера! Не запирайся в прошлом.
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;
}
}
И очень простое использование:
$value = TestClass::toValue(5)::add(3)::subtract(2)::add(8)::result();
var_dump($value);
Вернуть (или сгенерировать ошибку):
int(14)
завершенный контракт.
Правило одно: наиболее развитый и обслуживаемый всегда лучше.