php类设计:只有一个实例始终可访问

时间:2014-04-24 12:06:37

标签: php design-patterns singleton

在我的php应用程序中,我正在聚合一些rss feed。我希望每24小时完成一次,我不想将其存储在数据库中。

我已经构建了一个单独的类来创建我的rss信息($ ActuList):

class ActualitesManager{

/**
* @var Singleton
* @access private
* @static
*/
private static $_instance = null;

public $ActuList = null;

private function __construct()
{
    error_log("construct");
    /* My stuff to create $ActuList */

}

public static function getInstance()
{

    error_log("instance before : ". json_encode(self::$_instance) );
    //if(is_null(self::$_instance)){
    //if(!isset(self::$_instance)){
    if (null === self::$_instance)
{
        $object = __CLASS__;
        self::$_instance = new $object;
    }else
{
        error_log("skip constructor");
    }
    error_log("instance after : ". json_encode(self::$_instance) );
    return self::$_instance;
}    

}

但每次调用getInstance()都会调用构造函数,因为它通常只应执行一次,然后给出已经实例化的$ _instance

我的调试总是给出:

instance before : null
instance after : {"ActuList":null}

并且永远不会显示"跳过构造函数"。

我错过了什么?

并以一般方式:这是正确的方法吗?解析rss feed非常耗时我不想为每个访问者完成这项任务:如何将结果保存在一个总是实例化的php类中?

感谢您的想法!

2 个答案:

答案 0 :(得分:1)

  

我不希望每个访问者都完成这项任务:如何保持   导致一个总是实例化的php类

我专注于问题的那一部分,这让我觉得你很想念Singleton模式,对象和请求。

让我将您的样本更改为另一个可能您会更好理解的演示

<?php

class Singleton {
    public $x = 1;

    private static $_inst = null;

    private function __construct() { }

    /**
     * @return Singleton
     */
    public static function getInstace() {
        if (self::$_inst == null) {
            self::$_inst = new self();
        }
        return self::$_inst;
    }
}

if (isset($_POST['y'])) {
    Singleton::getInstace()->x++;
    echo Singleton::getInstace()->x;
}
?>

<form action="" method="post">
    <input type="submit" name="y"/>
</form>

我们有一个Singleton类,其中包含可通过其实例访问的公共属性$x。由于构造函数是私有的,因此只能从getInstance()方法访问实例。 Singleton::getInstace()->x将访问该媒体资源$x

在此代码中,如果单击该按钮,我们希望$x属性增加1。

首次启动脚本时,该属性的值为1。按下按钮后,它的值为1 + 1 = 2.现在,您希望2的值以某种方式写入内存中,因此如果第三次单击该按钮,则应该显示3。但是,遗憾的是,它不是真的,无论你多少次点击按钮,你总会收到2的值,因为在第N次请求页面后,它会重新实现该类,并且它失去了所有的变化。

没有办法只在内存中保持所有客户端之间的持久性,因为它在使用后立即刷新。

我的建议是将更改保存到数据库中。

您还可以执行对象序列化,以便将更改保存到数据库中;

E.g:

serialize(Singleton::getInstance());

输出:

O:9:"Singleton":1:{s:1:"x";i:1;}

您可以将其存储在某个位置,即db col serialized

然后提取它并将其分配给变量:

$singleton = unserialize($row['serialized']);

执行更改:

$singleton->x++;

再次查看序列化更改:

O:9:"Singleton":1:{s:1:"x";i:2;}

将它们保存回来。

再次分配

$singleton = unserialize($row['serialized']);
$singleton->x++;
echo $singleton->x;

输出:3

这可能不是最有效的方法,但您不能依赖PHP对象来保存在内存中,如数据库。这就是数据库存在的原因,用户的所有信息等都没有存储在内存中保存的对象中。

如果要保存大量凭据,最好的设计决策是将每个字段保存到数据库而不是序列化对象,这样就可以将新实例设置为数据库中的值。这实际上是ORM的用途。将结果集绑定到对象。

两次考虑哪种方法可以满足您的需求,如果为每个用户重新安排一个对象/从数据库中提取成本很高,您应该考虑一种缓存方法,如果没有更改 - 没有数据库拉,从缓存中获取。

答案 1 :(得分:0)

我想您正在尝试获取ActualitesManager的子类实例。

  1. 要实现此目的,您需要将$ _instance声明为protected。将它重命名为$ instances,因为它将是一个数组。
  2. 将getInstance的代码更改为以下内容:

    foreach ( static::$instances as $instance )
    {
        if ( $instance instanceof static )
        {
            return $instance;
        }
    }
    $instance = new static;
    static::$instances[] = $instance;
    return $instance;
    
  3. 我们将每个实例存储到一个数组中并循环它。在static中是指向扩展类的指针,它可以与 instanceof 一起使用。

    使用“static”将使用当前的子类而不是父类。 “self”始终是指向包含此函数的类的指针。

    由于您通过父级而不是类本身访问实例,因此您需要将其声明为“受保护”才能访问。

    但是,你只有一个静态保护$ instance,因此它是一个数组。循环该数组并返回与当前类匹配的实例(如果找到)。否则,您创建它并返回新实例。

    希望我能帮助解决您的问题:)