Czy można tworzyć klasy statyczne w PHP (jak w C #)?

139

Chcę utworzyć klasę statyczną w PHP i sprawić, by zachowywała się tak, jak w C #, więc

  1. Konstruktor jest wywoływany automatycznie przy pierwszym wywołaniu klasy
  2. Nie jest wymagana żadna instancja

Coś w tym rodzaju ...

static class Hello {
    private static $greeting = 'Hello';

    private __construct() {
        $greeting .= ' There!';
    }

    public static greet(){
        echo $greeting;
    }
}

Hello::greet(); // Hello There!
aleemb
źródło
Czy mógłbyś pokrótce wyjaśnić, jak powinna zachowywać się klasa statyczna? Czy jest to wdrożenie narzędzia?
xtofl
Po prostu rzucam własną opinię, ale z mojego doświadczenia w PHP, ze względu na rozsądek, testowalność i skalowalność, klasy statyczne powinny być prawie całkowicie bezstanowe, prezentować bardziej funkcjonalny interfejs programowania podobny do programowania niż zorientowany obiektowo i generalnie są najlepiej używać jako fasady ułatwień dostępu dla w pełni utworzonych obiektów lub opakowań narzędzi dla pomocników lub podobnych konstrukcji, jeśli w ogóle są używane.
mopsyd

Odpowiedzi:

200

Możesz mieć statyczne klasy w PHP, ale nie wywołują one konstruktora automatycznie (jeśli spróbujesz wywołać self::__construct(), pojawi się błąd).

Dlatego musiałbyś stworzyć initialize()funkcję i wywołać ją w każdej metodzie:

<?php

class Hello
{
    private static $greeting = 'Hello';
    private static $initialized = false;

    private static function initialize()
    {
        if (self::$initialized)
            return;

        self::$greeting .= ' There!';
        self::$initialized = true;
    }

    public static function greet()
    {
        self::initialize();
        echo self::$greeting;
    }
}

Hello::greet(); // Hello There!


?>
Greg
źródło
20
Dość często robię to tylko po to, aby umieścić wszystkie funkcje w jednym miejscu. Narzędzie IE :: doSomethingUseful ();
smack0007
16
Zamiast Therefore you'd have to create an initialize() function and call it in each method:tego łatwiej byłoby stworzyć initializefunkcję publiczną i wywołać ją zaraz po deklaracji klasy.
chacham15
4
Wiem, że to jest dość stare, ale teraz możesz użyć magic __callStatic, więc kiedy wywołujesz jakąkolwiek statyczną metodę lub cokolwiek, najpierw wywoła __callStatic, tam możesz zobaczyć, czy została zainicjowana, a następnie zrobić, self::$methodczy cokolwiek dzwonisz. Jeśli nadal wywołuje metodę bezpośrednio, spróbuj zmienić wszystko na prywatne i zobacz tam.
matiaslauriti
1
Co się stanie, jeśli dwa wątki wywołają powitanie w tym samym czasie? Ponieważ nie ma synchronizacji, inicjalizacja nie zostanie wywołana dwukrotnie (co w tym przypadku jest w porządku, ale w wielu innych przypadkach nie). A może php jest jednowątkowy i nie wywodzi jak węzeł?
John Little
53

Oprócz odpowiedzi Grega zalecałbym ustawienie konstruktora jako prywatnego, aby nie było możliwe utworzenie instancji klasy.

Moim skromnym zdaniem jest to bardziej kompletny przykład oparty na przykładzie Grega:

<?php

class Hello
{
    /**
     * Construct won't be called inside this class and is uncallable from
     * the outside. This prevents instantiating this class.
     * This is by purpose, because we want a static class.
     */
    private function __construct() {}
    private static $greeting = 'Hello';
    private static $initialized = false;

    private static function initialize()
    {
        if (self::$initialized)
            return;

        self::$greeting .= ' There!';
        self::$initialized = true;
    }

    public static function greet()
    {
        self::initialize();
        echo self::$greeting;
    }
}

Hello::greet(); // Hello There!


?>
Phil
źródło
1
To świetne podejście, funkcja konstruowania nie może jednak zostać zaimplementowana, jeśli twój singelton dziedziczy z pewnych obiektów, które wymagają publicznego konstruktora.
Eric Herlitz
4
@EricHerlitz To pytanie nie dotyczy singletonów, chodzi o klasy statyczne. Dlaczego miałbyś chcieć utworzyć klasę statyczną, która dziedziczy po klasie, która ma zostać utworzona?
Mark Amery,
3
Deklarowanie klasy jako abstrakcyjnej w równym stopniu zapobiega jej tworzeniu i nadal zezwala na wywołania metod statycznych.
bstoney
24

możesz mieć te „statyczne” klasy. ale przypuszczam, że brakuje czegoś naprawdę ważnego: w php nie masz cyklu aplikacji, więc nie dostaniesz prawdziwego statycznego (lub pojedynczego) w całej aplikacji ...

zobacz Singleton w PHP

Andreas Niedermair
źródło
1
Klasy statyczne i singletony to tylko dwie różne rzeczy.
Max Cuttins
4
final Class B{

    static $staticVar;
    static function getA(){
        self::$staticVar = New A;
    }
}

struktura b jest nazywana pojedynczym handlerem, możesz to również zrobić w a

Class a{
    static $instance;
    static function getA(...){
        if(!isset(self::$staticVar)){
            self::$staticVar = New A(...);
        }
        return self::$staticVar;
    }
}

to jest użycie singletona $a = a::getA(...);

borrel
źródło
3

Generalnie wolę pisać zwykłe niestatyczne klasy i używać klasy fabrycznej do tworzenia instancji pojedynczych (sudo static) instancji obiektu.

W ten sposób konstruktor i destruktor działają normalnie i mogę tworzyć dodatkowe niestatyczne instancje, jeśli chcę (na przykład drugie połączenie DB)

Używam tego przez cały czas i jest to szczególnie przydatne do tworzenia niestandardowych programów obsługi sesji w bazie danych, ponieważ gdy strona kończy działanie, destruktor wypycha sesję do bazy danych.

Kolejną zaletą jest to, że możesz zignorować kolejność, którą nazywasz, ponieważ wszystko zostanie skonfigurowane na żądanie.

class Factory {
    static function &getDB ($construct_params = null)
    {
        static $instance;
        if( ! is_object($instance) )
        {
            include_once("clsDB.php");
            $instance = new clsDB($construct_params);   // constructor will be called
        }
        return $instance;
    }
}

Klasa DB ...

class clsDB {

    $regular_public_variables = "whatever";

    function __construct($construct_params) {...}
    function __destruct() {...}

    function getvar() { return $this->regular_public_variables; }
}

Gdziekolwiek chcesz go używać, po prostu zadzwoń ...

$static_instance = &Factory::getDB($somekickoff);

Następnie potraktuj wszystkie metody jako niestatyczne (ponieważ są)

echo $static_instance->getvar();
dave.zap
źródło
1
W rzeczywistości jest to implementacja pojedynczego wzorca i nie powinna być używana - zamiast tego trzymaj się wstrzykiwania zależności, co jest testowalne i ułatwia debugowanie.
Thomas Hansen
1
Czy możesz podać przykład, jak użyć iniekcji zależności w tej odpowiedzi i jak dzięki temu jest ona bardziej testowalna?
cjsimon
2

obiektu nie można zdefiniować statycznie, ale to działa

final Class B{
  static $var;
  static function init(){
    self::$var = new A();
}
B::init();
borrel
źródło
1
Andreas Niedermair: tak działa php (cykl aplikacji = pojedyncze żądanie) Ale singleton (na tym, który żyje w żądaniu) jest pozycją w php (w php singleton to obiekt, który ma 1 instancję (w aplikacji) cykl)
borrel