Pergunta

É possível métodos estáticos cadeia em conjunto, utilizando uma classe estática? Digamos que eu queria fazer algo como isto:

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

. . . e, obviamente, eu gostaria $ valor a ser atribuído o número 14. Isso é possível?

Atualizar : Não funciona (você pode não voltar "eu" - não é uma instância!), Mas este é o lugar onde meus pensamentos me levaram:

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

Depois de trabalhar isso, eu acho que seria apenas fazer mais sentido do que simplesmente trabalhar com uma instância de classe em vez de tentar chamadas de função cadeia estáticos (que não parece possível, a menos que o exemplo acima pode ser ajustado de alguma forma).

Foi útil?

Solução

Eu gosto da solução fornecida pela Camilo acima, essencialmente, pois tudo que você está fazendo é alterar o valor de um membro estático, e desde que você quer encadeamento (mesmo que seja açúcar única sintática), então instanciar TestClass é provavelmente a melhor caminho a percorrer.

Eu sugiro um padrão Singleton se você quiser restringir instanciação da classe:

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

Outras dicas

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

código louco pequeno em PHP5.3 ... só por diversão.

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

Com php7 você será capaz de usar a sintaxe desejado por causa do novo Variável Uniforme Sintaxe

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

Demonstração

Se toValue (x) retorna um objeto, você pode fazer assim:

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

Desde que toValue retorna uma nova instância do objeto, e cada próximos mutates método que ele, retornando uma instância de $ this.

Você pode sempre usar o primeiro método como um estático eo restante como métodos de instância:

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

Ou ainda melhor:

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

Apenas um FYI .. Eu escrevi este em cima da minha cabeça (aqui no site). Então, ele não pode correr, mas essa é a idéia. Eu também poderia ter fez uma chamada de método recursivo para eval, mas eu pensei que isso pode ser mais simples. Por favor, deixe-me saber se você gostaria de me de elaborar ou fornecer qualquer outro tipo de ajuda.

Tecnicamente, você pode chamar um método estático em uma instância como $object::method() em PHP 7+, então retornar uma nova instância deve funcionar como um substituto para return self. E na verdade ele funciona.

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

Saídas int(14).

Este cerca mesma como retornando __CLASS__ como usado em outra resposta . Prefiro espero nunca ninguém decide realmente usar estas formas de API, mas você pediu para ele.

Em poucas palavras ... não. :) O operador de resolução. (: :) iria trabalhar para o TetsClass :: toValue (5) parte, mas tudo depois que só vai dar um erro de sintaxe

Uma vez que namespaces são implementados no 5.3, você pode ter "acorrentado" :: operadores, mas tudo o que vai fazer é perfurar para baixo através da árvore de namespace; não será possível ter métodos no meio de coisas como esta.

O melhor que pode ser feito

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

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

Este é mais preciso, mais fácil, e leia-friendly (permite que o código-conclusão)

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

Exemplo:

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

resultado: 0,63

Não, isso não vai funcionar. As necessidades do operador :: para avaliar de volta para uma classe, então depois os avalia TestClass::toValue(5), o método ::add(3) só seria capaz de avaliar a resposta da última.

Então, se toValue(5) retornou o inteiro 5, você iria ser basicamente chamando int(5)::add(3) que, obviamente, é um erro.

A maneira mais fácil que eu já encontrei para encadeamento método da nova instância ou método estático da classe é como abaixo. Eu tenho usado atrasado vinculação estática aqui e eu realmente amei esta solução.

Eu criei um utilitário para enviar múltiplos Notificação do usuário na próxima página usando tostr em 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);
    }
}

Isto irá funcionar como abaixo.

Toaster::success("Success Message", "Success Title")

    ->setOption('showDuration', 5000)

    ->warning("Warning Message", "Warning Title")

    ->error("Error Message");

exemplo totalmente funcional do método de encadeamento com atributos estáticos:

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

Exemplo de utilização:

Response::getInstance()->code(400)->sendError("Lacks id in request");

Use PHP 7! Se o seu provedor de web não pode -> provedor de mudança! Não bloquear passado.

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

E uso muito simples:

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

var_dump($value);

Return (ou erro de lance):

int(14)

contrato concluído.

Regra número um: mais evoluído e sustentável é sempre melhor.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top