We help IT Professionals succeed at work.

How does this Singleton Class work?

Bruce Gust
Bruce Gust asked
on
95 Views
1 Endorsement
Last Modified: 2018-09-19
I've been able to successfully incorporate a Singleton class into my app and am now using that to establish my database connection.

But while it's working, I want to know WHY it works. Below is a breakdown of what I understand combined with some questions. If you could "fill in the gaps," that would be awesome!

First of all, I'm going to use the Scople Resolution Operator to call what I know to be a static method from the "DB" class tht is also protected...

$display_page=DB::query("SELECT * from employers where id=?", [$id]);

Open in new window


Here's the private static function that's beeing called:

private static function query($statement, $bindParams=NULL, $assoc=NULL)

Open in new window


 Because its private, the, __callStatic method is going to be called which looks like this:
 
 
public static function __callStatic($name, $args=NULL){
        if(!self::$conn){
            // connect here

			self::$conn = new PDO("mysql:dbname=adsf; host=adsf", "adsf", 'adsf');
			self::$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
			}
    
        return call_user_func_array('self::'.$name, $args);
    }

Open in new window

     
But here's where I'm getting confused.

If I understand things correctly, "$name, $args=NULL" is my query ("SELECT * from employers were id=?") and my [$id], correct?

...and then when you get to return call_user_func_array('self::'.$name, $args), 'self::'.$name is my $statement and the $args is my [$id'] value.

So how does $conn get included / implemented?

From what I understand, __calStatic is going to be called automatically when "private static function query" is called. I see how the "($name, $args=NULL)" dynamic places those two entities in a spot where they can be acted upon, but how does $conn get established?

Thanks!
Comment
Watch Question

CERTIFIED EXPERT
Most Valuable Expert 2018
Distinguished Expert 2019

Commented:
If you take a look at the __callStatic function, it has this at the start:

if(!self::$conn) {
    self::$conn = new PDO("mysql:dbname=adsf; host=adsf", "adsf", 'adsf');
    self::$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}

Open in new window

When the function is called, it checks whether $conn is true or false (has been set to a value or hasn't been set to a value). If it hasn't been set, then it sets it by creating a new PDO connection. Now the next time the callStatic is called, it will skip this creation because !self::$conn will return false, having already been set. It then goes on to run the function defined in the $name argument.

With regard to the args, you're slightly off. When you call:

DB::query("SELECT * from employers where id=?", [$id]);

This becomes:

$name = "query";
$args = array(
    "SELECT * from employers where id=?",
    array( $id );
);

So when it then calls call_user_func_array('self::'.$name, $args);

what it's actually doing is calling

self::query($args);

Which in turn is run as

self::query("SELECT * from employers where id=?);", [$id]);

And I'm guessing that the query() function will then use the static self::$conn that was set when the callStatic function was run for the first time
Bruce GustPHP Developer

Author

Commented:
Chris!

This is exceptional!

Here's the complete "database.php" file. This is the Singleton Class that's calling my database. Take a look at lines #16 and #13 which will lead to the final question I've got at the bottom of this post:

class DB {
   
   private static $conn; //here's your $conn property

    public static function __callStatic($name, $args=NULL){
        if(!self::$conn){
            // connect here

			self::$conn = new PDO("mysql:dbname=test; host=localhost", "test", 'test);
			self::$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
			}
    
        return call_user_func_array('self::'.$name, $args); /here is where you included some inspired commentary, but this is where I have my final question. Please refer to bottom of my post...
    }
	
	private static function query($statement, $bindParams=NULL, $assoc=NULL) //this is what's being initially called by the user. Because it's a private funcdtion, the "__callStatic" is going to be automatically triggered
	{
		if(isset($bindParams))
		{
			$sql=self::$conn->prepare("$statement");
			$sql->execute($bindParams);
		}
		else
		{
			$sql=self::$conn->prepare("$statement");
			$sql->execute();
		}
		
		$count=$sql->rowCount();
		
		if($count>0)
		{
			if($assoc==NULL)
			{
				$result=[];
				while($row=$sql->fetch(PDO::FETCH_ASSOC))
				{
					$result[]=$row;
				}
				return $result;
			}
			else
			{
				$row=$sql->fetch(PDO::FETCH_OBJ);
				return $row->$assoc;
			}
		}
		else
		{
			return 0;
		}	
	}
}

Open in new window


Bottom line: Where is the query actually being executed?

The "__callStatic" is instantiating the database connection. I get that. Thanks to your wonderful commentary, I understand the structure of the "return call_user_func_array('self::'.$name, $args);" dynamic. But what is actually being returned?

The reason I'm hesitant is because if you look at lines #20-#21, you have something that almost seems a little redundant. I know I'm "off," but if the final result of "return call_user_func_array" is..

self::query("SELECT * from employers where id=?);", [$id]);

How does that "fit" with...

$sql=self::$conn->prepare("$statement");
$sql->execute($bindParams);

Thanks!
CERTIFIED EXPERT
Most Valuable Expert 2018
Distinguished Expert 2019
Commented:
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION
Bruce GustPHP Developer

Author

Commented:
Chris, it's not lost on me that your explanation could've been far less detailed, but you took the time to explain both the "what" and the "why" and that's how I'm wired, friend. Thank you very much!

The $20,000.00 comment that you made (and I'm explaining this back to you) is, "...it will return whatever the function you're calling returns." That was the "thing" that allowed of this to make sense.

In short, the "call_user_fun_array" is not returning a mere database connection. Rather, it is absorbing the entire mechanical workings of the "query" method from lines #16-#51 and publishing the result. The "query" method is DOA because it being private. But the "__callStatic,' being present, is automatically invoked and because it includes the needed database connection, the sql as well as all of the parameters that are being passed into the "__callStatic" method can be processed as a legitimate database interaction and although the user is instantiating the "query" method, the actual data that's being returned is from the "__callStatic" method that's operating behind the curtain.

Correct?

Thanks again!
CERTIFIED EXPERT
Most Valuable Expert 2018
Distinguished Expert 2019

Commented:
Hey Bruce,

Yep - that's about the size of it :)

Glad it all made sense - it was quite a long post and a lot to take in !!
Unlock the solution to this question.
Join our community and discover your potential

Experts Exchange is the only place where you can interact directly with leading experts in the technology field. Become a member today and access the collective knowledge of thousands of technology experts.

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.