views:

340

answers:

4

I have a singleton db connection which I get with:

$dbConnect = myDatabase::getInstance();

which is easy enough. My question is what is the least rhetorical and legitimate way of using this connection in functions and classes? It seems silly to have to declare the variable global, pass it into every single function, and/or recreate this variable within every function. Is there another answer for this?

Obviously I'm a noob and I can work my way around this problem 10 different ways, none of which is really attractive to me. It would be a lot easier if I could have that $dbConnect variable accessible in any function without needing to declare it global or pass it in. I do know I can add the variable to the $_SERVER array...is there something wrong with doing this? It seems somewhat inappropriate to me.

Another quick question: Is it bad practice to do this:

$result = myDatabase::getInstance()->query($query);

from directly within a function?

A: 

I use the superglobal $GLOBALS variable to hold my database connection. Works wonders.

Has all the benefits you mention and there is absolutely nothing wrong or inappropriate about it as long as you are sensible and use a unique key for the hash array and only one entry function for setting up the database connection and storing it in the array.

I'll put my ear plugs on now, ready for all the moaning about using global variables!

@Downvoters - some bedtime reading:

http://stackoverflow.com/questions/484635/are-global-variables-bad

http://stackoverflow.com/questions/357187/global-variables-when-are-they-acceptable

Some quotes for your amusement:

"Global variables are used all the time, by many, many programmers. We just call them Singletons now." - DannySmurf

"They are totally, fundamentally, absolutely, incredibly, astoundingly evil." - Vinko Vrsalovic

And the best answer here from cletus himself: http://stackoverflow.com/questions/1557787/are-global-variables-in-php-considered-bad-practice

zaf
I am fairly new to PHP so I can't comment but I am looking forward to hearing what others might think about that idea. Thanks for piping in.
dscher
Unfortunately Globals are considered a bad thing and bad practice, there are way more secure, clean and better ways to solve this, Singleton is among them.
ChrisR
if you save your DB object in `$GLOBALS` (let say you save it in `$GLOBALS['DB']`), there is a risk that you can always destroy it with simple: `$GLOBALS['DB'] = null;` or `unset($GLOBALS['DB']);` and then you have no DB connection... Where using `myDatabase::getInstance()` makes you more safe that it is always there
Laimoncijus
@ChrisRamakers See my comment to your answer. He's asking something else.
zaf
@Laimoncijus True. But I did say be sensible :)
zaf
@zaf, uhh sorry, missed the word "sensible" while reading :)
Laimoncijus
@Loimoncijus, thanks for your input. What you said makes a lot of sense.
dscher
Using global variables just leads to messy code. This may not be obvious in small applications like a simple blog or forum but it will haunt you in large projects.Just don't use globals. There is literally NO situation where global variables are needed in PHP5.
Techpriester
@Techpriester The OP has a situation and the only solution is the one I've given. Using the $GLOBALS is not exactly like using global variables. It's not like 'global $myvar'. And your statement about "NO situation where global variables are needed" - then what are $_GET, $_POST, $_SERVER etc etc.?
zaf
A: 

There is nothing wrong with singleton use throughout your application, there are people who are against the Singleton pattern for database abastraction classes because it could prove difficult in the future to work with multiple connections. But in that case the Registry pattern, which shows several similarities to Singleton, should work as well.

I'd say you're on the right track!

ChrisR
That was not his question. He's not saying if he should use singletons (he is), he wants to know how to pass the reference to this object around his code.
zaf
@zaf, thanks for clarifying. @chris, that's correct...I'm trying to figure out what is the most lazy, safest, and least repetitive way to handle handing out that db connection. I know I could instantiate it at the bottom of the db class's file and inject it into my other classes. Just wondering if there's another way that's not as frowned upon as GLOBALS to do the same thing. Thanks for the input.
dscher
Well if you want to stay away from Globals (which i can't blame you for) and still want class instances to be available throughout your application you might want to look at the Registry pattern
ChrisR
A: 

I've dealt with this quite a bit over the years. I would say that adding a Singleton-derived object to a superglobal variable like $_GLOBAL or $_SERVER is removing a lot of the point of building a Singleton class in the first place.

The best practice as I see it is to access it just like you suggested:

$result = Database_Class::singleton()->query('whatever');

The only thing I would add is that in the case of some local scope (like a function or method) where you want to use the Singleton object multiple times, it might be worth doing something like:

function func() {
  $db = Database_Class::singleton();
  $db->query('insert into whereever');
  $db->query('insert into whereever');
  $db->query('insert into whereever');
  $db->query('insert into whereever');
}
Jonathan Hanson
Why would you recommend re-instantiating the connection as opposed to just using one already available such as $this->db->query if you've instantiated the object in the class constructor. Is there a speed issue?
dscher
Singleton classes do not re-instantiate the object when the singleton method is called. The singleton method always returns the same object (as opposed to a new object of the same class).http://en.wikipedia.org/wiki/Singleton_patternSo for instance:class MyClass { function singleton() { static $cached_object = null; if( is_null($cached_object) ) { $cached_object = new MyClass(); } return $cached_object; }}Then:$object = MyClass::singleton(); // Makes and returns a new object$object = MyClass::singleton(); // Returns the same cached object
Jonathan Hanson
Ungh, sorry for the lack of carriage returns. You should be able to piece it together.
Jonathan Hanson
+1  A: 

Some of this will come down to taste. At work we also obtain an ADODB connection handle via

$db = Registry :: getDB();

Likewise you can shorthand some of this code by using method chaining:

$name = Registry :: getDB()->getOne(
    "SELECT name FROM user WHERE user_id = ?", $userId
);

Using a registry means your calling code has a concrete dependency on your Registry class. This makes testing your code difficult (personally this is the only time I've banged my head on the wall using a Registry).

If you want to add unit tests for your code, you'd often want to mock your database connection so you don't manipulate the database each time you run your test suite.

You can still get round this by examining your running environment and configuring your registry with a mock database abstraction class but it's not as elegant as dependency injection (DI).

The best explanation of DI and containers (for a PHP developer) I've seen are Fabien Potencier's Dependency Injection with PHP 5.3 (starting on slide 9).

You pass a DI container around in place of a Registry, it provides an elegant and easy way to obtain handles to dependencies (like the Registry) but is flexible and more loosely coupled so you can mock those dependencies when needed.

Greg K
Thanks a lot Greg. Great information.
dscher