PHP中如何弥补静态初始化器的缺失?

3

我在考虑将每个类放入单独的文件中,并在类定义之外进行静态初始化。

问题在于,在实际需要该类之前就会发生初始化(它将在包含该类的文件被首次包含时发生)。这是一个问题,因为可能根本不使用该类,因此初始化是不必要的。我认为将已使用的文件包含到代码开头之外的做法很不好。

如果有人对这个问题有可行的解决方案,我将非常感激。


你认为在哪些情况下需要静态初始化程序?如果你对你要做的事情更清楚,我们可以更好地帮助你。我从未需要它们;你可能想太多了。 - ryeguy
2
我需要他们建立到数据库的连接。但请注意,解释为什么我不应该这样做并不能真正解决问题,因为问题 - 如我所猜测的那样 - 不仅仅是我的问题。 - Attila Kun
5个回答

3

看一下这样的代码。它使用数据库的单例实例,但你仍然可以创建类的实例:

class DB
{
    private static $_db;

    public static function pdo()
    {
        if (is_null(self::$_db))
        {
            self::$_db=new PDO('pgsql:host=localhost;
                                port=5432;
                                dbname=testdb;
                                user=postgres;
                                password=abc123');
            self::$_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        }

        return self::$_db;
    }

public static function factory()
{
    return new self;
}

private function __construct() {}

当你想要一个DB实例时,使用DB::factory()。DB使用self::pdo()返回单例以访问数据库。

谢谢,但我已经做过了。我的问题是,每当您定义进一步使用静态 $_db 变量的方法时,您将不得不执行空值检查以避免抛出异常。这可能会导致严重的性能损失。 - Attila Kun
一个单独的空值检查几乎不会对性能造成影响。如果你想使用这种设计模式,那么这是你唯一的选择。你要求解决PHP本身不支持的问题;你必须放弃一些东西。 - ryeguy
3
我刚刚检查了一下,在一台旧的双核笔记本上,我大约可以每秒执行100万次is_null比较。你真的认为这很重要吗?如果表达能力和易用性受到影响,那么性能就不再重要了。 - ryeguy
3
在每个函数开头额外添加2-3行代码并不是一个好主意,我认为这是一种糟糕的编程风格。如果你忘记在一个总是被其他函数调用的函数顶部进行检查,直到维护人员改变了调用顺序并发现问题时,你才会注意到。 - Cem Kalyoncu
@CemKalyoncu 这就是单元测试的作用。如果该类具有100%的覆盖率,那么如果有人添加使用未初始化的类成员的方法,您将立即注意到。此外,这种抽象的想法是它被整洁地嵌入另一个类中 - 您不应该需要向其添加新方法。它是一个DB工厂,其唯一目的是实例化数据库连接并返回它们。通过使用此DB工厂抽象,您的其他类与未初始化的数据库处理程序的问题隔离开来。 - Mark E. Haase

3

这里有一个单例模式的问题...如果在创建类的实例之前使用静态方法...那么静态初始化程序就没有运行...所以你可能会说可以在每个静态方法中运行初始化程序...这是正确的,但是对于只是获取和设置静态变量的情况呢?我想也可以使用__get和__set魔术函数来实现...只是语言本身不支持静态初始化...以下是解决方法:

在Hi.php文件中:

class Hi{
public static $v = "0";
}

Hi::$v= "2";
// end of Hi.php file

每个类都使用一个 .php 文件... 并在类定义外部进行静态初始化...


2

当一个类未被找到时,你可能需要查找 __autoload 函数并调用它来包含包含该类的文件。你可以在该函数中放置静态初始化器。


1
你可以在包含类的 PHP 文件中放置静态初始化器,并让 __autoload 为您包含类文件。这样手动包含类文件就不会引起任何问题。 - Cem Kalyoncu
我之前不知道__autoload,但我真的很喜欢这个解决方案。我相信这是我能够模拟所需功能的最接近方法。另一个好处是我可以避免逐个包含每个需要的文件;我只需创建一个包含__autoload方法的文件,并在需要时让它完成工作。谢谢! - Attila Kun
这是我们为我们的课程所做的事情,使用 includes 管理 40-50 个班级非常困难。我希望有一种方法可以在 JS 中实现相同的功能。我们最终会添加许多不必要的 JS 文件。 - Cem Kalyoncu

0

使用单例模式而不是静态调用:

<?php
class Foo {

   protected static $_instance;

   /**
   * @return Foo
   */
   public static function getInstance() {
        return (null === self::$_instance)? 
            self::$_instance :
            self::$_instance = new Foo();
   }

   protected function __construct() {
        // initialize here
   }
}

0

这是我做这件事的方式。唯一的成本是每次构造时测试 $doneinit 静态成员。

class Foo {
   private static $doneinit=false;

   private static init() {
      // do static initialisation here

      self::$doneinit=true;
   }

   public function __construct() {
      if (!self::$doneinit) self::init();

      // go on to do the rest of construction
   }
}

简单,而且它将所有内容都保留在类内部。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接