TalkPHP
 
 
Account Login
Latest Articles
» The basic usage of PHPTAL, a XML/XHTML template library for PHP
» Vulnerable methods and the areas they are commonly trusted in.
» Simple way to protect a form from bot
» The Basics On: How Session Stealing Works
» How to keep your forms from double posting data
IRC Channel
IRC Speech Bubble Join the friendly bunch on IRC...
(#TalkPHP on Freenode)

...Also available via a web interface.

See this thread for information on the TalkPHP Free Hugs Initiative™. Subject to availability.
Associates
Associates
CSS Tutorials
 
 
LinkBack Thread Tools Search this Thread Display Modes
Prev Previous Post   Next Post Next
Old 08-17-2008, 01:27 AM   #1 (permalink)
The Wanderer
 
Join Date: Aug 2008
Posts: 11
Thanks: 4
Theo is on a distinguished road
Big Grin A Generic Singleton Base Class

I'm from a C++ background and a Singleton hater. C++ just doesn't need them! However, I've found they're actually very useful for databases in PHP.

Anyways, I was trying to find a generic base class for Singletons in PHP. It's actually quite easy to do in C++ (which is probably why Singleton overload is a big problem). Most articles I find offer something like this:

PHP Code:
class Singleton
{
    
//    The sole access point for singletons.
    
final public static function GetInstance$inClass )
    {
        static 
$gInstances = array();
        if( !
array_key_exists$inClass$gInstances ) )
        {
            
self::$gInstances[$class] =& new $inClass;
        }       

        
$theInstance =& $gInstances[$inClass];
        return 
$theInstance;
    }

What on earth? That's not a singleton at all. It creates an array that can hold every concievable class. The array can only hold one of each class and has only a single point of access. However, the classes themselves can easily be instantiated again and/or cloned.

The cloning problem can be fixed by making the __clone function final and private in every class you want to be a singleton. Making __construct private prevents instantiation even by this class, not what you'd want at all.

Anyways, I can't really be bothered ensuring every class has a private clone function and constructor. That leads to errors. And even if they did, this class could still instantiate non-singletons as well.

So I came up with this:

PHP Code:
<?php

//------------------------------------------------------------------------------
//    Singleton
//        A class for managing singletons in the project.
//------------------------------------------------------------------------------

class Singleton
{
    
//    The sole access point for singletons.
    
final public static function GetInstance$inClass )
    {
        
//    Do we have one yet?
        
if( !array_key_exists$inClassself::$gInstances ) )
        {
            
self::$gInstances[$inClass] =& new $inClass;
            
            
//    Is this a singleton class?
            
if( !( self::$gInstances[$inClass] instanceof Singleton ) )
            {
                unset( 
self::$gInstances[$inClass] );
                throw new 
Exception"$inClass is not a Singleton class." );
            }
            
            
self::$gInstances[$inClass]->Construct();
        }

        
$theInstance =& self::$gInstances[$inClass];
        return 
$theInstance;
    }
    
    
//    Construct function - to be overrided by sub-classes if needed.
    
protected function Construct() {}

    
//    No creation or cloning.
    
final private function __construct() {}
    final private function 
__clone() {}
    
    
//    The instances of the class(es).
    
private static $gInstances = array();
}

?>
Singletons have to inherit from this class. If you call Singleton::GetInstance() with a class that isn't a Singleton, this function unsets it and throws an exception.

The Construct function is called the first time a Singleton is created. This can be overrided in sub-classes to do any initialisation the object may need.

The constructor and the clone function are declared final private - only accessible by this class and unavailable for overriding. Unfortunately, even if this constructor is private / protected this can be overrided by sub-classes as public if they are not final, which allows them to be instantiated more than once. This is the reason for the Construct() function.

If you create this test file:

PHP Code:
<?php

include_once( $_SERVER['DOCUMENT_ROOT'].'/../Include/Singleton.php' );

//    A test singleton with a 'Constructer'
class TestSingleton extends Singleton
{    
    function 
Construct()
    {
        echo 
"Constructing...<br />\n";
    }
    
    public 
$mVar;
}

//    A non-singleton class.
class NonSingleton {}

//    Instantiate a Singleton...
$theSingleton Singleton::GetInstance'Singleton' );

//    Instantiate the Test Singleton.
$theTestSingleton Singleton::GetInstance'TestSingleton' );

//    Assign some text to the member variable.
$theTestSingleton->mVar "Hello World!<br />\n";

//    Create another access point to the Test Singleton.
$theOtherTestSingleton Singleton::GetInstance'TestSingleton' );

//    Output the member variable.
echo $theOtherTestSingleton->mVar;

//    Try and instantiate a Non Singleton via Singleton.
$theNonSingleton Singleton::GetInstance'NonSingleton' );

?>
You get this output:

Quote:
Constructing...
Hello World!

Fatal error: Uncaught exception 'Exception' with message 'NonSingleton is not a Singleton class.' in /home/.sambrina/theoddman/Include/Singleton.php:22 Stack trace: #0 /home/.sambrina/theoddman/www.theoddman.com/test.php(31): Singleton::GetInstance('NonSingleton') #1 {main} thrown in /home/.sambrina/theoddman/Include/Singleton.php on line 22
As you can see, TestSingleton::Construct() only gets called once, despite us accessing it twice. We can also see that $TestSingleton and $OtherTestSingleton reference the same object.

When we try to create NonSingleton via Singleton, we get our exception as desired (which is uncaught in this case).

The only potential problems I can see in this are:

1) You can instantiate the Singleton class itself as demonstrated in the above code. This could be prevented by checking the input value, but I don't think it's worth it - surely only the worst coders would do this?

2) Construct() can be overrided to be public. This could be good and bad, I'm not sure how I feel about it...

3) Overhead?

Anyways, what do you guys think? Any other potential problems? Is this even gonna work? :)

M

Last edited by Theo : 08-17-2008 at 01:45 AM. Reason: Typo (I'm a language geek - sorry...)
Theo is offline  
Reply With Quote
 



Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On


All times are GMT. The time now is 02:21 PM.

 
     

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2013, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.1.0
Inactive Reminders By Icora Web Design