Можно ли создавать статические классы в PHP (как в C #)?

139

Я хочу создать статический класс в PHP и вести себя так же, как в C #, поэтому

  1. Конструктор автоматически вызывается при первом вызове класса
  2. Инстанцирование не требуется

Что-то в этом роде ...

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

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

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

Hello::greet(); // Hello There!
aleemb
источник
Не могли бы вы вкратце объяснить, как должен вести себя статический класс? Это реализация Утилиты?
xtofl
Просто высказываю свое собственное мнение, но из моего опыта в PHP, для здравомыслия, тестируемости и масштабируемости, статические классы должны быть практически полностью без состояний, представлять более функциональный API-интерфейс, чем объектно-ориентированный, и обычно лучше всего использовать в качестве фасадов доступности для полностью созданных экземпляров объектов или служебных оболочек для помощников или подобных конструкций, если они вообще используются вообще.
mopsyd

Ответы:

200

У вас могут быть статические классы в PHP, но они не вызывают конструктор автоматически (если вы попытаетесь вызвать, self::__construct()вы получите ошибку).

Поэтому вам нужно создать initialize()функцию и вызывать ее в каждом методе:

<?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
источник
20
Я довольно часто делаю это, чтобы обернуть все функции в одном месте. IE Utility :: doSomethingUseful ();
smack0007
16
Вместо Therefore you'd have to create an initialize() function and call it in each method:этого было бы проще сделать initializeпубличную функцию и вызывать ее сразу после объявления класса.
chacham15
4
Я знаю, что это довольно старо, но теперь вы можете использовать магию __callStatic, поэтому, когда вы вызываете любой статический метод или что-то еще, он сначала будет вызываться __callStatic, там вы сможете увидеть, был ли он инициализирован, а затем делать self::$methodили как вы вызываете. Если он все еще вызывает метод напрямую, попробуйте изменить все на private и посмотреть там.
matiaslauriti
1
Что произойдет, если два потока вызывают greet одновременно? Поскольку синхронизация отсутствует, инициализация не будет вызываться дважды (что в этом случае нормально, но во многих других случаях - нет). Или php однопоточный и не вытесняющий, как узел?
Джон Литтл
53

В дополнение к ответу Грега я бы порекомендовал установить конструктор частным, чтобы невозможно было создать экземпляр класса.

Так что, по моему скромному мнению, это более полный пример, основанный на примере Грега:

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


?>
Фил
источник
1
Это отличный подход, однако функция конструкции не может быть реализована, если ваш сингелтон наследует от определенных объектов, которые требуют общедоступного конструктора.
Эрик Херлиц
4
@EricHerlitz Этот вопрос не о синглетонах, а о статических классах. Почему вы хотите создать статический класс, который наследуется от класса, который должен быть создан?
Марк Амери
3
Точно так же объявление класса как абстрактного с предотвращением его создания и по-прежнему допускает вызовы статических методов.
Bstoney
24

вы можете иметь эти "статические" классы. но я полагаю, что чего-то действительно важного не хватает: в php у вас нет цикла приложений, поэтому вы не получите настоящую статичность (или синглтон) во всем приложении ...

увидеть синглтон в PHP

Андреас Нидермаир
источник
1
Статические классы и синглтоны - это просто две разные вещи.
Макс Куттинс
4
final Class B{

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

структура b называется обработчиком Singeton, вы также можете сделать это в

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

это одноразовое использование $a = a::getA(...);

Боррел
источник
3

Я обычно предпочитаю писать обычные нестатические классы и использовать фабричный класс для создания экземпляров одного (sudo static) экземпляра объекта.

Таким образом, конструктор и деструктор работают как обычно, и я могу создать дополнительные нестатические экземпляры, если пожелаю (например, второе соединение с БД)

Я использую это все время, и это особенно полезно для создания пользовательских обработчиков сеансов хранилища БД, поскольку, когда страница заканчивается, деструктор отправляет сеанс в базу данных.

Еще одним преимуществом является то, что вы можете игнорировать порядок вызова, так как все будет настроено по требованию.

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

Класс БД ...

class clsDB {

    $regular_public_variables = "whatever";

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

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

Везде, где вы хотите использовать это просто позвоните ...

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

Тогда просто обработайте все методы как нестатические (потому что они есть)

echo $static_instance->getvar();
dave.zap
источник
1
На самом деле это реализация одноэлементного паттерна, и ее не следует использовать - вместо этого придерживайтесь внедрения зависимости, что является тестируемым и облегчает отладку.
Томас Хансен
1
Можете ли вы привести пример того, как использовать внедрение зависимостей для этого ответа и как это делает его более тестируемым?
cjsimon
2

объект не может быть определен статически, но это работает

final Class B{
  static $var;
  static function init(){
    self::$var = new A();
}
B::init();
Боррел
источник
1
Andreas Niedermair: вот как работает php (app-цикл = один запрос). Но синглтон (на том, который живет в запросе) - это возможность в php (в php синглтон - это объект, у которого есть 1 экземпляр (внутри приложения). цикл)
Borrel