Инициализатор статического класса в PHP

У меня есть вспомогательный класс с некоторыми статическими функциями. Все функции в классе требуют, чтобы "сильная функция инициализации запускалась один раз (как если бы это был конструктор).

Есть ли хорошая практика для достижения этого?

Единственное, о чем я думал, это вызов функции init и нарушение ее потока, если он уже запускался один раз (используя статический $initialized var). Проблема в том, что мне нужно вызвать ее для каждой из функций классов.

Ответ 1

Похоже, вам лучше будет обслуживать синглтон, а не пучок статических методов

class Singleton
{
  /**
   * 
   * @var Singleton
   */
  private static $instance;

  private function __construct()
  {
    // Your "heavy" initialization stuff here
  }

  public static function getInstance()
  {
    if ( is_null( self::$instance ) )
    {
      self::$instance = new self();
    }
    return self::$instance;
  }

  public function someMethod1()
  {
    // whatever
  }

  public function someMethod2()
  {
    // whatever
  }
}

И затем, в использовании

// As opposed to this
Singleton::someMethod1();

// You'd do this
Singleton::getInstance()->someMethod1();

Ответ 2

// file Foo.php
class Foo
{
  static function init() { /* ... */ }
}

Foo::init();

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

Ответ 3

Фактически, я использую открытый статический метод __init__() для моих статических классов, для которых требуется инициализация (или, по крайней мере, нужно выполнить какой-то код). Затем, в моем автозагрузчике, когда он загружает класс, он проверяет is_callable($class, '__init__'). Если это так, он вызывает этот метод. Быстро, просто и эффективно...

Ответ 4

Есть способ вызвать метод init() один раз и запретить его использование, вы можете превратить функцию в приватный инициализатор и вызвать ее после объявления класса следующим образом:

class Example {
    private static function init() {
        // do whatever needed for class initialization
    }
}
(static function () {
    static::init();
})->bindTo(null, Example::class)();

Ответ 5

Другой вариант, похожий на ответ Питера, заключается в использовании метода __ callStatic. (PHP 5.3 +)

class Singleton
{
    /** @var Singleton */
    private static $instance;

    private $foo = 0;

    public function __construct()
    {
        echo $this->foo . PHP_EOL;
        $this->foo = 1;
    }

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

    // Need this method since someMethod1() cannot be public.
    public function callStaticMethod($name, $arguments)
    {
        // Obviously, add necessary checks here before calling.
        return $this->$name($arguments);
    }

    public static function __callStatic($name, $arguments)
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }

        self::$instance->callStaticMethod($name, $arguments);
    }

    private function someMethod1()
    {
        echo $this->foo . PHP_EOL;
    }
}

Pros

  • Конструктор является общедоступным.
  • Очиститель при вызове статического метода.

против

  • someMethod1() должен быть защищен/закрыт или будет выдавать ошибку (не уверен, что это плохо, хотя).
  • Недостатки, связанные с магическими методами.
// Instead of this
Singleton::getInstance()->someMethod1();

// Can do either of these
Singleton::someMethod1();
Singleton::someMethod1();

$s = new Singleton();
$s->callPrivateMethod();
$s::someMethod1();

// Will result in this
0
1
1

0
1
1

Ответ 6

Если вам не нравится public статический инициализатор, отражение может стать обходным путем.

<?php

class LanguageUtility
{
    public static function initializeClass($class)
    {
        try
        {
            // Get a static method named 'initialize'. If not found,
            // ReflectionMethod() will throw a ReflectionException.
            $ref = new \ReflectionMethod($class, 'initialize');

            // The 'initialize' method is probably 'private'.
            // Make it accessible before calling 'invoke'.
            // Note that 'setAccessible' is not available
            // before PHP version 5.3.2.
            $ref->setAccessible(true);

            // Execute the 'initialize' method.
            $ref->invoke(null);
        }   
        catch (Exception $e)
        {
        }
    }
}

class MyClass
{
    private static function initialize()
    {
    }
}

LanguageUtility::initializeClass('MyClass');

?>

Ответ 7

ПРИМЕЧАНИЕ: это именно то, что OP сказал, что они сделали. (Но не показывал код для.) Я показываю здесь детали, чтобы вы могли сравнить их с принятым ответом. Я хочу сказать, что оригинальный инстинкт ОП был, ИМХО, лучше, чем ответ, который он принял.


Учитывая, насколько высоко оценен принятый ответ, я хотел бы отметить, что "наивный" ответ на одноразовую инициализацию статических методов, едва ли больше кода, чем реализация Singleton, - и имеет существенное преимущество.

final class MyClass  {
    public static function someMethod1() {
        MyClass::init();
        // whatever
    }

    public static function someMethod1() {
        MyClass::init();
        // whatever
    }


    private static $didInit = false;

    private static function init() {
        if (!$didInit) {
            $didInit = true;
            // one-time init code.
        }
    }

    // private, so can't create an instance.
    private function __construct() {
        // Nothing to do - there are no instances.
    }
}

Преимущество этого подхода в том, что вы можете вызывать с простым синтаксисом статической функции:

MyClass::someMethod1();

Сравните это с вызовами, требуемыми принятым ответом:

MyClass::getInstance->someMethod1();

Как правило, лучше всего заплатить цену кодирования один раз, когда вы кодируете класс, чтобы сделать звонящих проще.


Из всех ответов (включая этот) я предпочитаю ответ Виктора Николь. Просто. Никакого дополнительного кодирования не требуется. Нет "продвинутого" кодирования, чтобы понять. (Я рекомендую включить комментарий FrancescoMM, чтобы "init" никогда не выполнялся дважды.)

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

Ответ 8

Примечание. RFC, предлагающий это, все еще находится в проекте.


class Singleton
{
    private static function __static()
    {
        //...
    }
    //...
}

предложенный для PHP 7.x(см. https://wiki.php.net/rfc/static_class_constructor)