106

我有一个带有一些静态函数的助手类。类中的所有函数都需要一个“繁重”的初始化函数才能运行一次(就好像它是一个构造函数一样)。

有没有实现这一目标的好习惯?

我唯一想到的是调用一个init函数,如果它已经运行过一次(使用静态变量)则中断它的流程$initialized。问题是我需要在每个类的函数上调用它。

4

8 回答 8

124

听起来你最好由一个单例而不是一堆静态方法来服务

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();
于 2010-07-22T19:56:05.223 回答
100
// file Foo.php
class Foo
{
  static function init() { /* ... */ }
}

Foo::init();

这样,当包含类文件时就会发生初始化。您可以通过使用自动加载确保仅在必要时(且仅一次)发生这种情况。

于 2010-07-22T19:58:22.800 回答
58

__init__()实际上,我在需要初始化(或至少需要执行一些代码)的静态类上使用公共静态方法。然后,在我的自动加载器中,当它加载一个类时,它会检查is_callable($class, '__init__'). 如果是,它会调用该方法。快速、简单、有效...

于 2010-07-22T20:29:17.797 回答
7

注意:这正是 OP 所说的。(但没有显示代码。)我在此处显示详细信息,以便您可以将其与接受的答案进行比较。我的观点是,OP 最初的直觉是,恕我直言,比他接受的答案更好。


鉴于接受的答案有多么高,我想指出对静态方法的一次性初始化的“天真”答案,几乎没有比 Singleton 的实现更多的代码 - 并且具有基本优势

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

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


    private static $didInit = false;

    private static function init() {
        if (!self::$didInit) {
            self::$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();

作为一般原则,最好在编写类时支付一次编码费用,以使调用者更简单。


如果您使用 PHP 7.4 opcode.cache,请使用Victor Nicollet 的答案。简单的。无需额外编码。没有“高级”编码可以理解。(我建议包括 FrancescoMM 的评论,以确保 "init" 永远不会执行两次。)请参阅Szczepan 对Victor 的技术无法与opcode.cache.

如果您正在使用AFAIKopcode.cache,那么我的回答就是尽可能干净。成本只是在每个公共方法MyClass::init();的开头添加一行。注意:如果您需要公共属性,请将它们编码为/一对方法,以便您可以添加该调用。getsetinit

私有成员不需要该init调用,因为它们无法从外部访问 - 因此在执行到达私有成员时已经调用了一些公共方法。)

于 2019-04-12T20:07:52.377 回答
6

有一种方法可以调用init()一次并禁止它的使用,您可以将函数转换为私有初始化程序并在类声明后调用它,如下所示:

class Example {
    private static function init() {
        // do whatever needed for class initialization
    }
}
(static function () {
    static::init();
})->bindTo(null, Example::class)();
于 2018-09-10T08:19:50.043 回答
3

我将其发布为答案,因为这对于 PHP 7.4 非常重要。

PHP 7.4的opcache.preload机制使得为类预加载操作码成为可能。如果您使用它来预加载包含类定义一些副作用的文件,则该文件中定义的类对于此 FPM 服务器及其工作人员执行的所有后续脚本将“存在”,但副作用不会生效,并且自动加载器将不需要包含它们的文件,因为该类已经“存在”。这完全击败了依赖于在包含类定义的文件中执行顶级代码的任何和所有静态初始化技术。

于 2020-05-03T16:11:17.470 回答
0

如果您不喜欢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');

?>
于 2018-01-15T20:44:45.820 回答
-2

注意 - 提出此建议的 RFC 仍处于草案状态。


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

为 PHP 7.x 提议(参见https://wiki.php.net/rfc/static_class_constructor

于 2016-06-14T18:02:51.167 回答