是否可以在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!

您能否简要解释一下静态类的行为?它是实用程序的实现吗?
xtofl,2009年

只是抛出我自己的观点,但是从我在PHP方面的经验来看,出于理智,可测试性和可伸缩性的考虑,静态类应该几乎完全是无状态的,与面向对象的api相比,它提供了更具功能性的类似于编程的api,通常最好用作完全实例化的对象的可访问性外观,或者用作辅助程序或类似结构的实用程序包装,即使它们甚至都被使用过。
mopsyd

Answers:


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!


?>

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
如果两个线程同时打招呼怎么办?由于没有同步,因此不会两次调用初始化(在这种情况下可以,但是在许多其他情况下则不会)。还是php是单线程且非抢占式节点?
约翰·利特尔

53

除了Greg的答案外,我建议将构造函数设置为私有,以便无法实例化该类。

因此,以我的拙见,这是一个基于Greg的完整示例:

<?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
这是一种很好的方法,但是,如果您的singelton继承自某些需要公共构造函数的对象,则无法实现构造函数。
埃里克·赫里兹

4
@EricHerlitz这个问题与单例无关,与静态类有关。您为什么要创建一个静态类,该类继承要实例化的类?
Mark Amery 2014年

3
将类声明为抽象类,以防止实例化该类,并且仍然允许调用静态方法。
bstoney


4
final Class B{

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

b的结构称为singeton处理程序,您也可以在a

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

DB类...

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();

1
这实际上是一个单例模式的实现,实际上不应该使用-而是坚持依赖注入,这是可测试的,并且易于调试。
Thomas Hansen 2014年

1
您能否举一个示例,说明如何将依赖注入用于此答案,以及如何使其更易于测试?
cjsimon

2

对象不能静态定义,但是可以工作

final Class B{
  static $var;
  static function init(){
    self::$var = new A();
}
B::init();

1
Andreas Niedermair:那就是php的工作方式(应用周期=一个请求),但是单例(存在于请求中)是php中的可能性(在php中,单例是一个具有1个实例的对象(在应用内部-周期)
borrel 2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.