Question

in my php application, I'm aggregating some rss feeds. I want this to be done every 24h and I don't want to store it in database.

I have built a singleton class that creates my rss informations ($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;
}    

}

but each call to the getInstance() calls the constructor as it should normally be done only once and then give the already instanciated $_instance

My debug always gives :

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

and never displays the "skip constructor".

What am I missing ?

and in a general way : is this the correct way to do ? As parsing rss feeds is time consuming I don't want this task to be done for each visitor : how to keep results in an always instanciated php class ?

Thanks for your ideas !

Was it helpful?

Solution

I don't want this task to be done for each visitor : how to keep results in an always instanciated php class

I focused on that part of the question, which makes me think that you rather missconcept the Singleton pattern, objects and requests.

Let me change your sample as another demonstration which maybe you will understand better

<?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>

We have a Singleton class which contains public property $x accessible via its instance. Since constructor is private, you can access instance only from getInstance() method. Singleton::getInstace()->x will access the property $x.

In this code, if the button is clicked, we expect the $x property to increment by one.

When we first launch the script, the property has value of 1. After we press the button, it has value of 1 + 1 = 2. And now, you expect, the value of 2 somehow to be written in the memory, so if the button is clicked for third time, it should show 3. But, it unfortunately is not true, and no matter how many times you do click the button, you will always recieve a value of 2, since after requesting the page for N-th time, it reinstantiates the class and it loses all of its changes.

There is no way to keep that persistence between all your clients only in the memory, because it's flushed right after it is used.

My suggestion is to keep changes into a database.

You can also do an object serialization, so you can save your changes into the database;

E.g.:

serialize(Singleton::getInstance());

outputs:

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

You can store this somewhere i.e. in db col serialized

afterwards extract it and assign it to variable:

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

Perform a change:

$singleton->x++;

See the serialized changes again:

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

Save them back.

Assign again

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

Outputs: 3

This might not be the most efficient way, but you cannot rely on PHP objects to save in memory like database. That's why databases are present, and all the information for users, etc. is not stored into objects kept in the memory.

If there are a lot of credentials to save, the best design decision is to save each field into the DB instead of a serialized object, so you can set to the new instance the value from the database. That's what, in practice, the ORM's are for. Bind resultset to object.

Think twice which approach can fit your needs, if reinstantiating an object / pulling from the database for each user is costly, you should think about a caching approach, if no changes - no db pull, take from the cache.

OTHER TIPS

I guess you are trying to get an instance of a child-class of ActualitesManager.

  1. To achieve this, you need to declare $_instance as protected. Rename it to $instances too since it will be an array.
  2. Change the code of getInstance to something like the following:

    foreach ( static::$instances as $instance )
    {
        if ( $instance instanceof static )
        {
            return $instance;
        }
    }
    $instance = new static;
    static::$instances[] = $instance;
    return $instance;
    

We store every instance into one array and loop this. In static is a pointer to the extending class and it can be used with instanceof.

Using "static" will use the current child-class and not the parent. "self" is always a pointer to the class containing this function.

Since you access the instance via parent and not the class itself you need to declare it as "protected" to be accessible.

However you only have one static protected $instances therefor it is an array. You loop that array and return the instance matching the current class if found. Else you create it and return the new instance.

Hope I could help with your issue :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top