A PHP Singleton Flip-Flop, TRUE or FALSE

flip-flop2-225When I was learning computer internals, the hardware logic component that was the foundation of the computer was the flip-flop. The flip-flop was an electrical circuit that was either a one or zero. That usually translated to +9 volts or 0 Volts. In computer software, we call this a BOOLEAN, they're either set to TRUE or FALSE. After all, we do live in a digital, thus binary world. Let's have some fun with a couple of puzzles that deal with a BOOLEAN, true or false.

The other day, I was happily coding away, and ran into a little conundrum. I had created an event that fired globally after every save in my web application. The event allowed me to write additional code to alter some values depending on where in the application the user had saved his form, and then re-save the changed values back to the database. Everything went along swimmingly until I saved in my added code. Whereupon I looped around several hundred times, and hung my system, a good reason, to always do your coding on your local computer. Yep, you guessed it, my save in my additional code, triggered the global event that happened on every save, which triggered the save in my added code, and around and around we went until we stopped only where Apache knows.

Hmm, what to do to stop the loop? This is the pregnant pause that allows you to come up with a solution until I tell you mine.

Got it, well in pseudocode speak, here's how to stop the looping.

The event triggers on a user's save.
You set a "$flag" variable to TRUE initially to tell the additional code to run the first time.
You check to see if $flag is set to TRUE, if so run the added code.
Right before you save in your added code, you set the $flag variable to FALSE.
When you save your additional code, it fires the global save trigger.
You go back to your additional code, and this time the $flag variable is set to FALSE
You set $flag variable to TRUE, and return out of the added code without running it.

Simple! Not so fast. The $flag variable is a simple BOOLEAN, it's either TRUE of FALSE. How do we save our "$flag" variable on our system? The second pregnant pause for you to come up with a solution.

OK, let's go through our options.

1. We save the $flag variable locally as a private property of our class. Seems like it would work, but the problem here is initializing the variable, since every time you run an instance of the class you reinitialize the variable. Won't work, because if you go to FALSE, it will be reset to TRUE when re-initialized. Hmm...

2. We make a GLOBAL variable. This works, except every time I hear GLOBAL, I cringe. GLOBAL variables are vulnerable to any other coder who changes the variable to something else, or names a variable the same in their code, they are not exclusive. One of the reasons that "namespace" came into being. Since I work with a bunch of developers, who all work on the same application, and the application runs on a government, highly secure computer, I shy away from the GLOBAL variable. I do not have exclusive control of a GLOBAL. Let's find something else.

3. A $_SESSION variable. This works. My problem here is the flaky way new versions of browsers are handling variables created locally, and on a government web site, your not sure how long session variables are maintained, or how they are treated. It works, but less than ideal.

4. Use a Singleton. This works, and is the solution I choose, and will cover in the rest of this article. The singleton has the advantage in that it is accessible globally, but you need to call the class to get the private variable, and only one instance of the variable can exist, not multiple values that might occur with GLOBAL variables.

The reason for this article is to give you, my readers, a template for a TRUE or FALSE, flip-flop, singleton you can use in your own code anytime you need a BOOLEAN global variable. Here's the code:

/**
 * A singleton used as a secure BOOLEAN global variable
 * 
 * @param BOOL $flag  can only be TRUE or FALSE
 */
class saveFlagSingleton 
{
   private $flag; 
   private $defaultFlag = TRUE;
   private static $FlagObject = NULL;  // stores the instance of the object that stores $flag

// Only initiate the object within this class, "new" will not generate an instance outside this class
private function __construct() {}

// Prevent any copying or cloning of object
private function __clone() {}

// One instance only, generate the object to hold the variable
public static function getFlagObject()
{
   // Does the object already exist? If not make a new instance of the class
   if (empty(self::$FlagObject ))
   {
       self::$FlagObject = new saveFlagSingleton();
   }
   $FlagObj = self::$FlagObject;
   return $FlagObj;
} 

// Get current $flag setting
public function getFlag() 
{
   // if the Flag is empty, assign the default value
   if (empty($this->flag))
   {
      $this->flag = $this->defaultFlag; 
   }
   $theFlag = $this->flag;
   return $theFlag;
} 

// Set the current Flag
public function setFlag($setFlag)
{
   $this->flag = $setFlag;
}
} // End class

Only 47 lines of code with comments, and once you have this template, you can drop it in a file, and then call it when ever you need a global BOOLEAN.

Let go through the code, real quick. The variables are: $flag, a property of the object, which is where the current state of the TRUE or FALSE will reside. $flag is a private variable, so you have to use a method to access it, in the one, and only one, instance of the saveFlagSingleton class. The one instance of the class is called $FlagObject. There is a $defaultFlag variable used as a default value to initialize $flag.

OK, we have the singleton set up that we can drop into our code, how do you use it?
In your code, where you want to use a global TRUE or FALSE, first you have to include the class, like so:

require_once "saveflagsingleton.php";

We start through our code, and want to see if we will run our added code, or not, like so:

// This stops you from running the added code if $flag is set to FALSE
// Get the current flag state
$flag = saveFlagSingleton::getFlagObject()->getFlag();

if ( $flag === FALSE )
{
   $flag = TRUE;
   // Set the current Flag state back to TRUE for the next run
   saveFlagSingleton::getFlagObject()->setFlag($flag);
   // Return to bypass the added code
   return;
}

Now at the bottom of your added code, right before the added code save, we want to set the Flag back to FALSE so when the global event fires and starts through the added code again it will encounter a FALSE and won't run. Here goes:

// Stop global event from firing again 
$flag = FALSE ;
saveFlagSingleton::getFlagObject()->setFlag($flag);

// Now do the added code save

Well, as Bugs Bunny would say, "That's All Folks," or was that Elmer Fudd? Happy coding!

elmer

 

Comments are closed.