views:

1067

answers:

7

Let's say I'm writing a PHP (>= 5.0) class that's meant to be a singleton. All of the docs I've read say to make the class constructor private so the class can't be directly instantiated.

So if I have something like this:

class SillyDB
{
  private function __construct()
  {

  }

  public static function getConnection()
  {

  }
}

Are there any cases where __construct() is called other than if I'm doing a

new SillyDB()

call inside the class itself?

And why am I allowed to instantiate SillyDB from inside itself at all?

+10  A: 

__construct() would only be called if you called it from within a static method for the class containing the private constructor. So for your Singleton, you might have a method like so:

class DBConnection
{
   private static $Connection = null;

   public static function getConnection()
   {
      if(!isset(DBConnection::Connection))
      {
         DBConnection::Connection = new DBConnection();
      }
      return DBConnection::Connection;
   }

   private function __construct()
   {

   }
}

$dbConnection = DBConnection::getConnection();

The reason you are able/would want to instantiate the class from within itself is so that you can check to make sure that only one instance exists at any given time. This is the whole point of a Singleton, after all. Using a Singleton for a database connection ensures that your application is not making a ton of DB connections at a time.

Brian Warshaw
A: 

A little nit picking: For completeness you'd probably declare the class as final too since you wouldn't want someone subclassing this class and implementing its own public constructor.

(Forgive me if the compiler catches the overriding of a private constructor, but I don't think it does)

Allain Lalonde
A: 

@Allain, have you tested to see if it throws an error?

Brian Warshaw
A: 

@allain
That's handy to know about the declaring the class as final. However I can think of cases where you might not want to do that.

Would there every be a case where you might want to create a subclass which was also a singleton but added some functionality?

At any rate, subclassing doesn't seem to throw any errors unless you actually try to call the parent constructor.

@Brian
On another note, I believe that everywhere you have DBConnection::Connection should actually be DBConnection::$Connection

Mark Biek
+1  A: 

Test the code, Mark, and let me know what you find out.

EDIT: Also, in this particular situation, I'm not sure why you would need to be concerned with preventing a person from subclassing. If someone has access to your PHP code to subclass it, then they also have access to your code to copy it and change access modifiers to something that they (for whatever reason) find suitable.

The practical usefulness of the Singleton in this case is that, by using it, you can ensure that you're always using the same database connection for a given HTTP request. It accomplishes that. The other stuff (using final and private constructors) is useful to know from a theory perspective, and even more useful to know if you want to distribute API-quality code to other programmers, but in the case of this particular example, all the keywords are doing is adding bytes to your class file's size.

Brian Warshaw
A: 

Here's a very simple singleton that just generates a date/time string:

class TheDate
{
    private static $DateInstance = null;
    private $dateVal;

    public static function getDateInstance()
    {
        if(!isset(TheDate::$DateInstance))
        {
            TheDate::$DateInstance = new TheDate();
        }

        return TheDate::$DateInstance;
    }

    public static function getDateVal()
    {
        return TheDate::$DateInstance->dateVal;
    }

    private function __construct()
    {
        $this->dateVal = strftime("%Y-%m-%d %H:%M:%S");
    }
}

Doing something like this obviously gives me the same date over and over:

$date1 = TheDate::getDateInstance();
echo $date1->getDateVal() . "<br />";

$date2 = TheDate::getDateInstance();
echo $date2->getDateVal() . "<br />";

And doing this doesn't generate any errors:

class NewDate extends TheDate
{
    public function __construct()
    {

    }
}
Mark Biek
+1  A: 

OK, I ran some tests of my own.

  • If you don't declare your own constructor in the subclass, trying to instantiate it will throw a fatal error, because it tries to call the superclass constructor.
  • In the case of a DB connection, when you're presumably returning (in PHP, anyway) a mysqli instance or the resource returned by the mysql_connect() function (or some other link to some other RDBMS), as long as you mark the instance private, there's no threat of somebody subclassing it and tampering with the link.
  • As I alluded to before, if somebody really wants to override your behavior and make multiple connections, they could just as well do it by writing a new class.
Brian Warshaw