1

I have a singleton class that I am using as part of a CAPTCHA system to generate the code and image etc.

I have one script included on the html form that loads the class singleton, generates the code and image, and outputs it to the browser. I then have another script to validate and process the form. This loads the class singleton to retrieve the instance that was created previously, and calls the function to validate the code.

The problem I'm having is that when I'm validating the form the code that was generated on the form has changed or is completely absent when I come to validate it!

I haven't started or stored anything in the php session, but a session is created on the page the form is loaded in. Is the instance of the singleton somehow linked to that session? If it's a named session or something?

OR...have I completely misunderstood how singleton classes work? In which case can anyone tell me how I can retrieve the instance of the class that is created on the html form page to use again to validate the code in the form processing script? - And maybe tell me how I should be using singletons!

Many thanks.

Chris
  • 872
  • 1
  • 10
  • 20
  • [Regarding: "how I should be using singletons"](http://stackoverflow.com/questions/4595964/who-needs-singletons/4596323#4596323) – Gordon Feb 21 '12 at 12:37
  • updated my answer with a meaningful session persistent singleton example – Kaii Feb 21 '12 at 13:02
  • Firstly thanks to all answers and comments below, I think I understand: When I call the singleton in the form processing script I am in fact creating a new instance of the singleton rather than retrieving the previous one. I have passed classes as objects before (between classes) but not serialised through the session - is that like JSON? – Chris Feb 21 '12 at 13:06
  • @chris: sort of. In computer science, in the context of data storage and transmission, serialization is the process of converting a data structure or object state into a format that can be stored (for example, in a file or memory buffer, or transmitted across a network connection link) and "resurrected" later in the same or another computer environment. -> [Wikipedia on Serialisation](http://en.wikipedia.org/wiki/Serialization) – Kaii Feb 21 '12 at 14:11
  • @Kaii - Thanks for the clarification. I accepted CodeCaster's answer as it's the method I've gone with (for simplicity) but up-voted your answer as it's the most detailed and explanatory. – Chris Feb 21 '12 at 19:17

3 Answers3

3

Singletons exist for the duration of the request, not the duration of the session.

The idea of a singleton is to provide access to the same object across all included scripts, without having to use any explicit initialisation logic.

So, the first call to $foo = MyObject::singleton() creates a new MyObject, but the second call will simply return that object instead of creating a new one. This is incredibly useful for classes that access external resources such as databases and files.

Polynomial
  • 25,567
  • 8
  • 75
  • 106
2

No, it's not.

You'd have to serialize your singleton object and store it to the session when your code execution ends. When the next page is displayed, you can unserialize the object from your session.

PHP serializes/unserializes objects automatically when you assign them to a session.

This only works correctly under the precondition that your singleton does not use link identifiers to external resources.

Here is an example implementation taken from the comments in PHP docs

class SessionSingleton {
    /**
     *    Returns an instance of the singleton class.
     *    @return    object        The singleton instance
     */
    public static function _instance()
    {
        // Start a session if not already started
        Session::start();

        if ( false == isset( $_SESSION[ self::$_singleton_class ] ) )
        {
            $class = self::$_singleton_class;
            $_SESSION[ self::$_singleton_class ] = new $class;
        }

        return $_SESSION[ self::$_singleton_class ];       
    }

    /**
     *    Destroy the singleton object. Deleting the session variable in the
     *    destructor does not make sense since the destructor is called every
     *    time the script ends.
     */
    public static function _destroy()
    {
        $_SESSION[ self::$_singleton_class ] = null;
    }

    /**
     *    Initialize the singleton object. Use instead of constructor.
     */
    public function _initialize( $name )
    {
        // Something...
    }

    /**
     *    Prevent cloning of singleton.
     */
    private function __clone()
    {
        trigger_error( "Cloning a singleton object is not allowed.", E_USER_ERROR );
    }

    private static $_singleton_class = __CLASS__;
}
Kaii
  • 18,421
  • 3
  • 33
  • 58
  • 2
    It may not be possible to provide reliable serialisation. Resource handles can't be persisted within a session. – Polynomial Feb 21 '12 at 12:36
  • 1
    Worth to mention, that you cannot expect, that you can serialize a singleton, because it may not be "single" anymore (`$clone =unserialize(serialize($singleton))`). – KingCrunch Feb 21 '12 at 12:37
  • @KingCrunch depends on the implementation of the singleton. This may sound odd to you, but in fact you *could* clone most singleton implementations in PHP. A singleton would have to prevent this by using the mentioned "magic methods" ... __sleep() and __wakeup() – Kaii Feb 21 '12 at 12:40
  • 1
    `serialize()`/`unserialize()` (defined by `Serializable`) a slightly more modern (just as a sidenote). However, in nearly all implementations I've seen so far `__construct()` and `__clone()` are `private`, meaning that you can _not_ just instanciate or `clone` a singleton. You are right, that this is usually not the case for one of that magic Serialization-methods, but I wouldn't rely on it. – KingCrunch Feb 21 '12 at 12:45
  • @KingCrunch updated my answer with a meaningful session persistent singleton example – Kaii Feb 21 '12 at 13:02
2

OR...have I completely misunderstood how singleton classes work?

Partially. Since PHP has no ASP.NET alike application variables, objects in PHP live as long as the request does, unless serialized (for example in a session).

As a solution to your problem: save the captcha code (or the captcha class, which is a bit of overkill imho) in a session variable, like $_SESSION['captcha'].

CodeCaster
  • 131,656
  • 19
  • 190
  • 236