8

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?

Charles
  • 50,943
  • 13
  • 104
  • 142
dscher
  • 1,515
  • 2
  • 16
  • 23
  • You've answered your own question, your usage example is exactly how you'd do it. – Neel Mar 12 '10 at 20:13
  • If you plan to create some classes/functions, which could be used with multiple databases at the same time (e.g. working with 2+ databases at the same time) - then it's better to pass always `$dbConnect` to every class/function as parameter, but if you are sure you will use just single DB always - then you could simply `myDatabase::getInstance()` in every class/function when you need it – Laimoncijus Apr 07 '10 at 06:45
  • If you modify getInstance() to take a parameter specifying which connection you want (with an appropriate default) then you can get around the multiple database problem as well (so long as the function knows which database it needs to talk to). – El Yobo Apr 09 '10 at 09:22

4 Answers4

1

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
  • 10,770
  • 10
  • 45
  • 62
0

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:

Are global variables bad?

https://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: Are global variables in PHP considered bad practice? If so, why?

Community
  • 1
  • 1
zaf
  • 22,776
  • 12
  • 65
  • 95
  • 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 Apr 06 '10 at 19:10
  • 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 Apr 07 '10 at 06:36
  • 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 Apr 07 '10 at 06:53
  • @ChrisRamakers See my comment to your answer. He's asking something else. – zaf Apr 07 '10 at 08:24
  • @Laimoncijus True. But I did say be sensible :) – zaf Apr 07 '10 at 08:36
  • @Loimoncijus, thanks for your input. What you said makes a lot of sense. – dscher Apr 07 '10 at 13:57
  • 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. – selfawaresoup Apr 07 '10 at 14:51
  • @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 Apr 07 '10 at 15:31
0

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
  • 14,370
  • 16
  • 70
  • 107
  • 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 Apr 07 '10 at 08:22
  • @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 Apr 07 '10 at 13:56
  • 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 Apr 08 '10 at 14:36
0

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
  • 4,661
  • 1
  • 19
  • 16
  • 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 Apr 11 '10 at 14:57
  • 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_pattern So 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 Apr 12 '10 at 23:33
  • Ungh, sorry for the lack of carriage returns. You should be able to piece it together. – Jonathan Hanson Apr 12 '10 at 23:35