Связывание статических методов в PHP?

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

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

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

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

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
57
0
27 712
16
Перейти к ответу Данный вопрос помечен как решенный

Ответы 16

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

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

Нет, это не сработает. Оператор :: должен возвращать оценку классу, поэтому после оценки TestClass::toValue(5) метод ::add(3) сможет оценить только ответ последнего.

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

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

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

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

Ответ принят как подходящий

Мне нравится решение, предоставленное 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();

public function result() { return $this::$value; } эта линия должна быть public function result() { return $this::$currentValue; } ????

Val 14.01.2010 17:16

Это не сработает, если вы захотите использовать более одного вместе. $a = TestClass::getInstance()->toValue(3)->add(5);$b = TestClass::getInstance()->toValue(7)->add($a->result());echo $b->result(); Вы получите 14 вместо 15. Не обращайтесь с деньгами с этой математикой.

Glenn Moss 14.06.2011 02:56

Определение конструктора private __construct () { } должно быть private function __construct () { }. Кроме того, return $this::$currentValue; должен быть return self::$currentValue;.

Glenn Moss 14.06.2011 02:59

Я также обнаружил, что это не цепочка переданного одного метода. Также это не сработает, если вы используете ключевое слово final в своем классе.

Kevin Beal 16.01.2014 00:56

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

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

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

Маленький сумасшедший код на 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();

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

Я думаю, что когда дело касается синтаксиса (а не лучших практик программирования), это лучший ответ на этот вопрос.

nmirceac 26.01.2020 13:19

В этом ответе отсутствует образовательное объяснение.

mickmackusa 29.11.2020 00:02

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

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

$_t='S';
${${S::X()}::F()}::C();

это путь назад php5.4?

ichimaru 19.06.2018 09:02

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

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

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

Демо

Самый простой способ, который я когда-либо находил для связывания методов из нового экземпляра или статического метода класса, приведен ниже. Я использовал здесь 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");

Используйте 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)

завершенный договор.

Правило первое: самое развитое и удобное в обслуживании всегда лучше.

Технически вы можете вызвать статический метод для такого экземпляра, как $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, но вы просили об этом.

Полнофункциональный пример объединения методов со статическими атрибутами:

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

Люди слишком усложняют это, как сумасшедшие.

Проверь это:

class OopClass
{
    public $first;
    public $second;
    public $third;

    public static function make($first)
    {
        return new OopClass($first);
    }

    public function __construct($first)
    {
        $this->first = $first;
    }

    public function second($second)
    {
        $this->second = $second;
        return $this;
    }

    public function third($third)
    {
        $this->third = $third;
        return $this;
    }
}

Использование:

OopClass::make('Hello')->second('To')->third('World');

Вот еще один способ без использования метода getInstance (протестирован на PHP 7.x):

class TestClass
{
    private $result = 0;

    public function __call($method, $args)
    {
        return $this->call($method, $args);
    }

    public static function __callStatic($method, $args)
    {
        return (new static())->call($method, $args);
    }

    private function call($method, $args)
    {
        if (! method_exists($this , '_' . $method)) {
            throw new Exception('Call undefined method ' . $method);
        }

        return $this->{'_' . $method}(...$args);
    }

    private function _add($num)
    {
        $this->result += $num;

        return $this;
    }

    private function _subtract($num)
    {
        $this->result -= $num;

        return $this;
    }

    public function result()
    {
        return $this->result;
    }
}

Класс можно использовать следующим образом:

$res1 = TestClass::add(5)
    ->add(3)
    ->subtract(2)
    ->add(8)
    ->result();

echo $res1 . PHP_EOL; // 14

$res2 = TestClass::subtract(1)->add(10)->result();
echo $res2 . PHP_EOL; // 9

Другие вопросы по теме