The Singleton – Using a Singleton

In our last article, we built a framework for the singleton class. In my real world example, we designed a singleton class that would give us a current Year for use throughout our accounting system. How do we use it in our system?

At first, it seems simple and straight forward, but as we get into it we'll have to alter our previous class to fix some implementation problems.

Since we are going to call a singleton from any file in the system, you want to make sure that the system knows where the singleton class resides. Usually, you use an autoloader utility, which allows all classes in the system to be accessed from anywhere in the system. I'll assume you have that in place, or, at least, set a path to your classes in php.ini. Let's get started.

I'm coding along in a file in my system and decide I need the $currentYear to make sure I only have this year's data. Here goes:

// Get the object instance 
// (remember its static, so you use the ::)
$Yearobj = TheYear::getYearObject();

// Get the current year
// (remember it's not static)
$currentYear = $Yearobj->getTheYear();

Not bad, if you want to do it all in one line:

$currentYear = TheYear::getYearObject()->getTheYear();

We now have the current year any time we want it in our system with one line of code. Nice, but we're not quite finished yet, and there are problems. Let's see what's happening here in words.

Here's the class from our last article:

class TheYear 
{
private $currentYear ;
private static $YearObject ;

private function __construct() { }

public static function getYearObject()
{
	if( empty(self::$YearObject() ))
	{
        self::$YearObject = new TheYear();
    }
    return self::$YearObject;
}

public function getTheYear() 
{
	return $this->currentYear;
}

public function setCurrenYear($acctYear)
{
   $this->currentYear = $acctYear;
}}

When we get the year in our application, if $YearObject has not been populated, we get a new instance with getYearObject(). Once we have the object variable we use it to call getTheYear() and get the year.

Basically that's it, but there's implementation problems, especially with web applications.

When you first start the system the year is not set, we can initialize the year, but that is permanent, and we want a more dynamic year, let's add a method to do that. We will need to check to see if a year has been set before returning the current year to the system, we'll do that inside the getTheYear() method.

We have one more gotcha that happens in a web application, and that has to do with persistance. Our class is set up to create a new instance, if one is not present. All is well and good, but how long will that instance persist in our application?

As you know the Internet is stateless, when I move to a new web page, nothing comes with it in the way of data unless you specifically pass the data to the next page. We normally do that with sessions, hmmm, isn't this where we started this exercise?

One of the reasons people have trouble implementing singletons, is when they move to a new web page and get the current Year, the singleton starts from scratch, which means we create a new instance, and our previous Year is lost, and replaced with the default year, an error that may not be discovered for awhile.

How do we get around this conundrum? Well, we bring back our old friend the session variable, and save the the year in a session variable so it's not lost, only, instead of having to worry about setting sessions, we hide our session assigns inside the class, so we never have to deal with them, the class will do it for us. Here's the completed singleton class with the default year method and sessions added.

class ncboVW_ProjYear {

private $currentYear;
private static $YearInstance = NULL;

private function __construct() {}

public static function getYearInstance()
{
	if( empty(self::$YearInstance() ))
	{
    	self::$YearInstance = new TheYear();
    }
    return self::$YearInstance;
}

private function getDefaultYear() 
{
	$tempYear = date('Y');
	return $tempYear;
}

public function getTheYear() 
{
	// If the year is empty, get the 	
	// current year in session
	if( empty($this->currentYear) )
	{
		$this->currentYear = $_SESSION['the_year'];
	}

	// If year is not in session, 
	// get the default year
	if(empty($this->currentYear))
	{
		$this->setTheYear( $this->getDefaultYear() );
	}
	return $this->currentYear;
}

public function setTheYear($newYear)
{
	$this->currentYear = $newYear;
	$_SESSION['the_year'] = $newYear;
}

// for validation	
public function checkTheYear($newyear)
{
	if(is_int($newyear))
	{
		$this->setTheYear($newyear);
	}
}

Notice the double empty check in getTheYear() to see if the Year is empty. If we already have a year we previously put in the session variable, we use that, otherwise we generate a default Year.

We set the session variable "the_year" every time we change the year. We never use this session variable in our application, only within the class to set our date back to what it was previously if we lose our state.

You may want to do some additional validation on the incoming year in the checkTheDate method, I limit my possible Year values in my form input, so the basic is_int test suffices in this case. If we want to set a new year, say to 2014, we would do it this way:

$newyear = 2014 ;
$Yearobj = TheYear::getYearInstance();
$currentYear = $Yearobj->checkTheYear($newyear);

To sum up, we use the static accessibility through out our system to get an instance of the object, we then use the get and set method to for our date.

As you can see, setting up a singleton class is not trivial. Is it worth the extra effort? I think so.

We end up with a class that will give us the current Year in one line anywhere in our application. All the functionality and any additional functionality you want to add is contained within the class, and not spread out and duplicated all over your system. And the year is protected from any inadvertent changes by another programmer, or even you, somewhere else in your code.

Comments are closed.