2

I've trying to learn PHP OOP and have made some research on how to make a global database class to use around in my project. From what I've seen the most appropriate pattern available is a singleton which would ensure that only one database connection are present at all times. However as this is my first time working with the Singleton pattern, I am not sure that I have made it right.

Is this a proper singleton? Will this code ensure one database connection only? Is there any way I can test this? (Learn a man to fish and he will have food for the rest of his life...)

I am using redbean as my ORM and here's how I set it up plainly:

require_once PLUGINPATH.'rb.php';
$redbean= R::setup("mysql:host=192.168.1.1;dbname=myDatabase",'username','password');

I've made the following script based upon this source, as my own singleton Database class;

class database {
    private $connection = null;

    private function __construct(){
        require_once PLUGINPATH.'rb.php';
        $this->connection = R::setup("mysql:host=192.168.1.1;dbname=myDatabase",'username','password');
    }

    public static function get() {
        static $db = null;

        if ( $db === null )
        $db = new database();

        return $db;
    }

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

Thanks!

Gordon
  • 312,688
  • 75
  • 539
  • 559
Industrial
  • 41,400
  • 69
  • 194
  • 289
  • Learn a man to search and etc. ;) possible duplicate of [Creating the Singleton design pattern in PHP5](http://stackoverflow.com/questions/203336/creating-the-singleton-design-pattern-in-php5) – bzlm Feb 10 '11 at 18:17
  • Hi - I think of myself as a decent Googler, but as I wasn't able to find anything on Singletons and in this case how to share the `Static` connection that redbean provides, I am not sure what to do and look forward to some help :) – Industrial Feb 10 '11 at 18:20
  • 1
    Just a tidbit: For singletons/factories, `new self()` is an interesting shorthand that makes it easier to change the class name later, should need for that arise at all. It's an almost negligible benefit, but I find it's worth knowing about the option, at least. – pinkgothic Feb 10 '11 at 18:29
  • 1
    @pinkgothic: [Beware when using the `self` keyword and inheritance](http://codepad.org/AdHFUEH6). You may be in for a surprise. – netcoder Feb 12 '11 at 05:06
  • @netcoder: I wouldn't consider that surprising, personally. If your example had returned SuperSingleton, that would have surprised me! :) The way it works is how it should. (That is, of course, opinion, and the very fact PHP introduced late static binding as an alternative to how PHP handles static aspects in general is a good indicator others disagree with me.) – pinkgothic Feb 14 '11 at 09:23

2 Answers2

6

The instance variable needs to be a static member of the class. I haven't tested this code, but it should work. Connection should probably be static too. With this code, you will have one instance of your database class, and one instance of the connection. It is possible to do this without connection being static, but making it static will ensure there is only one connection instance. I also changed the literal names of the class to php magic constants. This make your code more maintainable. If you change the name of your class down the road, you won't have to go and find all of the literal instances of the old class name in your code. This may seem like overkill now, but trust me, as you work on more and more complex projects, you will appreciate the value.

class database {
    private static $connection = null;
    private static $db;

    private function __construct(){
        require_once PLUGINPATH.'rb.php';
        self::$connection = R::setup("mysql:host=192.168.1.1;dbname=myDatabase",'username','password');
    }

    public static function get() {
        if ( !(self::$db instanceof __CLASS__) ) {
            $klass = __CLASS__; // have to set this to a var, cant use the constant with "new"
            self::$db = new $klass();
        }

        return self::$db;
        }

    public function connection() {
        return self::$connection;
    }
}
superultranova
  • 1,294
  • 8
  • 14
  • 1
    +1 for the `$klass` / `__CLASS__` thingy. Also, your answer is close to the OPs question than mine. – Linus Kleen Feb 10 '11 at 18:37
  • 1
    @Linus Kleen, thanks. I've had to do some horrific find and replace sessions in the past. – superultranova Feb 10 '11 at 18:40
  • You will need to use self::$connection to get the static $connection variable. I have changed the code. You need to use self:: to access all static variables. Using $this-> will error, as you have seen. – superultranova Feb 10 '11 at 18:47
  • Hi Casey - Thanks a lot for this. It looks awesome! As being sort of a newbie on Patterns and Singletons in PHP, would you mind telling me more about when to do what (`Normally you would make the connection a singleton and then have multiple instances of your database wrapper`)? Thanks – Industrial Feb 10 '11 at 18:47
  • 1
    @Industrial Actually, I think this is fine. I was a little mistaken when I said that. I thought that connection for was an object you created, I didn't read the code well enough. – superultranova Feb 10 '11 at 18:52
  • Hi again - I got confused after the last edit - should I still call the class by `database::get()->connection();`? Thanks! – Industrial Feb 10 '11 at 18:54
  • 2
    @industrial, the connection method will be called with an -> because the method is not static. As a rule in php, if you are accessing a variable or method that is static you need to use the scope resolution operator (::) whether you are inside or outside of a class. If you are accessing a non static class member or function you use the arrow operator (->). Hope that helps, if not, let me know and I can clarify further. – superultranova Feb 10 '11 at 19:04
  • Thanks a lot for all this Casey, really appreciated! You mentioned in your answer that making the `connection()` static would be the way to ensure that only one connection exist. Would I change the call to `database::get()::connection();` if I make the connection() static? – Industrial Feb 10 '11 at 19:10
  • @industrial, you're welcome. When I said that, I was referring to the $connection variable. The connection() method is not static, therefore the call would be database::get()->connection. Inside the connection() method, you will need to access the connection variable as self::connection, but the caller of the connection() method would use (->). If you were to make the connection() method static, then yes, the call would be database::get()::connection(). However, you don't need to make the connection() method static, it is good the way it is. – superultranova Feb 10 '11 at 19:28
  • Thanks a lot for your support Casey! One of the best answers on SO ever! – Industrial Feb 10 '11 at 19:34
  • all redbean methods are static,how can i instantiate it? – Thiago Dias Jul 22 '17 at 17:18
2

You're singleton is almost correct.

The private member (no pun intended) $connection needs to be static as well. You might go with the following too:

class database {
    private static $instance = NULL;
    protected $conn;

    private function __construct() {
        $this->conn = mysql_connect( ... );
    }

    public static function init() {
        if (NULL !== self::$instance) 
            throw new SingletonException();
        self::$instance = new database();
    }

    public static function get_handle() {
        if (NULL === self::$instance)
            ; // error handling here
        return self::$instance->conn;
    }
}
Linus Kleen
  • 33,871
  • 11
  • 91
  • 99
  • This is half correct. $connection should be made static, but connection() doesn't need to be static since it's intended to be used in the scope of the singleton instance, on in the scope of the class. i.e., he intends to call: `database::get()->connection()` – Matt Huggins Feb 10 '11 at 18:26