Инициализатор статического класса в 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)