web-dev-qa-db-fra.com

Chaînage de méthodes statiques en PHP?

Est-il possible de chaîner des méthodes statiques en utilisant une classe statique? Dites que je voulais faire quelque chose comme ça:

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

. . . et évidemment je voudrais que $ value se voit attribuer le numéro 14. Est-ce possible?

Mise à jour : Cela ne fonctionne pas (vous ne pouvez pas retourner "self" - ce n'est pas un exemple!), Mais c'est ici que mes pensées m'ont emmené:

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

Après avoir résolu ce problème, je pense qu'il serait plus logique de travailler simplement avec une instance de classe plutôt que d'essayer de chaîner des appels de fonctions statiques (ce qui ne semble pas possible, à moins que l'exemple ci-dessus ne soit modifié d'une manière ou d'une autre).

46
Wilco

J'aime la solution fournie par Camilo ci-dessus, essentiellement parce que tout ce que vous faites est de modifier la valeur d'un membre statique, et puisque vous voulez chaîner (même s'il ne s'agit que de sucre syntaxique), alors instancier TestClass est probablement la meilleure solution. .

Je suggérerais un modèle Singleton si vous souhaitez limiter l'instanciation de la 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();
44
Mathew Byrne
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();
39
Ariful Islam

Petit code fou sur php5.3 ... juste pour le 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();
31
sectus

Si toValue (x) retourne un objet, vous pouvez faire comme ceci:

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

Dans la mesure où toValue renvoie une nouvelle instance de l'objet, chaque méthode suivante la mue, renvoyant une instance de $ this.

9
Camilo Díaz Repka

Avec php7, vous pourrez utiliser la syntaxe de votre choix à cause du nouveau 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;

Démo

7
sectus

Vous pouvez toujours utiliser la méthode First en tant que méthode statique et les méthodes restantes en tant qu'instance:

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

Ou mieux encore:

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

Juste un FYI .. Je l'ai écrit sur le dessus de ma tête (ici sur le site). Donc, ça ne fonctionne peut-être pas, mais c'est l'idée. J'aurais aussi pu faire appel à eval par une méthode récursive, mais je pensais que cela pourrait être plus simple. S'il vous plaît laissez-moi savoir si vous souhaitez que je développe ou de fournir toute autre aide.

3
Phobis

Le meilleur que l'on puisse faire

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

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

Techniquement, vous pouvez appeler une méthode statique sur une instance telle que $object::method() dans PHP 7+, ainsi le renvoi d'une nouvelle instance devrait remplacer return self. Et en effet ça marche.

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

Sorties int(14)

Ceci revient au même que renvoyer __CLASS__ comme utilisé dans une autre réponse . J'espère plutôt que personne ne décidera jamais d'utiliser réellement ces formes d'API, mais vous l'avez demandé.

1
sanmai

C'est plus précis, plus facile et plus convivial (permet la complétion de code)

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

Exemple:

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

résultat: 0.63

En un mot ... non. :) L'opérateur de résolution (: :) fonctionnerait pour la partie TetsClass :: toValue (5), mais tout ce qui suit ne donnera qu'une erreur de syntaxe.

Une fois que les espaces de noms sont implémentés dans la version 5.3, vous pouvez avoir des opérateurs "chaînés" ::, mais tout ce que vous ferez sera de parcourir l’arborescence de l’espace de noms; il ne sera pas possible d'avoir des méthodes au milieu de choses comme celle-ci.

1
dirtside

La manière la plus simple que j'ai jamais trouvée pour le chaînage de méthodes à partir d'une nouvelle méthode de classe Instance ou Static est la suivante. J'ai utilisé Late Statique Binding ici et j'ai vraiment adoré cette solution.

J'ai créé un utilitaire pour envoyer plusieurs notifications d'utilisateur sur la page suivante en utilisant tostr dans 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);
    }
}

Cela fonctionnera comme ci-dessous.

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

    ->setOption('showDuration', 5000)

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

    ->error("Error Message");
0
Pratik Soni

Non, ça ne marchera pas. L'opérateur :: doit être réévalué dans une classe. Ainsi, après l'évaluation de TestClass::toValue(5), la méthode ::add(3) ne pourra évaluer que la réponse de la dernière.

Donc, si toValue(5) a renvoyé le nombre entier 5, vous devriez appeler int(5)::add(3), ce qui est évidemment une erreur.

0
jW.

Exemple entièrement fonctionnel de chaînage de méthodes avec attributs statiques:

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

Exemple d'utilisation:

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